Aptana Journal #1: Menambahkan Content Assistant jQuery


Melanjutkan dari artikel tentang Mengubah Kode Program Aptana Studio 3, kali ini saya ingin melakukan sedikit perubahan.   Pada saat memakai Aptana Studio 3 untuk meng-edit kode program jQuery, saya menemukan bahwa content assist tidak berfungsi sebagaimana harusnya.  Sama sekali tidak ada bantuan untuk fungsi-fungsi di class JQuery, seperti yang terlihat pada gambar berikut ini:

Function jQuery Tidak Ditampilkan Oleh Aptana

Function jQuery Tidak Ditampilkan Oleh Aptana

Sementara itu, pada file JavaScript (js), kadang-kadang content asisst untuk jQuery dapat ditampilkan dengan baik dan kadang-kadang tidak sama sekali.

Mengapa demikian?  Untuk menjawab pertanyaan ini, saya perlu membedah isi file JSContentAssistProcessor.java yang ada di plugin com.aptana.editor.js.   Saya dapat menampilkan class ini secara cepat dengan menekan tombol Ctrl+Shift+R di Eclipse RCP dan mengetikkan namanya.  Method yang menjadi pusat perhatian adalah doComputeCompletionProposal(). Berikut ini adalah cuplikan dari isi method tersebut:

protected ICompletionProposal[] doComputeCompletionProposal(ITextViewer viewer, int offset, char activationChar, boolean autoActivated) {
  ... // diabaikan
  LocationType location = getLocationType(document, offset);
  switch (location) {
    case IN_PROPERTY_NAME:
       addProperties(result, offset);
       break;

    case IN_VARIABLE_NAME:
    case IN_GLOBAL:
    case IN_CONSTRUCTOR:
       addKeywords(result, offset);
       addCoreGlobals(result, offset);
       addProjectGlobals(result, offset);
       addSymbolsInScope(result, offset);
       break;
    ... // diabaikan
  }
  ... // diabaikan
}

Agar method pada object JQuery ditampilkan, Aptana Studio harus mengerjakan method addProperties(). Atau dengan kata lain, getLocationType() harus mengembalikan IN_PROPERTY_NAME. Tapi ternyata yang terjadi adalah nilai IN_GLBOAL yang dikembalikan.

Wow, kenapa bisa begitu?  Untuk menjawab pertanyaan ini, saya perlu menelusuri lagi method getLocationType(). Salah satu method yang dipanggil olehnya adalah method getActiveASTNode(). Dari namanya, method ini berfungsi untuk mengembalikan Abstract Syntaxt Tree (AST). Berikut ini adalah cuplikan dari method tersebut:

IParseNode getActiveASTNode(int offset)
{
  IParseNode result = null;
  try 
  {
    ... // diabaikan
  } 
  catch (Exception e)
  {
    // ignore parse error exception since the user will get markers and/or entriesi n Problems View
  }
  return result;
}

Ok, mulai terlihat ada titik terang.  Saya melihat bahwa jika ada kesalahan parsing, maka kesalahan tersebut akan diabaikan!  Dengan perasaan ingin tahu, saya menambahkan e.printStackTrace() pada bagian catch. Ternyata benar! Ada kesalahan parsing yang terjadi padahal syntax JavaScript saya adalah syntax yang valid dan benar.

Mengapa bisa demikian?  Untuk melakukan parsing, Aptana Studio akan memanggil class JSParser.  Dan perlu diketahui bahwa saya sedang menyertakan JavaScript di dalam HTML!  Karena dokumen saya adalah campuran dari HTML dan JavaScript, tentu saja JSParser yang hanya mengola JavaScript akan protes keras.

Lalu apa saya harus mengubah isi file JSParser.java?  Tidak!  Tidak secara langsung, karena file ini dihasilkan oleh Beaver, sebuah parser generator.   Aptana Studio juga memakai JFlex sebagai scanner generator.

Apa itu scanner?  Agar komputer bisa mengerti kode program yang diketik, maka ia perlu memecah kode program tersebut ke dalam token (ibaratkan dengan “kata” dalam dunia manusia).  Bagian kode program yang memiliki tugas seperti ini disebut sebagai scanner.

Apa itu parser?  Token-token yang terpisah hasil dari scanner perlu dikelompokkan berdasarkan aturan yang berlaku sehingga bisa dimengerti komputer (ibaratkan dengan “kalimat” yang dirangkai dari “kata” dalam dunia manusia).  Bagian kode program yang memiliki tugas seperti ini disebut sebagai parser.

Lalu apa itu scanner generator dan parser generator?  Setahun setelah saya lulus kuliah, saya membantu adik kelas untuk membuat sebuah bahasa pemograman beserta compiler-nya.  Saat itu kami harus membuat sendiri scanner dan parser secara manual.  Ternyata, scanner dan parser dapat dihasilkan secara otomatis dimana kita hanya perlu memberi tahu aturan token & syntax yang berlaku.  Scanner generator seperti JFlex akan menghasilkan file Java berdasarkan aturan di file *.flex.  Parser generator seperti Beaver akan menghasilkan file Java berdasarkan aturan di file *.grammar.  Kenapa tidak buat sendiri saja dari awal?  Selain alasan waktu, juga algoritma yang kita pakai belum tentu lebih baik dari yang sudah ada.

Saya dapat menemukan file-file yang berhubungan generator di folder parsing seperti yang terlihat pada gambar berikut ini:

Isi Folder Parsing

Isi Folder Parsing

Karena saya ingin membuang HTML pada saat melakukan parsing JavaScript, tanpa mengubah struktur bahasa JavaScript, maka saya cukup melakukan perubahan pada JFlex di file JS.flex.  Perubahan yang saya lakukan bersifat naif tanpa memperhatikan aspek lain dan hanya untuk keperluan pribadi.  Saya tidak tahu apakah ini berguna bagi orang lain dan saya tidak ada kaitannya dengan tim pengembang Aptana Studio (karena ini software open-source, saya pikir, saya bebas mengubahnya untuk keperluan pribadi).

Untuk mempermudah melihat kesalahan selama bermain-main dengan JFlex, saya menambahkan option %debug di file JS.flex.  Selain itu, saya menambahkan definisi macro seperti berikut ini:

ScriptOpen = "<script" ("type=" \.*)? ~">"
ScriptClose = "</script>"
HTMLTag = "<html>"

Macro di JFlex pada dasarnya berisi regular expression yang dicocokkan dengan input (sehingga bisa mewakili sebuah token).

Berikutnya saya juga menambahkan state baru yaitu HTML sehingga terlihat seperti berikut ini:

%state DIVISION, REGEX, HTML

State YYINITIAL adalah state yang selalu ada dan akan aktif pertama kali.   Saya menambahkan dua buah lexical rule di YYINITIAL sehingga bila ditemukan tag HTML atau setelah tag </script>, akan beralih ke state HTML.  Isinya akan terlihat seperti berikut ini:

<YYINITIAL> {

  {HTMLTag}  { yybegin(HTML); }

  {ScriptClose}  { yybegin(HTML); }

  ... // diabaikan

}

Setelah itu, pada state HTML, saya akan beralih ke state YYINITIAL (melakukan parsing JavaScript secara normal) bila ditemukan tag <script> pembuka.  Isi lexical rule untuk state HTML akan terlihat seperti berikut ini:

<HTML> {
  {ScriptOpen}  { yybegin(YYINITIAL); }
  [\"']\\?|.  { /* ignore in HTML */ }
}

Langkah berikutnya setelah melakukan perubahan file JS.flex adalah menghasilkan sebuah class scanner berdasarkan aturan yang baru.  Class yang dimaksud adalah JSFlexScanner.java.  Class ini akan dihasilkan secara otomatis oleh JFlex.  Caranya adalah dengan men-klik kanan pada file build.js.xml, memilih Run As,  Ant Build.

Setelah proses build selesai,  saya perlu men-klik kanan package com.aptana.js.core.parsing dan memilih refresh seperti yang terlihat pada gambar berikut ini:

Class yang dihasilkan oleh scanner & parser generator

Class yang dihasilkan oleh scanner & parser generator

Bila terjadi kesalahan pada file JSParser.java, saya perlu membuka file tersebut, kemudian menekan tombol Ctrl+Shift+O untuk memperbaharui import.  Selain itu, build.js.xml juga mengandung salah ketik sehingga menghasilkan package di lokasi baru (saya tidak memeriksa apakah versi terbaru sudah diperbaiki oleh tim developernya).

Langkah terakhir, saya membuka file ParseUtil.java di package com.aptana.editor.js.contentassist.  Pada method getParentObjectTypes(), terdapat komentar FIXME: (hopefully temporary) hack to fixup static properties on $ and jQuery. Saya menghilangkan bagian tersebut dan hanya menyisakan result.add(type);.

Sekarang, bila saya menjalankan proyek ini, saya akan menemukan bahwa content assist untuk jQuery telah bekerja seperti yang terlihat di gambar berikut ini:

Content Assist Untuk jQuery Yang Sudah Bekerja

Content Assist Untuk jQuery Yang Sudah Bekerja

Perlu diperhatikan bahwa saya perlu menyertakan sebuah file source  jQuery seperti file jquery-1.8.2.js di proyek (letaknya boleh dimana saja).  Aptana Studio akan men-parsing file ini dan memberikan item content assist berdasarkan hasil parsing tersebut.  Hal ini berbeda dengan cara yang ditempuh di Adobe Dreamweaver CS5.5, dimana hint untuk jQuery bersifat ‘statis‘ sehingga untuk menyesuaikan dengan jQuery versi baru, saya perlu meng-update Dreamweaver.

Tentang Solid Snake
I'm nothing...