From 07781e2316358933c76c95b2690be25a01e96045 Mon Sep 17 00:00:00 2001 From: gbr Date: Fri, 10 Mar 2023 20:50:59 -0800 Subject: [PATCH] Refactor startup code, including part and property tracking. --- .gitignore | 1 - .mvn/extensions.xml | 8 - .project | 6 + LICENSE.md | 2 +- net.certiv.tools.indentguide.feature/.project | 12 + .../.classpath | 10 + .../.project | 34 ++ .../META-INF/MANIFEST.MF | 10 + .../build.properties | 2 + .../pom.xml | 15 + .../tools/indentguide/ActivatorTest.java | 13 + .../indentguide/painter/GuidePainterTest.java | 0 .../tools/indentguide/util/LineTest.java | 0 .../src/test/resources/line_base_data.csv | 0 .../test/resources/line_blank_logic_data.csv | 0 .../src/test/resources/line_cmt_data.csv | 0 .../.classpath | 12 + .../build.properties | 2 - .../certiv/tools/indentguide/Activator.java | 26 +- .../net/certiv/tools/indentguide/Starter.java | 336 +++++++++++------- .../adaptors/ContentTypeAdaptor.java | 123 +++++++ .../indentguide/adaptors/PageAdaptor.java | 19 - .../indentguide/painter/GuidePainter.java | 61 ++-- .../tools/indentguide/painter/Line.java | 10 +- .../indentguide/preferences/GuidePage.java | 50 +-- .../tools/indentguide/util/MsgBuilder.java | 129 +++---- .../certiv/tools/indentguide/util/Utils.java | 195 ++++++++-- net.certiv.tools.indentguide.site/.project | 12 + 28 files changed, 763 insertions(+), 325 deletions(-) delete mode 100644 .mvn/extensions.xml create mode 100644 net.certiv.tools.indentguide.plugin.test/.classpath create mode 100644 net.certiv.tools.indentguide.plugin.test/.project create mode 100644 net.certiv.tools.indentguide.plugin.test/META-INF/MANIFEST.MF create mode 100644 net.certiv.tools.indentguide.plugin.test/build.properties create mode 100644 net.certiv.tools.indentguide.plugin.test/pom.xml create mode 100644 net.certiv.tools.indentguide.plugin.test/src/test/java/net/certiv/tools/indentguide/ActivatorTest.java rename {net.certiv.tools.indentguide.plugin => net.certiv.tools.indentguide.plugin.test}/src/test/java/net/certiv/tools/indentguide/painter/GuidePainterTest.java (100%) rename {net.certiv.tools.indentguide.plugin => net.certiv.tools.indentguide.plugin.test}/src/test/java/net/certiv/tools/indentguide/util/LineTest.java (100%) rename {net.certiv.tools.indentguide.plugin => net.certiv.tools.indentguide.plugin.test}/src/test/resources/line_base_data.csv (100%) rename {net.certiv.tools.indentguide.plugin => net.certiv.tools.indentguide.plugin.test}/src/test/resources/line_blank_logic_data.csv (100%) rename net.certiv.tools.indentguide.plugin/src/test/resources/line_cmt_logic_data.csv => net.certiv.tools.indentguide.plugin.test/src/test/resources/line_cmt_data.csv (100%) create mode 100644 net.certiv.tools.indentguide.plugin/.classpath create mode 100644 net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/adaptors/ContentTypeAdaptor.java delete mode 100644 net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/adaptors/PageAdaptor.java diff --git a/.gitignore b/.gitignore index da64f08..db23103 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ # Ignore certain Eclipse related files -.classpath .settings/ bin/ target/ diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml deleted file mode 100644 index 8c6d561..0000000 --- a/.mvn/extensions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - org.eclipse.tycho - tycho-build - 3.0.1 - - diff --git a/.project b/.project index cbd0d2d..5ebfaa6 100644 --- a/.project +++ b/.project @@ -5,7 +5,13 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature diff --git a/LICENSE.md b/LICENSE.md index abe301e..fa8ad5a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ ## The MIT License -Copyright (c) 2006-2018 The IndentGuide Authors. +Copyright (c) 2006-2023 The IndentGuide Authors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/net.certiv.tools.indentguide.feature/.project b/net.certiv.tools.indentguide.feature/.project index 52f6138..0253be1 100644 --- a/net.certiv.tools.indentguide.feature/.project +++ b/net.certiv.tools.indentguide.feature/.project @@ -5,7 +5,19 @@ + + org.eclipse.pde.FeatureBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.pde.FeatureNature + org.eclipse.m2e.core.maven2Nature diff --git a/net.certiv.tools.indentguide.plugin.test/.classpath b/net.certiv.tools.indentguide.plugin.test/.classpath new file mode 100644 index 0000000..dee3a50 --- /dev/null +++ b/net.certiv.tools.indentguide.plugin.test/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/net.certiv.tools.indentguide.plugin.test/.project b/net.certiv.tools.indentguide.plugin.test/.project new file mode 100644 index 0000000..ded4595 --- /dev/null +++ b/net.certiv.tools.indentguide.plugin.test/.project @@ -0,0 +1,34 @@ + + + net.certiv.tools.indentguide.plugin.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + org.eclipse.m2e.core.maven2Nature + + diff --git a/net.certiv.tools.indentguide.plugin.test/META-INF/MANIFEST.MF b/net.certiv.tools.indentguide.plugin.test/META-INF/MANIFEST.MF new file mode 100644 index 0000000..5bdca77 --- /dev/null +++ b/net.certiv.tools.indentguide.plugin.test/META-INF/MANIFEST.MF @@ -0,0 +1,10 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: tychotest.test +Bundle-Vendor: tychotest +Bundle-SymbolicName: net.certiv.tools.indentguide.plugin.test +Bundle-Version: 2.1.0.qualifier +Fragment-Host: net.certiv.tools.indentguide;bundle-version="2.1.0.qualifier" +Bundle-RequiredExecutionEnvironment: JavaSE-11 +Require-Bundle: junit-jupiter-api +Automatic-Module-Name: tychotest.test diff --git a/net.certiv.tools.indentguide.plugin.test/build.properties b/net.certiv.tools.indentguide.plugin.test/build.properties new file mode 100644 index 0000000..577870e --- /dev/null +++ b/net.certiv.tools.indentguide.plugin.test/build.properties @@ -0,0 +1,2 @@ +bin.includes = META-INF/,\ + . diff --git a/net.certiv.tools.indentguide.plugin.test/pom.xml b/net.certiv.tools.indentguide.plugin.test/pom.xml new file mode 100644 index 0000000..221002d --- /dev/null +++ b/net.certiv.tools.indentguide.plugin.test/pom.xml @@ -0,0 +1,15 @@ + + + + 4.0.0 + eclipse-test-plugin + + + net.certiv + net.certiv.tools.indentguide.parent + 2.1.0-SNAPSHOT + + + net.certiv.tools.indentguide.plugin.test + diff --git a/net.certiv.tools.indentguide.plugin.test/src/test/java/net/certiv/tools/indentguide/ActivatorTest.java b/net.certiv.tools.indentguide.plugin.test/src/test/java/net/certiv/tools/indentguide/ActivatorTest.java new file mode 100644 index 0000000..5d7823d --- /dev/null +++ b/net.certiv.tools.indentguide.plugin.test/src/test/java/net/certiv/tools/indentguide/ActivatorTest.java @@ -0,0 +1,13 @@ +package net.certiv.tools.indentguide; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ActivatorTest { + + @Test + public void testActivatorId() { + Assertions.assertNotNull(Activator.PLUGIN_ID); + } + +} diff --git a/net.certiv.tools.indentguide.plugin/src/test/java/net/certiv/tools/indentguide/painter/GuidePainterTest.java b/net.certiv.tools.indentguide.plugin.test/src/test/java/net/certiv/tools/indentguide/painter/GuidePainterTest.java similarity index 100% rename from net.certiv.tools.indentguide.plugin/src/test/java/net/certiv/tools/indentguide/painter/GuidePainterTest.java rename to net.certiv.tools.indentguide.plugin.test/src/test/java/net/certiv/tools/indentguide/painter/GuidePainterTest.java diff --git a/net.certiv.tools.indentguide.plugin/src/test/java/net/certiv/tools/indentguide/util/LineTest.java b/net.certiv.tools.indentguide.plugin.test/src/test/java/net/certiv/tools/indentguide/util/LineTest.java similarity index 100% rename from net.certiv.tools.indentguide.plugin/src/test/java/net/certiv/tools/indentguide/util/LineTest.java rename to net.certiv.tools.indentguide.plugin.test/src/test/java/net/certiv/tools/indentguide/util/LineTest.java diff --git a/net.certiv.tools.indentguide.plugin/src/test/resources/line_base_data.csv b/net.certiv.tools.indentguide.plugin.test/src/test/resources/line_base_data.csv similarity index 100% rename from net.certiv.tools.indentguide.plugin/src/test/resources/line_base_data.csv rename to net.certiv.tools.indentguide.plugin.test/src/test/resources/line_base_data.csv diff --git a/net.certiv.tools.indentguide.plugin/src/test/resources/line_blank_logic_data.csv b/net.certiv.tools.indentguide.plugin.test/src/test/resources/line_blank_logic_data.csv similarity index 100% rename from net.certiv.tools.indentguide.plugin/src/test/resources/line_blank_logic_data.csv rename to net.certiv.tools.indentguide.plugin.test/src/test/resources/line_blank_logic_data.csv diff --git a/net.certiv.tools.indentguide.plugin/src/test/resources/line_cmt_logic_data.csv b/net.certiv.tools.indentguide.plugin.test/src/test/resources/line_cmt_data.csv similarity index 100% rename from net.certiv.tools.indentguide.plugin/src/test/resources/line_cmt_logic_data.csv rename to net.certiv.tools.indentguide.plugin.test/src/test/resources/line_cmt_data.csv diff --git a/net.certiv.tools.indentguide.plugin/.classpath b/net.certiv.tools.indentguide.plugin/.classpath new file mode 100644 index 0000000..157bd7d --- /dev/null +++ b/net.certiv.tools.indentguide.plugin/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/net.certiv.tools.indentguide.plugin/build.properties b/net.certiv.tools.indentguide.plugin/build.properties index c2a9c0d..8cb5351 100644 --- a/net.certiv.tools.indentguide.plugin/build.properties +++ b/net.certiv.tools.indentguide.plugin/build.properties @@ -5,5 +5,3 @@ bin.includes = plugin.xml,\ META-INF/,\ OSGI-INF/,\ icons/ -src.excludes = src/test/java/,\ - src/test/resources/ diff --git a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/Activator.java b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/Activator.java index 1a31d9a..60da491 100644 --- a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/Activator.java +++ b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/Activator.java @@ -35,7 +35,7 @@ public static Activator getDefault() { public void start(BundleContext context) throws Exception { super.start(context); plugin = this; - log("Startup"); + log("Starting..."); } @Override @@ -44,15 +44,31 @@ public void stop(BundleContext context) throws Exception { super.stop(context); } + // ------------------------------------------ + + public static void log(Throwable e) { + plugin.getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage())); + } + public static void log(String fmt, Object... args) { - plugin.getLog().log(new Status(IStatus.INFO, PLUGIN_ID, PREFIX + String.format(fmt, args))); + String msg = print(PREFIX + String.format(fmt, args)); + plugin.getLog().log(new Status(IStatus.INFO, PLUGIN_ID, msg)); } public static void log(MsgBuilder mb) { - plugin.getLog().log(new Status(IStatus.INFO, PLUGIN_ID, PREFIX + mb.toString())); + String msg = print(PREFIX + mb.toString()); + plugin.getLog().log(new Status(IStatus.INFO, PLUGIN_ID, msg)); } - public static void log(Throwable e) { - plugin.getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage())); + private static String print(String msg) { + StackTraceElement caller = Thread.currentThread().getStackTrace()[3]; + return info(caller.getClassName(), caller.getLineNumber(), msg); + } + + private static String info(String clsname, int line, String msg) { + if (clsname.startsWith(PLUGIN_ID)) { + clsname = clsname.substring(PLUGIN_ID.length() + 1); + } + return String.format("%s:%s \t%s", clsname, line, msg); } } diff --git a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/Starter.java b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/Starter.java index 65fc0e9..dacf8dc 100644 --- a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/Starter.java +++ b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/Starter.java @@ -8,7 +8,8 @@ *****************************************************************************/ package net.certiv.tools.indentguide; -import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Objects; import java.util.Set; import org.eclipse.core.runtime.CoreException; @@ -23,6 +24,7 @@ import org.eclipse.jface.text.ITextViewerExtension2; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IStartup; @@ -50,43 +52,17 @@ public class Starter implements IStartup { - private static final String UNKNOWN = "Unknown"; // $NON-NLS-1$ - private static final String ACTIVE_EDITOR = "getActiveEditor"; // $NON-NLS-1$ private static final String SOURCE_VIEWER = "getSourceViewer"; // $NON-NLS-1$ private IPreferenceStore store; - private Set excludedTypeIds; // excluded content types - - // row=window; col=page/editor; val=painter - private HashMap> paintMap = new HashMap<>(); - - private final IPropertyChangeListener propsListener = evt -> { - String prop = evt.getProperty(); - Object old = evt.getOldValue(); - Object cur = evt.getNewValue(); + private Set excludedTypeIds; // excluded content type ids - if (prop.equals(IThemeManager.CHANGE_CURRENT_THEME)) { - Activator.log("theme change '%s' [%s] => [%s]", prop, old, cur); - loadPaintPrefs(); + // value=unique data records + private final LinkedHashSet datas = new LinkedHashSet<>(); - } else if (prop.startsWith(Pref.KEY)) { - if (prop.equals(Pref.CONTENT_TYPES)) { - updateContentTypes(); - - Delta delta = Utils.delta(Utils.undelimit((String) old), Utils.undelimit((String) cur)); - MsgBuilder mb = new MsgBuilder("content type change [%s]", prop); - if (!delta.added.isEmpty()) mb.nl().indent("added [%s]", delta.added); - if (!delta.rmved.isEmpty()) mb.nl().indent("removed [%s]", delta.rmved); - Activator.log(mb.toString()); - - } else { - Activator.log("property change '%s' [%s] => [%s]", prop, old, cur); - } - - loadPaintPrefs(); - } - }; + private final PartWatcher partWatcher = new PartWatcher(); + private final PropWatcher propWatcher = new PropWatcher(); @Override public void earlyStartup() { @@ -94,134 +70,162 @@ public void earlyStartup() { @Override public IStatus runInUIThread(IProgressMonitor monitor) { - PlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(propsListener); + IWorkbench wb = PlatformUI.getWorkbench(); + wb.getThemeManager().addPropertyChangeListener(propWatcher); store = Activator.getDefault().getPreferenceStore(); - store.addPropertyChangeListener(propsListener); - updateContentTypes(); + store.addPropertyChangeListener(propWatcher); - IWorkbench workbench = PlatformUI.getWorkbench(); - for (IWorkbenchWindow window : workbench.getWorkbenchWindows()) { - initWorkbenchWindow(window); - } + updateContentTypes(); + initWorkbenchWindows(); - workbench.addWindowListener(new WindowWatcher()); + wb.addWindowListener(new WindowWatcher()); return Status.OK_STATUS; } }; job.setPriority(Job.SHORT); job.setSystem(true); - job.schedule(); + job.schedule(200); } - private void initWorkbenchWindow(IWorkbenchWindow window) { - if (window != null) { - IWorkbenchPage page = window.getActivePage(); - if (page != null) { - IWorkbenchPart part = page.getActivePart(); - Activator.log("workbench page [%s]", name(part)); - - if (part instanceof MultiPageEditorPart) { - IEditorPart editor = activeEditor((MultiPageEditorPart) part); - if (editor != null) installPainter(editor, part); + private void initWorkbenchWindows() { + IWorkbench wb = PlatformUI.getWorkbench(); + for (IWorkbenchWindow window : wb.getWorkbenchWindows()) { + initWorkbenchWindow(window); + window.getPartService().addPartListener(partWatcher); + } + } - } else { - IEditorPart editor = page.getActiveEditor(); - if (editor != null) installPainter(editor, part); - } + private void initWorkbenchWindow(IWorkbenchWindow window) { + for (IWorkbenchPage page : window.getPages()) { + IWorkbenchPart part = page.getActivePart(); + if (part instanceof MultiPageEditorPart || part instanceof AbstractTextEditor) { + installPainter(part); } - window.getPartService().addPartListener(new PartWatcher()); } } - private void installPainter(IEditorPart part, IWorkbenchPart window) { + private void installPainter(IWorkbenchPart part) { if (!store.getBoolean(Pref.ENABLED)) return; - if (part instanceof AbstractTextEditor) { - AbstractTextEditor editor = (AbstractTextEditor) part; - Activator.log("inspecting editor [%s]", name(part)); + AbstractTextEditor editor = activeEditor(part); + if (editor == null) return; - if (!validType(editor)) return; + IContentType type = typeOf(editor); + boolean valid = valid(type); + Activator.log("painter %sallowed for '%s' [%s]", valid ? "" : "dis", srcname(editor), type.getName()); + if (!valid) return; - Class cls = editor.getClass(); - while (!cls.equals(AbstractTextEditor.class)) { - cls = cls.getSuperclass(); + try { + ISourceViewer viewer = Utils.invoke(editor, SOURCE_VIEWER); + + if (viewer instanceof ITextViewerExtension2) { + Data data = findRecord(part, editor); + if (data == null) { + data = new Data(part, editor, type, viewer); + datas.add(data); + } + if (data.painter == null) { + data.painter = new GuidePainter(viewer); + ((ITextViewerExtension2) viewer).addPainter(data.painter); + Activator.log("painter installed"); + } + + } else { + Activator.log("painter not installable in viewer [%s]", Utils.nameOf(viewer)); } - try { - ISourceViewer viewer = Utils.invoke(cls, SOURCE_VIEWER); + } catch (Throwable e) { + Activator.log(e); + } + } - if (viewer instanceof ITextViewerExtension2) { - HashMap painters = paintMap.get(window); - if (painters == null) painters = new HashMap<>(); - if (!painters.containsKey(viewer)) { - GuidePainter painter = new GuidePainter(viewer); - painters.put(viewer, painter); + private AbstractTextEditor activeEditor(IWorkbenchPart part) { + IEditorPart editor = null; - ((ITextViewerExtension2) viewer).addPainter(painter); - Activator.log("painter installed"); - } - paintMap.put(window, painters); - } + if (part instanceof FormEditor) { + editor = ((FormEditor) part).getActiveEditor(); + + } else if (part instanceof MultiPageEditorPart) { + try { + editor = Utils.invoke(part, ACTIVE_EDITOR); } catch (Throwable e) { Activator.log(e); } - } - } - private IEditorPart activeEditor(MultiPageEditorPart mpe) { - if (mpe instanceof FormEditor) { - return ((FormEditor) mpe).getActiveEditor(); + } else if (part instanceof IEditorPart) { + editor = (IEditorPart) part; } - try { - return Utils.invoke(mpe, ACTIVE_EDITOR); + return (editor instanceof AbstractTextEditor) ? (AbstractTextEditor) editor : null; + } - } catch (Throwable e) { - Activator.log(e); - return null; - } + private Data findRecord(IWorkbenchPart part, AbstractTextEditor editor) { + return datas.stream() // + .filter(d -> d.part.equals(part) && d.editor.equals(editor)) // + .findFirst() // + .orElse(null); } - private boolean validType(AbstractTextEditor editor) { - IEditorInput src = editor.getEditorInput(); - String srcname = src != null ? src.getName() : UNKNOWN; - IContentType type = null; + private boolean valid(IContentType type) { + if (type == null) return false; + return !excludedTypeIds.contains(type.getId()); + } + private IContentType typeOf(AbstractTextEditor editor) { IDocumentProvider provider = editor.getDocumentProvider(); if (provider instanceof IDocumentProviderExtension4) { try { - type = ((IDocumentProviderExtension4) provider).getContentType(src); + IContentType type = ((IDocumentProviderExtension4) provider).getContentType(editor.getEditorInput()); + if (type != null) return type; } catch (CoreException e) { Activator.log(e); } } + return Utils.getPlatformTextType(Utils.UNKNOWN); + } - if (type == null) { - Activator.log("painter disallowed for '%s' [%s]", srcname, UNKNOWN); - return false; - } - if (!excludedTypeIds.contains(type.getId())) { - Activator.log("installing painter on '%s' [%s]", srcname, type.getName()); - return true; - } - - Activator.log("painter disallowed for '%s' [%s]", srcname, type.getName()); - return false; + private String srcname(AbstractTextEditor editor) { + IEditorInput src = editor.getEditorInput(); + return src != null ? src.getName() : Utils.UNKNOWN; } private void updateContentTypes() { excludedTypeIds = Utils.undelimit(store.getString(Pref.CONTENT_TYPES)); } - private String name(IWorkbenchPart part) { - return part.getClass().getName(); + private void refreshAll() { + Activator.log("refreshAll..."); + for (Data d : datas) { + if (d.painter != null) { + d.painter.loadPrefs(); + d.painter.redrawAll(); + } + } + } + + private void deactivate(IWorkbenchPart part) { + AbstractTextEditor editor = activeEditor(part); + Data d = findRecord(part, editor); + if (d != null && d.painter != null) { + ((ITextViewerExtension2) d.viewer).removePainter(d.painter); + d.painter = null; + } + } + + private void deactivate(Set types) { + for (Data d : datas) { + if (types.contains(d.type) && d.painter != null) { + ((ITextViewerExtension2) d.viewer).removePainter(d.painter); + d.painter = null; + } + } } - private void loadPaintPrefs() { - for (HashMap map : paintMap.values()) { - for (GuidePainter painter : map.values()) { - painter.loadPrefs(); - painter.redrawAll(); + private void deactivateAll() { + for (Data d : datas) { + if (d.painter != null) { + ((ITextViewerExtension2) d.viewer).removePainter(d.painter); + d.painter = null; } } } @@ -239,14 +243,9 @@ private class PartWatcher extends PartAdaptor { @Override public void partOpened(IWorkbenchPartReference ref) { IWorkbenchPart part = ref.getPart(false); - Activator.log("part opened '%s'", name(part)); - - if (part instanceof MultiPageEditorPart) { - IEditorPart editor = activeEditor((MultiPageEditorPart) part); - if (editor != null) installPainter(editor, part); - - } else if (part instanceof IEditorPart) { - installPainter((IEditorPart) part, part); + if (part instanceof MultiPageEditorPart || part instanceof AbstractTextEditor) { + installPainter(part); + Activator.log("part opened '%s'", Utils.nameOf(part)); } } @@ -254,28 +253,99 @@ public void partOpened(IWorkbenchPartReference ref) { public void partClosed(IWorkbenchPartReference ref) { IWorkbenchPart part = ref.getPart(false); if (part instanceof MultiPageEditorPart || part instanceof AbstractTextEditor) { - HashMap painters = paintMap.remove(part); - if (painters != null) { - for (GuidePainter painter : painters.values()) { - painter.deactivate(true); - } - painters.clear(); - } - Activator.log("part closed '%s'", name(part)); + deactivate(part); + Activator.log("part closed '%s'", Utils.nameOf(part)); } } @Override - public void pageChanged(PageChangedEvent event) { - IPageChangeProvider provider = event.getPageChangeProvider(); + public void pageChanged(PageChangedEvent evt) { + IPageChangeProvider provider = evt.getPageChangeProvider(); if (provider instanceof MultiPageEditorPart) { - Activator.log("MultiPageEditor page change '%s'", name((IWorkbenchPart) provider)); + Activator.log("MultiPageEditor page change '%s'", Utils.nameOf(provider)); + installPainter((IWorkbenchPart) provider); + } + } + } + + private class PropWatcher implements IPropertyChangeListener { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + String prop = evt.getProperty(); + Object old = evt.getOldValue(); + Object now = evt.getNewValue(); + + if (prop.equals(IThemeManager.CHANGE_CURRENT_THEME)) { + Activator.log("theme change '%s' [%s] => [%s]", prop, old, now); + refreshAll(); + + } else if (prop.startsWith(Pref.KEY)) { + if (prop.equals(Pref.ENABLED)) { + Activator.log("status change '%s' [%s] => [%s]", prop, old, now); + if ((boolean) now) { + initWorkbenchWindows(); + + } else { + deactivateAll(); + } - IEditorPart editor = activeEditor((MultiPageEditorPart) provider); - if (editor != null) { - installPainter(editor, (IWorkbenchPart) provider); + } else if (prop.equals(Pref.CONTENT_TYPES)) { + updateContentTypes(); + + // Note: logic is reversed because it is an exclusion list + Delta delta = Delta.of(Utils.undelimit((String) now), Utils.undelimit((String) old)); + if (delta.changed()) { + MsgBuilder mb = new MsgBuilder("content type change [%s]", prop); + + if (delta.increased()) { + initWorkbenchWindows(); + mb.nl().indent("enabled [%s]", delta.added); + } + + if (delta.decreased()) { + mb.nl().indent("disabled [%s]", delta.rmved); + deactivate(Utils.getPlatformTextType(delta.rmved)); + } + + Activator.log(mb.toString()); + } + + } else { + Activator.log("property change '%s' [%s] => [%s]", prop, old, now); } + + refreshAll(); } } } + + private class Data { + IWorkbenchPart part; + AbstractTextEditor editor; + IContentType type; + ISourceViewer viewer; + GuidePainter painter; + + Data(IWorkbenchPart part, AbstractTextEditor editor, IContentType type, ISourceViewer viewer) { + this.part = part; + this.editor = editor; + this.type = type; + this.viewer = viewer; + } + + @Override + public int hashCode() { + return Objects.hash(part, editor); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Data d = (Data) obj; + return Objects.equals(part, d.part) && Objects.equals(editor, d.editor); + } + } } diff --git a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/adaptors/ContentTypeAdaptor.java b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/adaptors/ContentTypeAdaptor.java new file mode 100644 index 0000000..e88ee80 --- /dev/null +++ b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/adaptors/ContentTypeAdaptor.java @@ -0,0 +1,123 @@ +package net.certiv.tools.indentguide.adaptors; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.content.IContentTypeSettings; +import org.eclipse.core.runtime.preferences.IScopeContext; + +import net.certiv.tools.indentguide.util.Utils; + +public class ContentTypeAdaptor implements IContentType { + + private static final String ID_UNK = Utils.UNKNOWN; + private static final String NAME_UNK = "Unknown/Undefined Content Type"; + + private final String id; + private final String name; + private final IContentType baseType; + private final String baseTypeId; + + public static IContentType unknown(IContentType baseType) { + return new ContentTypeAdaptor(ID_UNK, NAME_UNK, baseType); + } + + public ContentTypeAdaptor(String uniqueId, String name, IContentType baseType) { + this.id = uniqueId; + this.name = name; + this.baseType = baseType; + this.baseTypeId = baseType.getId(); + } + + @Override + public String getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + public String getBaseId() { + return baseTypeId; + } + + @Override + public IContentType getBaseType() { + return baseType; + } + + @Override + public boolean isKindOf(IContentType type) { + if (type == null) return false; + if (this == type) return true; + return baseType != null && baseType.isKindOf(type); + } + + @Override + public boolean isUserDefined() { + return false; + } + + @Override + public String toString() { + return id; + } + + // ---------------------------------------------------- + + @Override + public void addFileSpec(String fileSpec, int type) throws CoreException {} + + @Override + public void removeFileSpec(String fileSpec, int type) throws CoreException {} + + @Override + public void setDefaultCharset(String userCharset) throws CoreException {} + + @Override + public IContentDescription getDefaultDescription() { + return null; + } + + @Override + public IContentDescription getDescriptionFor(InputStream contents, QualifiedName[] options) throws IOException { + return null; + } + + @Override + public IContentDescription getDescriptionFor(Reader contents, QualifiedName[] options) throws IOException { + return null; + } + + @Override + public String getDefaultCharset() { + return null; + } + + @Override + public String[] getFileSpecs(int type) { + return null; + } + + @Override + public boolean isAssociatedWith(String fileName) { + return false; + } + + @Override + public boolean isAssociatedWith(String fileName, IScopeContext context) { + return false; + } + + @Override + public IContentTypeSettings getSettings(IScopeContext context) throws CoreException { + return null; + } +} diff --git a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/adaptors/PageAdaptor.java b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/adaptors/PageAdaptor.java deleted file mode 100644 index 19f5b1e..0000000 --- a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/adaptors/PageAdaptor.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.certiv.tools.indentguide.adaptors; - -import org.eclipse.jface.dialogs.IPageChangeProvider; -import org.eclipse.jface.dialogs.IPageChangedListener; - -public class PageAdaptor implements IPageChangeProvider { - - @Override - public Object getSelectedPage() { - return null; - } - - @Override - public void addPageChangedListener(IPageChangedListener listener) {} - - @Override - public void removePageChangedListener(IPageChangedListener listener) {} - -} diff --git a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/painter/GuidePainter.java b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/painter/GuidePainter.java index fb3cd94..bf6f265 100644 --- a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/painter/GuidePainter.java +++ b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/painter/GuidePainter.java @@ -62,6 +62,7 @@ public GuidePainter(ITextViewer viewer) { widget = viewer.getTextWidget(); advanced = Utils.setAdvanced(widget); store = Activator.getDefault().getPreferenceStore(); + loadPrefs(); } @@ -101,9 +102,9 @@ public void redrawAll() { @Override public void paintControl(PaintEvent evt) { - if (widget == null) return; - - handleDrawRequest(evt.gc, evt.x, evt.y, evt.width, evt.height); + if (widget != null) { + handleDrawRequest(evt.gc, evt.x, evt.y, evt.width, evt.height); + } } // Draw characters in view range. @@ -166,7 +167,7 @@ private void drawLineRange(GC gc, int begLine, int endLine, int x, int w) { nextNb = nextNonblankLine(nextNb, docLine, tabWidth); // log(ln.dir, prevNb, ln, nextNb); - // change in dents: -1 <- 0 -> 1 + // change in dents: - <-> + ln.delta = nextNb.tabs() - prevNb.tabs(); ln.stops.clear(); @@ -187,33 +188,37 @@ private void drawLineRange(GC gc, int begLine, int endLine, int x, int w) { boolean last = stop == ln.stops.peekLast(); if (ln.comment) { - - // never draw on first visible character + // skip first visible character if (stop.col == ln.beg) continue; - // do not draw if not - // drawComment && !first || - // drawComment && only && first || - // drawLeadEdge && first - if (!(drawComment && (!first || only && first) || drawLeadEdge && first)) continue; + // skip first where only unless drawComment or drawLeadEdge + if (only && !(drawComment || drawLeadEdge)) continue; + + // skip first where not only unless drawLeadEdge + if (first && !only && !drawLeadEdge) continue; + + // skip last where !only unless drawComment + if (last && !only && !drawComment) continue; } else if (ln.blank) { + // skip first where only and zero + if (first && only && zero) continue; - // do not draw if not - // drawBlankLn && !first && !(last && zero) || - // drawBlankLn && drawLeadEdge && first && zero && nest || - // drawBlankLn && drawLeadEdge && first && !zero - if (!(drawBlankLn - && (!first && !(last && zero) || drawLeadEdge && first && (zero && nest || !zero)))) - continue; + // skip last where not only and zero + if (last && !only && zero) continue; - } else { + // skip first where not zero unless drawBlankLn and drawLeadEdge + if (first && !zero && !(drawBlankLn && drawLeadEdge)) continue; - // never draw on first visible character + // skip first where zero and nest unless drawBlankLn and drawLeadEdge + if (first && zero && nest && !(drawBlankLn && drawLeadEdge)) continue; + + } else { + // skip first visible character if (stop.col == ln.beg) continue; - // do not draw if not drawLeadEdge at first stop - if (!drawLeadEdge && first) continue; + // skip first unless drawLeadEdge + if (first && !drawLeadEdge) continue; } draw(gc, offset, stop.col, spcWidth); @@ -270,6 +275,18 @@ public void loadPrefs() { drawComment = store.getBoolean(Pref.DRAW_COMMENT_BLOCK); } + public boolean isActive() { + return active; + } + + public void activate(boolean redraw) { + if (!active) { + active = true; + widget.addPaintListener(this); + if (redraw) redrawAll(); + } + } + @Override public void deactivate(boolean redraw) { if (active) { diff --git a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/painter/Line.java b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/painter/Line.java index 54b13a1..c514f72 100644 --- a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/painter/Line.java +++ b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/painter/Line.java @@ -9,7 +9,15 @@ public class Line implements Iterable { - private static final Pattern COMMENT = Pattern.compile("^(\\h*/\\*.*|\\h*\\*(\\h.*|/.*|))$"); // $NON-NLS-1$ + private static final Pattern COMMENT = Pattern.compile( // + "^(?:\\h*(?:" // $NON-NLS-1$ + + "/\\*.*|" // $NON-NLS-1$ -> ^'/*'.*$ + + " \\*|" // $NON-NLS-1$ -> ^' *'$ + + " \\* .*|" // $NON-NLS-1$ -> ^' * '.*$ + + " \\*/.*|" // $NON-NLS-1$ -> ^' */'.*$ + + " (?:\\*.*)?\\*/" // $NON-NLS-1$ -> ^' *'.*'*/'.*$ + + "))$" // $NON-NLS-1$ + ); /** The line number (0..n) */ public final int num; diff --git a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/preferences/GuidePage.java b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/preferences/GuidePage.java index 4d143de..96abca3 100644 --- a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/preferences/GuidePage.java +++ b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/preferences/GuidePage.java @@ -11,7 +11,6 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.content.IContentType; -import org.eclipse.core.runtime.content.IContentTypeManager; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.preference.ColorFieldEditor; @@ -38,18 +37,20 @@ import net.certiv.tools.indentguide.Activator; import net.certiv.tools.indentguide.util.Utils; -import net.certiv.tools.indentguide.util.Utils.Delta; public class GuidePage extends PreferencePage implements IWorkbenchPreferencePage { - private static final String[] STYLES = { Messages.style_solid, Messages.style_dash, Messages.style_dot, - Messages.style_dash_dot, Messages.style_dash_dot_dot }; + private static final String[] STYLES = { // + Messages.style_solid, // + Messages.style_dash, // + Messages.style_dot, // + Messages.style_dash_dot, // + Messages.style_dash_dot_dot // + }; private final List blocks = new LinkedList<>(); private final List parts = new LinkedList<>(); - private final IContentTypeManager mgr; - // platform 'text' content type private final IContentType txtType; @@ -59,11 +60,12 @@ public class GuidePage extends PreferencePage implements IWorkbenchPreferencePag public GuidePage() { setPreferenceStore(Activator.getDefault().getPreferenceStore()); - mgr = Platform.getContentTypeManager(); - txtType = mgr.getContentType(IContentTypeManager.CT_TEXT); + txtType = Utils.getPlatformTextType(); Set exclude = Utils.undelimit(getPreferenceStore().getString(Pref.CONTENT_TYPES)); - excludeTypes = exclude.stream().map(e -> mgr.getContentType(e)).filter(t -> !t.equals(txtType)) + excludeTypes = exclude.stream() // + .map(e -> Utils.getPlatformTextType(e)) // + .filter(t -> !t.equals(txtType)) // .collect(Collectors.toCollection(LinkedHashSet::new)); } @@ -100,7 +102,6 @@ public void widgetSelected(final SelectionEvent e) { control.setEnabled(active); } } - // TODO: call #redrawAll on guide painter(s) } }); createVerticalSpacer(comp, 1); @@ -138,10 +139,10 @@ private void createContentTypesGroup(Composite parent) { viewer.setContentProvider(new TypesContentProvider()); viewer.setLabelProvider(new TypesLabelProvider()); viewer.setComparator(new ViewerComparator()); - viewer.setInput(Platform.getContentTypeManager()); viewer.addFilter(new TextTypeFilter()); + viewer.setInput(Platform.getContentTypeManager()); - viewer.expandAll(); // force viewer internal state update + viewer.expandAll(); // req'd to force viewer internal state update viewer.collapseAll(); viewer.expandToLevel(2); @@ -288,7 +289,6 @@ protected void performDefaults() { for (Object type : Utils.platformTextTypes()) { if (!viewer.getChecked(type)) { viewer.setChecked(type, true); - // viewer.setGrayed(type, false); } } } @@ -323,17 +323,7 @@ public boolean performOk() { } else if (part instanceof CheckboxTreeViewer) { CheckboxTreeViewer viewer = (CheckboxTreeViewer) part; - Set unchecked = getUnChecked(viewer); - - Delta delta = Utils.delta(excludeTypes, unchecked); - if (!delta.added.isEmpty()) { - Activator.log("content types excluded: %s", delta.added); - } - if (!delta.rmved.isEmpty()) { - Activator.log("content types restored: %s", delta.rmved); - } - - excludeTypes = unchecked; + excludeTypes = getUnChecked(viewer); store.setValue(Pref.CONTENT_TYPES, Utils.delimitTypes(excludeTypes)); } } @@ -416,15 +406,13 @@ public String getText(Object element) { private class TypesContentProvider implements ITreeContentProvider { - private IContentTypeManager mgr; - @Override public Object[] getChildren(Object parent) { List elements = new ArrayList<>(); IContentType base = (IContentType) parent; - for (IContentType type : mgr.getAllContentTypes()) { - if (Objects.equals(type.getBaseType(), base) && type.isKindOf(txtType)) { - elements.add(type); // constrained to text content types + for (IContentType type : Utils.platformTextTypes()) { + if (Objects.equals(type.getBaseType(), base)) { + elements.add(type); } } return elements.toArray(new IContentType[0]); @@ -447,8 +435,6 @@ public Object[] getElements(Object inputElement) { } @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - mgr = (IContentTypeManager) newInput; - } + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {} } } diff --git a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/util/MsgBuilder.java b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/util/MsgBuilder.java index 033fea3..8b971af 100644 --- a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/util/MsgBuilder.java +++ b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/util/MsgBuilder.java @@ -15,7 +15,7 @@ public MsgBuilder(String format, Object... args) { /** * Append a formatted string. - * + * * @param format the format specification * @param args supplied arguments * @return this @@ -27,7 +27,7 @@ public MsgBuilder append(String format, Object... args) { /** * Conditionally append a formatted string. - * + * * @param cond the append enable condition * @param format the format specification * @param args supplied arguments @@ -38,38 +38,9 @@ public MsgBuilder append(boolean cond, String format, Object... args) { return this; } - /** - * Append a EOL terminal followed by a formatted string. - * - * @param format the format specification - * @param args supplied arguments - * @return this - */ - public MsgBuilder appendNL(String format, Object... args) { - nl(); - sb.append(String.format(format, args)); - return this; - } - - /** - * Append a EOL terminal followed by a formatted string. - * - * @param cond the append enable condition - * @param format the format specification - * @param args supplied arguments - * @return this - */ - public MsgBuilder appendNL(boolean cond, String format, Object... args) { - if (cond) { - nl(); - sb.append(String.format(format, args)); - } - return this; - } - /** * Append a formatted string with a leading tab character. - * + * * @param format the format specification * @param args supplied arguments * @return this @@ -82,7 +53,7 @@ public MsgBuilder indent(String format, Object... args) { /** * Append a formatted string with a leading tab character. - * + * * @param cond the append enable condition * @param format the format specification * @param args supplied arguments @@ -96,37 +67,6 @@ public MsgBuilder indent(boolean cond, String format, Object... args) { return this; } - /** - * Append a EOL terminal followed by a tab character and formatted string. - * - * @param format the format specification - * @param args supplied arguments - * @return this - */ - public MsgBuilder indentNL(String format, Object... args) { - nl(); - indent(); - sb.append(String.format(format, args)); - return this; - } - - /** - * Append a EOL terminal followed by a tab character and formatted string. - * - * @param cond the append enable condition - * @param format the format specification - * @param args supplied arguments - * @return this - */ - public MsgBuilder indentNL(boolean cond, String format, Object... args) { - if (cond) { - nl(); - indent(); - sb.append(String.format(format, args)); - } - return this; - } - /** * Append a tab character. * @@ -180,4 +120,65 @@ public String substring(int start, int end) { public String toString() { return sb.toString(); } + + // /** + // * Append a EOL terminal followed by a formatted string. + // * + // * @param format the format specification + // * @param args supplied arguments + // * @return this + // */ + // public MsgBuilder appendNL(String format, Object... args) { + // nl(); + // sb.append(String.format(format, args)); + // return this; + // } + // + // /** + // * Append a EOL terminal followed by a formatted string. + // * + // * @param cond the append enable condition + // * @param format the format specification + // * @param args supplied arguments + // * @return this + // */ + // public MsgBuilder appendNL(boolean cond, String format, Object... args) { + // if (cond) { + // nl(); + // sb.append(String.format(format, args)); + // } + // return this; + // } + + // /** + // * Append a EOL terminal followed by a tab character and formatted string. + // * + // * @param format the format specification + // * @param args supplied arguments + // * @return this + // */ + // public MsgBuilder indentNL(String format, Object... args) { + // nl(); + // indent(); + // sb.append(String.format(format, args)); + // return this; + // } + // + // /** + // * Append a EOL terminal followed by a tab character and formatted string. + // * + // * @param cond the append enable condition + // * @param format the format specification + // * @param args supplied arguments + // * @return this + // */ + // public MsgBuilder indentNL(boolean cond, String format, Object... args) { + // if (cond) { + // nl(); + // indent(); + // sb.append(String.format(format, args)); + // } + // return this; + // } + } diff --git a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/util/Utils.java b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/util/Utils.java index fcbddc1..5f66954 100644 --- a/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/util/Utils.java +++ b/net.certiv.tools.indentguide.plugin/src/main/java/net/certiv/tools/indentguide/util/Utils.java @@ -2,9 +2,10 @@ import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; -import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import java.util.stream.Collectors; @@ -32,18 +33,21 @@ import org.eclipse.ui.texteditor.AbstractTextEditor; import org.eclipse.ui.themes.ColorUtil; +import net.certiv.tools.indentguide.adaptors.ContentTypeAdaptor; import net.certiv.tools.indentguide.preferences.Pref; public class Utils { public static final char SPC = ' '; // $NON-NLS-1$ + public static final char DOT = '.'; // $NON-NLS-1$ public static final char TAB = '\t'; // $NON-NLS-1$ public static final char NLC = '\n'; // $NON-NLS-1$ public static final char RET = '\r'; // $NON-NLS-1$ - public static final String EOL = System.lineSeparator(); + public static final String EMPTY = ""; // $NON-NLS-1$ public static final String SPACE = " "; // $NON-NLS-1$ public static final String DELIM = "|"; // $NON-NLS-1$ + public static final String EOL = System.lineSeparator(); public static final char SP_THIN = '\u2009'; // public static final char SP_MARK = '\u00b7'; // · @@ -51,30 +55,93 @@ public class Utils { public static final char RET_MARK = '\u00a4'; // ¤ public static final char NL_MARK = '\u00b6'; // ¶ + public static final String UNKNOWN = "Unknown"; // $NON-NLS-1$ + public static final Class[] NoParams = new Class[0]; + public static final Object[] NoArgs = new Object[0]; + private static final String EditorsID = "org.eclipse.ui.editors"; //$NON-NLS-1$ private static final IEclipsePreferences[] Scopes = new IEclipsePreferences[] { InstanceScope.INSTANCE.getNode(EditorsID), // DefaultScope.INSTANCE.getNode(EditorsID) // }; - public static final Class[] NoParams = null; - public static final Object[] NoArgs = null; - private static Set platformTextTypes; + private static IContentType txtType; private Utils() {} + /** Returns the class name of the given object, or {@code UNKNOWN} if {@code null}. */ + public static String nameOf(Object obj) { + return nameOf(obj, UNKNOWN); + } + + /** Returns the class name of the given object, or the given default if {@code null}. */ + public static String nameOf(Object obj, String def) { + return obj != null ? obj.getClass().getName() : def; + } + + /** + * Invoke the method corresponding to the given method name having no parameters on the given + * target object. The method may be private. Invokes the first matching method found in the + * class hierarchy of the given target. + * + * @param the return object type + * @param target the invocation target object + * @param name the method name + * @return the invocation result + * @throws Throwable on any failure + */ public static T invoke(Object target, String methodName) throws Throwable { return invoke(target, methodName, NoParams, NoArgs); } + /** + * Invoke the method corresponding to the given method name and parameter types on the given + * target object. The method may be private. Invokes the first matching method found in the + * class hierarchy of the given target. + * + * @param the return object type + * @param target the invocation target object + * @param name the method name + * @param params the method parameter types + * @param args the method parameter arguments + * @return the invocation result + * @throws Throwable on any failure + */ @SuppressWarnings("unchecked") - public static T invoke(Object target, String methodName, Class[] params, Object[] args) throws Throwable { - Method m = target.getClass().getMethod(methodName, params); + public static T invoke(Object target, String name, Class[] params, Object[] args) throws Throwable { + Class cls = find(target.getClass(), name, params); + Method m = cls.getDeclaredMethod(name, params); m.setAccessible(true); return (T) m.invoke(target, args); } + /** + * Finds the Class (the given class or a superclass thereof) that declares a method with the + * given name and parameter types. + * + * @param from the initial class to consider + * @param name the target method name + * @param params the target method parameter types + * @return the Class that declares a method with the given name and parameter types, or + * {@code null} if not found + */ + public static Class find(Class from, String name, Class[] params) { + Class cls = from; + while (cls != null) { + Method[] methods = cls.getDeclaredMethods(); + for (Method method : methods) { + if (method.getName().equals(name)) { + if (Arrays.equals(method.getParameterTypes(), params)) { + return cls; + } + } + } + cls = cls.getSuperclass(); + } + return null; + } + /** Restrict the range of the given val to between -1 and 1. */ public static int limit(int val) { return (val > 1) ? 1 : (val < -1 ? -1 : val); @@ -93,7 +160,6 @@ public static String encode(String in) { break; case TAB: sb.append(TAB_MARK); - // sb.append(SP_THIN); break; case RET: sb.append(RET_MARK); @@ -173,20 +239,61 @@ public static int lineHeight(Control comp, int lines) { } /** - * Returns the text content type identifiers known to the platform at plugin startup. + * Returns the text content types known to the platform at plugin startup. Includes an 'UNKNOWN' + * placeholder entry for unknown/undefined content types * * @return the known text content type identifiers */ public static Set platformTextTypes() { if (platformTextTypes == null) { IContentTypeManager mgr = Platform.getContentTypeManager(); - IContentType txtType = mgr.getContentType(IContentTypeManager.CT_TEXT); + IContentType txtType = getPlatformTextType(); platformTextTypes = Stream.of(mgr.getAllContentTypes()).filter(t -> t.isKindOf(txtType)) .collect(Collectors.toCollection(LinkedHashSet::new)); + + // add a limited placeholder entry for unknown/undefined content types + platformTextTypes.add(ContentTypeAdaptor.unknown(txtType)); + + // make immutable + platformTextTypes = Collections.unmodifiableSet(platformTextTypes); } return platformTextTypes; } + /** + * Returns the well-defined platform text content type. + * + * @return the text content type + */ + public static IContentType getPlatformTextType() { + if (txtType == null) { + IContentTypeManager mgr = Platform.getContentTypeManager(); + txtType = mgr.getContentType(IContentTypeManager.CT_TEXT); + } + return txtType; + } + + /** + * Returns the platform text content type matching the given type identifier. + * + * @return the text content type for the given identifier, or {@code null} if not found + */ + public static IContentType getPlatformTextType(String id) { + return platformTextTypes().stream().filter(t -> t.getId().equals(id)).findFirst().orElse(null); + } + + /** + * Returns the platform text content types matching the given type identifiers. + * + * @return set of text content types for the given identifiers + */ + public static Set getPlatformTextType(Set ids) { + return ids.stream() // + .map(id -> getPlatformTextType(id)) // + .filter(Objects::nonNull) // + .collect(Collectors.toUnmodifiableSet()); + } + /** * Convert a set of content types to a delimited string suitable for preference storage. * @@ -215,8 +322,8 @@ public static String delimit(Set terms) { * @param delimited a delimited string of preference terms * @return set containing the individual terms */ - public static Set undelimit(String delimited) { - Set types = new LinkedHashSet<>(); + public static LinkedHashSet undelimit(String delimited) { + LinkedHashSet types = new LinkedHashSet<>(); StringTokenizer tokens = new StringTokenizer(delimited, DELIM); while (tokens.hasMoreTokens()) { types.add(tokens.nextToken()); @@ -230,7 +337,7 @@ public static Set undelimit(String delimited) { * @param array the source array to convert * @return a list containing the array elements */ - public static List toList(T[] array) { + public static LinkedList toList(T[] array) { return Arrays.stream(array).collect(Collectors.toCollection(LinkedList::new)); } @@ -240,7 +347,7 @@ public static List toList(T[] array) { * @param array the source array to convert * @return a set containing the array elements */ - public static Set toSet(T[] array) { + public static LinkedHashSet toSet(T[] array) { return Arrays.stream(array).collect(Collectors.toCollection(LinkedHashSet::new)); } @@ -249,8 +356,8 @@ public static Set toSet(T[] array) { *

* [0,1,2], [1,2,3] -> [0] */ - public static Set subtract(Set a, Set b) { - Set res = new LinkedHashSet<>(a); + public static LinkedHashSet subtract(Set a, Set b) { + LinkedHashSet res = new LinkedHashSet<>(a); res.removeAll(b); return res; } @@ -260,35 +367,49 @@ public static Set subtract(Set a, Set b) { *

* [0,1,2], [1,2,3] -> [0,3] */ - public static Set disjoint(Set a, Set b) { - Set res = new LinkedHashSet<>(); + public static LinkedHashSet disjoint(Set a, Set b) { + LinkedHashSet res = new LinkedHashSet<>(); res.addAll(subtract(a, b)); res.addAll(subtract(b, a)); return res; } - /** - * Returns the delta between an initial set A and later set B. - *

- * [0,1,2], [1,2,3] -> added [3] & removed [0] - */ - public static Delta delta(Set before, Set after) { - return new Delta<>(subtract(after, before), subtract(before, after)); - } - public static class Delta { - public Set added; - public Set rmved; + public final Set added; + public final Set rmved; + + /** + * Computes the delta between a prior set A and later set B. + *

+ * [0,1,2], [1,2,3] -> added [3] & removed [0] + * + * @param prior the relatively earlier state set + * @param later the relatively later state set + * @return the delta between the prior set A and later set B + */ + public static Delta of(Set prior, Set later) { + return new Delta<>(subtract(later, prior), subtract(prior, later)); + } + + private Delta(Set added, Set rmved) { + this.added = Collections.unmodifiableSet(added); + this.rmved = Collections.unmodifiableSet(rmved); + } + + /** Returns {@code true} if either added or removed set is non-empty. */ + public boolean changed() { + return increased() || decreased(); + } - public Delta(Set added, Set rmved) { - this.added = added; - this.rmved = rmved; + /** Returns {@code true} if the added set is non-empty. */ + public boolean increased() { + return !added.isEmpty(); } - /** Returns {@code true} if both added and removed sets are empty. */ - public boolean isEmpty() { - return added.isEmpty() & rmved.isEmpty(); + /** Returns {@code true} if the removed set is non-empty. */ + public boolean decreased() { + return !rmved.isEmpty(); } } @@ -302,9 +423,9 @@ public boolean isEmpty() { public static boolean setAdvanced(StyledText widget) { GC gc = new GC(widget); gc.setAdvanced(true); - boolean advanced = gc.getAdvanced(); + boolean adv = gc.getAdvanced(); gc.dispose(); - return advanced; + return adv; } public static Color getColor(IPreferenceStore store) { diff --git a/net.certiv.tools.indentguide.site/.project b/net.certiv.tools.indentguide.site/.project index d51bf70..3b67b8b 100644 --- a/net.certiv.tools.indentguide.site/.project +++ b/net.certiv.tools.indentguide.site/.project @@ -5,7 +5,19 @@ + + org.eclipse.pde.UpdateSiteBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.pde.UpdateSiteNature + org.eclipse.m2e.core.maven2Nature