Repository: eclipse-corrosion/corrosion (language: rust)

Issue:
Linkable/clickable file paths from Cargo output in Console
Even if the RLS does it in the background, It would be great to have the output parsed in the Console (for cargo build and cargo run) so that file names and line numbers are transformed into clickable links, for instance:

```
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::print
             at libstd/sys_common/backtrace.rs:71
             at libstd/sys_common/backtrace.rs:59
```

```
warning: where clauses are not enforced in type aliases
  --> src/main.rs:58:2
   |
58 |     D: gfx::Device,
   |     ^^^^^^^^^^^^^^
   |
```


Discussion / hints:
It seems to be a matter of adding an extension to https://help.eclipse.org/2018-12/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fextension-points%2Forg_eclipse_ui_workbench_texteditor_hyperlinkDetectors.html .
This would need to be in Corrosion as the given path are not absolute and specific to a cargo build.
RustDT had this working, perhaps we can graft?
RustDT seems to use a deprecated framework (Melnorme) for that. We shouldn't try to reuse this framework and it seems more sustainable and easier to reimplement hyperlink detection from scratch instead of reusing deprecated code.
I see.

Fix patch:
diff --git a/org.eclipse.corrosion/plugin.xml b/org.eclipse.corrosion/plugin.xml
index 91e6523..67699b8 100644
--- a/org.eclipse.corrosion/plugin.xml
+++ b/org.eclipse.corrosion/plugin.xml
@@ -406,5 +406,16 @@
             contentTypeId="org.eclipse.corrosion.rust">
       </contentTypeBinding>
    </extension>
+   <extension
+         point="org.eclipse.ui.console.consolePatternMatchListeners">
+       <consolePatternMatchListener
+            class="org.eclipse.corrosion.ErrorLineMatcher"
+            id="org.eclipse.corrosion.ErrorLineMatcher"
+            regex="(-->).*:\d+:\d+">
+         <enablement>
+            <test property="org.eclipse.ui.console.consoleTypeTest" value="org.eclipse.debug.ui.ProcessConsoleType"/>
+         </enablement>
+      </consolePatternMatchListener>
+   </extension>
 
 </plugin>
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/ErrorLineMatcher.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/ErrorLineMatcher.java
new file mode 100644
index 0000000..ab75661
--- /dev/null
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/ErrorLineMatcher.java
@@ -0,0 +1,118 @@
+/*********************************************************************
+ * Copyright (c) 2019 Red Hat Inc. 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:
+ *  Alexander Kurtakov (Red Hat Inc.) - Initial implementation
+ *******************************************************************************/
+package org.eclipse.corrosion;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.corrosion.launch.RustLaunchDelegateTools;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.console.IHyperlink;
+import org.eclipse.ui.console.IPatternMatchListenerDelegate;
+import org.eclipse.ui.console.PatternMatchEvent;
+import org.eclipse.ui.console.TextConsole;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+public class ErrorLineMatcher implements IPatternMatchListenerDelegate {
+
+	private TextConsole console;
+
+	@Override
+	public void connect(TextConsole console) {
+		this.console = console;
+	}
+
+	@Override
+	public void disconnect() {
+		console = null;
+	}
+
+	@Override
+	public void matchFound(PatternMatchEvent event) {
+		try {
+			int offset = event.getOffset();
+			int length = event.getLength();
+			IProcess process = (IProcess) console.getAttribute(IDebugUIConstants.ATTR_CONSOLE_PROCESS);
+			if (process != null) {
+				ILaunch launch = process.getLaunch();
+				String projectName = launch.getLaunchConfiguration()
+						.getAttribute(RustLaunchDelegateTools.PROJECT_ATTRIBUTE, ""); //$NON-NLS-1$
+				IWorkspaceRoot myWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+				IProject myProject = myWorkspaceRoot.getProject(projectName);
+				String errorString = console.getDocument().get(event.getOffset() + 4, event.getLength() - 4);
+				String[] coordinates = errorString.split(":"); //$NON-NLS-1$
+				IHyperlink link = makeHyperlink(myProject.getFile(coordinates[0]), Integer.parseInt(coordinates[1]),
+						Integer.parseInt(coordinates[2]));
+				console.addHyperlink(link, offset + 4, length - 4);
+			}
+		} catch (BadLocationException | CoreException e) {
+			// ignore
+		}
+	}
+
+	private static IHyperlink makeHyperlink(IFile file, int lineNumber, int lineOffset) {
+		return new IHyperlink() {
+			@Override
+			public void linkExited() {
+				// ignore
+			}
+
+			@Override
+			public void linkEntered() {
+				// ignore
+			}
+
+			@Override
+			public void linkActivated() {
+				IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+				try {
+					IEditorPart editorPart = IDE.openEditor(page, file);
+					jumpToPosition(editorPart, lineNumber, lineOffset);
+				} catch (PartInitException e) {
+					// nothing to do here
+				}
+			}
+		};
+	}
+
+	private static void jumpToPosition(IEditorPart editorPart, int lineNumber, int lineOffset) {
+		if (editorPart instanceof ITextEditor) {
+			ITextEditor textEditor = (ITextEditor) editorPart;
+			IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
+
+			if (document != null) {
+				IRegion region = null;
+				try {
+					region = document.getLineInformation(lineNumber - 1);
+				} catch (BadLocationException exception) {
+					// ignore
+				}
+
+				if (region != null)
+					textEditor.selectAndReveal(region.getOffset() + lineOffset - 1, 0);
+			}
+		}
+	}
+}


Test patch: