Skip to content

Commit c75dcfd

Browse files
committed
Start implementing custom code templates
Still WIP
1 parent 1516eb5 commit c75dcfd

File tree

15 files changed

+724
-0
lines changed

15 files changed

+724
-0
lines changed
387 Bytes
Loading

org.eclipse.tm4e.ui/plugin.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ TextMatePreferencePage.name=TextMate
2929
GrammarPreferencePage.name=Grammar
3030
TaskTagsPreferencePage.name=Task Tags
3131
ThemePreferencePage.name=Theme
32+
TemplatesPreferencePage.name=Templates (TM4E)
3233

3334
# Wizards
3435
TextMateWizard.category=TextMate

org.eclipse.tm4e.ui/plugin.xml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
class="org.eclipse.tm4e.ui.internal.preferences.ThemePreferencePage"
7272
id="org.eclipse.tm4e.ui.preferences.ThemePreferencePage"
7373
category="org.eclipse.tm4e.ui.preferences.TextMatePreferencePage" />
74+
<page name="%TemplatesPreferencePage.name"
75+
class="org.eclipse.tm4e.ui.internal.preferences.CustomCodeTemplatePreferencePage"
76+
id="org.eclipse.tm4e.ui.preferences.CustomCodeTemplatePreferencePage"
77+
category="org.eclipse.tm4e.ui.preferences.TextMatePreferencePage">
78+
</page>
7479
</extension>
7580

7681
<!-- Wizards -->
@@ -155,4 +160,39 @@
155160
class="org.eclipse.tm4e.ui.internal.hover.TMTokenTextHover"
156161
contentType="org.eclipse.core.runtime.text" />
157162
</extension>
163+
164+
<extension point="org.eclipse.ui.editors.templates">
165+
<contextType
166+
class="org.eclipse.tm4e.ui.templates.DefaultTm4eTemplateContextType"
167+
id="org.eclipse.tm4e.ui.templates.context"
168+
name="Default context (TM4E)"
169+
registryId="org.eclipse.tm4e.ui.templates">
170+
</contextType>
171+
<contextType
172+
class="org.eclipse.tm4e.ui.templates.CommentTemplateContextType"
173+
id="org.eclipse.tm4e.ui.templates.context.comment"
174+
name="Comment (TM4E)"
175+
registryId="org.eclipse.tm4e.ui.templates">
176+
</contextType>
177+
<contextType
178+
class="org.eclipse.tm4e.ui.templates.DocumentationCommentTemplateContextType"
179+
id="org.eclipse.tm4e.ui.templates.context.comment.doc"
180+
name="Documentation Comment (TM4E)"
181+
registryId="org.eclipse.tm4e.ui.templates">
182+
</contextType>
183+
<include
184+
file="src/main/resources/templates/templates.xml"
185+
translations="src/main/resources/templates/templates.properties">
186+
</include>
187+
<contextTypeRegistry
188+
id="org.eclipse.tm4e.ui.templates">
189+
</contextTypeRegistry>
190+
</extension>
191+
192+
<extension point="org.eclipse.ui.genericeditor.contentAssistProcessors">
193+
<contentAssistProcessor
194+
class="org.eclipse.tm4e.ui.internal.templates.Tm4eTemplateCompletionProcessor"
195+
contentType="org.eclipse.core.runtime.text" />
196+
</extension>
197+
158198
</plugin>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Advantest Europe GmbH and others.
3+
* This program and the accompanying materials are made
4+
* available under the terms of the Eclipse Public License 2.0
5+
* which is available at https://www.eclipse.org/legal/epl-2.0/
6+
*
7+
* SPDX-License-Identifier: EPL-2.0
8+
*
9+
* Contributors:
10+
* Dietrich Travkin (SOLUNAR GmbH) - initial implementation
11+
*******************************************************************************/
12+
package org.eclipse.tm4e.ui;
13+
14+
import java.net.URL;
15+
16+
import org.eclipse.core.runtime.FileLocator;
17+
import org.eclipse.core.runtime.Path;
18+
import org.eclipse.core.runtime.Platform;
19+
import org.eclipse.jdt.annotation.Nullable;
20+
import org.eclipse.jface.resource.ImageDescriptor;
21+
import org.eclipse.jface.resource.ImageRegistry;
22+
import org.eclipse.swt.graphics.Image;
23+
import org.osgi.framework.Bundle;
24+
25+
public final class TMImages {
26+
27+
private static final String ICONS_PATH = "$nl$/icons/full/"; //$NON-NLS-1$
28+
private static final String OBJECT = ICONS_PATH + "obj16/"; // basic colors - size 16x16 //$NON-NLS-1$
29+
30+
public static final String IMG_TEMPLATE = "IMG_TEMPALTE"; //$NON-NLS-1$
31+
32+
private TMImages() {
33+
// no instantiation desired
34+
}
35+
36+
private static @Nullable ImageRegistry imageRegistry;
37+
38+
public static void initalize(final ImageRegistry registry) {
39+
imageRegistry = registry;
40+
41+
registerImage(IMG_TEMPLATE, OBJECT + "template_obj.svg"); //$NON-NLS-1$
42+
}
43+
44+
private static void registerImage(final String key, final String path) {
45+
ImageDescriptor desc = ImageDescriptor.getMissingImageDescriptor();
46+
final Bundle bundle = Platform.getBundle(TMUIPlugin.PLUGIN_ID);
47+
final ImageRegistry imageRegistry = getImageRegistry();
48+
URL url = null;
49+
if (bundle != null) {
50+
url = FileLocator.find(bundle, new Path(path), null);
51+
if (url != null) {
52+
desc = ImageDescriptor.createFromURL(url);
53+
}
54+
}
55+
if (imageRegistry != null) {
56+
imageRegistry.put(key, desc);
57+
}
58+
}
59+
60+
/**
61+
* Returns the {@link Image} identified by the given key, or <code>null</code> if it does not exist.
62+
*/
63+
public static @Nullable Image getImage(final String key) {
64+
final ImageRegistry imageRegistry = getImageRegistry();
65+
if (imageRegistry == null) {
66+
return null;
67+
}
68+
return imageRegistry.get(key);
69+
}
70+
71+
/**
72+
* Returns the {@link ImageDescriptor} identified by the given key, or <code>null</code> if it does not exist.
73+
*/
74+
public static @Nullable ImageDescriptor getImageDescriptor(final String key) {
75+
final ImageRegistry imageRegistry = getImageRegistry();
76+
if (imageRegistry == null) {
77+
return null;
78+
}
79+
return imageRegistry.getDescriptor(key);
80+
}
81+
82+
public static @Nullable ImageRegistry getImageRegistry() {
83+
if (imageRegistry == null) {
84+
final TMUIPlugin plugin = TMUIPlugin.getDefault();
85+
if (plugin == null) {
86+
return null;
87+
}
88+
imageRegistry = plugin.getImageRegistry();
89+
}
90+
return imageRegistry;
91+
}
92+
93+
}

org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/TMUIPlugin.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
*
99
* Contributors:
1010
* Angelo Zerr <[email protected]> - initial API and implementation
11+
* Dietrich Travkin (SOLUNAR GmbH) - Additions for custom code templates
1112
*/
1213
package org.eclipse.tm4e.ui;
1314

15+
import java.io.IOException;
16+
import java.util.Iterator;
1417
import java.util.logging.Handler;
1518
import java.util.logging.Level;
1619
import java.util.logging.LogRecord;
@@ -19,14 +22,24 @@
1922
import org.eclipse.core.runtime.IStatus;
2023
import org.eclipse.core.runtime.Platform;
2124
import org.eclipse.core.runtime.Status;
25+
import org.eclipse.jdt.annotation.NonNull;
2226
import org.eclipse.jdt.annotation.Nullable;
27+
import org.eclipse.jface.text.templates.TemplateContextType;
28+
import org.eclipse.jface.text.templates.persistence.TemplateStore;
29+
import org.eclipse.text.templates.ContextTypeRegistry;
30+
import org.eclipse.tm4e.core.internal.utils.NullSafetyHelper;
2331
import org.eclipse.tm4e.ui.internal.model.TMModelManager;
2432
import org.eclipse.tm4e.ui.internal.samples.SampleManager;
2533
import org.eclipse.tm4e.ui.internal.themes.ThemeManager;
2634
import org.eclipse.tm4e.ui.model.ITMModelManager;
2735
import org.eclipse.tm4e.ui.samples.ISampleManager;
36+
import org.eclipse.tm4e.ui.templates.CommentTemplateContextType;
37+
import org.eclipse.tm4e.ui.templates.DefaultTm4eTemplateContextType;
38+
import org.eclipse.tm4e.ui.templates.DocumentationCommentTemplateContextType;
2839
import org.eclipse.tm4e.ui.themes.ColorManager;
2940
import org.eclipse.tm4e.ui.themes.IThemeManager;
41+
import org.eclipse.ui.editors.text.templates.ContributionContextTypeRegistry;
42+
import org.eclipse.ui.editors.text.templates.ContributionTemplateStore;
3043
import org.eclipse.ui.plugin.AbstractUIPlugin;
3144
import org.osgi.framework.BundleContext;
3245

@@ -39,9 +52,17 @@ public class TMUIPlugin extends AbstractUIPlugin {
3952
public static final String PLUGIN_ID = "org.eclipse.tm4e.ui"; //$NON-NLS-1$
4053
private static final String TRACE_ID = PLUGIN_ID + "/trace"; //$NON-NLS-1$
4154

55+
// IDs for custom code templates
56+
private static final String CUSTOM_TEMPLATES_KEY = PLUGIN_ID + ".text.templates.custom"; //$NON-NLS-1$
57+
private static final String TEMPLATES_REGISTRY_ID = PLUGIN_ID + ".templates"; //$NON-NLS-1$
58+
4259
// The shared instance
4360
private static volatile @Nullable TMUIPlugin plugin;
4461

62+
// registry and store for custom code templates
63+
private @Nullable ContributionContextTypeRegistry contextTypeRegistry = null;
64+
private @Nullable TemplateStore templateStore = null;
65+
4566
/**
4667
* Returns the shared instance
4768
*
@@ -146,12 +167,91 @@ public void close() throws SecurityException {
146167
}
147168
});
148169
}
170+
171+
TMImages.initalize(getImageRegistry());
149172
}
150173

151174
@Override
152175
public void stop(final BundleContext context) throws Exception {
176+
if (templateStore != null) {
177+
templateStore.stopListeningForPreferenceChanges();
178+
}
153179
ColorManager.getInstance().dispose();
154180
plugin = null;
155181
super.stop(context);
156182
}
183+
184+
public ContextTypeRegistry getTemplateContextRegistry() {
185+
@NonNull
186+
ContributionContextTypeRegistry result;
187+
188+
if (contextTypeRegistry == null) {
189+
result = new ContributionContextTypeRegistry(TEMPLATES_REGISTRY_ID);
190+
contextTypeRegistry = result;
191+
192+
result.addContextType(DefaultTm4eTemplateContextType.CONTEXT_ID);
193+
result.addContextType(CommentTemplateContextType.CONTEXT_ID);
194+
result.addContextType(DocumentationCommentTemplateContextType.CONTEXT_ID);
195+
196+
// TODO add language-specific context types, probably from extensions
197+
} else {
198+
result = NullSafetyHelper.castNonNull(contextTypeRegistry);
199+
}
200+
return result;
201+
}
202+
203+
@SuppressWarnings("deprecation")
204+
private static class ContextTypeRegistryWrapper extends org.eclipse.jface.text.templates.ContextTypeRegistry {
205+
206+
private final ContextTypeRegistry delegate;
207+
208+
public ContextTypeRegistryWrapper(final ContextTypeRegistry registry) {
209+
this.delegate = registry;
210+
delegate.contextTypes();
211+
}
212+
213+
@Override
214+
public Iterator<TemplateContextType> contextTypes() {
215+
return delegate.contextTypes();
216+
}
217+
218+
@Override
219+
public void addContextType(final @Nullable TemplateContextType contextType) {
220+
delegate.addContextType(contextType);
221+
}
222+
223+
@Override
224+
public @Nullable TemplateContextType getContextType(final @Nullable String id) {
225+
return delegate.getContextType(id);
226+
}
227+
228+
}
229+
230+
public static ContextTypeRegistryWrapper from(final ContextTypeRegistry registry) {
231+
return new ContextTypeRegistryWrapper(registry);
232+
}
233+
234+
public TemplateStore getTemplateStore() {
235+
@NonNull
236+
TemplateStore result;
237+
238+
if (templateStore == null) {
239+
result = new ContributionTemplateStore(from(getTemplateContextRegistry()), getPreferenceStore(),
240+
CUSTOM_TEMPLATES_KEY);
241+
templateStore = result;
242+
243+
try {
244+
result.load();
245+
} catch (final IOException e) {
246+
Platform.getLog(this.getClass()).error(e.getMessage(), e);
247+
}
248+
249+
result.startListeningForPreferenceChanges();
250+
} else {
251+
result = NullSafetyHelper.castNonNull(templateStore);
252+
}
253+
254+
return result;
255+
}
256+
157257
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Advantest Europe GmbH and others.
3+
* This program and the accompanying materials are made
4+
* available under the terms of the Eclipse Public License 2.0
5+
* which is available at https://www.eclipse.org/legal/epl-2.0/
6+
*
7+
* SPDX-License-Identifier: EPL-2.0
8+
*
9+
* Contributors:
10+
* Dietrich Travkin (SOLUNAR GmbH) - initial implementation
11+
*******************************************************************************/
12+
package org.eclipse.tm4e.ui.internal.preferences;
13+
14+
import org.eclipse.jdt.annotation.Nullable;
15+
import org.eclipse.jface.text.source.SourceViewer;
16+
import org.eclipse.jface.text.templates.Template;
17+
import org.eclipse.jface.window.Window;
18+
import org.eclipse.swt.widgets.Composite;
19+
import org.eclipse.swt.widgets.Shell;
20+
import org.eclipse.tm4e.ui.TMUIPlugin;
21+
import org.eclipse.ui.texteditor.templates.TemplatePreferencePage;
22+
23+
public class CustomCodeTemplatePreferencePage extends TemplatePreferencePage {
24+
25+
public CustomCodeTemplatePreferencePage() {
26+
final TMUIPlugin plugin = TMUIPlugin.getDefault();
27+
if (plugin == null) {
28+
return;
29+
}
30+
31+
setPreferenceStore(plugin.getPreferenceStore());
32+
setTemplateStore(plugin.getTemplateStore());
33+
setContextTypeRegistry(TMUIPlugin.from(plugin.getTemplateContextRegistry()));
34+
}
35+
36+
private static class Tm4eEditTemplateDialog extends TemplatePreferencePage.EditTemplateDialog {
37+
38+
public Tm4eEditTemplateDialog(final Shell shell, final Template template, final boolean edit, final boolean isNameModifiable,
39+
final org.eclipse.jface.text.templates.ContextTypeRegistry contextTypeRegistry) {
40+
super(shell, template, edit, isNameModifiable, contextTypeRegistry);
41+
}
42+
43+
@Override
44+
protected SourceViewer createViewer(final @Nullable Composite parent) {
45+
// TODO configure viewer
46+
return super.createViewer(parent);
47+
}
48+
}
49+
50+
@Override
51+
protected String getFormatterPreferenceKey() {
52+
return super.getFormatterPreferenceKey();
53+
// TODO adapt pref key
54+
//return PreferenceConstants.TEMPLATES_USE_CODEFORMATTER;
55+
}
56+
57+
@Override
58+
protected @Nullable Template editTemplate(final @Nullable Template template, final boolean edit, final boolean isNameModifiable) {
59+
if (template == null) {
60+
return null;
61+
}
62+
63+
final Tm4eEditTemplateDialog dialog = new Tm4eEditTemplateDialog(getShell(), template, edit, isNameModifiable,
64+
getContextTypeRegistry());
65+
if (dialog.open() == Window.OK) {
66+
return dialog.getTemplate();
67+
}
68+
return null;
69+
}
70+
71+
@Override
72+
protected SourceViewer createViewer(final @Nullable Composite parent) {
73+
final SourceViewer sourceViewer = super.createViewer(parent);
74+
75+
// TODO configure source viewer for syntax highlighting with TM4E (adapt highlighting depending on context type)
76+
// sourceViewer.configure(new SourceViewerConfiguration() {
77+
// @Override
78+
// public IPresentationReconciler getPresentationReconciler(@Nullable final ISourceViewer sourceViewer) {
79+
// // TODO check if we need special config for highlighting template variables
80+
// return new TMPresentationReconciler();
81+
// }
82+
// });
83+
84+
return sourceViewer;
85+
}
86+
87+
}

0 commit comments

Comments
 (0)