diff --git a/org.eclipse.xtext.ide.tests/src/org/eclipse/xtext/ide/tests/server/ValidatorTest.xtend b/org.eclipse.xtext.ide.tests/src/org/eclipse/xtext/ide/tests/server/ValidatorTest.xtend index 499fa9d98..1dd8af46c 100644 --- a/org.eclipse.xtext.ide.tests/src/org/eclipse/xtext/ide/tests/server/ValidatorTest.xtend +++ b/org.eclipse.xtext.ide.tests/src/org/eclipse/xtext/ide/tests/server/ValidatorTest.xtend @@ -38,6 +38,7 @@ class ValidatorTest extends AbstractTestLangLanguageServerTest { assertEquals(1, range.end.line) assertEquals(1, range.end.character) } + @Test def void testMultilineDiagnostic_02() { 'MyType1.testlang'.writeFile(''' diff --git a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/LanguageServerImpl.xtend b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/LanguageServerImpl.xtend index 77dc15cc9..5a24e53b6 100644 --- a/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/LanguageServerImpl.xtend +++ b/org.eclipse.xtext.ide/src/org/eclipse/xtext/ide/server/LanguageServerImpl.xtend @@ -104,6 +104,8 @@ import org.eclipse.xtext.util.internal.Log import org.eclipse.xtext.validation.Issue import static org.eclipse.xtext.diagnostics.Severity.* +import org.eclipse.xtext.validation.Issue.IssueExtension +import org.eclipse.lsp4j.Position /** * @author Sven Efftinge - Initial contribution and API @@ -319,19 +321,13 @@ import static org.eclipse.xtext.diagnostics.Severity.* initialized.thenAccept([ val diagnostics = new PublishDiagnosticsParams => [ it.uri = toUriString(uri) - if (issues.isEmpty) { - diagnostics = #[] - } else { - diagnostics = workspaceManager.doRead(uri) [document, resource| - issues.filter[severity !== IGNORE].map[toDiagnostic(document, it)].toList - ] - } + it.diagnostics = issues.filter[severity !== IGNORE].map[toDiagnostic].toList ] client.publishDiagnostics(diagnostics) ]) } - private def Diagnostic toDiagnostic(Document document, Issue issue) { + private def Diagnostic toDiagnostic(Issue issue) { new Diagnostic => [ code = issue.code severity = switch issue.severity { @@ -341,11 +337,33 @@ import static org.eclipse.xtext.diagnostics.Severity.* default: DiagnosticSeverity.Hint } message = issue.message - val start = document.getPosition(issue.offset) - val end = document.getPosition(issue.offset+issue.length) + val lineNumber = (issue.lineNumber ?: 1) - 1 + val column = if ((issue.column ?: -1) == -1) { + 0 + } else { + issue.column - 1 + } + val length = (issue.length ?: 0) + + val endLineNumber = if (issue instanceof IssueExtension) { + (issue.endLineNumber ?: 1) - 1 + } else { + lineNumber + } + + val endColumn = if (issue instanceof IssueExtension) { + if ((issue.endColumn ?: -1) == -1) { + 0 + } else { + issue.endColumn - 1 + } + } else { + column + length + } + range = new Range( - start, - end + new Position(lineNumber, column), + new Position(endLineNumber, endColumn) ) ] } diff --git a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/LanguageServerImpl.java b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/LanguageServerImpl.java index 17c27299b..6cb8741e1 100644 --- a/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/LanguageServerImpl.java +++ b/org.eclipse.xtext.ide/xtend-gen/org/eclipse/xtext/ide/server/LanguageServerImpl.java @@ -512,22 +512,14 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex PublishDiagnosticsParams _publishDiagnosticsParams = new PublishDiagnosticsParams(); final Procedure1 _function_1 = (PublishDiagnosticsParams it_1) -> { it_1.setUri(this._uriExtensions.toUriString(uri)); - boolean _isEmpty = IterableExtensions.isEmpty(issues); - if (_isEmpty) { - it_1.setDiagnostics(Collections.unmodifiableList(CollectionLiterals.newArrayList())); - } else { - final Function2> _function_2 = (Document document, XtextResource resource) -> { - final Function1 _function_3 = (Issue it_2) -> { - Severity _severity = it_2.getSeverity(); - return Boolean.valueOf((_severity != Severity.IGNORE)); - }; - final Function1 _function_4 = (Issue it_2) -> { - return this.toDiagnostic(document, it_2); - }; - return IterableExtensions.toList(IterableExtensions.map(IterableExtensions.filter(issues, _function_3), _function_4)); - }; - it_1.setDiagnostics(this.workspaceManager.>doRead(uri, _function_2)); - } + final Function1 _function_2 = (Issue it_2) -> { + Severity _severity = it_2.getSeverity(); + return Boolean.valueOf((_severity != Severity.IGNORE)); + }; + final Function1 _function_3 = (Issue it_2) -> { + return this.toDiagnostic(it_2); + }; + it_1.setDiagnostics(IterableExtensions.toList(IterableExtensions.map(IterableExtensions.filter(issues, _function_2), _function_3))); }; final PublishDiagnosticsParams diagnostics = ObjectExtensions.operator_doubleArrow(_publishDiagnosticsParams, _function_1); this.client.publishDiagnostics(diagnostics); @@ -535,7 +527,7 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex this.initialized.thenAccept(_function); } - private Diagnostic toDiagnostic(final Document document, final Issue issue) { + private Diagnostic toDiagnostic(final Issue issue) { Diagnostic _diagnostic = new Diagnostic(); final Procedure1 _function = (Diagnostic it) -> { it.setCode(issue.getCode()); @@ -561,12 +553,77 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Tex } it.setSeverity(_switchResult); it.setMessage(issue.getMessage()); - final Position start = document.getPosition((issue.getOffset()).intValue()); - Integer _offset = issue.getOffset(); + Integer _elvis = null; + Integer _lineNumber = issue.getLineNumber(); + if (_lineNumber != null) { + _elvis = _lineNumber; + } else { + _elvis = Integer.valueOf(1); + } + final int lineNumber = ((_elvis).intValue() - 1); + int _xifexpression = (int) 0; + Integer _elvis_1 = null; + Integer _column = issue.getColumn(); + if (_column != null) { + _elvis_1 = _column; + } else { + _elvis_1 = Integer.valueOf((-1)); + } + boolean _equals = ((_elvis_1).intValue() == (-1)); + if (_equals) { + _xifexpression = 0; + } else { + Integer _column_1 = issue.getColumn(); + _xifexpression = ((_column_1).intValue() - 1); + } + final int column = _xifexpression; + Integer _elvis_2 = null; Integer _length = issue.getLength(); - int _plus = ((_offset).intValue() + (_length).intValue()); - final Position end = document.getPosition(_plus); - Range _range = new Range(start, end); + if (_length != null) { + _elvis_2 = _length; + } else { + _elvis_2 = Integer.valueOf(0); + } + final Integer length = _elvis_2; + int _xifexpression_1 = (int) 0; + if ((issue instanceof Issue.IssueExtension)) { + Integer _elvis_3 = null; + Integer _endLineNumber = ((Issue.IssueExtension)issue).getEndLineNumber(); + if (_endLineNumber != null) { + _elvis_3 = _endLineNumber; + } else { + _elvis_3 = Integer.valueOf(1); + } + _xifexpression_1 = ((_elvis_3).intValue() - 1); + } else { + _xifexpression_1 = lineNumber; + } + final int endLineNumber = _xifexpression_1; + int _xifexpression_2 = (int) 0; + if ((issue instanceof Issue.IssueExtension)) { + int _xifexpression_3 = (int) 0; + Integer _elvis_4 = null; + Integer _endColumn = ((Issue.IssueExtension)issue).getEndColumn(); + if (_endColumn != null) { + _elvis_4 = _endColumn; + } else { + _elvis_4 = Integer.valueOf((-1)); + } + boolean _equals_1 = ((_elvis_4).intValue() == (-1)); + if (_equals_1) { + _xifexpression_3 = 0; + } else { + Integer _endColumn_1 = ((Issue.IssueExtension)issue).getEndColumn(); + _xifexpression_3 = ((_endColumn_1).intValue() - 1); + } + _xifexpression_2 = _xifexpression_3; + } else { + _xifexpression_2 = (column + (length).intValue()); + } + final int endColumn = _xifexpression_2; + Position _position = new Position(lineNumber, column); + Position _position_1 = new Position(endLineNumber, endColumn); + Range _range = new Range(_position, _position_1); it.setRange(_range); }; return ObjectExtensions.operator_doubleArrow(_diagnostic, _function); diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/diagnostics/AbstractDiagnostic.java b/org.eclipse.xtext/src/org/eclipse/xtext/diagnostics/AbstractDiagnostic.java index b386ad25e..505954df9 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/diagnostics/AbstractDiagnostic.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/diagnostics/AbstractDiagnostic.java @@ -50,6 +50,15 @@ public abstract class AbstractDiagnostic implements Diagnostic { } return 0; } + + public int getEndColumn() { + INode node = getNode(); + if (node != null) { + LineAndColumn lineAndColumn = NodeModelUtils.getLineAndColumn(node, getOffset()+getLength()); + return lineAndColumn.getColumn(); + } + return 0; + } @Override public int getLine() { @@ -58,6 +67,13 @@ public abstract class AbstractDiagnostic implements Diagnostic { return node.getStartLine(); return -1; } + + public int getEndLine() { + INode node = getNode(); + if (node != null) + return node.getEndLine(); + return -1; + } @Override public String getLocation() { diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/validation/DiagnosticConverterImpl.java b/org.eclipse.xtext/src/org/eclipse/xtext/validation/DiagnosticConverterImpl.java index 2f8057a8e..aa21479d5 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/validation/DiagnosticConverterImpl.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/validation/DiagnosticConverterImpl.java @@ -49,6 +49,17 @@ public class DiagnosticConverterImpl implements IDiagnosticConverter { */ public Integer offset; public Integer length; + + /** + * 1-based line number. + * @since 2.19 + */ + public Integer endLineNumber; + /** + * 1-based column. + * @since 2.19 + */ + public Integer endColumn; } @Override @@ -59,7 +70,10 @@ public class DiagnosticConverterImpl implements IDiagnosticConverter { issue.setLineNumber(diagnostic.getLine()); issue.setColumn(diagnostic.getColumn()); issue.setMessage(diagnostic.getMessage()); - + // set default value + issue.setEndColumn(-1); + + if (diagnostic instanceof org.eclipse.xtext.diagnostics.Diagnostic) { org.eclipse.xtext.diagnostics.Diagnostic xtextDiagnostic = (org.eclipse.xtext.diagnostics.Diagnostic) diagnostic; issue.setOffset(xtextDiagnostic.getOffset()); @@ -70,6 +84,9 @@ public class DiagnosticConverterImpl implements IDiagnosticConverter { issue.setUriToProblem(castedDiagnostic.getUriToProblem()); issue.setCode(castedDiagnostic.getCode()); issue.setData(castedDiagnostic.getData()); + issue.setEndLineNumber(castedDiagnostic.getEndLine()); + issue.setEndColumn(castedDiagnostic.getEndColumn()); + } issue.setType(CheckType.FAST); acceptor.accept(issue); @@ -83,13 +100,17 @@ public class DiagnosticConverterImpl implements IDiagnosticConverter { return; IssueImpl issue = new Issue.IssueImpl(); issue.setSeverity(severity); - + // set default value + issue.setColumn(-1); + issue.setEndColumn(-1); IssueLocation locationData = getLocationData(diagnostic); if (locationData != null) { issue.setLineNumber(locationData.lineNumber); issue.setColumn(locationData.column); issue.setOffset(locationData.offset); issue.setLength(locationData.length); + issue.setEndLineNumber(locationData.endLineNumber); + issue.setEndColumn(locationData.endColumn); } final EObject causer = getCauser(diagnostic); if (causer != null) @@ -196,6 +217,9 @@ public class DiagnosticConverterImpl implements IDiagnosticConverter { LineAndColumn lineAndColumn = NodeModelUtils.getLineAndColumn(parserNode, castedDiagnostic.getOffset()); result.lineNumber = lineAndColumn.getLine(); result.column = lineAndColumn.getColumn(); + LineAndColumn endLineAndColumn = NodeModelUtils.getLineAndColumn(parserNode, castedDiagnostic.getOffset() + castedDiagnostic.getLength()); + result.endLineNumber = endLineAndColumn.getLine(); + result.endColumn = endLineAndColumn.getColumn(); } result.offset = castedDiagnostic.getOffset(); result.length = castedDiagnostic.getLength(); @@ -244,6 +268,8 @@ public class DiagnosticConverterImpl implements IDiagnosticConverter { result.column = 1; result.offset = 0; result.length = 0; + result.endLineNumber = 1; + result.endColumn = 1; return result; } @@ -254,6 +280,8 @@ public class DiagnosticConverterImpl implements IDiagnosticConverter { result.offset = nodeRegion.getOffset(); result.column = NodeModelUtils.getLineAndColumn(node, result.offset).getColumn(); result.length = nodeRegion.getLength(); + result.endLineNumber = nodeRegion.getEndLineNumber(); + result.endColumn = NodeModelUtils.getLineAndColumn(node, result.offset + result.length).getColumn(); return result; } diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/validation/Issue.java b/org.eclipse.xtext/src/org/eclipse/xtext/validation/Issue.java index 97e892d53..b9cb0f0aa 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/validation/Issue.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/validation/Issue.java @@ -18,6 +18,25 @@ import org.eclipse.xtext.diagnostics.Severity; */ public interface Issue { + /** + * @author Christian Dietrich - Initial contribution and API + * @since 2.19 + */ + interface IssueExtension { + /** + * Returns the column in the end line of the issue. It's not the virtual column but literally + * the character offset in the column, e.g. tab ('\t') counts as one character. + * The first char in a line has column number 1, the number is one-based. + * + * If no column information is available, returns -1. + */ + Integer getEndColumn(); + /** + * Returns the one-based end line number of the issue. + */ + Integer getEndLineNumber(); + } + String CODE_KEY = "CODE_KEY"; String URI_KEY = "URI_KEY"; /** @@ -66,11 +85,11 @@ public interface Issue { */ String[] getData(); - static class IssueImpl implements Issue { + static class IssueImpl implements Issue, IssueExtension { private static Logger LOG = Logger.getLogger(IssueImpl.class); - private Integer length, lineNumber, offset, column; + private Integer length, lineNumber, offset, column, endLineNumber, endColumn; private String code, message; private boolean isSyntaxError = false; private URI uriToProblem; @@ -198,5 +217,22 @@ public interface Issue { result.append(" line : ").append(getLineNumber()).append(" column : ").append(getColumn()).append(")"); return result.toString(); } + + public Integer getEndLineNumber() { + return endLineNumber; + } + + public void setEndLineNumber(Integer endLineNumber) { + this.endLineNumber = endLineNumber; + } + + public Integer getEndColumn() { + return endColumn; + } + + public void setEndColumn(Integer endColumn) { + this.endColumn = endColumn; + } + } }