88import com .sun .source .doctree .DocCommentTree ;
99import com .sun .source .doctree .DocTree ;
1010import com .sun .source .doctree .ParamTree ;
11+ import com .sun .source .tree .ClassTree ;
12+ import com .sun .source .tree .CompilationUnitTree ;
1113import com .sun .source .tree .MethodTree ;
14+ import com .sun .source .tree .Tree ;
15+ import com .sun .source .tree .VariableTree ;
16+ import com .sun .source .util .TreePath ;
1217import java .io .BufferedReader ;
1318import java .io .IOException ;
1419import java .io .StringReader ;
@@ -228,7 +233,7 @@ public JavaLanguageServer(LanguageClient client) {
228233 public List <SymbolInformation > workspaceSymbols (WorkspaceSymbolParams params ) {
229234 var list = new ArrayList <SymbolInformation >();
230235 for (var s : compiler .findSymbols (params .query , 50 )) {
231- var i = Parser . asSymbolInformation (s );
236+ var i = asSymbolInformation (s );
232237 list .add (i );
233238 }
234239 return list ;
@@ -742,35 +747,112 @@ public List<Location> findReferences(ReferenceParams position) {
742747 return result ;
743748 }
744749
750+ private ParseFile cacheParse ;
751+ private URI cacheParseFile = URI .create ("file:///NONE" );;
752+ private int cacheParseVersion = -1 ;
753+
754+ private void updateCachedParse (URI file ) {
755+ if (file .equals (cacheParseFile ) && contents (file ).version == cacheParseVersion ) return ;
756+ LOG .info (String .format ("Updating cached parse file to %s" , file ));
757+ var contents = contents (file );
758+ cacheParse = compiler .parseFile (file , contents .content );
759+ cacheParseFile = file ;
760+ cacheParseVersion = contents .version ;
761+ }
762+
745763 @ Override
746764 public List <SymbolInformation > documentSymbol (DocumentSymbolParams params ) {
747765 var uri = params .textDocument .uri ;
748766 if (!isJavaFile (uri )) return List .of ();
749- var content = contents (uri ).content ;
750- var result =
751- Parser .documentSymbols (Paths .get (uri ), content )
752- .stream ()
753- .map (Parser ::asSymbolInformation )
754- .collect (Collectors .toList ());
755- return result ;
767+ updateCachedParse (uri );
768+ var paths = cacheParse .documentSymbols ();
769+ var infos = new ArrayList <SymbolInformation >();
770+ for (var p : paths ) {
771+ infos .add (asSymbolInformation (p ));
772+ }
773+ return infos ;
774+ }
775+
776+ static SymbolInformation asSymbolInformation (TreePath path ) {
777+ var i = new SymbolInformation ();
778+ var t = path .getLeaf ();
779+ i .kind = asSymbolKind (t .getKind ());
780+ i .name = symbolName (t );
781+ i .containerName = containerName (path );
782+ i .location = Parser .location (path );
783+ return i ;
784+ }
785+
786+ private static Integer asSymbolKind (Tree .Kind k ) {
787+ switch (k ) {
788+ case ANNOTATION_TYPE :
789+ case CLASS :
790+ return SymbolKind .Class ;
791+ case ENUM :
792+ return SymbolKind .Enum ;
793+ case INTERFACE :
794+ return SymbolKind .Interface ;
795+ case METHOD :
796+ return SymbolKind .Method ;
797+ case TYPE_PARAMETER :
798+ return SymbolKind .TypeParameter ;
799+ case VARIABLE :
800+ // This method is used for symbol-search functionality,
801+ // where we only return fields, not local variables
802+ return SymbolKind .Field ;
803+ default :
804+ return null ;
805+ }
806+ }
807+
808+ private static String containerName (TreePath path ) {
809+ var parent = path .getParentPath ();
810+ while (parent != null ) {
811+ var t = parent .getLeaf ();
812+ if (t instanceof ClassTree ) {
813+ var c = (ClassTree ) t ;
814+ return c .getSimpleName ().toString ();
815+ } else if (t instanceof CompilationUnitTree ) {
816+ var c = (CompilationUnitTree ) t ;
817+ return Objects .toString (c .getPackageName (), "" );
818+ } else {
819+ parent = parent .getParentPath ();
820+ }
821+ }
822+ return null ;
823+ }
824+
825+ private static String symbolName (Tree t ) {
826+ if (t instanceof ClassTree ) {
827+ var c = (ClassTree ) t ;
828+ return c .getSimpleName ().toString ();
829+ } else if (t instanceof MethodTree ) {
830+ var m = (MethodTree ) t ;
831+ return m .getName ().toString ();
832+ } else if (t instanceof VariableTree ) {
833+ var v = (VariableTree ) t ;
834+ return v .getName ().toString ();
835+ } else {
836+ LOG .warning ("Don't know how to create SymbolInformation from " + t );
837+ return "???" ;
838+ }
756839 }
757840
758841 @ Override
759842 public List <CodeLens > codeLens (CodeLensParams params ) {
760843 // TODO just create a blank code lens on every method, then resolve it async
761844 var uri = params .textDocument .uri ;
762845 if (!isJavaFile (uri )) return List .of ();
763- var content = contents (uri ).content ;
764- var parse = compiler .parseFile (uri , content );
765- var declarations = parse .declarations ();
846+ updateCachedParse (uri );
847+ var declarations = cacheParse .declarations ();
766848 var result = new ArrayList <CodeLens >();
767849 for (var d : declarations ) {
768- var range = parse .range (d );
850+ var range = cacheParse .range (d );
769851 if (!range .isPresent ()) continue ;
770852 var className = JavaCompilerService .className (d );
771853 var memberName = JavaCompilerService .memberName (d );
772854 // If test class or method, add "Run Test" code lens
773- if (parse .isTestClass (d )) {
855+ if (cacheParse .isTestClass (d )) {
774856 var arguments = new JsonArray ();
775857 arguments .add (uri .toString ());
776858 arguments .add (className );
@@ -780,7 +862,7 @@ public List<CodeLens> codeLens(CodeLensParams params) {
780862 result .add (lens );
781863 // TODO run all tests in file
782864 // TODO run all tests in package
783- } else if (parse .isTestMethod (d )) {
865+ } else if (cacheParse .isTestMethod (d )) {
784866 var arguments = new JsonArray ();
785867 arguments .add (uri .toString ());
786868 arguments .add (className );
@@ -823,6 +905,11 @@ private void updateCacheCountReferences(URI current) {
823905
824906 @ Override
825907 public CodeLens resolveCodeLens (CodeLens unresolved ) {
908+ // TODO This is pretty klugey, should happen asynchronously after CodeLenses are shown
909+ if (!recentlyOpened .isEmpty ()) {
910+ lint (recentlyOpened );
911+ recentlyOpened .clear ();
912+ }
826913 // Unpack data
827914 var data = unresolved .data ;
828915 var command = data .get (0 ).getAsString ();
@@ -916,14 +1003,17 @@ private boolean isJavaFile(URI uri) {
9161003 return uri .getScheme ().equals ("file" ) && uri .getPath ().endsWith (".java" );
9171004 }
9181005
1006+ private List <URI > recentlyOpened = new ArrayList <>();
1007+
9191008 @ Override
9201009 public void didOpenTextDocument (DidOpenTextDocumentParams params ) {
9211010 var document = params .textDocument ;
9221011 var uri = document .uri ;
923- if (isJavaFile (uri )) {
924- activeDocuments .put (uri , new VersionedContent (document .text , document .version ));
925- lint (Collections .singleton (uri ));
926- }
1012+ if (!isJavaFile (uri )) return ;
1013+ LOG .info (String .format ("Opened %s" , Parser .fileName (uri )));
1014+ activeDocuments .put (uri , new VersionedContent (document .text , document .version ));
1015+ recentlyOpened .add (uri );
1016+ updateCachedParse (uri ); // So that subsequent documentSymbol and codeLens requests will be faster
9271017 }
9281018
9291019 @ Override
0 commit comments