2222import java .util .ArrayList ;
2323import java .util .Arrays ;
2424import java .util .List ;
25+ import java .util .Optional ;
2526import java .util .Set ;
27+ import java .util .concurrent .CompletableFuture ;
2628import java .util .concurrent .atomic .AtomicReference ;
2729import java .util .stream .Collectors ;
2830
3537import org .eclipse .lsp4j .LocationLink ;
3638import org .eclipse .lsp4j .Position ;
3739import org .eclipse .lsp4j .Range ;
40+ import org .eclipse .lsp4j .ShowDocumentParams ;
3841import org .eclipse .lsp4j .TextDocumentIdentifier ;
3942import org .eclipse .lsp4j .jsonrpc .CancelChecker ;
4043import org .slf4j .Logger ;
4649import org .springframework .ide .vscode .commons .java .IClasspathUtil ;
4750import org .springframework .ide .vscode .commons .java .IJavaProject ;
4851import org .springframework .ide .vscode .commons .java .SpringProjectUtil ;
52+ import org .springframework .ide .vscode .commons .languageserver .java .JavaProjectFinder ;
53+ import org .springframework .ide .vscode .commons .languageserver .util .SimpleLanguageServer ;
4954import org .springframework .ide .vscode .commons .languageserver .util .SimpleTextDocumentService ;
5055import org .springframework .ide .vscode .commons .util .BadLocationException ;
5156
57+ import com .google .gson .Gson ;
58+ import com .google .gson .JsonElement ;
59+
5260public class GenAotQueryMethodDefinitionProvider implements IJavaDefinitionProvider {
5361
5462 private static Logger log = LoggerFactory .getLogger (GenAotQueryMethodDefinitionProvider .class );
5563
64+ public static final String CMD_NAVIGATE_TO_IMPL = "sts/boot/open-data-query-method-aot-definition" ;
65+
5666 private final CompilationUnitCache cuCache ;
5767 private final SimpleTextDocumentService docService ;
68+ private final JavaProjectFinder projectFinder ;
5869
59- public GenAotQueryMethodDefinitionProvider (CompilationUnitCache cuCache , SimpleTextDocumentService docService ) {
70+ public GenAotQueryMethodDefinitionProvider (SimpleLanguageServer server , CompilationUnitCache cuCache , JavaProjectFinder projectFinder ) {
6071 this .cuCache = cuCache ;
61- this .docService = docService ;
72+ this .docService = server .getTextDocumentService ();
73+ this .projectFinder = projectFinder ;
74+ registerCommands (server );
6275 }
6376
6477 @ Override
@@ -73,17 +86,29 @@ public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject
7386 && methodBinding .getDeclaringClass () != null
7487 && ASTUtils .isAnyTypeInHierarchy (methodBinding .getDeclaringClass (),
7588 List .of (Constants .REPOSITORY_TYPE ))) {
76- String genRepoFqn = methodBinding .getDeclaringClass ().getQualifiedName () + "Impl__Aot" ;
77- Path relativeGenSourcePath = Paths .get ("%s.java" .formatted (genRepoFqn .replace ('.' , '/' )));
78- List <LocationLink > defs = findInSourceFolder (project , relativeGenSourcePath , docId , md , methodBinding , genRepoFqn );
79- return defs .isEmpty () ? findInBuildFolder (project , relativeGenSourcePath , docId , md , methodBinding , genRepoFqn ) : defs ;
89+
90+ try {
91+ Range originRange = docService .getLatestSnapshot (docId .getUri ()).toRange (md .getName ().getStartPosition (), md .getName ().getLength ());
92+ GoToImplParams params = new GoToImplParams (docId , methodBinding .getDeclaringClass ().getQualifiedName (), methodBinding .getName (), Arrays .stream (methodBinding .getParameterTypes ()).map (b -> b .getQualifiedName ()).toArray (String []::new ), originRange );
93+ return findDefinitions (project , params );
94+ } catch (BadLocationException e ) {
95+ log .error ("" , e );
96+ }
97+
8098 }
8199 }
82100 }
83101 return List .of ();
84102 }
85103
86- private List <LocationLink > getLocationInGenFile (IJavaProject project , TextDocumentIdentifier docId , MethodDeclaration md , IMethodBinding methodBinding , Path genRepoSourcePath , String genRepoFqn ) {
104+ private List <LocationLink > findDefinitions (IJavaProject project , GoToImplParams implParams ) {
105+ String genRepoFqn = implParams .repoFqName () + "Impl__Aot" ;
106+ Path relativeGenSourcePath = Paths .get ("%s.java" .formatted (genRepoFqn .replace ('.' , '/' )));
107+ List <LocationLink > defs = findInSourceFolder (project , relativeGenSourcePath , genRepoFqn , implParams );
108+ return defs .isEmpty () ? findInBuildFolder (project , relativeGenSourcePath , genRepoFqn , implParams ) : defs ;
109+ }
110+
111+ private List <LocationLink > getLocationInGenFile (IJavaProject project , Path genRepoSourcePath , String genRepoFqn , GoToImplParams params ) {
87112 if (Files .exists (genRepoSourcePath )) {
88113 URI genUri = genRepoSourcePath .toUri ();
89114 return cuCache .withCompilationUnit (project , genUri , genCu -> {
@@ -94,21 +119,18 @@ private List<LocationLink> getLocationInGenFile(IJavaProject project, TextDocume
94119 public boolean visit (MethodDeclaration node ) {
95120 IMethodBinding genBinding = node .resolveBinding ();
96121 if (genBinding != null
97- && genBinding .getName ().equals (methodBinding . getName ())
98- && Arrays .equals (Arrays .stream (genBinding .getParameterTypes ()).map (b -> b .getQualifiedName ()).toArray (), Arrays . stream ( methodBinding . getParameterTypes ()). map ( b -> b . getQualifiedName ()). toArray () )
122+ && genBinding .getName ().equals (params . queryMethodName ())
123+ && Arrays .equals (Arrays .stream (genBinding .getParameterTypes ()).map (b -> b .getQualifiedName ()).toArray (), params . paramTypes )
99124 && genRepoFqn .equals (genBinding .getDeclaringClass ().getQualifiedName ())) {
100125 LocationLink ll = new LocationLink ();
101126 ll .setTargetUri (genUri .toASCIIString ());
102- try {
103- ll .setOriginSelectionRange (docService .getLatestSnapshot (docId .getUri ()).toRange (md .getName ().getStartPosition (), md .getName ().getLength ()));
104- } catch (BadLocationException e ) {
105- log .error ("" , e );
106- }
127+ ll .setOriginSelectionRange (params .originSelection ());
107128 SimpleName genName = node .getName ();
108129 int startLine = genCu .getLineNumber (genName .getStartPosition ());
109- Position targetStartPosition = new Position (startLine , genName .getStartPosition () - genCu .getPosition (startLine , 0 ));
130+ // LSP line are 0-based hence -1 from line number when building LSP Range/Position
131+ Position targetStartPosition = new Position (startLine - 1 , genName .getStartPosition () - genCu .getPosition (startLine , 0 ));
110132 int endLine = genCu .getLineNumber (genName .getStartPosition () + genName .getLength ());
111- Position targetEndPosition = new Position (endLine , genName .getStartPosition () + genName .getLength () - genCu .getPosition (endLine , 0 ));
133+ Position targetEndPosition = new Position (endLine - 1 , genName .getStartPosition () + genName .getLength () - genCu .getPosition (endLine , 0 ));
112134 Range targetRange = new Range (targetStartPosition , targetEndPosition );
113135 ll .setTargetRange (targetRange );
114136 ll .setTargetSelectionRange (targetRange );
@@ -124,15 +146,15 @@ public boolean visit(MethodDeclaration node) {
124146 return List .of ();
125147 }
126148
127- private List <LocationLink > findInSourceFolder (IJavaProject project , Path relativeGenSourcePath , TextDocumentIdentifier docId , MethodDeclaration md , IMethodBinding methodBinding , String genRepoFqn ) {
149+ private List <LocationLink > findInSourceFolder (IJavaProject project , Path relativeGenSourcePath , String genRepoFqn , GoToImplParams params ) {
128150 for (File f : IClasspathUtil .getSourceFolders (project .getClasspath ()).collect (Collectors .toSet ())) {
129151 Path genRepoSourcePath = f .toPath ().resolve (relativeGenSourcePath );
130- return getLocationInGenFile (project , docId , md , methodBinding , genRepoSourcePath , genRepoFqn );
152+ return getLocationInGenFile (project , genRepoSourcePath , genRepoFqn , params );
131153 }
132154 return List .of ();
133155 }
134156
135- private List <LocationLink > findInBuildFolder (IJavaProject project , Path relativeGenSourcePath , TextDocumentIdentifier docId , MethodDeclaration md , IMethodBinding methodBinding , String genRepoFqn ) {
157+ private List <LocationLink > findInBuildFolder (IJavaProject project , Path relativeGenSourcePath , String genRepoFqn , GoToImplParams params ) {
136158 Path buildDirRelativePath = null ;
137159 Path projectPath = Paths .get (project .getLocationUri ());
138160 Set <Path > outputFolders = IClasspathUtil .getOutputFolders (project .getClasspath ()).map (f -> f .toPath ()).collect (Collectors .toSet ());
@@ -182,7 +204,30 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
182204 } catch (IOException e ) {
183205 log .error ("" , e );
184206 }
185- return genSourceFilePathRef .get () == null ? List .of () : getLocationInGenFile (project , docId , md , methodBinding , genSourceFilePathRef .get (), genRepoFqn );
207+ return genSourceFilePathRef .get () == null ? List .of () : getLocationInGenFile (project , genSourceFilePathRef .get (), genRepoFqn , params );
186208 }
187209
210+ private void registerCommands (SimpleLanguageServer server ) {
211+ server .onCommand (CMD_NAVIGATE_TO_IMPL , params -> {
212+ return CompletableFuture .supplyAsync (() -> {
213+ GoToImplParams implParams = new Gson ().fromJson ((JsonElement ) params .getArguments ().get (0 ), GoToImplParams .class );
214+ Optional <IJavaProject > project = projectFinder .find (implParams .docId ());
215+ if (project .isEmpty ()) {
216+ return List .<LocationLink >of ();
217+ }
218+ return findDefinitions (project .get (), implParams );
219+ }).thenCompose (links -> {
220+ if (links .isEmpty ()) {
221+ return CompletableFuture .completedFuture (null );
222+ } else {
223+ ShowDocumentParams showDocParams = new ShowDocumentParams (links .get (0 ).getTargetUri ());
224+ showDocParams .setSelection (links .get (0 ).getTargetRange ());
225+ return server .getClient ().showDocument (showDocParams );
226+ }
227+ });
228+ });
229+ }
230+
231+ public record GoToImplParams (TextDocumentIdentifier docId , String repoFqName , String queryMethodName , String [] paramTypes , Range originSelection ) {}
232+
188233}
0 commit comments