1212 *******************************************************************************/
1313package org .eclipse .lsp4e .operations .diagnostics ;
1414
15+ import java .net .URI ;
1516import java .util .ArrayList ;
1617import java .util .HashMap ;
1718import java .util .HashSet ;
4647import org .eclipse .lsp4e .internal .ArrayUtil ;
4748import org .eclipse .lsp4j .Diagnostic ;
4849import org .eclipse .lsp4j .PublishDiagnosticsParams ;
49- import org .eclipse .lsp4j .Range ;
5050import org .eclipse .lsp4j .jsonrpc .messages .Either ;
5151import org .eclipse .ui .IEditorReference ;
5252import org .eclipse .ui .texteditor .MarkerUtilities ;
@@ -57,6 +57,13 @@ public class LSPDiagnosticsToMarkers implements Consumer<PublishDiagnosticsParam
5757 public static final String LANGUAGE_SERVER_ID = "languageServerId" ; //$NON-NLS-1$
5858 public static final String LS_DIAGNOSTIC_MARKER_TYPE = "org.eclipse.lsp4e.diagnostic" ; //$NON-NLS-1$
5959
60+ // LSP range attributes stored on markers to allow matching without computing
61+ // document offsets
62+ public static final String LSP_START_LINE = "lspStartLine" ; //$NON-NLS-1$
63+ public static final String LSP_START_CHAR = "lspStartChar" ; //$NON-NLS-1$
64+ public static final String LSP_END_LINE = "lspEndLine" ; //$NON-NLS-1$
65+ public static final String LSP_END_CHAR = "lspEndChar" ; //$NON-NLS-1$
66+
6067 private static final IMarkerAttributeComputer DEFAULT_MARKER_ATTRIBUTE_COMPUTER = new IMarkerAttributeComputer () {
6168
6269 @ Override
@@ -165,8 +172,15 @@ private void doRun() throws CoreException {
165172 // when we're done
166173 IDocument existingDocument = LSPEclipseUtils .getExistingDocument (resource );
167174 final boolean hasDiagnostics = !diagnostics .getDiagnostics ().isEmpty ();
168- final boolean temporaryLoadDocument = existingDocument == null ;
169- IDocument document = (hasDiagnostics && temporaryLoadDocument ) ? LSPEclipseUtils .getDocument (resource ): existingDocument ;
175+ boolean temporaryLoadDocument = false ;
176+ IDocument document = existingDocument ;
177+ if (hasDiagnostics && document == null ) {
178+ final @ Nullable URI resourceUri = LSPEclipseUtils .toUri (resource );
179+ if (resourceUri != null && "file" .equals (resourceUri .getScheme ())) { //$NON-NLS-1$
180+ temporaryLoadDocument = true ;
181+ document = LSPEclipseUtils .getDocument (resource );
182+ }
183+ }
170184 for (Diagnostic diagnostic : diagnostics .getDiagnostics ()) {
171185 IMarker associatedMarker = getExistingMarkerFor (document , diagnostic , toDeleteMarkers );
172186 if (associatedMarker == null ) {
@@ -222,21 +236,43 @@ protected void updateMarker(Map<String, Object> targetAttributes, IMarker marker
222236 }
223237
224238 private @ Nullable IMarker getExistingMarkerFor (@ Nullable IDocument document , Diagnostic diagnostic , Set <IMarker > remainingMarkers ) {
225- if (document == null ) {
226- return null ;
227- }
228-
229239 final var markerMessage = markerAttributeComputer .computeMarkerMessage (diagnostic );
240+ final var rangeStart = diagnostic .getRange ().getStart ();
241+ final var rangeEnd = diagnostic .getRange ().getEnd ();
230242 for (IMarker marker : remainingMarkers ) {
231243 if (!marker .exists ()) {
232244 continue ;
233245 }
234246 try {
235- if (LSPEclipseUtils .toOffset (diagnostic .getRange ().getStart (), document ) == MarkerUtilities .getCharStart (marker )
236- && (LSPEclipseUtils .toOffset (diagnostic .getRange ().getEnd (), document ) == MarkerUtilities .getCharEnd (marker ) || Objects .equals (diagnostic .getRange ().getStart (), diagnostic .getRange ().getEnd ()))
237- && Objects .equals (marker .getAttribute (IMarker .MESSAGE ), markerMessage )
238- && Objects .equals (marker .getAttribute (LANGUAGE_SERVER_ID ), this .languageServerId )) {
239- return marker ;
247+ // Always require same server and same user-visible message
248+ if (!languageServerId .equals (marker .getAttribute (LANGUAGE_SERVER_ID ))
249+ || !markerMessage .equals (marker .getAttribute (IMarker .MESSAGE ))) {
250+ continue ;
251+ }
252+ if (document != null ) {
253+ // Document available: match by precise character offsets
254+ final int startOff = LSPEclipseUtils .toOffset (rangeStart , document );
255+ final int endOff = LSPEclipseUtils .toOffset (rangeEnd , document );
256+ if (startOff == MarkerUtilities .getCharStart (marker )
257+ && (endOff == MarkerUtilities .getCharEnd (marker ) || rangeStart .equals (rangeEnd ))) {
258+ return marker ;
259+ }
260+ } else {
261+ // No document: match by raw LSP range attributes stored on the marker
262+ final int markerStartLine = marker .getAttribute (LSP_START_LINE , Integer .MIN_VALUE );
263+ final int markerStartChar = marker .getAttribute (LSP_START_CHAR , Integer .MIN_VALUE );
264+ final int markerEndLine = marker .getAttribute (LSP_END_LINE , Integer .MIN_VALUE );
265+ final int markerEndChar = marker .getAttribute (LSP_END_CHAR , Integer .MIN_VALUE );
266+ if (markerStartLine == Integer .MIN_VALUE || markerEndLine == Integer .MIN_VALUE ) {
267+ continue ;
268+ }
269+ final boolean sameStart = markerStartLine == rangeStart .getLine ()
270+ && markerStartChar == rangeStart .getCharacter ();
271+ final boolean sameEnd = markerEndLine == rangeEnd .getLine ()
272+ && markerEndChar == rangeEnd .getCharacter ();
273+ if (sameStart && (sameEnd || rangeStart .equals (rangeEnd ))) {
274+ return marker ;
275+ }
240276 }
241277 } catch (CoreException | BadLocationException e ) {
242278 LanguageServerPlugin .logError (e );
@@ -255,24 +291,31 @@ private Map<String, Object> computeMarkerAttributes(@Nullable IDocument document
255291 if (source != null ) {
256292 diagnostic .setSource (source .intern ());
257293 }
258- final var attributes = new HashMap <String , Object >(8 );
294+ final var attributes = new HashMap <String , Object >(12 );
259295 attributes .put (LSP_DIAGNOSTIC , diagnostic );
260296 attributes .put (LANGUAGE_SERVER_ID , languageServerId );
261297 attributes .put (IMarker .MESSAGE , markerAttributeComputer .computeMarkerMessage (diagnostic ));
262298 attributes .put (IMarker .SEVERITY , LSPEclipseUtils .toEclipseMarkerSeverity (diagnostic .getSeverity ()));
263299
300+ final var rangeStart = diagnostic .getRange ().getStart ();
301+ final var rangeEnd = diagnostic .getRange ().getEnd ();
302+ attributes .put (IMarker .LINE_NUMBER , rangeStart .getLine () + 1 );
303+ attributes .put (LSP_START_LINE , rangeStart .getLine ());
304+ attributes .put (LSP_START_CHAR , rangeStart .getCharacter ());
305+ attributes .put (LSP_END_LINE , rangeEnd .getLine ());
306+ attributes .put (LSP_END_CHAR , rangeEnd .getCharacter ());
307+
264308 if (document != null ) {
265- Range range = diagnostic .getRange ();
266309 int documentLength = document .getLength ();
267310 int start ;
268311 try {
269- start = Math .min (LSPEclipseUtils .toOffset (range . getStart () , document ), documentLength );
312+ start = Math .min (LSPEclipseUtils .toOffset (rangeStart , document ), documentLength );
270313 } catch (BadLocationException ex ) {
271314 start = documentLength ;
272315 }
273316 int end ;
274317 try {
275- end = Math .min (LSPEclipseUtils .toOffset (range . getEnd () , document ), documentLength );
318+ end = Math .min (LSPEclipseUtils .toOffset (rangeEnd , document ), documentLength );
276319 } catch (BadLocationException ex ) {
277320 end = documentLength ;
278321 }
0 commit comments