apache / netbeans

Apache NetBeans
https://netbeans.apache.org/
Apache License 2.0
2.62k stars 841 forks source link

TextMate/TypeScript: code templates don't work #4064

Open negora opened 2 years ago

negora commented 2 years ago

Apache NetBeans version

Apache NetBeans 13, 14 RC1

What happened

I created a code template (a.k.a. snippet or live template) to be used in TypeScript source files, but it didn't take effect.

How to reproduce

Go to Tools → Options → Editor → Code templates. There, choose TextMate languages in the list of languages. Then, click on New. Type test in the dialog window that asks for an abbreviation, and then, in Expanded text, type this:

This is just a sample test.

In the field called Expand template on, choose Tab.

Now open a TypeScript file, type test and push [Tab]. This should be expanded to This is just a sample test. but it isn't.

I've tried this in both NetBeans IDE versions, 13 and 14 RC1. I've tried it with the TypeScript plug-in that is bundled with NetBeans, and also using an external LSP server.

I've changed the key used to expand the abbreviation, but there hasn't been any luck either.

Did this work correctly in an earlier version?

No

Operating System

Debian GNU/Linux 11.3 (Bullseye)

JDK

OpenJDK Runtime Environment (build 17.0.2+8-Debian-1deb11u1)

Apache NetBeans packaging

Apache NetBeans binary zip

Anything else

I've read that other IDEs, such as Eclipse and its Wild Web Developers plug-in, prefer to delegate this type of feature to the language servers (see Custom snippets/templates). Although I understand their position (human resources are limited), Wouldn't this create a very heterogeneous and confusing experience when you use multiple language servers in the same IDE? Because code templates / snippets can be implemented in very different ways: some allow to use abbreviations (NetBeans IDE), whereas others only let you choose from a list (KDE's Kate).

Update no. 1: I've taken a look to LSP Specification / Snippet Syntax and I've seen that the syntax is standardized at the protocol level. So my concern about each server using its own syntax is wrong. Indeed, the syntax used by LSP looks like powerful: it allows to set a series of predefined values (choices) and even transforms based on regular expressions.

I wonder how this could fit in the NetBeans configuration, since the syntax has some differences from the one used for Java code.

Update no 2: I've read this: Support for snippetSyntax. Unfortunately, LSP4J seems not to have implemented this feature. And NetBeans uses this library, Right? LSP4E (the own Eclipse IDE's LSP client) uses a very basic implementation for its own consumption.

Are you willing to submit a pull request?

No

Code of Conduct

Yes

Chris2011 commented 2 years ago

I can reproduce it, but I guess that this is the wrong context. Or the context is missing. The problem here is, that a snippet in this way will be part of each language that I registered, which doesn't make sense for 95%. log -> console.log, make sense for TS and JS but not for C#, F#, erlang, etc.

So if you have a look at the select box and choose "php" below the list of snippets there are 3 register cards: expanded text, description and context, which is missing for textmate language. So either we need to add each language, that I have created via textmate grammar into the drop down list to set the context automatically or we need to add the context and have a list there for each language that I have registered.

When you have a look into a language based on textmate for vs code, you have this piece of code to register snippets:

"snippets": [{
   "language": "erlang",
   "path": "./snippets/erlang.json"
}],

which is part of the package.json and points to a json file where we add those snippets via json:

"erlang gen_event behavior" : {
        "prefix" : "erl-gen_event-behavior",
        "body"  : [
            "-behaviour(gen_event).",
            "",
            "-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]).",
            "",
            "init(_Args) ->",
            "    {ok, []}.",
            "",
            "handle_event(_Event, State) ->",
            "    {ok, State}.",
            "",
            "handle_call(_Request, State) ->",
            "    {ok, no_reply, State}.",
            "",
            "handle_info(_Info, State) ->",
            "    {ok, State}.",
            "",
            "terminate(_Args, _State) ->",
            "    ok.",
            "",
            "code_change(_OldVsn, State, _Extra) ->",
            "    {ok, State}.",
            ""
        ],
        "description" : "generate an empty gen_event"
    },

So I also prefer this way of doing it and for this, the UI needs to have more fields like "Snippets file: ". Just an idea. At the moment, you can create basic syntax highlighting, folding, snippets easily with some files as you can see in the VS Code example of erlang. What needs to be added to our textmate implementation is exactly this option for snippets which covers your ticket.

Chris2011 commented 2 years ago

And to your comment for LSP, I don't see that in LSP. Sure that would be also cool, but with a simple config file like for NetBeans core langauges it is XML and like VS Code does, is json, that could be enough to work. We just need to implement this. :)

negora commented 2 years ago

So if you have a look at the select box and choose "php" below the list of snippets there are 3 register cards: expanded text, description and context, which is missing for textmate language.

Surprisingly I had never realized about the Context tab.

So either we need to add each language, that I have created via textmate grammar into the drop down list to set the context automatically or we need to add the context and have a list there for each language that I have registered.

I believe that the first option would be more correct.

However, I think that snippets should already work in their current form. We (users) can already introduce prefixes in our abbreviations to overcome this limitation. So, for example, ts_while would generate a while loop for TypeScript, kt_while would be generate one for Kotlin, and so on. If we don't want to press the [Shift] key to type the underscore, we can use any other delimiter or none at all.

With all this I'm not saying that the IDE doesn't need snippets/templates separated by language (I think the opposite). I'm just saying that, first, templates should work with TextMate grammars in general. But I don't know why they don't.

And to your comment for LSP, I don't see that in LSP. Sure that would be also cool, but with a simple config file like for NetBeans core langauges it is XML and like VS Code does, is json, that could be enough to work. We just need to implement this. :)

The LSP specification seems to support snippets already, as I said. They've at least one advantage over snippets in the client-side: the server knows better about the context (i.e. variable types) and can be more precise when providing choices to fill the placeholders. For example, think on the whileit template for Java code, in NetBeans IDE. The editor only offers those variables, in the current scope, that inherit from java.util.Iterator.

But LSP also has its disadvantages. The most important is that each LSP server that you want to use needs to support this feature (which is optional). If it doesn't support it, you're left in mud.

So, after thinking much on this, I believe that client-side support is a must. After all, if a specific LSP server ever supports this feature, maybe NetBeans can delegate to it (as long as the templates are minimally compatible).

Chris2011 commented 2 years ago

However, I think that snippets should already work in their current form. We (users) can already introduce prefixes in our abbreviations to overcome this limitation. So, for example, ts_while would generate a while loop for TypeScript, kt_while would be generate one for Kotlin, and so on. If we don't want to press the [Shift] key to type the underscore, we can use any other delimiter or none at all.

Yeah, that could be a good workaround or first solution :)