Skip to content

Commit 13091af

Browse files
committed
Refactor Groovy lib cache
Signed-off-by: Ben Sherman <bentshermann@gmail.com>
1 parent dd1c786 commit 13091af

File tree

6 files changed

+103
-61
lines changed

6 files changed

+103
-61
lines changed

src/main/java/nextflow/lsp/NextflowLanguageServer.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -495,9 +495,8 @@ private void initializeWorkspaces() {
495495
progress.update(progressMessage, count * 100 / total);
496496
count++;
497497

498-
var uri = workspaceRoots.get(name);
499-
scriptServices.get(name).initialize(uri, configuration);
500-
configServices.get(name).initialize(uri, configuration);
498+
scriptServices.get(name).initialize(configuration);
499+
configServices.get(name).initialize(configuration);
501500
}
502501

503502
progress.end();
@@ -523,8 +522,8 @@ public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) {
523522
var uri = workspaceFolder.getUri();
524523
log.debug("workspace/didChangeWorkspaceFolders add " + name + " " + uri);
525524
addWorkspaceFolder(name, uri);
526-
scriptServices.get(name).initialize(uri, configuration);
527-
configServices.get(name).initialize(uri, configuration);
525+
scriptServices.get(name).initialize(configuration);
526+
configServices.get(name).initialize(configuration);
528527
}
529528
}
530529

@@ -584,11 +583,11 @@ public CompletableFuture<Either<List<? extends SymbolInformation>, List<? extend
584583
private void addWorkspaceFolder(String name, String uri) {
585584
workspaceRoots.put(name, uri);
586585

587-
var scriptService = new ScriptService();
586+
var scriptService = new ScriptService(uri);
588587
scriptService.connect(client);
589588
scriptServices.put(name, scriptService);
590589

591-
var configService = new ConfigService();
590+
var configService = new ConfigService(uri);
592591
configService.connect(client);
593592
configServices.put(name, configService);
594593
}

src/main/java/nextflow/lsp/services/LanguageService.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,16 @@ public abstract class LanguageService {
9292

9393
private static Logger log = Logger.getInstance();
9494

95+
private String rootUri;
96+
9597
private LanguageClient client;
9698

9799
private FileCache fileCache = new FileCache();
98100

99101
private DebouncingExecutor updateExecutor;
100102

101-
public LanguageService() {
103+
public LanguageService(String rootUri) {
104+
this.rootUri = rootUri;
102105
this.updateExecutor = new DebouncingExecutor(DEBOUNCE_MILLIS, this::update);
103106
}
104107

@@ -120,15 +123,12 @@ public LanguageService() {
120123

121124
private volatile boolean scanned;
122125

123-
private volatile String rootUri;
124-
125126
private volatile LanguageServerConfiguration configuration;
126127

127-
public void initialize(String rootUri, LanguageServerConfiguration configuration) {
128+
public void initialize(LanguageServerConfiguration configuration) {
128129
synchronized (this) {
129130
this.initialized = false;
130131
this.scanned = false;
131-
this.rootUri = rootUri;
132132
this.configuration = configuration;
133133

134134
clearDiagnostics();

src/main/java/nextflow/lsp/services/config/ConfigService.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,24 @@
3131
*/
3232
public class ConfigService extends LanguageService {
3333

34-
private ConfigAstCache astCache = new ConfigAstCache();
34+
private ConfigAstCache astCache;
35+
36+
public ConfigService(String rootUri) {
37+
super(rootUri);
38+
astCache = new ConfigAstCache();
39+
}
3540

3641
@Override
3742
public boolean matchesFile(String uri) {
3843
return uri.endsWith(".config") && !uri.endsWith("nf-test.config");
3944
}
4045

4146
@Override
42-
public void initialize(String rootUri, LanguageServerConfiguration configuration) {
47+
public void initialize(LanguageServerConfiguration configuration) {
4348
synchronized (this) {
4449
astCache.initialize(configuration);
4550
}
46-
super.initialize(rootUri, configuration);
51+
super.initialize(configuration);
4752
}
4853

4954
@Override

src/main/java/nextflow/lsp/services/script/GroovyLibCache.java

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.codehaus.groovy.control.CompilationFailedException;
3737
import org.codehaus.groovy.control.CompilationUnit;
3838
import org.codehaus.groovy.control.CompilerConfiguration;
39+
import org.codehaus.groovy.control.Phases;
3940
import org.codehaus.groovy.control.SourceUnit;
4041
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
4142

@@ -46,50 +47,67 @@
4647
*/
4748
public class GroovyLibCache {
4849

50+
private Path libDir;
51+
4952
private Map<URI,Entry> cache = new HashMap<>();
5053

51-
List<ClassNode> load(String rootUri) {
52-
if( rootUri == null )
53-
return Collections.emptyList();
54+
public GroovyLibCache(Path libDir) {
55+
this.libDir = libDir;
56+
}
5457

58+
public List<ClassNode> refresh() {
5559
// collect Groovy files in lib directory
56-
var libDir = Path.of(URI.create(rootUri)).resolve("lib");
57-
if( !Files.isDirectory(libDir) )
60+
if( libDir == null || !Files.isDirectory(libDir) )
5861
return Collections.emptyList();
5962

60-
Set<URI> uris;
63+
var uris = groovyFiles(libDir);
64+
65+
if( uris.isEmpty() )
66+
return Collections.emptyList();
67+
68+
// compile source files
69+
var result = new ArrayList<ClassNode>();
70+
var compilationUnit = compile(uris, result);
71+
72+
// collect compiled class nodes
73+
collectClasses(compilationUnit, result);
74+
75+
return result;
76+
}
77+
78+
private static Set<URI> groovyFiles(Path libDir) {
6179
try {
62-
uris = Files.walk(libDir)
80+
return Files.walk(libDir)
6381
.filter(path -> path.toString().endsWith(".groovy"))
6482
.map(path -> path.toUri())
6583
.collect(Collectors.toSet());
6684
}
6785
catch( IOException e ) {
68-
System.err.println("Failed to read Groovy source files in lib directory: " + e.toString());
69-
return Collections.emptyList();
86+
System.err.println(String.format("Failed to read Groovy source files in lib directory: %s -- %s", libDir, e));
87+
return Collections.emptySet();
7088
}
89+
}
7190

72-
if( uris.isEmpty() )
73-
return Collections.emptyList();
74-
75-
// compile source files
76-
var cachedClasses = new ArrayList<ClassNode>();
91+
private CompilationUnit compile(Set<URI> uris, List<ClassNode> classes) {
92+
// create compilation unit
7793
var config = new CompilerConfiguration();
7894
config.getOptimizationOptions().put(CompilerConfiguration.GROOVYDOC, true);
7995
var classLoader = new GroovyClassLoader();
8096
var compilationUnit = new CompilationUnit(config, null, classLoader);
97+
98+
// create source units (or restore from cache)
8199
for( var uri : uris ) {
82-
var lastModified = getLastModified(uri);
100+
var lastModified = lastModified(uri);
83101
if( cache.containsKey(uri) ) {
84102
var entry = cache.get(uri);
85103
if( lastModified != null && lastModified.equals(entry.lastModified) ) {
86104
if( entry.classes != null )
87-
cachedClasses.addAll(entry.classes);
105+
classes.addAll(entry.classes);
88106
continue;
89107
}
90108
}
91109

92-
System.err.println("compile " + uri.toString());
110+
System.err.println("compile " + uri.getPath());
93111
var sourceUnit = new SourceUnit(
94112
new File(uri),
95113
config,
@@ -99,32 +117,47 @@ List<ClassNode> load(String rootUri) {
99117
cache.put(uri, new Entry(lastModified));
100118
}
101119

120+
// compile source files
102121
try {
103-
compilationUnit.compile(org.codehaus.groovy.control.Phases.CANONICALIZATION);
122+
compilationUnit.compile(Phases.CANONICALIZATION);
104123
}
105124
catch( CompilationFailedException e ) {
106125
// ignore
107126
}
108127
catch( GroovyBugError | Exception e ) {
109-
System.err.println("Failed to compile Groovy source files in lib directory -- " + e.toString());
128+
System.err.println(String.format("Failed to compile Groovy source files in lib directory -- %s", e));
110129
}
111130

112-
// collect class nodes and report errors
113-
var result = new ArrayList<ClassNode>();
114-
result.addAll(cachedClasses);
131+
return compilationUnit;
132+
}
133+
134+
private static FileTime lastModified(URI uri) {
135+
try {
136+
return Files.getLastModifiedTime(Path.of(uri));
137+
}
138+
catch( IOException e ) {
139+
System.err.println(String.format("Failed to get last modified time for %s -- %s", uri.getPath(), e));
140+
return null;
141+
}
142+
}
143+
144+
private void collectClasses(CompilationUnit compilationUnit, List<ClassNode> classes) {
115145
compilationUnit.iterator().forEachRemaining((sourceUnit) -> {
116146
var uri = sourceUnit.getSource().getURI();
147+
148+
// report syntax errors
117149
var errors = sourceUnit.getErrorCollector().getErrors();
118150
if( errors != null ) {
119151
for( var error : errors ) {
120152
if( !(error instanceof SyntaxErrorMessage) )
121153
continue;
122154
var sem = (SyntaxErrorMessage) error;
123155
var cause = sem.getCause();
124-
System.err.println(String.format("Groovy syntax error in %s -- %s: %s", uri, cause, cause.getMessage()));
156+
System.err.println(String.format("Groovy syntax error in %s -- %s: %s", uri.getPath(), cause, cause.getMessage()));
125157
}
126158
}
127159

160+
// collect compiled classes
128161
var moduleNode = sourceUnit.getAST();
129162
if( moduleNode == null )
130163
return;
@@ -139,22 +172,12 @@ List<ClassNode> load(String rootUri) {
139172
: packageName + "." + cn.getNameWithoutPackage();
140173
cn.setName(className);
141174
}
142-
result.addAll(moduleNode.getClasses());
175+
classes.addAll(moduleNode.getClasses());
143176

177+
// update cache
144178
var entry = cache.get(uri);
145179
entry.classes = moduleNode.getClasses();
146180
});
147-
return result;
148-
}
149-
150-
private FileTime getLastModified(URI uri) {
151-
try {
152-
return Files.getLastModifiedTime(Path.of(uri));
153-
}
154-
catch( IOException e ) {
155-
System.err.println(String.format("Failed to get last modified time for %s -- %s", uri, e));
156-
return null;
157-
}
158181
}
159182

160183
private static class Entry {

src/main/java/nextflow/lsp/services/script/ScriptAstCache.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package nextflow.lsp.services.script;
1717

1818
import java.net.URI;
19+
import java.nio.file.Path;
1920
import java.util.ArrayList;
2021
import java.util.Collections;
2122
import java.util.HashSet;
@@ -55,14 +56,20 @@
5556
*/
5657
public class ScriptAstCache extends ASTNodeCache {
5758

58-
private String rootUri;
59+
private GroovyLibCache libCache;
5960

6061
private LanguageServerConfiguration configuration;
6162

62-
private GroovyLibCache libCache = new GroovyLibCache();
63-
64-
public ScriptAstCache() {
63+
public ScriptAstCache(String rootUri) {
6564
super(createCompiler());
65+
this.libCache = createLibCache(rootUri);
66+
}
67+
68+
private static GroovyLibCache createLibCache(String rootUri) {
69+
if( rootUri == null )
70+
return null;
71+
var libDir = Path.of(URI.create(rootUri)).resolve("lib");
72+
return new GroovyLibCache(libDir);
6673
}
6774

6875
private static LanguageServerCompiler createCompiler() {
@@ -82,8 +89,7 @@ private static CompilerConfiguration createConfiguration() {
8289
return config;
8390
}
8491

85-
public void initialize(String rootUri, LanguageServerConfiguration configuration) {
86-
this.rootUri = rootUri;
92+
public void initialize(LanguageServerConfiguration configuration) {
8793
this.configuration = configuration;
8894
}
8995

@@ -113,7 +119,7 @@ protected Set<URI> analyze(Set<URI> uris, FileCache fileCache) {
113119
}
114120
}
115121

116-
var libImports = libCache.load(rootUri);
122+
var libImports = libImports();
117123

118124
for( var uri : changedUris ) {
119125
var sourceUnit = getSourceUnit(uri);
@@ -131,6 +137,10 @@ protected Set<URI> analyze(Set<URI> uris, FileCache fileCache) {
131137
return changedUris;
132138
}
133139

140+
private List<ClassNode> libImports() {
141+
return libCache != null ? libCache.refresh() : Collections.emptyList();
142+
}
143+
134144
@Override
135145
protected Map<ASTNode, ASTNode> visitParents(SourceUnit sourceUnit) {
136146
var visitor = new ScriptAstParentVisitor(sourceUnit);

src/main/java/nextflow/lsp/services/script/ScriptService.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,24 @@
4040
*/
4141
public class ScriptService extends LanguageService {
4242

43-
private ScriptAstCache astCache = new ScriptAstCache();
43+
private ScriptAstCache astCache;
44+
45+
public ScriptService(String rootUri) {
46+
super(rootUri);
47+
astCache = new ScriptAstCache(rootUri);
48+
}
4449

4550
@Override
4651
public boolean matchesFile(String uri) {
4752
return uri.endsWith(".nf");
4853
}
4954

5055
@Override
51-
public void initialize(String rootUri, LanguageServerConfiguration configuration) {
56+
public void initialize(LanguageServerConfiguration configuration) {
5257
synchronized (this) {
53-
astCache.initialize(rootUri, configuration);
58+
astCache.initialize(configuration);
5459
}
55-
super.initialize(rootUri, configuration);
60+
super.initialize(configuration);
5661
}
5762

5863
@Override

0 commit comments

Comments
 (0)