...
How to add support in NetBeans to
...
a programming language using LSP
...
- Create a new NetBeans module. There are two kinds of NetBeans modules, ant-based and maven-based.
- To add a new ant-based NetBeans module, click on File → New Project → Java with Ant → NetBeans modules category and select the Module project.
- Give a name to your module, e.g. Kotlin-editor and provide a location to it. Select Standalone Module. Click Next.
- Provide a Code Name Base; this is the package name, e.g.
org.netbeans.modules.kotlin.editor
. Click Finish.
- To add a new maven-based NetBeans module, click on File → New Project → Java with Maven category and select the NetBeans Module project.
- Provide a Project Name, e.g. kotlin-editor as well as the maven coordinates, e.g.
org.netbeans.modules
as the Group Id. - Select the NetBeans version (usually the same as the one of the NetBeans IDE you are developing in) and click on Finish.
- Provide a Project Name, e.g. kotlin-editor as well as the maven coordinates, e.g.
- To add a new ant-based NetBeans module, click on File → New Project → Java with Ant → NetBeans modules category and select the Module project.
Next you need to provide support for:
- syntax highlighting
- code completion
- jump to definition, peek definition, find all references, symbol search
- types and documentation on hover
- code formatting
- refactoring (e.g. rename, move)
- error squiggles and apply suggestions from errors
- snippets
- build tasks
Download the LSP implementation for the language you wish to add support for. Some useful links:
- Language Server implementations.
- Another list of Language Server Implementations
- A list of Textmate syntaxes.
1. File Type Recognition
See Adding New Language Support.
2. Custom project types
See Adding New Language Support.
3. Semantic Syntax highlighting and braces matching
To add syntax highlighting to your source file, follow the instructions provided in LSP Client demo - (ba)sh language server tutorial.
- Download the TextMate json file for your language. A list of Textmate syntaxes is provided here and here but in other places, too. Let's call it
my-grammar.json
for the purposes of this example. E.g. for Kotlin this would beKotlin.tmLanguage.json
and for Rustrust.tmLanguage.json
. Right-click on Libraries folder of your module project and select Add Module Dependency. Enter "TextMate Lexer" inside Filter and add the module found in the Module list as a dependency to your module.
In the DataObject that you created in the previous chapter, add this annotation:
@GrammarRegistration(grammar="my-grammar.json", mimeType="text/sh")
and import it:
import org.netbeans.modules.textmate.lexer.api.GrammarRegistration;
If you open your source file in NetBeans, via the Windows→Favourites tab, you should be able to see the various parts of the language (e.g. keywords) to be coloured as defined in the TextMate grammar file. Braces matching is also there. Take a look at java/kotlin.editor/src/org/netbeans/modules/kotlin/editor/KtDataObject.java
as an example.
4. 5. Code Completion, Formatting and Navigation
To add support for language-specific features like code completion, navigation, etc. you need the help of a Language Protocol Server. Again, follow the instructions provided in LSP Client demo - (ba)sh language server tutorial. One needs to tell NetBeans to download the LSP binary the first time the user opens a file of the language and to add support for it using the new LSP API provided by NetBeans.
How to add LSP support
Assuming you have download the LSP for your language (as it will be described in the next section), one needs to:
- add dependencies to "LSP Client" and "MIME Lookup API" modules to your module
- create a new class that implements
LanguageServerProvider
.
See for example, ShellClient
or java/kotlin.editor/src/org/netbeans/modules/kotlin/editor/lsp/KotlinLSPServer
:
@MimeRegistration(mimeType="text/x-kotlin", service=LanguageServerProvider.class)
public class KotlinLSPServer implements LanguageServerProvider {
ServerRestarter restarter = lookup.lookup(ServerRestarter.class);
Preferences settingsNode = Utils.settings().node(Utils.NODE_LSP);
settingsNode.addPreferenceChangeListener(new PreferenceChangeListener() {
@Override
public void preferenceChange(PreferenceChangeEvent evt) {
if (evt.getKey() == null || Utils.KEY_LOCATION.equals(evt.getKey())) {
restarter.restart();
settingsNode.removePreferenceChangeListener(this);
}
}
});
File server = Utils.getServer();
if (server == null) {
return null;
}
try {
ProcessBuilder builder = new ProcessBuilder(new String[] {server.getAbsolutePath()}).directory(server.getParentFile().getParentFile());
Map<String, String> env = builder.environment();
File mvnCommand = InstalledFileLocator.getDefault().locate("maven/bin/mvn", null, false);
if (mvnCommand != null) {
env.put("PATH", env.get("PATH") + File.pathSeparator + mvnCommand.getParentFile().getAbsolutePath());
}
Process p = builder.start();
return LanguageServerDescription.create(p);
} catch (IOException ex) {
LOG.log(Level.FINE, null, ex);
return null;
}
}
}
Utils
helper class will provide the location of the downloaded LSP file.
public final class Utils {
public static final String NODE_LSP = "lsp";
public static final String KEY_LOCATION = "location";
private Utils() {
}
public static Preferences settings() {
return NbPreferences.forModule(Utils.class);
}
public static File getServer() {
Preferences settingsNode = Utils.settings().node(Utils.NODE_LSP);
String location = settingsNode.get(Utils.KEY_LOCATION, "");
File serverExecutable = new File(new File(location, "bin"), "kotlin-language-server" + (Utilities.isWindows() ? ".bat" : ""));
if (!serverExecutable.isFile()) {
return null;
}
return serverExecutable;
}
}
How to add a Hint to download the LSP binary
A list of Language Server implementations and another list of Language Server Implementations. For example, to add support for Kotlin programming language, download and build kotlin-language-server. Once built, you will need to provide the path to download kotlin-language-server/server/build/install/server/bin/kotlin-language-server to a class that implements LanguageServerProvider
as will be described later.
This tutorial is an LSP Client demo to provide support for the (ba)sh language.
File Type Recognition
...
.
A thread that is executed @OnStart
@OnStart
public class UnconfiguredHint implements Runnable {
private static final Set<String> KOTLIN_MIME_TYPES = new HashSet<>(Arrays.asList("text/x-kotlin"));
@Override
public void run() {
EditorRegistry.addPropertyChangeListener((PropertyChangeEvent evt) -> updateFocused());
Utils.settings().addPreferenceChangeListener((PreferenceChangeEvent evt) -> updateFocused());
updateFocused();
}
@Messages("ERR_KotlinSupportMissing=Enhanced Kotlin support is missing, click to install.")
private synchronized void updateFocused() {
JTextComponent selectedComponent = EditorRegistry.focusedComponent();
if (selectedComponent == null) {
return ;
}
Document doc = selectedComponent.getDocument();
FileObject file = NbEditorUtilities.getFileObject(doc);
if (file == null || !KOTLIN_MIME_TYPES.contains(FileUtil.getMIMEType(file))) {
return ;
}
List<ErrorDescription> errors = new ArrayList<>();
File server = Utils.getServer();
if (server == null) {
errors.add(ErrorDescriptionFactory.createErrorDescription(Severity.WARNING, Bundle.ERR_KotlinSupportMissing(), Collections.singletonList(new InstallKotlinLSP()), doc, 0));
}
HintsController.setErrors(doc, UnconfiguredHint.class.getName(), errors);
}
private static final class InstallKotlinLSP implements Fix {
@Override
public String getText() {
return "Install Kotlin LSP";
}
@Override
@Messages("DN_KotlinLSP=Kotlin LSP")
public ChangeInfo implement() throws Exception {
PluginInstaller.getDefault().install("org.netbeans.libs.kotlin.lsp", null, null, Bundle.DN_KotlinLSP()); // NOI18N
return null;
}
}
will display the hint on the editor to download the LSP server. But, modifications are needed on nb/updatecenters
.
public class Install extends ModuleInstall {
@Override
public void restored() {
File serverCache = Places.getCacheSubdirectory("kotlin-lsp/server-1.3.3");
File serverDir = new File(serverCache, "server");
File executable = new File(new File(serverDir, "bin"), "kotlin-language-server");
if (!executable.isFile()) {
File serverZip = InstalledFileLocator.getDefault().locate("kotlin-lsp/server-1.3.3.zip", null, false);
try (ZipFile zf = new ZipFile(serverZip)) {
Enumeration<? extends ZipEntry> en = zf.entries();
while (en.hasMoreElements()) {
ZipEntry ze = en.nextElement();
if (ze.isDirectory()) {
continue;
}
File target = new File(serverCache, ze.getName());
target.getParentFile().mkdirs();
try (OutputStream out = new FileOutputStream(target)) {
FileUtil.copy(zf.getInputStream(ze), out);
}
}
if (!Utilities.isWindows()) {
executable.setExecutable(true);
}
for (ModuleInfo mi : Lookup.getDefault().lookupAll(ModuleInfo.class)) {
if ("org.netbeans.modules.kotlin.editor".equals(mi.getCodeNameBase())) {
Class<?> utils = mi.getClassLoader().loadClass("org.netbeans.modules.kotlin.editor.Utils");
NbPreferences.forModule(utils).node("lsp").put("location", serverDir.getAbsolutePath());
}
}
} catch (IOException | ClassNotFoundException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
More changes are needed. The interested reader can study java/kotlin.editor
and nb/updatecenters/extras/libs.kotlin.lsp
modules for more information.
Open the source file again and try code completion, formatting, navigation (e.g. jump to definition, peek definition), find all references, symbol search etc.
6. Types and documentation on hover
8. Configuration
See Adding New Language Support.
9. Refactoring
See Adding New Language Support.
10. Error squiggles and apply suggestions from errors
11. Debugging
12. Snippets
13. Build tasks
Plugins supported by LSP
Kotlin plugin is an example of a language support using LSP.
See the File Type Integration Tutorial for more details on how to add File Type recognition support.
Custom project types
You may want to create a new "Project Type" for your specific language. For instance, "Rust" projects usually have a folder structure defined by the "cargo" tool.
The "NetBeans Project Type Tutorial" available at https://netbeans.apache.org/tutorials/nbm-projecttype.html is probably a good starting point.
Adding editor features
You can also add language-specific features (folding, syntax highlighting, formatting, etc.) to the NetBeans editor to make it easier for users to program in your language.
...
NetBeans Specific Resources
...