Aptana Journal #2: Menampilkan Info Untuk Content Assist


Pada Aptana Journal #1, saya berusaha untuk menampilkan content assist jQuery di Aptana Studio. Walaupun content assist sudah berhasil muncul, tetapi belum ada context info yang ditampilkan. Padahal, bagi seorang pemula seperti saya, memiliki deskripsi teks menjelaskan fungsi dan parameter jQuery akan sangat membantu. Oleh sebab itu, kali ini saya akan mencoba menampilkan context info untuk content assist yang sedang terpilih.

Permasalahan awal yang saya hadapi adalah bagaimana memperoleh teks yang menjelaskan setiap fungsi jQuery yang ada? Pada bahasa pemograman Java, terdapat Javadoc yang dipakai untuk mendokumentasikan kode program sehingga IDE cukup menampilkan teks informasi berdasarkan Javadoc. Lalu bagaimana dengan JavaScript? Tidak ada sebuah metode dokumentasi standar di JavaScript! Beruntungnya, Aptana Studio 3 mendukung beberapa bentuk dokumentasi JavaScript, salah satunya adalah ScriptDoc XML (file *.sdocml).  jQuery secara resmi tidak menyediakan dokumentasi ScriptDoc XML, tetapi ada beberapa situs yang menyediakan dokumentasi jQuery buatan mereka. Sebagai bahan ‘percobaan‘, saya akan membuat sendiri sebuah file ScriptDoc XML dengan mama jQuery-1.8.2.sdocml yang  isinya seperti berikut ini:

<?xml version="1.0"?>
<!-- Aptana Studio support for the jQuery 1.8.2 JavaScript Libary -->
<javascript>
	<aliases>
		<alias name="$" type="jQuery" />
	</aliases>
	<class type="jQuery">
		<constructors>

			<constructor scope="instance">
				<description>Accepts a string containing a CSS selector which is then used to match a set of elements.</description>
				<parameters>
					<parameter name="selector" usage="required" type="String">
						<description>A string containing a selector expression</description>
					</parameter>
					<parameter name="context" usage="optional" type="Element,Document,jQuery">
						<description>A DOM Element, Document, or jQuery to use as context</description>
					</parameter>
				</parameters>
				<return-types>
					<return-type type="jQuery" />
				</return-types>
				<examples>
					<example>Find all div elements within an XML document from an Ajax reponse.
						<pre>
						$("div", xml.responseXML);
						</pre></example>
				</examples>
			</constructor>

			<constructor scope="instance">
				<description>Accepts a string containing a CSS selector which is then used to match a set of elements.</description>
				<parameters>
					<parameter name="selector" usage="required" type="String">
						<description>A string containing a selector expression</description>
					</parameter>
				</parameters>
				<return-types>
					<return-type type="jQuery" />
				</return-types>
				<examples>
					<example>Find all div elements.
						<pre>
						$("div");
						</pre></example>
				</examples>
			</constructor>

		</constructors>
	</class>
</javascript>

Isi file di atas diambil dari dokumentasi resmi jQuery di http://api.jquery.com.  Isinya masih jauh dari lengkap karena baru berisi dokumentasi dua variasi contructor jQuery! Agar Aptana Studio 3 menampilkan context info, saya perlu menambahkan file sdocml tersebut ke dalam proyek seperti yang terlihat pada gambar berikut ini:

Menyertakan ScriptDoc XML ke Proyek

Menyertakan ScriptDoc XML ke Proyek

Setelah memenuhi persyaratan yang ada, saya pun mencoba membuka content assist untuk melihat hasilnya. Saya sedikit kecewa karena hasil yang saya temukan adalah kejanggalan yang terlihat pada gambar berikut ini:

Tidak semua dokumentasi constructor ditampilkan

Tidak semua dokumentasi constructor ditampilkan

Padahal jelas-jelas saya menyertakan dua variasi constructor jQuery, tetapi kenapa Aptana Studio hanya menampilkan satu saja? Pertanyaan ini tiba-tiba mengingatkan saya pada fakta bahwa JavaScript tidak mendukung polymorphism (secara syntax)!   Dengan kata lain, bila ada lebih dari satu method dengan nama yang sama, tetap hanya satu method yang dikenali!

Lalu kenapa di dokumentasi jQuery ada method polymorphism, termasuk di constructor? Karena di satu method yang sama, kode program jQuery akan mencocokkan tipe parameter yang diberikan dan akan melakukan hal yang berbeda sesuai dengan tipe parameter tersebut, sehingga seolah-olah telah terjadi polymorphism.   Yup! Ini memang adalah bentuk polymorphism secara manual karena JavaScript tidak memiliki syntax yang ketat untuk ‘berjaga-jaga‘.  Bila bahasa pemograman tidak sanggup menerapkan integritas, bukankah alangkah baiknya lebih baik bila IDE bisa membantu sehingga pengguna bahasa pemograman bisa terhindar dari kesalahan?

Saya segera mencari class apa yang  bertanggung jawab untuk mengembalikan daftar method JavaScript.  Pencarian saya berakhir di  file com.aptana.editor.js/src/com/aptana/editor/js/contentassist/JSContentAssistProcessor.java di method getFunctionElement().  Method ini  hanya mengembalikan sebuah nilai FunctionElement.   Saya akan membuat sebuah method baru dengan isi yang tidak jauh berbeda, tetapi method baru akan mengembalikan nilai List<FunctionElement> (bisa lebih dari satu FunctionElement).    Berikut ini adalah isi method baru tersebut:

/**
 * Mendukung lebih dari satu <code>FunctionElement</code>.  Hal ini bisa berguna bila diterapkan pada
 * SDocML, tetapi tidak pada JavaScript karena JavaScript tidak mendukung polymorphism.
 * 
 */
private List<FunctionElement> getFunctionElementList(ITextViewer viewer, int offset) {
    JSArgumentsNode node = getArgumentsNode(offset);
    List<FunctionElement> listReturn = new ArrayList<FunctionElement>();

    // process arguments node as long as we're not to the left of the opening parenthesis
    if (node != null)
    {
        // save current replace range. A bit hacky but better than adding a flag into getLocation's signature
        IRange range = replaceRange;

        // grab the content assist location type for the symbol before the arguments list
        int functionOffset = node.getStartingOffset();
        //LocationType locationAndOffset = getLocationType(viewer.getDocument(), functionOffset);
        LocationType location = getLocationType(viewer.getDocument(), functionOffset);

        // restore replace range
        replaceRange = range;

        // init type and method names
        String typeName = null;
        String methodName = null;

        switch (location)
        {
            case IN_VARIABLE_NAME:
            {
                typeName = JSTypeUtil.getGlobalType(getProject(), getFilename());
                methodName = node.getParent().getFirstChild().getText();
                break;
            }

            case IN_PROPERTY_NAME:
            {
                JSGetPropertyNode propertyNode = ParseUtil.getGetPropertyNode(node,
                        ((JSNode) node).getContainingStatementNode());
                List<String> types = getParentObjectTypes(propertyNode, offset);

                if (types.size() > 0)
                {
                    typeName = types.get(0);
                    methodName = propertyNode.getLastChild().getText();
                }
                break;
            }

            default:
                break;
        }

        if (typeName != null && methodName != null)
        {
            Collection<PropertyElement> properties = indexHelper.getTypeMembers(getIndex(), typeName, methodName);

            if (properties != null)
            {
                for (PropertyElement property : properties)
                {
                    if (property instanceof FunctionElement)
                    {
                        FunctionElement currentFunction = (FunctionElement) property;

                        // Periksa apakah function dengan nama parameter yang sama sudah ada dalam list?
                        boolean sudahAda = false;
                        boolean ketemu = false;
                        for (int i=0; i<listReturn.size(); i++) {
                            if (currentFunction.getParameterNames().containsAll(listReturn.get(i).getParameterNames()) &&
                                currentFunction.getParameterNames().size()==listReturn.get(i).getParameterNames().size()) {
                                ketemu = true;
                                // jika sudah ada function dengan parameter yang sama dalam list									
                                if (currentFunction.getDescription().trim().length()>0) {
                                    listReturn.set(i, currentFunction);
                                    sudahAda = true;										
                                    break;
                                }
                            }
                        }

                        // Jika function belum ada, maka tambahkan ke dalam list.
                        if (!sudahAda && !ketemu) {
                            listReturn.add(currentFunction);
                        }
                    }
                }
            }
        }
    }

    return listReturn;
}

Setelah itu, saya mengubah setiap pemanggilan ke method getFunctionElement() menjadi getFunctionElementList(). Untung saja tidak banyak yang berubah karena FunctionElement tunggal yang dikembalikan juga dimasukkan ke List pada akhirnya.

Sekarang, bila saya mencoba content assist, polymorphism di dokumentasi akan ditampilkan dengan baik seperti yang terlihat pada gambar berikut ini:

Content Assist Yang Mendukung Polymorphism

Content Assist Yang Mendukung Polymorphism

Bila saya memilih salah satu pilihan yang ada, maka context info akan ditampilkan dengan baik seperti yang terlihat pada gambar berikut ini:

Tampilan Dokumentasi jQuery Saat Content Assist Dipilih

Tampilan Dokumentasi jQuery Saat Content Assist Dipilih

Perihal Solid Snake
I'm nothing...

2 Responses to Aptana Journal #2: Menampilkan Info Untuk Content Assist

  1. Ping-balik: Aptana Journal #4: Meningkatkan Content Assist « The Solid Snake

  2. Ping-balik: Aptana Journal #5: Perbaikan « The Solid Snake

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: