11/*******************************************************************************
2- * Copyright (c) 2016, 2023 Red Hat Inc. and others.
2+ * Copyright (c) 2016, 2026 Red Hat Inc. and others.
33 * This program and the accompanying materials are made
44 * available under the terms of the Eclipse Public License 2.0
55 * which is available at https://www.eclipse.org/legal/epl-2.0/
1616 *******************************************************************************/
1717package org .eclipse .lsp4e .operations .hover ;
1818
19- import static org .eclipse .lsp4e .internal .NullSafetyHelper .castNonNull ;
20-
2119import java .util .List ;
2220import java .util .NoSuchElementException ;
2321import java .util .Objects ;
@@ -98,10 +96,18 @@ public class LSPTextHover implements ITextHover, ITextHoverExtension, ITextHover
9896 }
9997
10098 public CompletableFuture <@ Nullable String > getHoverInfoFuture (ITextViewer textViewer , IRegion hoverRegion ) {
101- if (this .request == null || !textViewer .equals (this .lastViewer ) || !hoverRegion .equals (this .lastRegion )) {
99+ CompletableFuture <List <Hover >> locRequest = this .request ;
100+ if (locRequest == null || locRequest .isDone () || !textViewer .equals (this .lastViewer )
101+ || !hoverRegion .equals (this .lastRegion )) {
102102 initiateHoverRequest (textViewer , hoverRegion .getOffset ());
103+ locRequest = this .request ;
104+ }
105+
106+ if (locRequest == null ) {
107+ return CompletableFuture .completedFuture (null );
103108 }
104- return castNonNull (request ).thenApply (hoversList -> {
109+
110+ return locRequest .thenApply (hoversList -> {
105111 String result = hoversList .stream () //
106112 .filter (Objects ::nonNull ) //
107113 .map (LSPTextHover ::getHoverString ) //
@@ -110,9 +116,8 @@ public class LSPTextHover implements ITextHover, ITextHoverExtension, ITextHover
110116 .trim ();
111117 if (!result .isEmpty ()) {
112118 return MarkdownUtil .renderToHtml (result );
113- } else {
114- return null ;
115119 }
120+ return null ;
116121 });
117122 }
118123
@@ -149,28 +154,40 @@ public class LSPTextHover implements ITextHover, ITextHoverExtension, ITextHover
149154 @ Override
150155 public @ Nullable IRegion getHoverRegion (ITextViewer textViewer , int offset ) {
151156 final var lastRegion = this .lastRegion ;
152- if (this .request == null || lastRegion == null || !textViewer .equals (this .lastViewer )
153- || offset < lastRegion .getOffset () || offset > lastRegion .getOffset () + lastRegion .getLength ()) {
157+
158+ CompletableFuture <List <Hover >> locRequest = this .request ;
159+ if (locRequest == null || locRequest .isDone () || lastRegion == null || !textViewer .equals (this .lastViewer )
160+ || offset < lastRegion .getOffset () || offset >= lastRegion .getOffset () + lastRegion .getLength ()) {
154161 initiateHoverRequest (textViewer , offset );
162+ locRequest = this .request ;
155163 }
156164
157165 final IDocument document = textViewer .getDocument ();
158166 if (document == null ) {
159167 return null ;
160168 }
161169
170+ if (locRequest == null ) {
171+ return this .lastRegion = computeHeuristicRegion (document , offset );
172+ }
173+
162174 try {
163175 // Wait shortly for hover region result, fallback to heuristics if LS is laggy
164- Range range = castNonNull (this .request ).get (GET_HOVER_REGION_TIMEOUT_MS , TimeUnit .MILLISECONDS ).stream () //
176+ List <Hover > hovers = locRequest .get (GET_HOVER_REGION_TIMEOUT_MS , TimeUnit .MILLISECONDS );
177+
178+ Range range = hovers .stream () //
165179 .filter (Objects ::nonNull ) //
166180 .map (Hover ::getRange ) //
167181 .filter (Objects ::nonNull ) //
168182 .reduce ((first , second ) -> second ) //
169- .get ();
170- int regionStartOffset = Math .max (0 ,
171- LSPEclipseUtils .toOffset (range .getStart (), document ));
172- int regionEndOffset = Math .min (document .getLength (),
173- LSPEclipseUtils .toOffset (range .getEnd (), document ));
183+ .orElse (null );
184+
185+ if (range == null ) {
186+ return this .lastRegion = computeHeuristicRegion (document , offset );
187+ }
188+
189+ int regionStartOffset = Math .max (0 , LSPEclipseUtils .toOffset (range .getStart (), document ));
190+ int regionEndOffset = Math .min (document .getLength (), LSPEclipseUtils .toOffset (range .getEnd (), document ));
174191 return this .lastRegion = new Region (regionStartOffset , regionEndOffset - regionStartOffset );
175192 } catch (ExecutionException | BadLocationException e ) {
176193 if (!CancellationUtil .isRequestCancelledException (e )) {
0 commit comments