| <!-- |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| |
| --> |
| <html> |
| <body> |
| <h2>GSF Registration</h2> |
| <p> |
| <blockquote style="background-color: #ffdddd; color: black; padding: 20px; border: solid 1px black"> |
| WARNING: Registration is one of the rough spots in GSF |
| which will need to be changed. This section describes the current |
| way to do it, which is definitely not a great solution. |
| </blockquote> |
| </p> |
| <p> |
| GSF is based on mime types. You register mime services for a particular mime type, |
| such as <code>text/x-ruby</code> or <code>text/javascript</code>. When you are |
| adding a new language, you should go and see if there is a common mime type |
| that is associated with your language, and if so use it. It doesn't really matter |
| what you choose, since GSF doesn't interpret it in any way, but by picking something |
| standard, other modules have a chance of using your editor services. For example, |
| in the property sheet code, used by the Visual Web Pack, some properties are associated |
| with JavaScript onclick handlers. This code just creates a <code>JEditorPane</code> |
| and sets the mime type to <code>text/javascript</code>. "Magically" this makes |
| syntax highlighting etc. in these customizers for JavaScript attributes work, |
| since GSF has registered editing services for this mimetype. |
| </p> |
| <p> |
| The first think you'll want to do is make sure there is a mime resolver for files |
| of the type you're trying to edit. This is described in detail in the |
| <a href="mime-resolver.html">Mime Resolver</a> document. There is nothing GSF |
| specific about this, but if you're trying to start writing a language support, |
| you'll want to begin there. Registering a mime resolver will tell the IDE how |
| to figure out the mime type of a file, but it doesn't create NetBeans DataLoaders |
| or DataObjects for these files. GSF does that, as soon as you register the mime |
| type with GSF. |
| </p> |
| <h3>Layer Registration</h3> |
| <p> |
| To register your mime type with GSF, you'll want to modify your layer as follows: |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| <folder name="GsfPlugins"> |
| <folder name="text"> |
| <folder name="javascript"> |
| <file name="language.instance"> |
| <attr name="instanceClass" stringvalue="org.netbeans.modules.javascript.editing.JsLanguage"/> |
| </file> |
| </folder> |
| </folder> |
| </folder> |
| </pre> |
| This tells GSF that it should "own" the mime type "text/javascript". |
| (<b>NOTE</b>: When you're describing mime types in the layer system, you must use nested |
| folders for the mimetype. You can NOT create a <code>name="text/javascript"</code> item; it must |
| be a folder named <code>text</code> with a folder named <code>javascript</code> inside it!). |
| Once you've done this, GSF will claim files of this mime type and register its own |
| editor kit with it, and so on. (There is a way to use custom loaders with GSF if you |
| for example are trying to mix and match GSF with Schliemann or you have existing code |
| you are trying to integrate; that will be described later in this document.) |
| </p> |
| <h3>Build Modification</h3> |
| <p> |
| In addition, you also need to add the following target to your project's build file, |
| such that at build time, GSF gets a chance to interject some things into your layer file. |
| In earlier version, GSF would do this at runtime, by modifying your user directory |
| such that it would merge content into the layer file, but this has had several disadvantages. |
| An extra build step avoids this problem. It does however mean that when GSF changes incompatibly, |
| it cannot easily account for older module registrations. Until this issue is resolved, you |
| may have to regenerate your module when versions change. |
| </p> |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| <target name="jar" depends="init,compile,jar-prep" unless="is.jar.uptodate"> |
| <taskdef name="gsfjar" classname="org.netbeans.modules.gsf.GsfJar" classpath="${nb_all}/gsf.api/anttask/gsfanttask.jar:${nb_all}/nbbuild/nbantext.jar"/> |
| <gsfjar jarfile="${cluster}/${module.jar}" compress="${build.package.compress}" index="${build.package.index}" manifest="${manifest.mf}" stamp="${cluster}/.lastModified"> |
| <fileset dir="${build.classes.dir}"/> |
| </gsfjar> |
| </target> |
| </pre> |
| <h3>The Language Configuration</h3> |
| <p> |
| In the above registration, we have named the <code>JsLanguage</code> class as the language |
| configuration object for this class. This <code>JsLanguage</code> object is just an instance |
| of the <code>GsfLanguage</code> interface. It's actually a subclass of the |
| <code>DefaultLanguageConfig</code> class in the GSF SPI package, which implements this |
| interface. |
| </p> |
| <p> |
| The GSF language object defines key characteristics about your language. |
| The most important method you have to implement is |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| @NonNull Language <b>getLexerLanguage</b>(); |
| </pre> |
| This is referring to the Lexer API's Language class. GSF is built on top of the |
| Lexer API. You must create a Lexer for your language. The Lexer API is already quite |
| well documented so I won't repeat it here (though I should include a documentation |
| reference link). |
| </p> |
| <p> |
| The first step is implementing a Lexer for your language, and returning its language |
| from your language configuration subclass. If you subclass <code>DefaultLanguageConfig</code>, |
| you are finally ready to just test things. You can now start the IDE, double on source files in |
| the file navigator, which should open them in the editor - and they should be properly |
| syntax highlighted! There is more information about the lexer aspects in the |
| <a href="lexing.html">lexing document</a>. |
| </p> |
| <h3>Lexer Registration and Colors</h3> |
| <p> |
| You also need to register the lexer language with NetBeans. This |
| is described in the <a href="lexing.html#registration">lexing</a> document |
| in more detail. |
| </p> |
| <h3>Other Language Configuration settings</h3> |
| <p> |
| There are some other methods in the language configuration class you can override. |
| <table border="1" style="background: #ffffcc; border-collapse: collapse; border:solid 1px black"> |
| <tr> |
| <th>Method</th><th>Purpose</th> |
| </tr> |
| <tr> |
| <td><pre>String getLineCommentPrefix()</pre></td> |
| <td> |
| If your language supports line comments, return the comment string here. |
| For example, for Java and JavaScript it's <code>//</code>, and for Ruby it's <code>#</code>. |
| If you return non null from this method, GSF will enable the comment, uncomment |
| and toggle comment actions, add them to your toolbar and implement an editor |
| action which commments, uncomments and toggles comments in your source files. |
| </td> |
| </tr> |
| <tr> |
| <td><pre>boolean isIdentifierChar(char c)</pre></td> |
| <td>This method lets you return whether a character is an identifier character |
| in your language. This method will be used to for example automatically |
| handle the case where the user double clicks in your editor; this should |
| select a complete identifiers. By default in NetBeans you get the Java behavior, |
| but in a language like JavaScript for example, <code>$</code> should also be |
| included in the double click selection.<br/><br/>(Unfortunately, the current design |
| doesn't make it easy to handle more complicated scenarios like Ruby, where |
| a <code>$</code> should only be accepted as a prefix of the identifier, and |
| a <code>!</code> should only be accepted as a suffix. I plan to refine this |
| approach a bit to handle this. |
| </td> |
| </tr> |
| </table> |
| </p> |
| <p> |
| If you extend the <code>DefaultLanguageConfig</code> class, there are additional |
| methods you can subclass. I'll describe these after I introduce the other services |
| you can register with NetBeans. |
| </p> |
| <h3>Parsing</h3> |
| <p> |
| Most features in GSF will require parse information. This relies upon calling |
| your own implementation of the |
| <a href="org/netbeans/modules/gsf/api/Parser.html">Parser</a> interface. |
| If you are subclassing the <code>DefaultLanguageConfig</code> class, just override |
| the <code>getParser()</code> method: |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| @Override |
| public Parser getParser() { |
| return new JsParser(); |
| } |
| </pre> |
| Alternatively, you can register this service directly in the layer: |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| <folder name="GsfPlugins"> |
| <folder name="text"> |
| <folder name="javascript"> |
| ... |
| <b><file name="parser.instance"> |
| <attr name="instanceClass" stringvalue="org.netbeans.modules.javascript.editing.JsParser"/> |
| </file></b> |
| </folder> |
| </folder> |
| </folder> |
| </pre> |
| </p> |
| <h3>Indexing</h3> |
| <p> |
| This is just like the parser registration. You need to implement the |
| <a href="org/netbeans/modules/gsf/api/Indexer.html">Indexer</a> interface, and |
| then register it, either via subclassing <code>DefaultLanguageConfig</code> and |
| overriding <code>getIndexer()</code>: |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| @Override |
| public Parser getIndexer() { |
| return new JsIndexer(); |
| } |
| </pre> |
| Alternatively, you can register this service directly in the layer: |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| <folder name="GsfPlugins"> |
| <folder name="text"> |
| <folder name="javascript"> |
| ... |
| <b><file name="indexer.instance"> |
| <attr name="instanceClass" stringvalue="org.netbeans.modules.javascript.editing.JsIndexer"/> |
| </file></b> |
| </folder> |
| </folder> |
| </folder> |
| </pre> |
| </p> |
| <h3>Other Services</h3> |
| <p> |
| You get the idea. You can override configuration functiosn in <code>DefaultLanguageConfig</code>, |
| or register services directly in the layer. Sometimes you can do a combination of things. |
| For example, in the Ruby plugin, most editing related services are implemented by and registered |
| in the Ruby Editing module. These are instantiated directly by the <code>RubyLanguage</code> |
| configuration class. However, Ruby Quickfixes are implemented in a separate module, and therefore |
| the <code>HintsProvider</code> implementation for Ruby is registered via the layer in the |
| hint module. |
| </p> |
| <h3>Exceptions!</h3> |
| <p> |
| <span style="background: #ffcccc; color: black">HACK ALERT</span>: |
| There are two ugly exceptions to the ability to register services with the |
| <code>DefaultLanguageConfig</code>. Two attributes <b>must</b> be initialized |
| via the layer instead: |
| <ol> |
| <li> Structure scanners</li> |
| <li> The <code>customEditorKit</code> attribute</li> |
| </ol> |
| Structure scanners are used to for example populate the navigator view. |
| The customEditorKit attribute will be described later in this document. |
| The problem with both of these attributes is that they are needed early during startup, |
| even during IDE sessions where file types of the given mimetype is never loaded! |
| There are implementation reasons for this. Currently, the way we hook into the navigator |
| API, we have to know when we first see the GSF language registrations whether a given |
| language supports navigation (in which case we'll modify the system file system to register |
| a GSF navigator view for this file type). Similarly, we need to know during GSF data loader |
| initialization whether GSF should be on the lookout for files of the given mime type; this |
| is only the case if <code>customEditorKit</code> is false. |
| Well, if GSF were to look for these attributes in the langauge configuration objects, |
| it would have to load and instantiate all the language configuration objects at startup! |
| That would mean initializing a WHOLE bunch of classes at startup, since language configuration |
| classes typically reference a bunch of other services. (I in fact DID try this, and ended |
| up failing the blacklisted class commit validation test, so I had to revert initialization |
| of these two attributes to being layer-only). |
| </p> |
| |
| |
| <a name="UseCustomEditorKit"/> |
| <h3>Using Custom Loaders and Kits</h3> |
| <p> |
| Normally when you register a mime type with GSF, GSF will automatically handle |
| file type handling for you, by creating NetBeans implementations like DataObjects, |
| creating an EditorKit for these DataObjects, and so on. |
| </p> |
| <p> |
| However, there are cases where you have an existing EditorKit or DataLoader, and |
| you want to add GSF support selectively. To do that, you need to tell GSF |
| to back off. You do that with the <code>useCustomEditorKit</code> attribute, |
| which you add as an attribute to the mime folder in the <code>GsfPlugins</code> |
| folder in the layer: |
| |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| <folder name="GsfPlugins"> |
| <folder name="application"> |
| <folder name="x-httpd-eruby"> |
| <b><attr name="useCustomEditorKit" boolvalue="true"/></b> |
| ... |
| </folder> |
| </folder> |
| </folder> |
| </pre> |
| |
| When GSF sees this, it will not add ANY editing services for you. |
| You are now free to add these in yourself in your layer. |
| This is done by the HTML module for example, which had a lot of existing |
| legacy code we didn't want to rewrite (at least not yet). HTML already |
| has DataObjects (which were subclassed in other modules), it had custom |
| code to handle formatting and code completion, and we wanted to keep these |
| classes using the old code for now. So, the HTML module specifies |
| <code>useCustomEditorKit</code>. |
| </p> |
| <p> |
| With the <code>useCustomEditorKit</code> attribute you can use your own |
| EditorKit implementation. There is a price to pay though. When you do |
| this, you need to manually register all the GSF implementations you <b>do</b> |
| want to use. For example, if you're providing a semantic highlighter, you |
| have to register the GSF highlighting factory in the Editors folder: |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| <folder name="Editors"> |
| <folder name="text"> |
| <folder name="html"> |
| <i>Required for semantic highlighting:</i> |
| <file name="org-netbeans-modules-gsfret-editor-semantic-HighlightsLayerFactoryImpl.instance"/> |
| |
| <i>Required for code folding:</i> |
| <folder name="FoldManager"> |
| <file name="org-netbeans-modules-gsfret-editor-fold-GsfFoldManagerFactory.instance"/> |
| </folder> |
| <folder name="SideBar"> |
| <file name="org-netbeans-modules-editor-gsfret-GsfCodeFoldingSideBarFactory.instance"> |
| <attr name="position" intvalue="1200"/> |
| </file> |
| </folder> |
| <i>Required for GSF go to declaration:</i> |
| <folder name="HyperlinkProviders"> |
| <file name="GsfHyperlinkProvider.instance"> |
| <attr name="instanceClass" stringvalue="org.netbeans.modules.gsfret.editor.hyperlink.GsfHyperlinkProvider"/> |
| <attr name="instanceOf" stringvalue="org.netbeans.lib.editor.hyperlink.spi.HyperlinkProviderExt"/> |
| </file> |
| </folder> |
| <i>Required for error status:</i> |
| <folder name="UpToDateStatusProvider"> |
| <file name="org-netbeans-modules-gsfret-hints-GsfUpToDateStateProviderFactory.instance"/> |
| </folder> |
| <i>Required for code completion:</i> |
| <folder name="CompletionProviders"> |
| <file name="org-netbeans-modules-gsfret-editor-completion-GsfCompletionProvider.instance"/> |
| </folder> |
| </folder> |
| </folder> |
| </folder> |
| </pre> |
| I may be missing some services here - like live code templates and mark occurrences. |
| If you <b>do</b> go this route, you should look at the <code>LanguageRegistry</code> class |
| in the GSF implementation to make sure you're picking up everything you intend to, to |
| make sure you have the right class names, with all the right attributes, etc. |
| <div style="background: #ccffcc; color: black; border: solid 1px black; padding: 10px"> |
| I need to do something to make this more solid. Perhaps I can use .shadow or other forms of aliasing |
| such that all you need to do is reference a service by hand and the correct class name etc. is |
| used. I think I've seen this for actions (which are registered in one place, and just a shadow used |
| elsewhere to link to it) so it might work in this case if I switch to a logical name and use the |
| <code>instanceClass</code> attribute instead of the filename to designate the class to be instantiated. |
| </div> |
| </p> |
| <h3>Mixing and Matching APIs</h3> |
| <p> |
| You are free to use the NetBeans APIs directly. For example, in addition to relying on GSF's code completion |
| provider, you can add an additional direct implementation of NetBeans' code completion API if you should |
| want to. You don't need to switch to a custom editor kit to do this. |
| </p> |
| <h3>Adding Actions</h3> |
| <p> |
| In NetBeans 6.5, there is a new API which lets you register editor actions (along with keybindings) |
| for arbitrary mime types. This means that you can extend the available actions for an editor type |
| from your modules. GSF used to have an API to help with this, but it's no longer necessary. To do this, |
| you just have to implement the editor <code>BaseAction</code> class, and register it in the Editors mime |
| folder: |
| <pre style="background: #ffffcc; color: black; border: solid 1px black; padding: 5px"> |
| <folder name="Editors"> |
| <folder name="text"> |
| <folder name="x-ruby"> |
| ... |
| <b><folder name="Actions"> |
| <file name="org-netbeans-modules-ruby-ReflowParagraphAction.instance"/> |
| </folder> |
| </folder></b> |
| </folder> |
| </folder> |
| </pre> |
| From here, you can proceed to also create a <code>Popup</code> folder to add it to the context |
| menu for this file type, and/or register a keybinding for it via an XML file |
| referenced from the <code>Keybindings</code> folder. |
| </p> |
| <br/> |
| <span style="color: #cccccc">Tor Norbye <tor@netbeans.org></span> |
| </body> |
| </html> |