diff --git a/org.eclipse.tm4e.ui/META-INF/MANIFEST.MF b/org.eclipse.tm4e.ui/META-INF/MANIFEST.MF
index c56333e22..7d9a297a5 100644
--- a/org.eclipse.tm4e.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.tm4e.ui/META-INF/MANIFEST.MF
@@ -4,7 +4,7 @@ Bundle-Name: %pluginName
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Bundle-SymbolicName: org.eclipse.tm4e.ui;singleton:=true
-Bundle-Version: 0.6.2.qualifier
+Bundle-Version: 0.6.3.qualifier
Require-Bundle: org.eclipse.tm4e.core;bundle-version="0.5.2",
org.eclipse.jface.text,
org.eclipse.core.runtime,
diff --git a/org.eclipse.tm4e.ui/plugin.properties b/org.eclipse.tm4e.ui/plugin.properties
index 5a16eba0e..61557a201 100644
--- a/org.eclipse.tm4e.ui/plugin.properties
+++ b/org.eclipse.tm4e.ui/plugin.properties
@@ -25,6 +25,7 @@ Theme.Dark.name=Dark
# Preferences
TextMatePreferencePage.name=TextMate
GrammarPreferencePage.name=Grammar
+TaskTagsPreferencePage.name=Task Tags
ThemePreferencePage.name=Theme
# Wizards
diff --git a/org.eclipse.tm4e.ui/plugin.xml b/org.eclipse.tm4e.ui/plugin.xml
index 9bcf34b6e..fc8f1fb39 100644
--- a/org.eclipse.tm4e.ui/plugin.xml
+++ b/org.eclipse.tm4e.ui/plugin.xml
@@ -56,6 +56,10 @@
class="org.eclipse.tm4e.ui.internal.preferences.GrammarPreferencePage"
id="org.eclipse.tm4e.ui.preferences.GrammarPreferencePage"
category="org.eclipse.tm4e.ui.preferences.TextMatePreferencePage" />
+
org.eclipse.tm4e.ui
eclipse-plugin
- 0.6.2-SNAPSHOT
+ 0.6.3-SNAPSHOT
diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/TMUIMessages.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/TMUIMessages.java
index 78684179a..bba374688 100644
--- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/TMUIMessages.java
+++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/TMUIMessages.java
@@ -32,6 +32,7 @@ public final class TMUIMessages extends NLS {
// TextMate preferences page
public static String TextMatePreferencePage_GrammarRelatedLink;
public static String TextMatePreferencePage_LanguageConfigurationRelatedLink;
+ public static String TextMatePreferencePage_TaskTagsRelatedLink;
public static String TextMatePreferencePage_ThemeRelatedLink;
// Grammar preferences page
@@ -49,6 +50,18 @@ public final class TMUIMessages extends NLS {
public static String GrammarPreferencePage_tab_injection_text;
public static String GrammarPreferencePage_preview;
+ // Task Tags preferences page
+ public static String TaskTagsPreferencePage_description;
+ public static String TaskTagsPreferencePage_column_tag;
+ public static String TaskTagsPreferencePage_column_type;
+ public static String TaskTagsPreferencePage_column_level;
+ public static String TaskTagsPreferencePage_addTagDialog_windowTitle;
+ public static String TaskTagsPreferencePage_addTagDialog_header;
+ public static String TaskTagsPreferencePage_addTagDialog_message;
+ public static String TaskTagsPreferencePage_editTagDialog_windowTitle;
+ public static String TaskTagsPreferencePage_editTagDialog_header;
+ public static String TaskTagsPreferencePage_editTagDialog_message;
+
// Theme preferences page
public static String ThemePreferencePage_title;
public static String ThemePreferencePage_description;
diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/TMUIMessages.properties b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/TMUIMessages.properties
index 5ef9d1a10..538886b90 100644
--- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/TMUIMessages.properties
+++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/TMUIMessages.properties
@@ -19,6 +19,7 @@ Button_browse_Workspace=Browse Workspace...
# preference page
TextMatePreferencePage_GrammarRelatedLink=See ''{0}'' for associating editors with grammars.
TextMatePreferencePage_LanguageConfigurationRelatedLink=See ''{0}'' for associating editors with language configurations.
+TextMatePreferencePage_TaskTagsRelatedLink=See ''{0}'' for task tags configuration.
TextMatePreferencePage_ThemeRelatedLink=See ''{0}'' for associating editors with themes.
GrammarPreferencePage_title=TextMate grammars
@@ -33,6 +34,17 @@ GrammarPreferencePage_tab_theme_text=Theme
GrammarPreferencePage_tab_injection_text=Injection
GrammarPreferencePage_preview=Previe&w:
+TaskTagsPreferencePage_description=Manage comment task tags:
+TaskTagsPreferencePage_column_tag=Tag
+TaskTagsPreferencePage_column_type=Marker Type
+TaskTagsPreferencePage_column_level=Level
+TaskTagsPreferencePage_addTagDialog_windowTitle=Task Tag
+TaskTagsPreferencePage_addTagDialog_header=New task tag configuration
+TaskTagsPreferencePage_addTagDialog_message=Create a new task tag configuration
+TaskTagsPreferencePage_editTagDialog_windowTitle=Task Tag
+TaskTagsPreferencePage_editTagDialog_header=Edit task tag configuration
+TaskTagsPreferencePage_editTagDialog_message=Modify an existing task tag configuration
+
ThemePreferencePage_title=TextMate themes
ThemePreferencePage_description=&Create, edit or remove TextMate themes:
ThemePreferencePage_column_name=Name
diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceConstants.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceConstants.java
index 4e4cfb714..d14184dbc 100644
--- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceConstants.java
+++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceConstants.java
@@ -17,6 +17,8 @@
*/
public final class PreferenceConstants {
+ public static final String TASK_TAGS = "org.eclipse.tm4e.ui.taskTags";
+
public static final String THEMES = "org.eclipse.tm4e.ui.themes";
public static final String THEME_ASSOCIATIONS = "org.eclipse.tm4e.ui.themeAssociations";
diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceHelper.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceHelper.java
index 6e3663c1b..4a6edc056 100644
--- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceHelper.java
+++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceHelper.java
@@ -11,26 +11,72 @@
*/
package org.eclipse.tm4e.ui.internal.preferences;
+import java.io.IOException;
import java.util.Collection;
+import java.util.Set;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tm4e.ui.TMUIPlugin;
+import org.eclipse.tm4e.ui.internal.utils.MarkerConfig;
import org.eclipse.tm4e.ui.themes.IThemeAssociation;
import org.eclipse.tm4e.ui.themes.ThemeAssociation;
+import org.osgi.service.prefs.BackingStoreException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
/**
- * Helper class load, save theme preferences with Json format.
- *
+ * Helper class to load, save preferences in JSON format.
*/
public final class PreferenceHelper {
private static final Gson DEFAULT_GSON;
static {
- DEFAULT_GSON = new GsonBuilder().registerTypeAdapter(IThemeAssociation.class,
- (InstanceCreator) type -> new ThemeAssociation()).create();
+ DEFAULT_GSON = new GsonBuilder() //
+ .registerTypeAdapter(IThemeAssociation.class, (InstanceCreator) type -> new ThemeAssociation())
+ .registerTypeAdapterFactory(new TypeAdapterFactory() {
+ @SuppressWarnings("unchecked")
+ @Override
+ @NonNullByDefault({})
+ public @Nullable TypeAdapter create(final Gson gson, final TypeToken type) {
+ if (!MarkerConfig.class.isAssignableFrom(type.getRawType()))
+ return null;
+
+ final var jsonElementAdapter = gson.getAdapter(JsonElement.class);
+ final var problemAdapter = gson.getDelegateAdapter(this, TypeToken.get(MarkerConfig.ProblemMarkerConfig.class));
+ final var taskAdapter = gson.getDelegateAdapter(this, TypeToken.get(MarkerConfig.TaskMarkerConfig.class));
+ return (TypeAdapter) new TypeAdapter() {
+ @Override
+ public void write(final JsonWriter out, final MarkerConfig value) throws IOException {
+ if (value.getClass().isAssignableFrom(MarkerConfig.ProblemMarkerConfig.class)) {
+ problemAdapter.write(out, (MarkerConfig.ProblemMarkerConfig) value);
+ } else if (value.getClass().isAssignableFrom(MarkerConfig.TaskMarkerConfig.class)) {
+ taskAdapter.write(out, (MarkerConfig.TaskMarkerConfig) value);
+ }
+ }
+
+ @Override
+ public MarkerConfig read(final JsonReader in) throws IOException {
+ final var objectJson = jsonElementAdapter.read(in).getAsJsonObject();
+ return switch (MarkerConfig.Type.valueOf(objectJson.get("type").getAsString())) {
+ case PROBLEM -> problemAdapter.fromJsonTree(objectJson);
+ case TASK -> taskAdapter.fromJsonTree(objectJson);
+ };
+ }
+ };
+ }
+ }).create();
}
public static IThemeAssociation[] loadThemeAssociations(final String json) {
@@ -41,6 +87,37 @@ public static String toJsonThemeAssociations(final Collection
return DEFAULT_GSON.toJson(themeAssociations);
}
+ public static Set loadMarkerConfigs() {
+ final var prefs = InstanceScope.INSTANCE.getNode(TMUIPlugin.PLUGIN_ID);
+ final var json = prefs.get(PreferenceConstants.TASK_TAGS, null);
+ Set result = null;
+ try {
+ result = loadMarkerConfigs(json);
+ } catch (JsonSyntaxException ex) {
+ TMUIPlugin.logError(ex);
+ }
+ return result == null ? MarkerConfig.getDefaults() : result;
+ }
+
+ public static Set loadMarkerConfigs(final String json) {
+ return DEFAULT_GSON.fromJson(json, new TypeToken>() {
+ }.getType());
+ }
+
+ public static String toJsonMarkerConfigs(final Set markerConfigs) {
+ return DEFAULT_GSON.toJson(markerConfigs);
+ }
+
+ public static void saveMarkerConfigs(final Set markerConfigs) {
+ final var prefs = InstanceScope.INSTANCE.getNode(TMUIPlugin.PLUGIN_ID);
+ prefs.put(PreferenceConstants.TASK_TAGS, toJsonMarkerConfigs(markerConfigs));
+ try {
+ prefs.flush();
+ } catch (final BackingStoreException ex) {
+ TMUIPlugin.logError(ex);
+ }
+ }
+
private PreferenceHelper() {
}
}
diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/TaskTagsPreferencePage.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/TaskTagsPreferencePage.java
new file mode 100644
index 000000000..3f0bd5ba4
--- /dev/null
+++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/TaskTagsPreferencePage.java
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vegard IT GmbH and others.
+ * This program and the Faccompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Sebastian Thomschke (Vegard IT GmbH) - initial implementation
+ *******************************************************************************/
+package org.eclipse.tm4e.ui.internal.preferences;
+
+import static org.eclipse.tm4e.core.internal.utils.NullSafetyHelper.*;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.tm4e.ui.internal.TMUIMessages;
+import org.eclipse.tm4e.ui.internal.utils.MarkerConfig;
+import org.eclipse.tm4e.ui.internal.utils.MarkerConfig.*;
+import org.eclipse.tm4e.ui.internal.utils.MarkerUtils;
+import org.eclipse.tm4e.ui.internal.widgets.ColumnSelectionAdapter;
+import org.eclipse.tm4e.ui.internal.widgets.ColumnViewerComparator;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+/**
+ * Task Tags preferences page.
+ */
+public final class TaskTagsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
+
+ static final String PAGE_ID = "org.eclipse.tm4e.ui.preferences.TaskTagsPreferencePage";
+
+ private static final class TableLabelProvider extends LabelProvider implements ITableLabelProvider {
+ @Override
+ public @Nullable Image getColumnImage(final @Nullable Object element, final int columnIndex) {
+ return null;
+ }
+
+ @Override
+ public @Nullable String getText(final @Nullable Object element) {
+ return getColumnText(element, 0);
+ }
+
+ @Override
+ public @Nullable String getColumnText(final @Nullable Object element, final int columnIndex) {
+ if (element == null)
+ return "";
+
+ final MarkerConfig item = (MarkerConfig) element;
+ return switch (columnIndex) {
+ case 0 -> item.tag;
+ case 1 -> item.type.name().charAt(0) + item.type.name().substring(1).toLowerCase();
+ case 2 -> switch (item.type) {
+ case PROBLEM -> item.asProblemMarkerConfig().severity.toString();
+ case TASK -> item.asTaskMarkerConfig().priority.toString();
+ };
+ default -> "";
+ };
+ }
+ }
+
+ private final class MarkerConfigEditDialog extends TitleAreaDialog {
+
+ @Nullable
+ MarkerConfig markerConfig;
+
+ Text txtTag = lazyNonNull();
+ Combo cmbType = lazyNonNull();
+ Label lblLevel = lazyNonNull();
+ Combo cmbLevel = lazyNonNull();
+
+ MarkerConfigEditDialog(final Shell parentShell, final @Nullable MarkerConfig markerConfig) {
+ super(parentShell);
+ this.markerConfig = markerConfig;
+ }
+
+ @Override
+ public void create() {
+ super.create();
+ if (markerConfig == null) {
+ getShell().setText(TMUIMessages.TaskTagsPreferencePage_addTagDialog_windowTitle);
+ setTitle(TMUIMessages.TaskTagsPreferencePage_addTagDialog_header);
+ setMessage(TMUIMessages.TaskTagsPreferencePage_addTagDialog_message, IMessageProvider.INFORMATION);
+ } else {
+ getShell().setText(TMUIMessages.TaskTagsPreferencePage_editTagDialog_windowTitle);
+ setTitle(TMUIMessages.TaskTagsPreferencePage_editTagDialog_header);
+ setMessage(TMUIMessages.TaskTagsPreferencePage_editTagDialog_message, IMessageProvider.INFORMATION);
+ }
+ validateInput(null);
+ }
+
+ @Override
+ protected void okPressed() {
+ markerConfig = switch (Type.valueOf(cmbType.getText())) {
+ case PROBLEM -> new ProblemMarkerConfig(txtTag.getText(), ProblemSeverity.valueOf(cmbLevel.getText()));
+ case TASK -> new TaskMarkerConfig(txtTag.getText(), TaskPriority.valueOf(cmbLevel.getText()));
+ };
+ super.okPressed();
+ }
+
+ void validateInput(@SuppressWarnings("unused") final @Nullable Event e) {
+ var btn = getButton(IDialogConstants.OK_ID);
+ if (btn == null)
+ return;
+ btn.setEnabled(!txtTag.getText().isBlank() && cmbType.getSelectionIndex() > -1 && cmbLevel.getSelectionIndex() > -1);
+ }
+
+ @Override
+ protected Control createDialogArea(final @Nullable Composite parent) {
+ final var area = (Composite) super.createDialogArea(parent);
+ final var container = new Composite(area, SWT.NONE);
+ container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ final var layout = new GridLayout(2, false);
+ container.setLayout(layout);
+ createTagText(container);
+ createTypeCombo(container);
+ createLevel(container);
+
+ final var markerConfig = this.markerConfig;
+ if (markerConfig != null) {
+ txtTag.setText(markerConfig.tag);
+ cmbType.setText(markerConfig.type.name());
+ cmbLevel.setText(switch (markerConfig.type) {
+ case PROBLEM -> markerConfig.asProblemMarkerConfig().severity.name();
+ case TASK -> markerConfig.asTaskMarkerConfig().priority.name();
+ });
+ } else {
+ cmbType.setText(MarkerConfig.Type.TASK.name());
+ cmbLevel.setText(TaskPriority.NORMAL.name());
+ }
+
+ return area;
+ }
+
+ void createTagText(final Composite parent) {
+ final var label = new Label(parent, SWT.NONE);
+ label.setText(TMUIMessages.TaskTagsPreferencePage_column_tag);
+ txtTag = new Text(parent, SWT.BORDER);
+ final var layoutData = new GridData();
+ layoutData.grabExcessHorizontalSpace = true;
+ layoutData.horizontalAlignment = GridData.FILL;
+ txtTag.setLayoutData(layoutData);
+ txtTag.addListener(SWT.Modify, this::validateInput);
+ }
+
+ void createTypeCombo(final Composite parent) {
+ final var label = new Label(parent, SWT.NONE);
+ label.setText(TMUIMessages.TaskTagsPreferencePage_column_type);
+ cmbType = new Combo(parent, SWT.READ_ONLY);
+ cmbType.setItems(Stream.of(MarkerConfig.Type.values()).map(Enum::name).toArray(String[]::new));
+ cmbType.addListener(SWT.Modify, (final @Nullable Event e) -> {
+ if (!cmbType.getText().isBlank())
+ switch (MarkerConfig.Type.valueOf(cmbType.getText())) {
+ case PROBLEM:
+ lblLevel.setText("Severity");
+ cmbLevel.setItems(Stream.of(ProblemSeverity.values()).map(Enum::name).toArray(String[]::new));
+ break;
+ case TASK:
+ lblLevel.setText("Priority");
+ cmbLevel.setItems(Stream.of(TaskPriority.values()).map(Enum::name).toArray(String[]::new));
+ break;
+ }
+ validateInput(null);
+ });
+ }
+
+ void createLevel(final Composite parent) {
+ lblLevel = new Label(parent, SWT.NONE);
+ final var layoutData = new GridData();
+ layoutData.widthHint = computeMinimumColumnWidth("1234567890");
+ layoutData.horizontalAlignment = GridData.FILL;
+ lblLevel.setLayoutData(layoutData);
+ cmbLevel = new Combo(parent, SWT.READ_ONLY);
+ cmbLevel.addListener(SWT.Modify, this::validateInput);
+ }
+
+ @Override
+ public boolean isHelpAvailable() {
+ return false;
+ }
+ }
+
+ private final Set markerConfigs = PreferenceHelper.loadMarkerConfigs();
+ private TableViewer markerConfigsTable = lazyNonNull();
+
+ public TaskTagsPreferencePage() {
+ setDescription(TMUIMessages.TaskTagsPreferencePage_description);
+ }
+
+ @Override
+ protected Control createContents(final @Nullable Composite ancestor) {
+ final var parent = new Composite(ancestor, SWT.NONE);
+ final var layout = new GridLayout();
+ layout.numColumns = 2;
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ parent.setLayout(layout);
+
+ final var table = new Table(parent, SWT.BORDER | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE);
+ table.setLayoutData(new GridData(GridData.FILL_BOTH));
+ table.setHeaderVisible(true);
+ table.setLinesVisible(true);
+
+ markerConfigsTable = new TableViewer(table);
+ markerConfigsTable.setLabelProvider(new TableLabelProvider());
+ markerConfigsTable.setContentProvider(new IStructuredContentProvider() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object[] getElements(final @Nullable Object inputElement) {
+ return inputElement == null ? new MarkerConfig[0] : ((Collection) inputElement).toArray();
+ }
+ });
+
+ final var tableColumnSorter = new ColumnViewerComparator();
+ markerConfigsTable.setComparator(tableColumnSorter);
+
+ final var column1 = new TableColumn(table, SWT.NONE);
+ column1.setText(TMUIMessages.TaskTagsPreferencePage_column_tag);
+ column1.setWidth(computeMinimumColumnWidth("1234567890"));
+ column1.addSelectionListener(new ColumnSelectionAdapter(column1, markerConfigsTable, 0, tableColumnSorter));
+
+ final var column2 = new TableColumn(table, SWT.NONE);
+ column2.setText(TMUIMessages.TaskTagsPreferencePage_column_type);
+ column2.setWidth(column1.getWidth());
+ column2.addSelectionListener(new ColumnSelectionAdapter(column2, markerConfigsTable, 1, tableColumnSorter));
+
+ final var column3 = new TableColumn(table, SWT.NONE);
+ column3.setText(TMUIMessages.TaskTagsPreferencePage_column_level);
+ column3.setWidth(column1.getWidth());
+ column3.addSelectionListener(new ColumnSelectionAdapter(column3, markerConfigsTable, 2, tableColumnSorter));
+
+ // Specify default sorting
+ table.setSortColumn(column1);
+ table.setSortDirection(tableColumnSorter.getDirection());
+
+ final var buttons = new Composite(parent, SWT.NONE);
+ buttons.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
+ buttons.setLayout(new GridLayout());
+ final var newTagButton = new Button(buttons, SWT.PUSH);
+ newTagButton.setText(TMUIMessages.Button_new);
+ newTagButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ newTagButton.addListener(SWT.Selection, (final @Nullable Event e) -> {
+ final var dlg = new MarkerConfigEditDialog(getShell(), null);
+ if (dlg.open() == Window.OK) {
+ markerConfigs.add(castNonNull(dlg.markerConfig));
+ markerConfigsTable.refresh();
+ }
+ });
+ final var editTagButton = new Button(buttons, SWT.PUSH);
+ editTagButton.setText(TMUIMessages.Button_edit);
+ editTagButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ editTagButton.addListener(SWT.Selection, (final @Nullable Event e) -> {
+ final var selection = (MarkerConfig) ((IStructuredSelection) markerConfigsTable.getSelection()).getFirstElement();
+ if (selection != null) {
+ final var dlg = new MarkerConfigEditDialog(getShell(), selection);
+ if (dlg.open() == Window.OK) {
+ if (!selection.equals(dlg.markerConfig)) {
+ markerConfigs.remove(selection);
+ markerConfigs.add(castNonNull(dlg.markerConfig));
+ markerConfigsTable.refresh();
+ }
+ }
+ }
+ });
+ final var removeTagButton = new Button(buttons, SWT.PUSH);
+ removeTagButton.setText(TMUIMessages.Button_remove);
+ removeTagButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ removeTagButton.addListener(SWT.Selection, (final @Nullable Event e) -> {
+ final var selection = (MarkerConfig) ((IStructuredSelection) markerConfigsTable.getSelection()).getFirstElement();
+ if (selection != null) {
+ markerConfigs.remove(selection);
+ markerConfigsTable.refresh();
+ }
+ });
+ applyDialogFont(parent);
+
+ markerConfigsTable.setInput(markerConfigs);
+
+ return parent;
+ }
+
+ private int computeMinimumColumnWidth(final String string) {
+ final GC gc = new GC(getShell());
+ try {
+ gc.setFont(JFaceResources.getDialogFont());
+ return gc.stringExtent(string).x + 20; // pad 20 to accommodate table header trimmings
+ } finally {
+ gc.dispose();
+ }
+ }
+
+ @Override
+ public void init(final @Nullable IWorkbench workbench) {
+ }
+
+ @Override
+ protected void performDefaults() {
+ markerConfigs.clear();
+ markerConfigs.addAll(MarkerConfig.getDefaults());
+ markerConfigsTable.refresh();
+ }
+
+ @Override
+ public boolean performOk() {
+ PreferenceHelper.saveMarkerConfigs(markerConfigs);
+ MarkerUtils.reloadMarkerConfigs();
+ return true;
+ }
+}
diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/TextMatePreferencePage.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/TextMatePreferencePage.java
index a4657584e..cfe6e894c 100644
--- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/TextMatePreferencePage.java
+++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/preferences/TextMatePreferencePage.java
@@ -38,14 +38,16 @@ protected Control createContents(@Nullable final Composite parent) {
composite.setLayout(layout);
// Add link to grammar preference page
- addRelatedLink(composite, GrammarPreferencePage.PAGE_ID,
- TMUIMessages.TextMatePreferencePage_GrammarRelatedLink);
+ addRelatedLink(composite, GrammarPreferencePage.PAGE_ID, TMUIMessages.TextMatePreferencePage_GrammarRelatedLink);
// Add link to language configuration preference page
addRelatedLink(composite,
"org.eclipse.tm4e.languageconfiguration.preferences.LanguageConfigurationPreferencePage", //$NON-NLS-1$
TMUIMessages.TextMatePreferencePage_LanguageConfigurationRelatedLink);
+ // Add link to task tags preference page
+ addRelatedLink(composite, TaskTagsPreferencePage.PAGE_ID, TMUIMessages.TextMatePreferencePage_TaskTagsRelatedLink);
+
// Add link to theme preference page
addRelatedLink(composite, ThemePreferencePage.PAGE_ID, TMUIMessages.TextMatePreferencePage_ThemeRelatedLink);
@@ -64,6 +66,5 @@ private void addRelatedLink(final Composite parent, final String pageId, final S
@Override
public void init(@Nullable final IWorkbench workbench) {
-
}
}
diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/utils/MarkerConfig.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/utils/MarkerConfig.java
new file mode 100644
index 000000000..0105fe0f3
--- /dev/null
+++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/utils/MarkerConfig.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vegard IT GmbH and others.
+ * This program and the Faccompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Sebastian Thomschke (Vegard IT GmbH) - initial implementation
+ *******************************************************************************/
+package org.eclipse.tm4e.ui.internal.utils;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.jdt.annotation.Nullable;
+
+public abstract class MarkerConfig {
+
+ public static Set getDefaults() {
+ final var defaults = new HashSet();
+ // problem markers:
+ defaults.add(new ProblemMarkerConfig("ATTN", ProblemSeverity.INFO));
+ defaults.add(new ProblemMarkerConfig("NOTE", ProblemSeverity.INFO));
+ // task markers:
+ defaults.add(new TaskMarkerConfig("BUG", TaskPriority.HIGH));
+ defaults.add(new TaskMarkerConfig("FIXME", TaskPriority.HIGH));
+ defaults.add(new TaskMarkerConfig("HACK", TaskPriority.NORMAL));
+ defaults.add(new TaskMarkerConfig("OPTIMIZE", TaskPriority.NORMAL));
+ defaults.add(new TaskMarkerConfig("TODO", TaskPriority.NORMAL));
+ defaults.add(new TaskMarkerConfig("XXX", TaskPriority.NORMAL));
+ return defaults;
+ }
+
+ public static final class ProblemMarkerConfig extends MarkerConfig {
+ public ProblemMarkerConfig(final String tag, final ProblemSeverity severity) {
+ super(tag, Type.PROBLEM);
+ this.severity = severity;
+ }
+
+ public final ProblemSeverity severity;
+
+ @Override
+ public boolean equals(final @Nullable Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ProblemMarkerConfig other = (ProblemMarkerConfig) obj;
+ return Objects.equals(tag, other.tag) && type == other.type && severity == other.severity;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tag, type, severity);
+ }
+
+ @Override
+ public String toString() {
+ return "ProblemMarkerConfig [tag=" + tag + ", severity=" + severity + "]";
+ }
+ }
+
+ public enum ProblemSeverity {
+ ERROR(IMarker.SEVERITY_ERROR),
+ WARNING(IMarker.SEVERITY_WARNING),
+ INFO(IMarker.SEVERITY_INFO);
+
+ ProblemSeverity(final int value) {
+ this.value = value;
+ }
+
+ public final int value;
+ }
+
+ public static final class TaskMarkerConfig extends MarkerConfig {
+
+ public TaskMarkerConfig(final String tag, final TaskPriority priority) {
+ super(tag, Type.TASK);
+ this.priority = priority;
+ }
+
+ public final TaskPriority priority;
+
+ @Override
+ public boolean equals(final @Nullable Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TaskMarkerConfig other = (TaskMarkerConfig) obj;
+ return Objects.equals(tag, other.tag) && type == other.type && priority == other.priority;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tag, type, priority);
+ }
+
+ @Override
+ public String toString() {
+ return "TaskMarkerConfig [tag=" + tag + ", priority=" + priority + "]";
+ }
+ }
+
+ public enum TaskPriority {
+ HIGH(IMarker.PRIORITY_HIGH),
+ NORMAL(IMarker.PRIORITY_NORMAL),
+ LOW(IMarker.PRIORITY_LOW);
+
+ TaskPriority(final int value) {
+ this.value = value;
+ }
+
+ public final int value;
+ }
+
+ public enum Type {
+ PROBLEM,
+ TASK;
+ }
+
+ private MarkerConfig(final String tag, final Type type) {
+ this.tag = tag;
+ this.type = type;
+ }
+
+ public final String tag;
+ public final Type type;
+
+ public boolean isProblem() {
+ return type == Type.PROBLEM;
+ }
+
+ public boolean isTask() {
+ return type == Type.TASK;
+ }
+
+ public ProblemMarkerConfig asProblemMarkerConfig() {
+ return (ProblemMarkerConfig) this;
+ }
+
+ public TaskMarkerConfig asTaskMarkerConfig() {
+ return (TaskMarkerConfig) this;
+ }
+}
diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/utils/MarkerUtils.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/utils/MarkerUtils.java
index 9c0f825ae..1e1ae5143 100644
--- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/utils/MarkerUtils.java
+++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/utils/MarkerUtils.java
@@ -9,7 +9,7 @@
*/
package org.eclipse.tm4e.ui.internal.utils;
-import static org.eclipse.tm4e.core.internal.utils.NullSafetyHelper.*;
+import static org.eclipse.tm4e.core.internal.utils.NullSafetyHelper.castNonNull;
import java.util.ArrayList;
import java.util.Collections;
@@ -22,14 +22,13 @@
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tm4e.core.model.ITMModel;
import org.eclipse.tm4e.core.model.ModelTokensChangedEvent;
import org.eclipse.tm4e.core.model.TMToken;
import org.eclipse.tm4e.ui.TMUIPlugin;
import org.eclipse.tm4e.ui.internal.model.TMDocumentModel;
+import org.eclipse.tm4e.ui.internal.preferences.PreferenceHelper;
public final class MarkerUtils {
@@ -37,30 +36,22 @@ public final class MarkerUtils {
private static final String PROBLEMMARKER_TYPE = "org.eclipse.tm4e.ui.problemmarker";
private static final String TASKMARKER_TYPE = "org.eclipse.tm4e.ui.taskmarker";
- @NonNullByDefault({})
- private record MarkerConfig(@NonNull String type, int priority, int severity) {
- static MarkerConfig forProblem(final int severity) {
- return new MarkerConfig(PROBLEMMARKER_TYPE, IMarker.PRIORITY_NORMAL, severity);
- }
+ private static final Map MARKERCONFIG_BY_TAG = new HashMap<>();
+ static {
+ reloadMarkerConfigs();
+ }
+ private static final Pattern TAG_SELECTOR_PATTERN = Pattern.compile(
+ "^\\s(" + MARKERCONFIG_BY_TAG.keySet().stream().collect(Collectors.joining("|")) + ")\\b");
- static MarkerConfig forTask(final int priority) {
- return new MarkerConfig(TASKMARKER_TYPE, priority, IMarker.SEVERITY_INFO);
+ public static void reloadMarkerConfigs() {
+ synchronized (MARKERCONFIG_BY_TAG) {
+ MARKERCONFIG_BY_TAG.clear();
+ for (final var markerConfig : PreferenceHelper.loadMarkerConfigs()) {
+ MARKERCONFIG_BY_TAG.put(markerConfig.tag, markerConfig);
+ }
}
}
- private static final Map MARKERCONFIG_BY_TAG = Map.of(
- // problem markers:
- "BUG", MarkerConfig.forProblem(IMarker.SEVERITY_ERROR),
- "NOTE", MarkerConfig.forProblem(IMarker.SEVERITY_INFO),
- // task markers:
- "FIXME", MarkerConfig.forTask(IMarker.PRIORITY_HIGH),
- "HACK", MarkerConfig.forTask(IMarker.PRIORITY_NORMAL),
- "TODO", MarkerConfig.forTask(IMarker.PRIORITY_NORMAL),
- "XXX", MarkerConfig.forTask(IMarker.PRIORITY_NORMAL));
-
- private static final Pattern TAG_SELECTOR_PATTERN = Pattern.compile(
- "\\b(" + MARKERCONFIG_BY_TAG.keySet().stream().collect(Collectors.joining("|")) + ")\\b");
-
/**
* Updates all TM4E text markers of the corresponding document starting from
* event.ranges.get(0).fromLineNumber
until the end of the document.
@@ -82,7 +73,7 @@ public static void updateTextMarkers(final ModelTokensChangedEvent event) {
*
* @param startLineNumber 1-based
*/
- public static void updateTextMarkers(final TMDocumentModel docModel, final int startLineNumber)
+ private static void updateTextMarkers(final TMDocumentModel docModel, final int startLineNumber)
throws CoreException {
final var doc = docModel.getDocument();
@@ -148,17 +139,23 @@ public static void updateTextMarkers(final TMDocumentModel docModel, final int s
final var attrs = new HashMap();
attrs.put(IMarker.LINE_NUMBER, lineNumberObj);
attrs.put(IMarker.MESSAGE, markerText);
- attrs.put(IMarker.PRIORITY, markerConfig.priority);
- attrs.put(IMarker.SEVERITY, markerConfig.severity);
+ switch (markerConfig.type) {
+ case PROBLEM -> attrs.put(IMarker.SEVERITY, markerConfig.asProblemMarkerConfig().severity);
+ case TASK -> attrs.put(IMarker.PRIORITY, markerConfig.asTaskMarkerConfig().priority);
+ }
attrs.put(IMarker.USER_EDITABLE, Boolean.FALSE);
attrs.put(IMarker.SOURCE_ID, "TM4E");
// only create a new marker if no matching marker already exists
- if (!removeMatchingMarker(outdatedMarkers, markerConfig.type, attrs)) {
+ String markerTypeId = switch (markerConfig.type) {
+ case PROBLEM -> PROBLEMMARKER_TYPE;
+ case TASK -> TASKMARKER_TYPE;
+ };
+ if (!removeMatchingMarker(outdatedMarkers, markerTypeId, attrs)) {
final var markerTextStartOffset = lineOffset + token.startIndex + matcher.start();
attrs.put(IMarker.CHAR_START, markerTextStartOffset);
attrs.put(IMarker.CHAR_END, markerTextStartOffset + markerText.length());
- res.createMarker(markerConfig.type, attrs);
+ res.createMarker(markerTypeId, attrs);
}
} catch (final Exception ex) {
TMUIPlugin.logError(ex);
diff --git a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/widgets/ColumnViewerComparator.java b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/widgets/ColumnViewerComparator.java
index 20415ea35..bd867da7e 100644
--- a/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/widgets/ColumnViewerComparator.java
+++ b/org.eclipse.tm4e.ui/src/main/java/org/eclipse/tm4e/ui/internal/widgets/ColumnViewerComparator.java
@@ -20,7 +20,7 @@
import org.eclipse.swt.SWT;
/**
- * Viewer compoarator which sort a given column.
+ * Viewer comparator which sort a given column.
*
*/
public final class ColumnViewerComparator extends ViewerComparator {
diff --git a/org.eclipse.tm4e.ui/src/test/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceHelperTest.java b/org.eclipse.tm4e.ui/src/test/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceHelperTest.java
new file mode 100644
index 000000000..d4aae1a9f
--- /dev/null
+++ b/org.eclipse.tm4e.ui/src/test/java/org/eclipse/tm4e/ui/internal/preferences/PreferenceHelperTest.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Vegard IT GmbH and others.
+ * This program and the Faccompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Sebastian Thomschke (Vegard IT GmbH) - initial implementation
+ *******************************************************************************/
+package org.eclipse.tm4e.ui.internal.preferences;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.tm4e.ui.internal.utils.MarkerConfig;
+import org.junit.jupiter.api.Test;
+
+class PreferenceHelperTest {
+
+ @Test
+ void testMarkerConfigsSerialization() {
+ final var defaults = MarkerConfig.getDefaults();
+ final var defaultsAsJson = PreferenceHelper.toJsonMarkerConfigs(defaults);
+ assertEquals(defaults, PreferenceHelper.loadMarkerConfigs(defaultsAsJson));
+ }
+}