Skip to content

Commit

Permalink
feat: add option to enable displaying TM scope name in hover
Browse files Browse the repository at this point in the history
  • Loading branch information
sebthom committed May 16, 2024
1 parent 413a0dd commit 5392f9f
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 25 deletions.
8 changes: 7 additions & 1 deletion org.eclipse.tm4e.ui/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
path="./themes/Eclipse-light.css" />
<theme id="org.eclipse.tm4e.ui.themes.WtpXmlClassic"
name="%Theme.WtpXmlClassic.name"
path="./themes/WTP-XML-Classic.css" />
path="./themes/WTP-XML-Classic.css" />
<!-- "Dark" themes -->
<theme id="org.eclipse.tm4e.ui.themes.Dark"
name="%Theme.Dark.name"
Expand Down Expand Up @@ -138,4 +138,10 @@
<super type="org.eclipse.tm4e.ui.textmarker"/>
<persistent value="true"/>
</extension>

<extension point="org.eclipse.ui.genericeditor.hoverProviders">
<hoverProvider
class="org.eclipse.tm4e.ui.internal.hover.TMTokenTextHover"
contentType="org.eclipse.core.runtime.text" />
</extension>
</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public class TMUIPlugin extends AbstractUIPlugin {
@Nullable
private static volatile TMUIPlugin plugin;

public static boolean getPreference(final String key, final boolean defaultValue) {
return Platform.getPreferencesService().getBoolean(PLUGIN_ID, key, defaultValue, null /* = search in all available scopes */);
}

public static @Nullable String getPreference(final String key, final @Nullable String defaultValue) {
return Platform.getPreferencesService().getString(PLUGIN_ID, key, defaultValue, null /* = search in all available scopes */);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*******************************************************************************
* Copyright (c) 2024 Vegard IT GmbH and others.
* This program and the accompanying 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) - initial implementation
*******************************************************************************/
package org.eclipse.tm4e.ui.internal.hover;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextHoverExtension;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tm4e.core.model.TMToken;
import org.eclipse.tm4e.ui.internal.model.TMDocumentModel;
import org.eclipse.tm4e.ui.internal.model.TMModelManager;
import org.eclipse.tm4e.ui.internal.preferences.PreferenceHelper;
import org.eclipse.ui.editors.text.EditorsUI;

public class TMTokenTextHover implements ITextHover, ITextHoverExtension {

private static final class RegionWithTMToken extends Region {
final TMToken token;

RegionWithTMToken(final int offset, final int length, final TMToken token) {
super(offset, length);
this.token = token;
}
}

@Override
public IInformationControlCreator getHoverControlCreator() {
// setup a hover control that interprets basic HTML input
return new AbstractReusableInformationControlCreator() {
@Override
protected IInformationControl doCreateInformationControl(final @NonNullByDefault({}) Shell parent) {
return new DefaultInformationControl(parent, EditorsUI.getTooltipAffordanceString());
}
};
}

@Override
public @Nullable String getHoverInfo(final @NonNullByDefault({}) ITextViewer textViewer,
final @NonNullByDefault({}) IRegion hoverRegion) {
if (hoverRegion instanceof final RegionWithTMToken regionWithToken) {
return "<b>TextMate scope:</b> " + regionWithToken.token.type;
}
return null;
}

@Override
public @Nullable IRegion getHoverRegion(final @NonNullByDefault({}) ITextViewer textViewer, final int offset) {
if (!PreferenceHelper.isTMScopeHoverEnabled())
return null;

final @Nullable IDocument doc = textViewer.getDocument();
if (doc == null)
return null;

final TMDocumentModel model = TMModelManager.INSTANCE.getConnectedModel(doc);
if (model == null)
return null;

try {
// retrieve parsed TM tokens of the hovered line
final int lineIndex = doc.getLineOfOffset(offset);
final var tokens = model.getLineTokens(lineIndex);
if (tokens == null)
return null;

// find the TM token at the hover position
final int lineStartOffset = doc.getLineOffset(lineIndex);
TMToken hoveredToken = null;
TMToken nextToken = null;
for (final TMToken token : tokens) {
if (token.startIndex <= offset - lineStartOffset) {
hoveredToken = token;
} else {
nextToken = token;
break;
}
}
if (hoveredToken == null)
return null;

return new RegionWithTMToken(
lineStartOffset + hoveredToken.startIndex,
nextToken == null
? doc.getLineLength(lineIndex) - hoveredToken.startIndex
: nextToken.startIndex - hoveredToken.startIndex,
hoveredToken);
} catch (final BadLocationException e) {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NonNullByDefault
package org.eclipse.tm4e.ui.internal.hover;

import org.eclipse.jdt.annotation.NonNullByDefault;
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.text.IDocument;
import org.eclipse.tm4e.ui.model.ITMModelManager;

Expand Down Expand Up @@ -44,6 +45,10 @@ public void disconnect(final IDocument document) {
}
}

public @Nullable TMDocumentModel getConnectedModel(final IDocument document) {
return models.get(document);
}

@Override
public boolean isConnected(final IDocument document) {
return models.containsKey(document);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public abstract class AbstractPreferencePage extends PreferencePage implements I

private final @Nullable String title;

protected AbstractPreferencePage(final @Nullable String title, final String description) {
protected AbstractPreferencePage(final @Nullable String title, final @Nullable String description) {
this.title = title;
setDescription(description);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public final class PreferenceConstants {
public static final String DEFAULT_DARK_THEME = "org.eclipse.tm4e.ui.themes.defaultDarkTheme";
public static final String DEFAULT_LIGHT_THEME = "org.eclipse.tm4e.ui.themes.defaultLightTheme";

public static final String TMSCOPE_HOVER_ENABLED = "org.eclipse.tm4e.ui.tmScopeHoverEnabled";

private PreferenceConstants() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ public static void saveMarkerConfigs(final Set<MarkerConfig> markerConfigs) thro
prefs.flush();
}

public static boolean isTMScopeHoverEnabled() {
return TMUIPlugin.getPreference(PreferenceConstants.TMSCOPE_HOVER_ENABLED, false);
}

public static void saveTMScopeHoverEnabled(boolean enableTMScopeHover) throws BackingStoreException {
final var prefs = InstanceScope.INSTANCE.getNode(TMUIPlugin.PLUGIN_ID);
prefs.putBoolean(PreferenceConstants.TMSCOPE_HOVER_ENABLED, enableTMScopeHover);
prefs.flush();
}

private PreferenceHelper() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,63 +11,96 @@
*/
package org.eclipse.tm4e.ui.internal.preferences;

import static org.eclipse.tm4e.core.internal.utils.NullSafetyHelper.lazyNonNull;

import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.tm4e.ui.TMUIPlugin;
import org.eclipse.tm4e.ui.internal.TMUIMessages;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.dialogs.PreferenceLinkArea;
import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
import org.osgi.service.prefs.BackingStoreException;

/**
* TextMate Global preferences page.
*
*/
public final class TextMatePreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
public final class TextMatePreferencePage extends AbstractPreferencePage {

private Button enableTMScopeHover = lazyNonNull();

public TextMatePreferencePage() {
super(null, null);
}

@Override
protected Control createContents(@Nullable final Composite parent) {
protected Control createContents(final @NonNullByDefault({}) Composite parent) {
final var composite = new Composite(parent, SWT.NONE);
final var layout = new GridLayout(1, false);
layout.marginHeight = layout.marginWidth = 0;
composite.setLayout(layout);
composite.setLayout(GridLayoutFactory.fillDefaults().create());

addRelatedLinks(composite);

new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL)
.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

enableTMScopeHover = new Button(composite, SWT.CHECK);
enableTMScopeHover.setText("Show TextMate scope name in hovers.");
enableTMScopeHover.setSelection(PreferenceHelper.isTMScopeHoverEnabled());

applyDialogFont(composite);
return composite;
}

private void addRelatedLinks(final Composite parent) {
// Add link to grammar preference page
addRelatedLink(composite, GrammarPreferencePage.PAGE_ID, TMUIMessages.TextMatePreferencePage_GrammarRelatedLink);
addRelatedLink(parent, GrammarPreferencePage.PAGE_ID, TMUIMessages.TextMatePreferencePage_GrammarRelatedLink);

// Add link to language configuration preference page
if (Platform.getBundle("org.eclipse.tm4e.languageconfiguration") != null) { //$NON-NLS-1$
addRelatedLink(composite,
"org.eclipse.tm4e.languageconfiguration.preferences.LanguageConfigurationPreferencePage", //$NON-NLS-1$
if (Platform.getBundle("org.eclipse.tm4e.languageconfiguration") != null) {
addRelatedLink(parent,
"org.eclipse.tm4e.languageconfiguration.preferences.LanguageConfigurationPreferencePage",
TMUIMessages.TextMatePreferencePage_LanguageConfigurationRelatedLink);
}

// Add link to task tags preference page
addRelatedLink(composite, TaskTagsPreferencePage.PAGE_ID, TMUIMessages.TextMatePreferencePage_TaskTagsRelatedLink);
addRelatedLink(parent, TaskTagsPreferencePage.PAGE_ID, TMUIMessages.TextMatePreferencePage_TaskTagsRelatedLink);

// Add link to theme preference page
addRelatedLink(composite, ThemePreferencePage.PAGE_ID, TMUIMessages.TextMatePreferencePage_ThemeRelatedLink);

applyDialogFont(composite);
return composite;

addRelatedLink(parent, ThemePreferencePage.PAGE_ID, TMUIMessages.TextMatePreferencePage_ThemeRelatedLink);
}

private void addRelatedLink(final Composite parent, final String pageId, final String message) {
final var contentTypeArea = new PreferenceLinkArea(parent, SWT.NONE, pageId, message,
(IWorkbenchPreferenceContainer) getContainer(), null);

final var data = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
contentTypeArea.getControl().setLayoutData(data);
contentTypeArea.getControl()
.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
}

@Override
public void init(@Nullable final IWorkbench workbench) {
public void init(final @NonNullByDefault({}) IWorkbench workbench) {
}

@Override
protected void performDefaults() {
enableTMScopeHover.setSelection(PreferenceHelper.isTMScopeHoverEnabled());
}

@Override
public boolean performOk() {
try {
PreferenceHelper.saveTMScopeHoverEnabled(enableTMScopeHover.getSelection());
} catch (final BackingStoreException ex) {
TMUIPlugin.logError(ex);
return false;
}
return super.performOk();
}
}

0 comments on commit 5392f9f

Please sign in to comment.