-
Notifications
You must be signed in to change notification settings - Fork 60
feat: Add support for custom code templates (fix #987) #988
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@travkin79 to be consistent with other existing class, I suggest that you use |
|
Please add some screenshots to this PR. Thanks! |
|
@travkin79 I noticed that you have changed the Tm4e with Tm, please use |
I think it should be very cool to update the docs with screenshot. |
Hi @sebthom, New preferences dialog (very similar to the "Templates" preferences in JDT and CDT, due to reusing code from the Eclipse platform): Users can add custom code templates for registered languages by pressing the "New" button in the preferences dialog. Here is how the dialog for new code templates looks like (again very similar to the corresponding dialog in JDT and CDT): The context (context types) list is automatically created by checking registered grammars / languages. In the "New Template" dialog, you'll get a list of all available languages / scopes: Pushing the button "Insert variable" in the "New Template" dialog shows available code template variables (currently only the language-independent ones): The preview source viewer in the preferences dialog uses the syntax highlighting associated with the registered grammar corresponding to the template's context type, here Java: In an editor with syntax highlighting activated through TM4E, users get code proposals based on existing code templates: |
|
@travkin79 it looks super great! Congrats! But IMHO I think you should also support vscode snippet. TM4E already supports:
that you can share between vscode and Eclipse IDE. VSCode snippets is the last part which misses. For instance instead of using cursor or selection, you should use snippet syntax If you can support that, you could copy/paste for instance https://github.com/redhat-developer/vscode-java/blob/main/snippets/server.json and use it directly. And it would be very nice to provide an extension point to contribute to TM4E with snippet like we have for textmate and language configuration. If you can manage that, you could share snippets from all existing vscode extension and it will be very easy for developer to manages snippets for vscode and Eclipse IDE both. Please note that IntelliJ supports also vscode snippets. |
|
Hello @angelozerr, I see the benefits of VSCode syntax support. But I think, it would also demand a lot of work to add VSCode snippet support. For that reason I prefer to begin with a smaller feature set, without VSCode snippet syntax support for now. We can add that feature later with a separate PR (see my issue comment on that). In my implementation, I tried to reuse as many code template support code from the Eclipse platform as possible and though, it was already quite some work to get that far. Now, I'd like to clarify if I'm on the right way with my concept of using
In addition, I'd like to finish my implementation and clarify / improve a few things in code. Most of these things can be found in TODO comments in my changed files.
|
Those screenshot and description should be included in the doc that @sebthom has written |
|
Hi @angelozerr, I think, I'd add screenshots to the documentation as soon as the implementation is (more or less) final. Otherwise, the UI / screenshots are likely to change. |
Thanks! |
|
Hi @sebthom, |
2a14325 to
7721e86
Compare
|
Hi @sebthom, Thank you very much for your feedback. I've tried solving the issues / answering your questions.
In my case the text field was visible, but resizing the window could result in not showing the name text field. I fixed that by setting a minimum size, but I had to use a (hacky) work-around to find the text field from the parent class, since the corresponding variable was private. Suggestions for improvements are welcome.
Since I'm reusing code from the Eclipse platform, I'm not sure if I can shorten the labels in case they are too long. Besides, it might be useful to remove some entries of the list, e.g. grammars for embedded languages. But I'm not sure which filter criteria might be reasonable.
I'd like to, but I didn't yet find a way to modify the source viewer configuration from the parent class
You'll now find a first version of the new documentation in
I originally thought that the custom code template support implementation from the Eclipse platform would somehow support formatting the code templates after application, but it turns out that's not the case. So I switched the "Use code formatter" checkbox off in the preferences page and removed the unused setting.
My idea was to offer two special, generic context types for custom code templates:
These context types allow for specifying text templates that can be used in arbitrary languages, e.g. a copyright text block, a TODO comment with the user's name, a license reference or text, etc. That avoid re-specifying the same code templates for each language separately.
I didn't plan to add more pre-built code templates, but if you have any suggestions for more useful code templates that are not very user-specific, I'd happily add those. |
I think you could reuse the Lines 375 to 409 in 4a927cc
|
Unfortunately, re-using the Nevertheless, with a few tricks (that I am not proud of) I managed to activate syntax highlighting in the edit code template dialog. But if someone sees a better way of implementing this, I'd happily improve the code.
What is still missing is syntax highlighting for template variables. I don't know if there is a way to extend each of the registered grammars with grammar rules for template variables, only for the code templates preferences page and dialog. Update |
...src/main/java/org/eclipse/tm4e/ui/internal/preferences/CustomCodeTemplatePreferencePage.java
Fixed
Show fixed
Hide fixed
| this.delegate = registry; | ||
| } | ||
|
|
||
| // TODO How can this null-safety check be handled correctly? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the no-npe dependency in main branch that now contains additional external null annotations for the jface.text.templates classes, if you rebase on main you should be able to remove a few of the @Nullable annotations and also cleanly solve the warning here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @sebthom for updating the settings in the no-npe lib. I rebased my branch on main and adapted my target platform config to the same no-npe version (1.3.12) and I also tried using target-platforms/latest.target, but I still have the same null safety check issues:
As soon as I stop suppressing null check warnings, I see the following warning. Adding or removing any combination of @NonNull or @Nullable annotations in the method signature doesn't solve the failing check. Unfortunately, I don't see what exactly is mismatching. I guess, the overridden method's signature doesn't match to the overriding method's signature, but the returned value's type seems also not to match here, and it's coming from a class with the same name (org.eclipse.jface.text.templates.ContextTypeRegistry#contextTypes() vs. org.eclipse.text.templates.ContextTypeRegistry#contextTypes()). I also tried replacing the method content with return super.contextTypes(), but that changed nothing.

Trying to apply the quick fix "Change return type of 'contextTypes(..)' to '@NonNull'" fails with the following error message.

I'm stuck here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update: It seems, Maven didn't like the @nullable annotations in the overriden methods anymore, but now my IDE complains about illegally redefined method parameters. And the IDE still doesn't like the return type of contextTypes() method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably some caching bug in Eclipse. It works fine for me but I had situations before where the IDE did not pick up changes to external annotation files. sometimes cleaning/rebuilding the project helps, sometimes closing/opening the project, sometimes restarting eclipse itself.
if nothing works, as a temporary workaround you can also try to directly set the path to the EEA jar file like this:

This however should not be necessary since the jar is already on the class path and we have this option enabled:

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried once again using the TM4E target platforms. First I switched to target-platforms/oldest.target and suddenly the IDE didn't complain anymore about mismatching null constraints. Switching to target-platforms/latest.target now also works. I don't know what was wrong before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much for the hints.
I also had to use NullSafetyHelper.castNonNull(...) in a few cases where the compiler didn't realize that a variable or expression could not be null. Maybe we should re-evaluate those cases (like this one) once again, too.
I didn't find a way to satisfy the compiler with adapting the return type or modifying the returned value. It always complains about incompatible nullability constraints on the Iterator type in the method signature.
Also remove obsolete (dummy) code
I had to use some work-arounds in order to adapt the methods from parent class.
e96c861 to
feeaf09
Compare
But for some reason, my IDE shows me compile errors.
...e.ui/src/main/java/org/eclipse/tm4e/ui/internal/templates/TMTemplateCompletionProcessor.java
Outdated
Show resolved
Hide resolved
...e.ui/src/main/java/org/eclipse/tm4e/ui/internal/templates/TMTemplateCompletionProcessor.java
Outdated
Show resolved
Hide resolved
| } | ||
|
|
||
| try { | ||
| final String code = NullSafetyHelper.castNonNull(document).get(damage.getOffset(), damage.getLength()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to avoid this cast you have to capture the document into a local final variable.
final var document = this.document;
if (presentation == null || damage == null || document == null) {
return;
}
try {
final String code = document.get(damage.getOffset(), damage.getLength());FYI: It is tolerable to shadow the variable name for this case. We have a special codeql rule that disallows shadowing with non-final vars which can result in bugs: https://github.com/eclipse-tm4e/tm4e/blob/main/.github/codeql/queries/java/local-shadows-instance-field.ql
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the hint. I adapted the code accordingly, here and in a few other cases.
| @Override | ||
| public void onColorized(final TextPresentation presentation, @Nullable final Throwable error) { | ||
| if (viewer != null && viewer.getDocument() != null) { | ||
| final ITextViewer theViewer = NullSafetyHelper.castNonNull(viewer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, capture viewer in a local final var before the null check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
5c42d14 to
d08a405
Compare
| @Override | ||
| public ICompletionProposal[] computeCompletionProposals(final ITextViewer viewer, final int offset) { | ||
| // TODO check why Invalid thread access exception occurs here without syncExec() | ||
| final ArrayList<ICompletionProposal> templateProposals = new ArrayList<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be simplified to:
final ICompletionProposal[] templateProposals = UI
.runSync(() -> TMTemplateCompletionProcessor.super.computeCompletionProposals(viewer, offset));| final IGrammar languageGrammar = TMEclipseRegistryPlugin.getGrammarRegistryManager().getGrammarForScope(languageScope); | ||
| if (languageGrammar != null) { | ||
| // TODO It seems TemplatePreferencePage.EditTemplateDialog requires the context type names to be unique. Can we shorten the names somehow? | ||
| final String contextTypeName = CodeTemplateContextTypeUtils.toContextTypeName(languageGrammar); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the value of languageGrammar.name is the raw value from the textmate file which may not match under which name the grammar is registered in Eclipse or may not be defined at all (e.g. for PHP). So toContextTypeName(..) should not rely on IGrammar#getName but needs to get the name from TMEclipseRegistryPlugin.getGrammarRegistryManager().getContentTypesForScope(languageScope).iterator().next().getName();










This PR fixes issue #987 and adds