diff --git a/core/org.eclipse.cdt.core.native/src/org/eclipse/cdt/utils/spawner/Spawner.java b/core/org.eclipse.cdt.core.native/src/org/eclipse/cdt/utils/spawner/Spawner.java index 27329bb630c..0f49b5eea67 100644 --- a/core/org.eclipse.cdt.core.native/src/org/eclipse/cdt/utils/spawner/Spawner.java +++ b/core/org.eclipse.cdt.core.native/src/org/eclipse/cdt/utils/spawner/Spawner.java @@ -286,6 +286,11 @@ public class Spawner extends Process { } } + @Override + public long pid() { + return pid; + } + /** * On Windows, interrupt the spawned program by using Cygwin's utility 'kill -SIGINT' if it's a Cgywin * program, otherwise send it a CTRL-C. If Cygwin's 'kill' command is not available, send a CTRL-C. On diff --git a/terminal/plugins/org.eclipse.tm.terminal.connector.process/META-INF/MANIFEST.MF b/terminal/plugins/org.eclipse.tm.terminal.connector.process/META-INF/MANIFEST.MF index f3d85ac412a..fb07c4c3b8c 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.connector.process/META-INF/MANIFEST.MF +++ b/terminal/plugins/org.eclipse.tm.terminal.connector.process/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.tm.terminal.connector.process;singleton:=true -Bundle-Version: 4.7.0.qualifier +Bundle-Version: 4.8.0.qualifier Bundle-Activator: org.eclipse.tm.terminal.connector.process.activator.UIPlugin Bundle-Vendor: %providerName Import-Package: org.eclipse.cdt.utils.pty;mandatory:=native, diff --git a/terminal/plugins/org.eclipse.tm.terminal.connector.process/src/org/eclipse/tm/terminal/connector/process/ProcessConnector.java b/terminal/plugins/org.eclipse.tm.terminal.connector.process/src/org/eclipse/tm/terminal/connector/process/ProcessConnector.java index c7865a0876c..aa2337c70d3 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.connector.process/src/org/eclipse/tm/terminal/connector/process/ProcessConnector.java +++ b/terminal/plugins/org.eclipse.tm.terminal.connector.process/src/org/eclipse/tm/terminal/connector/process/ProcessConnector.java @@ -16,8 +16,12 @@ import java.io.File; import java.io.IOException; import java.io.StreamTokenizer; import java.io.StringReader; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import org.eclipse.cdt.utils.pty.PTY; import org.eclipse.cdt.utils.spawner.ProcessFactory; @@ -32,6 +36,7 @@ import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; import org.eclipse.tm.internal.terminal.provisional.api.NullSettingsStore; import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; +import org.eclipse.tm.terminal.connector.process.activator.UIPlugin; import org.eclipse.tm.terminal.connector.process.nls.Messages; import org.eclipse.tm.terminal.view.core.interfaces.constants.ILineSeparatorConstants; import org.eclipse.tm.terminal.view.core.utils.Env; @@ -296,4 +301,20 @@ public class ProcessConnector extends AbstractStreamsConnector { } } + /** + * @since 4.8 + */ + @Override + public Optional getWorkingDirectory() { + long pid = process.pid(); + try { + if (Platform.getOS().equals(Platform.OS_LINUX)) { + Path procCwd = Files.readSymbolicLink(FileSystems.getDefault().getPath("/proc/" + pid + "/cwd")); //$NON-NLS-1$//$NON-NLS-2$ + return Optional.of(procCwd.toAbsolutePath().toString()); + } + } catch (Exception e) { + UIPlugin.log("Failed to obtain working directory of process id " + pid, e); //$NON-NLS-1$ + } + return Optional.empty(); + } } diff --git a/terminal/plugins/org.eclipse.tm.terminal.connector.process/src/org/eclipse/tm/terminal/connector/process/activator/UIPlugin.java b/terminal/plugins/org.eclipse.tm.terminal.connector.process/src/org/eclipse/tm/terminal/connector/process/activator/UIPlugin.java index 64c735dd7d5..00a56ddb17e 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.connector.process/src/org/eclipse/tm/terminal/connector/process/activator/UIPlugin.java +++ b/terminal/plugins/org.eclipse.tm.terminal.connector.process/src/org/eclipse/tm/terminal/connector/process/activator/UIPlugin.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.tm.terminal.connector.process.activator; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.swt.graphics.Image; @@ -103,4 +105,13 @@ public class UIPlugin extends AbstractUIPlugin { public static ImageDescriptor getImageDescriptor(String key) { return getDefault().getImageRegistry().getDescriptor(key); } + + public static void log(String msg, Throwable e) { + log(new Status(IStatus.ERROR, getUniqueIdentifier(), IStatus.ERROR, msg, e)); + } + + public static void log(IStatus status) { + getDefault().getLog().log(status); + } + } diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.options b/terminal/plugins/org.eclipse.tm.terminal.control/.options index 7e591a4d4e7..752c05f737c 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/.options +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.options @@ -1,3 +1,4 @@ org.eclipse.tm.terminal.control/debug/log = false org.eclipse.tm.terminal.control/debug/log/char = false org.eclipse.tm.terminal.control/debug/log/VT100Backend = false +org.eclipse.tm.terminal.control/debug/log/hover = false diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/META-INF/MANIFEST.MF b/terminal/plugins/org.eclipse.tm.terminal.control/META-INF/MANIFEST.MF index 4279683d5a8..6689e2ee7a5 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/META-INF/MANIFEST.MF +++ b/terminal/plugins/org.eclipse.tm.terminal.control/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.tm.terminal.control; singleton:=true -Bundle-Version: 5.1.0.qualifier +Bundle-Version: 5.2.0.qualifier Bundle-Activator: org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalConnector.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalConnector.java index be2350a43f1..3737a5cde3f 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalConnector.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalConnector.java @@ -16,6 +16,7 @@ package org.eclipse.tm.internal.terminal.connector; import java.io.OutputStream; +import java.util.Optional; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.Platform; @@ -260,4 +261,12 @@ public class TerminalConnector implements ITerminalConnector { // maybe we have to be adapted.... return Platform.getAdapterManager().getAdapter(this, adapter); } + + @Override + public Optional getWorkingDirectory() { + if (fConnector != null) { + return fConnector.getWorkingDirectory(); + } + return Optional.empty(); + } } \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener.java index d926433c83b..0de6ff105ae 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener.java @@ -16,6 +16,7 @@ import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; /** * Terminal specific version of {@link org.eclipse.swt.events.MouseListener} * @since 4.1 + * @see ITerminalMouseListener2 */ public interface ITerminalMouseListener { /** diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener2.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener2.java new file mode 100644 index 00000000000..e33a663ab5a --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener2.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2021 Kichwa Coders Canada Inc. and others. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License 2.0 which accompanies this distribution, and is + * available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; + +/** + * Extension of {@link ITerminalMouseListener} for consumers that need the stateMask for a button mouse action. + * + * If ITerminalMouseListener2 is used, the methods in ITerminalMouseListener will not be called. + * + * @since 5.2 + * @see ITerminalMouseListener + */ +public interface ITerminalMouseListener2 extends ITerminalMouseListener { + /** + * Invoked when a double-click has happend inside the terminal control.
+ *
+ * Important: the event fires for every click, even outside the text region. + * @param terminalText a read-only view of the current terminal text + * @param button see {@link org.eclipse.swt.events.MouseEvent#button} for the meaning of the button values + * @param stateMask see {@link org.eclipse.swt.events.MouseEvent#stateMask} for the meaning of the values + */ + default void mouseDoubleClick(ITerminalTextDataReadOnly terminalText, int line, int column, int button, + int stateMask) { + // do nothing by default so that implementors only need to implement methods they care about + } + + @Override + default void mouseDoubleClick(ITerminalTextDataReadOnly terminalText, int line, int column, int button) { + throw new UnsupportedOperationException(); + } + + /** + * Invoked when a mouse button is pushed down inside the terminal control.
+ *
+ * Important: the event fires for every mouse down, even outside the text region. + * @param terminalText a read-only view of the current terminal text + * @param button see {@link org.eclipse.swt.events.MouseEvent#button} for the meaning of the button values + * @param stateMask see {@link org.eclipse.swt.events.MouseEvent#stateMask} for the meaning of the values + */ + default void mouseDown(ITerminalTextDataReadOnly terminalText, int line, int column, int button, int stateMask) { + // do nothing by default so that implementors only need to implement methods they care about + } + + @Override + default void mouseDown(ITerminalTextDataReadOnly terminalText, int line, int column, int button) { + throw new UnsupportedOperationException(); + } + + /** + * Invoked when a mouse button is released inside the terminal control.
+ *
+ * Important: the event fires for every mouse up, even outside the text region. + * @param terminalText a read-only view of the current terminal text + * @param button see {@link org.eclipse.swt.events.MouseEvent#button} for the meaning of the button values + * @param stateMask see {@link org.eclipse.swt.events.MouseEvent#stateMask} for the meaning of the values + */ + default void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column, int button, int stateMask) { + // do nothing by default so that implementors only need to implement methods they care about + } + + @Override + default void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column, int button) { + throw new UnsupportedOperationException(); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalViewControl.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalViewControl.java index 6891cbcb1e4..8ecccd536a9 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalViewControl.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalViewControl.java @@ -159,11 +159,13 @@ public interface ITerminalViewControl { /** * @since 4.1 + * @param listener may be a {@link ITerminalMouseListener2} for extra callbacks */ void addMouseListener(ITerminalMouseListener listener); /** * @since 4.1 + * @param listener may be a {@link ITerminalMouseListener2} for extra callbacks */ void removeMouseListener(ITerminalMouseListener listener); @@ -171,4 +173,9 @@ public interface ITerminalViewControl { * @since 5.1 */ void setTerminalTitle(String newTitle); + + /** + * @since 5.2 + */ + String getHoverSelection(); } diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100TerminalControl.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100TerminalControl.java index 2205ce34002..18fa668c9a2 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100TerminalControl.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100TerminalControl.java @@ -1405,4 +1405,9 @@ public class VT100TerminalControl implements ITerminalControlForText, ITerminalC getCtlText().removeTerminalMouseListener(listener); } + @Override + public String getHoverSelection() { + return fCtlText.getHoverSelection(); + } + } diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalConnector.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalConnector.java index c4869ad22f1..d1a5fe55f70 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalConnector.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalConnector.java @@ -16,6 +16,7 @@ package org.eclipse.tm.internal.terminal.provisional.api; import java.io.OutputStream; +import java.util.Optional; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; @@ -146,4 +147,12 @@ public interface ITerminalConnector extends IAdaptable { */ String getSettingsSummary(); + /** + * @return An optional with the absolute path if available of the current working dir, empty otherwise. + * @since 5.2 + */ + default Optional getWorkingDirectory() { + return Optional.empty(); + } + } diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/Logger.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/Logger.java index 1845104c0bb..f967bbf9e96 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/Logger.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/Logger.java @@ -47,14 +47,17 @@ public final class Logger { public static final String TRACE_DEBUG_LOG = "org.eclipse.tm.terminal.control/debug/log"; //$NON-NLS-1$ public static final String TRACE_DEBUG_LOG_CHAR = "org.eclipse.tm.terminal.control/debug/log/char"; //$NON-NLS-1$ public static final String TRACE_DEBUG_LOG_VT100BACKEND = "org.eclipse.tm.terminal.control/debug/log/VT100Backend"; //$NON-NLS-1$ + /** @since 5.2 */ + public static final String TRACE_DEBUG_LOG_HOVER = "org.eclipse.tm.terminal.control/debug/log/hover"; //$NON-NLS-1$ private static PrintStream logStream; static { - // Any of the three known debugging options turns on the creation of the log file + // Any of the known debugging options turns on the creation of the log file boolean createLogFile = TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG) || TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG_CHAR) - || TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG_VT100BACKEND); + || TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG_VT100BACKEND) + || TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG_HOVER); // Log only if tracing is enabled if (createLogFile && TerminalPlugin.getDefault() != null) { diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/provider/TerminalConnectorImpl.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/provider/TerminalConnectorImpl.java index 79ecb9b92fd..c19296bb725 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/provider/TerminalConnectorImpl.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/provider/TerminalConnectorImpl.java @@ -14,6 +14,7 @@ package org.eclipse.tm.internal.terminal.provisional.api.provider; import java.io.OutputStream; +import java.util.Optional; import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; @@ -151,4 +152,12 @@ public abstract class TerminalConnectorImpl { */ public void setTerminalSize(int newWidth, int newHeight) { } + + /** + * @since 5.2 + */ + public Optional getWorkingDirectory() { + return Optional.empty(); + } + } diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/AbstractTextCanvasModel.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/AbstractTextCanvasModel.java index cd32a5c8c9f..0d0e151a1a8 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/AbstractTextCanvasModel.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/AbstractTextCanvasModel.java @@ -19,10 +19,14 @@ import java.util.Iterator; import java.util.List; import org.eclipse.swt.graphics.Point; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; +import org.eclipse.tm.terminal.model.TextRange; abstract public class AbstractTextCanvasModel implements ITextCanvasModel { + private static final boolean DEBUG_HOVER = TerminalPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_HOVER); protected List fListeners = new ArrayList<>(); private int fCursorLine; private int fCursorColumn; @@ -45,6 +49,8 @@ abstract public class AbstractTextCanvasModel implements ITextCanvasModel { boolean fInUpdate; private int fCols; + private TextRange fHoverRange = TextRange.EMPTY; + public AbstractTextCanvasModel(ITerminalTextDataSnapshot snapshot) { fSnapshot = snapshot; fLines = fSnapshot.getHeight(); @@ -309,6 +315,98 @@ abstract public class AbstractTextCanvasModel implements ITextCanvasModel { return fCurrentSelection; } + @Override + public boolean hasHoverSelection(int line) { + if (fHoverRange.isEmpty()) { + return false; + } + return fHoverRange.contains(line); + } + + @Override + public Point getHoverSelectionStart() { + if (!fHoverRange.isEmpty()) { + return fHoverRange.getStart(); + } + return null; + } + + @Override + public Point getHoverSelectionEnd() { + // Note - to match behaviour of getSelectionEnd this method + // returns the inclusive end. As the fHoverRange is exclusive + // we need to decrement the end positions before returning them. + if (!fHoverRange.isEmpty()) { + Point end = fHoverRange.getEnd(); + end.x--; + end.y--; + return end; + } + return null; + } + + @Override + public void expandHoverSelectionAt(final int line, final int col) { + if (fHoverRange.contains(col, line)) { + // position is inside current hover range -> no change + return; + } + fHoverRange = TextRange.EMPTY; + if (line < 0 || line > fSnapshot.getHeight() || col < 0) { + return; + } + int row1 = line; + int row2 = line; + while (row1 > 0 && fSnapshot.isWrappedLine(row1 - 1)) + row1--; + while (row2 < fSnapshot.getHeight() && fSnapshot.isWrappedLine(row2)) + row2++; + row2++; + String lineText = ""; //$NON-NLS-1$ + for (int l = row1; l < row2; l++) { + char[] chars = fSnapshot.getChars(l); + if (chars == null) + return; + lineText += String.valueOf(chars); + } + int width = fSnapshot.getWidth(); + int col1 = col + (line - row1) * width; + if (lineText.length() <= col1 || isBoundaryChar(lineText.charAt(col1))) { + return; + } + int wordStart = 0; + int wordEnd = lineText.length(); + for (int c = col1; c >= 1; c--) { + if (isBoundaryChar(lineText.charAt(c - 1))) { + wordStart = c; + break; + } + } + for (int c = col1; c < lineText.length(); c++) { + if (isBoundaryChar(lineText.charAt(c))) { + wordEnd = c; + break; + } + } + if (wordStart < wordEnd) { + fHoverRange = new TextRange(row1 + wordStart / width, row1 + (wordEnd - 1) / width + 1, (wordStart % width), + (wordEnd - 1) % width + 1, lineText.substring(wordStart, wordEnd)); + if (DEBUG_HOVER) { + System.out.format("hover: %s <- [%s,%s][%s,%s]\n", //$NON-NLS-1$ + fHoverRange, col, line, wordStart, wordEnd); + } + } + } + + @Override + public String getHoverSelectionText() { + return fHoverRange.text; + } + + private boolean isBoundaryChar(char c) { + return Character.isWhitespace(c) || (c < '\u0020') || c == '"' || c == '\''; + } + // helper to sanitize text copied out of a snapshot private static String scrubLine(String text) { // get rid of the empty space at the end of the lines diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModel.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModel.java index 82362757726..01beac0789d 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModel.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModel.java @@ -90,6 +90,45 @@ public interface ITextCanvasModel { String getSelectedText(); + /** + * Expand the hover selection to the word at the given position. + * + * @param line line + * @param col column + */ + void expandHoverSelectionAt(int line, int col); + + /** + * @param line + * @return true if line is part of the hover selection + */ + boolean hasHoverSelection(int line); + + /** + * Get the text of the current hover selection. + * + * @return the hover selection text, never null. + */ + String getHoverSelectionText(); + + /** + * Get the start of the hover selection. + * + * @return the start of the hover selection or null if nothing is selected + * {@link Point#x} is the column and {@link Point#y} is the line. + * Returns non-null if {@link #hasHoverSelection(int)} returns true + */ + Point getHoverSelectionStart(); + + /** + * Get the end of the hover selection (inclusive). + * + * @return the end of the hover selection or null if nothing is selected + * {@link Point#x} is the column and {@link Point#y} is the line. + * Returns non-null if {@link #hasHoverSelection(int)} returns true + */ + Point getHoverSelectionEnd(); + /** * Collect and return all text present in the model. * @@ -100,4 +139,5 @@ public interface ITextCanvasModel { * @since 4.4 */ String getAllText(); + } \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextCanvas.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextCanvas.java index 449914c1f09..0d7e8bda732 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextCanvas.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextCanvas.java @@ -43,6 +43,7 @@ import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.tm.internal.terminal.control.ITerminalMouseListener; +import org.eclipse.tm.internal.terminal.control.ITerminalMouseListener2; import org.eclipse.tm.terminal.model.TerminalColor; /** @@ -146,7 +147,13 @@ public class TextCanvas extends GridCanvas { Point pt = screenPointToCell(e.x, e.y); if (pt != null) { for (ITerminalMouseListener l : fMouseListeners) { - l.mouseDoubleClick(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button); + if (l instanceof ITerminalMouseListener2) { + ITerminalMouseListener2 l2 = (ITerminalMouseListener2) l; + l2.mouseDoubleClick(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button, + e.stateMask); + } else { + l.mouseDoubleClick(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button); + } } } } @@ -170,7 +177,12 @@ public class TextCanvas extends GridCanvas { Point pt = screenPointToCell(e.x, e.y); if (pt != null) { for (ITerminalMouseListener l : fMouseListeners) { - l.mouseDown(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button); + if (l instanceof ITerminalMouseListener2) { + ITerminalMouseListener2 l2 = (ITerminalMouseListener2) l; + l2.mouseDown(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button, e.stateMask); + } else { + l.mouseDown(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button); + } } } } @@ -190,7 +202,12 @@ public class TextCanvas extends GridCanvas { Point pt = screenPointToCell(e.x, e.y); if (pt != null) { for (ITerminalMouseListener l : fMouseListeners) { - l.mouseUp(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button); + if (l instanceof ITerminalMouseListener2) { + ITerminalMouseListener2 l2 = (ITerminalMouseListener2) l; + l2.mouseUp(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button, e.stateMask); + } else { + l.mouseUp(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button); + } } } } @@ -200,7 +217,15 @@ public class TextCanvas extends GridCanvas { if (fDraggingStart != null) { updateHasSelection(e); setSelection(screenPointToCell(e.x, e.y)); + fCellCanvasModel.expandHoverSelectionAt(-1, -1); + } else if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.MOD1) { + // highlight (underline) word that would be used by MOD1 + mouse click + Point pt = screenPointToCell(e.x, e.y); + fCellCanvasModel.expandHoverSelectionAt(pt.y, pt.x); + } else { + fCellCanvasModel.expandHoverSelectionAt(-1, -1); } + redraw(); }); serVerticalBarVisible(true); setHorizontalBarVisible(false); @@ -540,4 +565,8 @@ public class TextCanvas extends GridCanvas { fMouseListeners.remove(listener); } + public String getHoverSelection() { + return fCellCanvasModel.getHoverSelectionText(); + } + } diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextLineRenderer.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextLineRenderer.java index 884232b78dd..ab3d08080e3 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextLineRenderer.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextLineRenderer.java @@ -26,6 +26,8 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; import org.eclipse.tm.terminal.model.LineSegment; import org.eclipse.tm.terminal.model.TerminalColor; @@ -35,6 +37,7 @@ import org.eclipse.tm.terminal.model.TerminalStyle; * */ public class TextLineRenderer implements ILinelRenderer { + private static final boolean DEBUG_HOVER = TerminalPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_HOVER); private final ITextCanvasModel fModel; private final StyleMap fStyleMap; @@ -76,6 +79,20 @@ public class TextLineRenderer implements ILinelRenderer { drawText(doubleBufferGC, 0, 0, colFirst, segment.getColumn(), text); drawCursor(model, doubleBufferGC, line, 0, 0, colFirst); } + if (fModel.hasHoverSelection(line)) { + if (DEBUG_HOVER) { + System.out.format("hover: %s contains hover selection\n", line); //$NON-NLS-1$ + } + Point hsStart = fModel.getHoverSelectionStart(); + Point hsEnd = fModel.getHoverSelectionEnd(); + int colStart = line == hsStart.y ? hsStart.x : 0; + int colEnd = line == hsEnd.y ? hsEnd.x : getTerminalText().getWidth(); + if (colStart < colEnd) { + RGB defaultFg = fStyleMap.getForegrondRGB(null); + doubleBufferGC.setForeground(new Color(doubleBufferGC.getDevice(), defaultFg)); + drawUnderline(doubleBufferGC, colStart, colEnd); + } + } if (fModel.hasLineSelection(line)) { TerminalStyle style = TerminalStyle.getStyle(TerminalColor.SELECTION_FOREGROUND, TerminalColor.SELECTION_BACKGROUND); @@ -168,6 +185,21 @@ public class TextLineRenderer implements ILinelRenderer { } } + /** + * + * @param gc + * @param colStart Starting text column to underline (inclusive) + * @param colEnd Ending text column to underline (inclusive) + */ + private void drawUnderline(GC gc, int colStart, int colEnd) { + int y = getCellHeight() - 1; + int x = getCellWidth() * colStart; + + // x2 is the right side of last column being underlined. + int x2 = (colEnd + 1) * getCellWidth() - 1; + gc.drawLine(x, y, x2, y); + } + private void setupGC(GC gc, TerminalStyle style) { RGB foregrondColor = fStyleMap.getForegrondRGB(style); gc.setForeground(new Color(gc.getDevice(), foregrondColor)); diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TextRange.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TextRange.java new file mode 100644 index 00000000000..b73576f4c3c --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TextRange.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2021 Fabrizio Iannetti. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tm.terminal.model; + +import org.eclipse.swt.graphics.Point; + +/** + * Represents a range of text in the terminal. + *

+ * Used, for example, to store location of active hover + * + * @since 5.2 + */ +public final class TextRange { + public final int colStart; + public final int colEnd; + public final int rowStart; + public final int rowEnd; + public final String text; + + public static final TextRange EMPTY = new TextRange(0, 0, 0, 0, ""); //$NON-NLS-1$ + + /** + * Constructor. + * + * @param rowStart start row + * @param rowEnd end row + * @param colStart start column (exclusive) + * @param colEnd end column (exclusive) + * @param text text in the range + */ + public TextRange(int rowStart, int rowEnd, int colStart, int colEnd, String text) { + super(); + this.colStart = colStart; + this.colEnd = colEnd; + this.rowStart = rowStart; + this.rowEnd = rowEnd; + this.text = text; + } + + public boolean contains(int col, int row) { + int colStartInrow = row == rowStart ? colStart : 0; + int colEndInRow = row == rowEnd - 1 ? colEnd : col + 1; + return col >= colStartInrow && col < colEndInRow && row >= rowStart && row < rowEnd; + } + + public boolean contains(int line) { + return line >= rowStart && line < rowEnd; + } + + /** + * Whether the range represents a non-empty (non-zero) amount of text + */ + public boolean isEmpty() { + return !(colEnd > colStart || rowEnd > rowStart); + } + + public Point getStart() { + return new Point(colStart, rowStart); + } + + public Point getEnd() { + return new Point(colEnd, rowEnd); + } + + public int getColStart() { + return colStart; + } + + public int getColEnd() { + return colEnd; + } + + public int getRowStart() { + return rowStart; + } + + public int getRowEnd() { + return rowEnd; + } + + @Override + public String toString() { + return String.format("TextRange (%s,%s)-(%s,%s)-'%s'", //$NON-NLS-1$ + colStart, rowStart, colEnd, rowEnd, text); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.view.ui/META-INF/MANIFEST.MF b/terminal/plugins/org.eclipse.tm.terminal.view.ui/META-INF/MANIFEST.MF index b6d072b9c77..0d4a0192d6b 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.view.ui/META-INF/MANIFEST.MF +++ b/terminal/plugins/org.eclipse.tm.terminal.view.ui/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.tm.terminal.view.ui;singleton:=true -Bundle-Version: 4.8.0.qualifier +Bundle-Version: 4.8.100.qualifier Bundle-Activator: org.eclipse.tm.terminal.view.ui.activator.UIPlugin Bundle-Vendor: %providerName Require-Bundle: org.eclipse.core.expressions;bundle-version="3.4.400", @@ -13,7 +13,10 @@ Require-Bundle: org.eclipse.core.expressions;bundle-version="3.4.400", org.eclipse.egit.ui;bundle-version="2.0.0";resolution:=optional, org.eclipse.tm.terminal.view.core;bundle-version="4.5.0", org.eclipse.tm.terminal.control;bundle-version="4.5.0", - org.eclipse.ui;bundle-version="3.8.0" + org.eclipse.ui;bundle-version="3.8.0", + org.eclipse.ui.ide;bundle-version="3.18.0";resolution:=optional, + org.eclipse.ui.editors;bundle-version="3.14.0";resolution:=optional, + org.eclipse.text;bundle-version="3.11.0";resolution:=optional Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Localization: plugin diff --git a/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/activator/UIPlugin.java b/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/activator/UIPlugin.java index f72d858a661..0fb148e3a6a 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/activator/UIPlugin.java +++ b/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/activator/UIPlugin.java @@ -16,6 +16,9 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.swt.custom.CTabFolder; @@ -253,4 +256,21 @@ public class UIPlugin extends AbstractUIPlugin { public static ImageDescriptor getImageDescriptor(String key) { return getDefault().getImageRegistry().getDescriptor(key); } + + public static void log(String msg, Throwable e) { + log(new Status(IStatus.ERROR, getUniqueIdentifier(), IStatus.ERROR, msg, e)); + } + + public static void log(IStatus status) { + getDefault().getLog().log(status); + } + + public static boolean isOptionEnabled(String strOption) { + String strEnabled = Platform.getDebugOption(strOption); + if (strEnabled == null) + return false; + + return Boolean.parseBoolean(strEnabled); + } + } diff --git a/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/tabs/OpenFileMouseHandler.java b/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/tabs/OpenFileMouseHandler.java new file mode 100644 index 00000000000..c4ca8c8bd87 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/tabs/OpenFileMouseHandler.java @@ -0,0 +1,271 @@ +/******************************************************************************* + * Copyright (c) 2021 Fabrizio Iannetti. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tm.terminal.view.ui.tabs; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Adapters; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.tm.internal.terminal.control.ITerminalMouseListener2; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; +import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; +import org.eclipse.tm.terminal.view.ui.activator.UIPlugin; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.internal.ide.dialogs.OpenResourceDialog; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; +import org.osgi.framework.Bundle; + +/** + * @noreference This class is not intended to be referenced by clients. + */ +public class OpenFileMouseHandler implements ITerminalMouseListener2 { + private static final boolean DEBUG_HOVER = UIPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_HOVER); + private static final List NEEDED_BUNDLES = // + List.of("org.eclipse.core.resources", //$NON-NLS-1$ + "org.eclipse.ui.ide", //$NON-NLS-1$ + "org.eclipse.ui.editors", //$NON-NLS-1$ + "org.eclipse.text"); //$NON-NLS-1$ + + private final ITerminalViewControl terminal; + private Pattern regex = Pattern.compile("(\\d*)(:(\\d*))?.*"); //$NON-NLS-1$ + private IWorkbenchPartSite site; + + /** + * Check if we have the bundles needed. + */ + private boolean neededBundlesAvailable; + + OpenFileMouseHandler(IWorkbenchPartSite site, ITerminalViewControl terminal) { + this.site = site; + this.terminal = terminal; + neededBundlesAvailable = true; + for (String bundleName : NEEDED_BUNDLES) { + if (!bundleAvailable(bundleName)) { + this.neededBundlesAvailable = false; + if (DEBUG_HOVER) { + System.out.format( + "hover: the %s bundle is not present, therefore full ctrl-click functionality is not available\n", //$NON-NLS-1$ + bundleName); + } + } + } + if (neededBundlesAvailable && DEBUG_HOVER) { + System.out.format("hover: the bundles needed for full ctrl-click functionality are available\n"); //$NON-NLS-1$ + } + } + + @Override + public void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column, int button, int stateMask) { + if ((stateMask & SWT.MODIFIER_MASK) != SWT.MOD1) { + // Only handle Ctrl-click + return; + } + String textToOpen = terminal.getHoverSelection(); + String lineAndCol = null; + if (textToOpen.length() > 0) { + try { + // if the selection looks like a web URL, open using the browser + if (textToOpen.startsWith("http://") || textToOpen.startsWith("https://")) { //$NON-NLS-1$//$NON-NLS-2$ + try { + PlatformUI.getWorkbench().getBrowserSupport().createBrowser(null).openURL(new URL(textToOpen)); + return; + } catch (MalformedURLException e) { + // not a valid URL, continue + } + } + + // After this we need Eclipse IDE features. If we don't have them then we stop here. + if (!neededBundlesAvailable) { + return; + } + + // extract the path from file:// URLs + if (textToOpen.startsWith("file://")) { //$NON-NLS-1$ + textToOpen = textToOpen.substring(7); + } + // remove optional position info name:[row[:col]] + { + int startOfRowCol = textToOpen.indexOf(':'); + if (startOfRowCol == 1 && textToOpen.length() > 2) { + // assume this is the device separator on Windows + startOfRowCol = textToOpen.indexOf(':', startOfRowCol + 1); + } + if (startOfRowCol >= 0) { + lineAndCol = textToOpen.substring(startOfRowCol + 1); + textToOpen = textToOpen.substring(0, startOfRowCol); + } + } + Optional fullPath = Optional.empty(); + if (!textToOpen.startsWith("/")) { //$NON-NLS-1$ + // relative path: try to append to the working directory + Optional workingDirectory = terminal.getTerminalConnector().getWorkingDirectory(); + if (workingDirectory.isPresent()) { + fullPath = Optional.of(workingDirectory.get() + "/" + textToOpen); + } + } + // if the selection is a file location that maps to a resource + // open the resource + IFile fileForLocation = ResourcesPlugin.getWorkspace().getRoot() + .getFileForLocation(new Path(fullPath.orElse(textToOpen))); + if (fileForLocation != null && fileForLocation.exists()) { + IEditorPart editor = IDE.openEditor( + PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), fileForLocation, + true); + goToLine(lineAndCol, editor); + return; + } + // try an external file, if it exists + File file = new File(fullPath.orElse(textToOpen)); + if (file.exists() && !file.isDirectory()) { + try { + IEditorPart editor = IDE.openEditor(site.getPage(), file.toURI(), + IDE.getEditorDescriptor(file.getName(), true, true).getId(), true); + goToLine(lineAndCol, editor); + return; + } catch (Exception e) { + // continue + } + } + OpenResourceDialog openResourceDialog = new OpenResourceDialog(site.getShell(), + ResourcesPlugin.getPlugin().getWorkspace().getRoot(), IResource.FILE); + openResourceDialog.setInitialPattern(textToOpen); + if (openResourceDialog.open() != Window.OK) + return; + Object[] results = openResourceDialog.getResult(); + List files = new ArrayList<>(); + for (Object result : results) { + if (result instanceof IFile) { + files.add((IFile) result); + } + } + if (files.size() > 0) { + + final IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) { + throw new ExecutionException("no active workbench window"); //$NON-NLS-1$ + } + + final IWorkbenchPage page = window.getActivePage(); + if (page == null) { + throw new ExecutionException("no active workbench page"); //$NON-NLS-1$ + } + + try { + for (IFile iFile : files) { + IEditorPart editor = IDE.openEditor(page, iFile, true); + goToLine(lineAndCol, editor); + } + } catch (final PartInitException e) { + throw new ExecutionException("error opening file in editor", e); //$NON-NLS-1$ + } + } + } catch (IllegalArgumentException | NullPointerException | ExecutionException | PartInitException e) { + UIPlugin.log("Failed to activate OpenResourceDialog", e); //$NON-NLS-1$ + } + + } + + } + + private boolean bundleAvailable(String symbolicName) { + Bundle bundle = Platform.getBundle(symbolicName); + boolean available = bundle != null && bundle.getState() != Bundle.UNINSTALLED + && bundle.getState() != Bundle.STOPPING; + return available; + } + + private void goToLine(String lineAndCol, IEditorPart editor) { + ITextEditor textEditor = Adapters.adapt(editor, ITextEditor.class); + if (textEditor != null) { + Optional optionalOffset = getRegionFromLineAndCol(textEditor, lineAndCol); + optionalOffset.ifPresent(offset -> textEditor.selectAndReveal(offset, 0)); + } + } + + /** + * Returns the line information for the given line in the given editor + */ + private Optional getRegionFromLineAndCol(ITextEditor editor, String lineAndCol) { + if (lineAndCol == null) { + return Optional.empty(); + } + Matcher matcher = regex.matcher(lineAndCol); + if (!matcher.matches()) { + return Optional.empty(); + } + String lineStr = matcher.group(1); + String colStr = matcher.group(3); + int line; + int col = 0; + try { + line = Integer.parseInt(lineStr); + } catch (NumberFormatException e1) { + return Optional.empty(); + } + try { + col = Integer.parseInt(colStr); + } catch (NumberFormatException e1) { + // if we can't get a column, go to the line alone + } + IDocumentProvider provider = editor.getDocumentProvider(); + IEditorInput input = editor.getEditorInput(); + try { + provider.connect(input); + } catch (CoreException e) { + return null; + } + try { + IDocument document = provider.getDocument(input); + if (document != null && line > 0) { + // document's lines are 0-offset + line = line - 1; + int lineOffset = document.getLineOffset(line); + if (col > 0) { + int lineLength = document.getLineLength(line); + if (col < lineLength) { + lineOffset += col; + } + } + return Optional.of(lineOffset); + } + } catch (BadLocationException e) { + } finally { + provider.disconnect(input); + } + return Optional.empty(); + } +} \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/tabs/TabFolderManager.java b/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/tabs/TabFolderManager.java index bd287945622..661e1af4c30 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/tabs/TabFolderManager.java +++ b/terminal/plugins/org.eclipse.tm.terminal.view.ui/src/org/eclipse/tm/terminal/view/ui/tabs/TabFolderManager.java @@ -271,6 +271,10 @@ public class TabFolderManager extends PlatformObject implements ISelectionProvid // Add middle mouse button paste support addMiddleMouseButtonPasteSupport(terminal); + + // add support to open resource on ctrl/meta + mouse click + addOpenResourceSupport(terminal); + // Add the "selection" listener to the terminal control new TerminalControlSelectionListener(terminal); // Configure the terminal encoding @@ -328,6 +332,10 @@ public class TabFolderManager extends PlatformObject implements ISelectionProvid return item; } + private void addOpenResourceSupport(ITerminalViewControl terminal) { + terminal.addMouseListener(new OpenFileMouseHandler(getParentView().getSite(), terminal)); + } + /** * Used for DnD of terminal tab items between terminal views *