diff --git a/terminal/bundles/org.eclipse.terminal.view.core/META-INF/MANIFEST.MF b/terminal/bundles/org.eclipse.terminal.view.core/META-INF/MANIFEST.MF index 0451eb54fce..1d1e10a0b8b 100644 --- a/terminal/bundles/org.eclipse.terminal.view.core/META-INF/MANIFEST.MF +++ b/terminal/bundles/org.eclipse.terminal.view.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.terminal.view.core;singleton:=true -Bundle-Version: 1.0.100.qualifier +Bundle-Version: 1.0.200.qualifier Bundle-Activator: org.eclipse.terminal.view.core.internal.CoreBundleActivator Bundle-Vendor: %providerName Require-Bundle: org.eclipse.core.expressions;bundle-version="[3.9.0,4.0.0)", diff --git a/terminal/bundles/org.eclipse.terminal.view.core/src/org/eclipse/terminal/view/core/internal/FileReference.java b/terminal/bundles/org.eclipse.terminal.view.core/src/org/eclipse/terminal/view/core/internal/FileReference.java new file mode 100644 index 00000000000..c55f856643a --- /dev/null +++ b/terminal/bundles/org.eclipse.terminal.view.core/src/org/eclipse/terminal/view/core/internal/FileReference.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2024 Fabrizio Iannetti 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.terminal.view.core.internal; + +/** + * An immutable value object representing a file reference that may include + * a line and column position. + * + * @param path the file path + * @param line the 1-based line number, or {@code -1} if absent + * @param column the 1-based column number, or {@code -1} if absent + */ +public record FileReference(String path, int line, int column) { + + /** + * Returns whether this reference contains a valid line number. + * + * @return {@code true} if this reference contains a valid line number + */ + public boolean hasLine() { + return line > 0; + } + + /** + * Returns whether this reference contains a valid column number. + * + * @return {@code true} if this reference contains a valid column number + */ + public boolean hasColumn() { + return column > 0; + } +} diff --git a/terminal/bundles/org.eclipse.terminal.view.core/src/org/eclipse/terminal/view/core/internal/FileReferenceParser.java b/terminal/bundles/org.eclipse.terminal.view.core/src/org/eclipse/terminal/view/core/internal/FileReferenceParser.java new file mode 100644 index 00000000000..044a941669a --- /dev/null +++ b/terminal/bundles/org.eclipse.terminal.view.core/src/org/eclipse/terminal/view/core/internal/FileReferenceParser.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2026 Fabrizio Iannetti 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.terminal.view.core.internal; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Parses terminal hover text to extract a {@link FileReference} containing + * the file path and optional line and column numbers. + *

+ * Supported formats are {@code path}, {@code path:}, {@code path:line}, + * {@code path:line:}, {@code path:line:column} and {@code path:line:column:}. + * Line and column numbers must be strictly positive (≥ 1). + * Windows drive letters ({@code C:\...}) are correctly distinguished + * from the {@code :line} suffix. + *

+ *

+ * This class is not intended to be instantiated or subclassed by clients. + *

+ */ +public class FileReferenceParser { + + private static final Pattern PATH_LINE_COL_PATTERN = Pattern.compile("^(.+):([1-9]\\d*):([1-9]\\d*):?$"); //$NON-NLS-1$ + private static final Pattern PATH_LINE_PATTERN = Pattern.compile("^(.+):([1-9]\\d*):?$"); //$NON-NLS-1$ + private static final String COLON = ":"; //$NON-NLS-1$ + + private FileReferenceParser() { + } + + /** + * Parses the given text into a {@link FileReference}. + *

+ * Never returns {@code null}. If no line/column suffix is detected, + * the result contains the whole input as the path with line and column + * set to {@code -1}. + *

+ * + * @param text the hover selection text, may be {@code null} + * @return a {@link FileReference}, never {@code null} + */ + public static FileReference parse(String text) { + if (text == null || text.isEmpty()) { + return new FileReference(text, -1, -1); + } + Matcher m = PATH_LINE_COL_PATTERN.matcher(text); + if (m.matches()) { + String path = m.group(1); + int line = Integer.parseInt(m.group(2)); + int column = Integer.parseInt(m.group(3)); + if (!path.isEmpty()) { + return new FileReference(path, line, column); + } + } + m = PATH_LINE_PATTERN.matcher(text); + if (m.matches()) { + String path = m.group(1); + int line = Integer.parseInt(m.group(2)); + if (!path.isEmpty()) { + return new FileReference(path, line, -1); + } + } + if (text.endsWith(COLON)) { + text = text.substring(0, text.length() - COLON.length()); + } + return new FileReference(text, -1, -1); + } +} diff --git a/terminal/bundles/org.eclipse.terminal.view.ui/src/org/eclipse/terminal/view/ui/internal/tabs/OpenFileMouseHandler.java b/terminal/bundles/org.eclipse.terminal.view.ui/src/org/eclipse/terminal/view/ui/internal/tabs/OpenFileMouseHandler.java index 700e22a2191..7fac2e044ee 100644 --- a/terminal/bundles/org.eclipse.terminal.view.ui/src/org/eclipse/terminal/view/ui/internal/tabs/OpenFileMouseHandler.java +++ b/terminal/bundles/org.eclipse.terminal.view.ui/src/org/eclipse/terminal/view/ui/internal/tabs/OpenFileMouseHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021 Fabrizio Iannetti. + * Copyright (c) 2026 Fabrizio Iannetti. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -16,8 +16,6 @@ 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; @@ -36,6 +34,8 @@ import org.eclipse.terminal.control.ITerminalMouseListener; import org.eclipse.terminal.control.ITerminalViewControl; import org.eclipse.terminal.model.ITerminalTextDataReadOnly; +import org.eclipse.terminal.view.core.internal.FileReference; +import org.eclipse.terminal.view.core.internal.FileReferenceParser; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; @@ -52,6 +52,7 @@ /** * @noreference This class is not intended to be referenced by clients. */ +@SuppressWarnings("restriction") public class OpenFileMouseHandler implements ITerminalMouseListener { private static final boolean DEBUG_HOVER = Platform.getDebugBoolean(Logger.TRACE_DEBUG_LOG_HOVER); private static final List NEEDED_BUNDLES = // @@ -61,7 +62,6 @@ public class OpenFileMouseHandler implements ITerminalMouseListener { "org.eclipse.text"); //$NON-NLS-1$ private final ITerminalViewControl terminal; - private final Pattern regex = Pattern.compile("(\\d*)(:(\\d*))?.*"); //$NON-NLS-1$ private final IWorkbenchPartSite site; /** @@ -88,7 +88,6 @@ public class OpenFileMouseHandler implements ITerminalMouseListener { } } - @SuppressWarnings("restriction") @Override public void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column, int button, int stateMask) { if ((stateMask & SWT.MODIFIER_MASK) != SWT.MOD1) { @@ -96,7 +95,6 @@ public void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column 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 @@ -118,18 +116,10 @@ public void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column 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); - } - } + // Parse file reference to extract path and optional line:column + FileReference fileRef = FileReferenceParser.parse(textToOpen); + textToOpen = fileRef.path(); + Optional fullPath = Optional.empty(); if (!textToOpen.startsWith("/")) { //$NON-NLS-1$ // relative path: try to append to the working directory @@ -146,7 +136,7 @@ public void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column IEditorPart editor = IDE.openEditor( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), fileForLocation, true); - goToLine(lineAndCol, editor); + goToLine(fileRef, editor); return; } // try an external file, if it exists @@ -155,7 +145,7 @@ public void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column try { IEditorPart editor = IDE.openEditor(site.getPage(), file.toURI(), IDE.getEditorDescriptor(file.getName(), true, true).getId(), true); - goToLine(lineAndCol, editor); + goToLine(fileRef, editor); return; } catch (Exception e) { // continue @@ -190,7 +180,7 @@ public void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column try { for (IFile iFile : files) { IEditorPart editor = IDE.openEditor(page, iFile, true); - goToLine(lineAndCol, editor); + goToLine(fileRef, editor); } } catch (final PartInitException e) { throw new ExecutionException("error opening file in editor", e); //$NON-NLS-1$ @@ -211,64 +201,44 @@ private boolean bundleAvailable(String symbolicName) { return available; } - private void goToLine(String lineAndCol, IEditorPart editor) { + private void goToLine(FileReference fileRef, IEditorPart editor) { + if (!fileRef.hasLine()) { + return; + } ITextEditor textEditor = Adapters.adapt(editor, ITextEditor.class); if (textEditor != null) { - Optional optionalOffset = getRegionFromLineAndCol(textEditor, lineAndCol); + Optional optionalOffset = getOffsetFromFileRef(textEditor, fileRef); 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 - } + private static Optional getOffsetFromFileRef(ITextEditor editor, FileReference fileRef) { + int line = fileRef.line(); IDocumentProvider provider = editor.getDocumentProvider(); IEditorInput input = editor.getEditorInput(); try { provider.connect(input); } catch (CoreException e) { - return null; + return Optional.empty(); } 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); + if (document == null) { + return Optional.empty(); } + // document lines are 0-based, input lines are 1-based + int zeroBasedLine = line - 1; + int lineOffset = document.getLineOffset(zeroBasedLine); + if (fileRef.hasColumn()) { + int lineLength = document.getLineLength(zeroBasedLine); + int col = Math.min(fileRef.column() - 1, lineLength); + lineOffset += col; + } + return Optional.of(lineOffset); } catch (BadLocationException e) { + return Optional.empty(); } finally { provider.disconnect(input); } - return Optional.empty(); } } \ No newline at end of file diff --git a/terminal/tests/org.eclipse.terminal.test/META-INF/MANIFEST.MF b/terminal/tests/org.eclipse.terminal.test/META-INF/MANIFEST.MF index 09adac293ec..d173673596f 100644 --- a/terminal/tests/org.eclipse.terminal.test/META-INF/MANIFEST.MF +++ b/terminal/tests/org.eclipse.terminal.test/META-INF/MANIFEST.MF @@ -9,7 +9,8 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.33.0,4)", org.eclipse.ui;bundle-version="[3.208.0,4)", org.opentest4j;bundle-version="[1.3.0,2)", org.eclipse.terminal.control;bundle-version="1.0.0", - org.eclipse.terminal.view.ui;bundle-version="[1.1.200,2.0.0)" + org.eclipse.terminal.view.ui;bundle-version="[1.1.200,2.0.0)", + org.eclipse.terminal.view.core;bundle-version="[1.0.0,2.0.0)" Bundle-RequiredExecutionEnvironment: JavaSE-21 Export-Package: org.eclipse.terminal.internal.connector;x-internal:=true, org.eclipse.terminal.internal.emulator;x-internal:=true, diff --git a/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/test/AutomatedTestSuite.java b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/test/AutomatedTestSuite.java index c146029c61a..9bafb3e96fb 100644 --- a/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/test/AutomatedTestSuite.java +++ b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/test/AutomatedTestSuite.java @@ -26,6 +26,8 @@ org.eclipse.terminal.internal.connector.TerminalConnectorTest.class, // org.eclipse.terminal.internal.connector.TerminalToRemoteInjectionOutputStreamTest.class, // org.eclipse.terminal.view.ui.tests.TerminalsViewReorderTest.class, // + org.eclipse.terminal.view.core.tests.FileReferenceTest.class, // + org.eclipse.terminal.view.core.tests.FileReferenceParserTest.class, // }) public class AutomatedTestSuite { diff --git a/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/view/core/tests/FileReferenceParserTest.java b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/view/core/tests/FileReferenceParserTest.java new file mode 100644 index 00000000000..0bc804f73c5 --- /dev/null +++ b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/view/core/tests/FileReferenceParserTest.java @@ -0,0 +1,234 @@ +/******************************************************************************* + * Copyright (c) 2026 Fabrizio Iannetti 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.terminal.view.core.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.terminal.view.core.internal.FileReference; +import org.eclipse.terminal.view.core.internal.FileReferenceParser; +import org.junit.jupiter.api.Test; + +public class FileReferenceParserTest { + + @Test + public void testPlainPath() { + FileReference ref = FileReferenceParser.parse("src/Test.java"); + assertEquals("src/Test.java", ref.path()); + assertEquals(-1, ref.line()); + assertEquals(-1, ref.column()); + assertFalse(ref.hasLine()); + assertFalse(ref.hasColumn()); + } + + @Test + public void testPlainPathEndingWithColon() { + FileReference ref = FileReferenceParser.parse("src/Test.java:"); + assertEquals("src/Test.java", ref.path()); + assertEquals(-1, ref.line()); + assertEquals(-1, ref.column()); + assertFalse(ref.hasLine()); + assertFalse(ref.hasColumn()); + } + + @Test + public void testPathWithLine() { + FileReference ref = FileReferenceParser.parse("src/Test.java:1"); + assertEquals("src/Test.java", ref.path()); + assertEquals(1, ref.line()); + assertEquals(-1, ref.column()); + assertTrue(ref.hasLine()); + assertFalse(ref.hasColumn()); + } + + @Test + public void testPathWithLineAndTrailingColon() { + FileReference ref = FileReferenceParser.parse("src/Test.java:42:"); + assertEquals("src/Test.java", ref.path()); + assertEquals(42, ref.line()); + assertEquals(-1, ref.column()); + } + + @Test + public void testPathWithLineAndColumn() { + FileReference ref = FileReferenceParser.parse("src/Test.java:42:7"); + assertEquals("src/Test.java", ref.path()); + assertEquals(42, ref.line()); + assertEquals(7, ref.column()); + assertTrue(ref.hasColumn()); + } + + @Test + public void testPathWithLineAndColumnAndTrailingColon() { + FileReference ref = FileReferenceParser.parse("src/Test.java:42:7:"); + assertEquals("src/Test.java", ref.path()); + assertEquals(42, ref.line()); + assertEquals(7, ref.column()); + } + + @Test + public void testPathWithLineAndTrailingTextAfterSpace() { + FileReference ref = FileReferenceParser.parse("src/Test.java:42: private Prix prix;"); + assertEquals("src/Test.java:42: private Prix prix;", ref.path()); + assertEquals(-1, ref.line()); + } + + @Test + public void testPathWithLineColumnAndTrailingText() { + FileReference ref = FileReferenceParser.parse("src/Test.java:42:7: message"); + assertEquals("src/Test.java:42:7: message", ref.path()); + assertEquals(-1, ref.line()); + } + + @Test + public void testPathWithSpaces() { + FileReference ref = FileReferenceParser.parse("src/main/My File.java:12"); + assertEquals("src/main/My File.java", ref.path()); + assertEquals(12, ref.line()); + } + + @Test + public void testUnixAbsolutePath() { + FileReference ref = FileReferenceParser.parse("/home/user/project/Test.java:12"); + assertEquals("/home/user/project/Test.java", ref.path()); + assertEquals(12, ref.line()); + } + + @Test + public void testWindowsAbsolutePath() { + FileReference ref = FileReferenceParser.parse("C:\\work\\project\\Test.java:12"); + assertEquals("C:\\work\\project\\Test.java", ref.path()); + assertEquals(12, ref.line()); + } + + @Test + public void testWindowsAbsolutePathWithColumn() { + FileReference ref = FileReferenceParser.parse("C:\\work\\project\\Test.java:12:8"); + assertEquals("C:\\work\\project\\Test.java", ref.path()); + assertEquals(12, ref.line()); + assertEquals(8, ref.column()); + } + + @Test + public void testWindowsRelativePath() { + FileReference ref = FileReferenceParser.parse("..\\module\\Test.java:15"); + assertEquals("..\\module\\Test.java", ref.path()); + assertEquals(15, ref.line()); + } + + @Test + public void testWindowsForwardSlash() { + FileReference ref = FileReferenceParser.parse("C:/work/project/Test.java:12"); + assertEquals("C:/work/project/Test.java", ref.path()); + assertEquals(12, ref.line()); + } + + @Test + public void testLineZeroIsNotLine() { + FileReference ref = FileReferenceParser.parse("src/Test.java:0"); + assertEquals("src/Test.java:0", ref.path()); + assertEquals(-1, ref.line()); + assertFalse(ref.hasLine()); + } + + @Test + public void testColumnZeroIsNotColumn() { + FileReference ref = FileReferenceParser.parse("src/Test.java:42:0"); + assertEquals("src/Test.java:42:0", ref.path()); + assertEquals(-1, ref.line()); + assertEquals(-1, ref.column()); + } + + @Test + public void testNonNumericSuffix() { + FileReference ref = FileReferenceParser.parse("src/Test.java:abc"); + assertEquals("src/Test.java:abc", ref.path()); + assertEquals(-1, ref.line()); + } + + @Test + public void testUrlLikeText() { + FileReference ref = FileReferenceParser.parse("http://localhost:8080/path"); + assertEquals("http://localhost:8080/path", ref.path()); + assertEquals(-1, ref.line()); + assertEquals(-1, ref.column()); + } + + @Test + public void testTimeLikeText() { + FileReference ref = FileReferenceParser.parse("12:30:45"); + assertEquals("12", ref.path()); + assertEquals(30, ref.line()); + assertEquals(45, ref.column()); + } + + @Test + public void testMavenCoordinates() { + FileReference ref = FileReferenceParser.parse("groupId:artifactId:version"); + assertEquals("groupId:artifactId:version", ref.path()); + assertEquals(-1, ref.line()); + } + + @Test + public void testNullInput() { + FileReference ref = FileReferenceParser.parse(null); + assertEquals(null, ref.path()); + assertEquals(-1, ref.line()); + assertFalse(ref.hasLine()); + } + + @Test + public void testEmptyInput() { + FileReference ref = FileReferenceParser.parse(""); + assertEquals("", ref.path()); + assertEquals(-1, ref.line()); + assertFalse(ref.hasLine()); + } + + @Test + public void testGitGrepOutput() { + FileReference ref = FileReferenceParser + .parse("ecore/app/src/main/java/com/example/CombinaisonTarifaire.java:42:"); + assertEquals("ecore/app/src/main/java/com/example/CombinaisonTarifaire.java", ref.path()); + assertEquals(42, ref.line()); + assertEquals(-1, ref.column()); + } + + @Test + public void testGitGrepOutputWithColumn() { + FileReference ref = FileReferenceParser.parse("ecore/app/src/main/java/com/example/MyClass.java:42:17"); + assertEquals("ecore/app/src/main/java/com/example/MyClass.java", ref.path()); + assertEquals(42, ref.line()); + assertEquals(17, ref.column()); + } + + @Test + public void testMultipleColonsInPath() { + FileReference ref = FileReferenceParser.parse("/path/with:colon/File.java:42"); + assertEquals("/path/with:colon/File.java", ref.path()); + assertEquals(42, ref.line()); + } + + @Test + public void testJustFileNameWithLine() { + FileReference ref = FileReferenceParser.parse("MyClass.java:42"); + assertEquals("MyClass.java", ref.path()); + assertEquals(42, ref.line()); + } + + @Test + public void testNonFilePathLikeMot123() { + FileReference ref = FileReferenceParser.parse("mot:123"); + assertEquals("mot", ref.path()); + assertEquals(123, ref.line()); + } +} diff --git a/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/view/core/tests/FileReferenceTest.java b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/view/core/tests/FileReferenceTest.java new file mode 100644 index 00000000000..ccd19cd1507 --- /dev/null +++ b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/view/core/tests/FileReferenceTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2026 Fabrizio Iannetti 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.terminal.view.core.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.terminal.view.core.internal.FileReference; +import org.junit.jupiter.api.Test; + +public class FileReferenceTest { + + @Test + public void testDefaultValues() { + FileReference ref = new FileReference("/path/to/file.java", -1, -1); + assertEquals("/path/to/file.java", ref.path()); + assertEquals(-1, ref.line()); + assertEquals(-1, ref.column()); + assertFalse(ref.hasLine()); + assertFalse(ref.hasColumn()); + } + + @Test + public void testWithLineOnly() { + FileReference ref = new FileReference("/path/to/file.java", 42, -1); + assertTrue(ref.hasLine()); + assertFalse(ref.hasColumn()); + } + + @Test + public void testWithLineAndColumn() { + FileReference ref = new FileReference("/path/to/file.java", 42, 7); + assertTrue(ref.hasLine()); + assertTrue(ref.hasColumn()); + } + + @Test + public void testLineZeroIsAbsent() { + FileReference ref = new FileReference("file.java", 0, -1); + assertFalse(ref.hasLine()); + } + + @Test + public void testColumnZeroIsAbsent() { + FileReference ref = new FileReference("file.java", 42, 0); + assertTrue(ref.hasLine()); + assertFalse(ref.hasColumn()); + } +}