Skip to content

Commit ed8a2d2

Browse files
committed
Improve resolution of plugin includes
1 parent c3f0dc6 commit ed8a2d2

File tree

4 files changed

+97
-14
lines changed

4 files changed

+97
-14
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,9 @@ private Map<String, SpecNode> pluginConfigScopes(ConfigNode cn) {
131131
.map(spec -> spec.configScopes())
132132
.toList();
133133

134-
// set current versions in plugin spec cache
135-
pluginSpecCache.setCurrentVersions(refs);
134+
// save current versions to plugin cache
135+
var uri = sourceUnit.getSource().getURI();
136+
pluginSpecCache.setCurrentVersions(uri, refs);
136137

137138
// collect config scopes from plugin specs
138139
var result = new HashMap<String, SpecNode>();

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@ public void visit() {
5858

5959
@Override
6060
public void visitInclude(IncludeNode node) {
61+
var uri = sourceUnit.getSource().getURI();
6162
var source = node.source.getText();
6263
if( !source.startsWith("plugin/") )
6364
return;
6465
var pluginName = source.split("/")[1];
65-
var spec = pluginSpecCache.getCurrent(pluginName);
66+
var spec = pluginSpecCache.getCurrent(uri, pluginName);
6667
if( spec == null ) {
6768
addError("Plugin '" + pluginName + "' does not exist or is not specified in the configuration file", node);
6869
return;

src/main/java/nextflow/lsp/spec/PluginSpecCache.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.net.URI;
2020
import java.net.http.HttpClient;
2121
import java.net.http.HttpRequest;
22+
import java.nio.file.Path;
2223
import java.util.Collections;
2324
import java.util.HashMap;
2425
import java.util.List;
@@ -43,7 +44,7 @@ public class PluginSpecCache {
4344

4445
private Map<PluginRef, PluginSpec> cache = new HashMap<>();
4546

46-
private List<PluginRef> currentVersions;
47+
private Map<Path, List<PluginRef>> pluginsMap = new HashMap<>();
4748

4849
public PluginSpecCache(String registryUrl) {
4950
this.registryUri = URI.create(registryUrl);
@@ -136,23 +137,39 @@ private static List<Map> pluginDefinitions(Map release) {
136137
}
137138

138139
/**
139-
* Set the plugin versions currently specified by the config.
140+
* Set the plugin versions currently specified for a given config file.
140141
*
141-
* @param currentVersions
142+
* Only "main" config files, i.e. files named `nextflow.config`, are recorded.
143+
*
144+
* @param uri
145+
* @param refs
142146
*/
143-
public void setCurrentVersions(List<PluginRef> currentVersions) {
144-
this.currentVersions = currentVersions;
147+
public void setCurrentVersions(URI uri, List<PluginRef> refs) {
148+
if( uri.getPath() == null || !uri.getPath().endsWith("nextflow.config") )
149+
return;
150+
var parent = Path.of(uri).getParent();
151+
this.pluginsMap.put(parent, refs);
145152
}
146153

147154
/**
148155
* Get the currently loaded spec for a plugin.
149-
*
156+
*
157+
* Given the URI of the including file, "./nextflow.config" is
158+
* used if present, otherwise "../nextflow.config" is used if
159+
* present, and so on.
160+
*
161+
* @param uri
150162
* @param name
151163
*/
152-
public PluginSpec getCurrent(String name) {
153-
if( currentVersions == null )
164+
public PluginSpec getCurrent(URI uri, String name) {
165+
var parent = Path.of(uri).getParent();
166+
var key = pluginsMap.keySet().stream()
167+
.filter(p -> parent.startsWith(p))
168+
.sorted((a, b) -> b.compareTo(a))
169+
.findFirst().orElse(null);
170+
if( key == null )
154171
return null;
155-
var ref = currentVersions.stream()
172+
var ref = pluginsMap.get(key).stream()
156173
.filter(r -> r.name().equals(name))
157174
.findFirst().orElse(null);
158175
if( ref == null )

src/test/groovy/nextflow/lsp/spec/PluginSpecCacheTest.groovy

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,76 @@ class PluginSpecCacheTest extends Specification {
2626

2727
def 'should fetch a plugin spec' () {
2828
given:
29-
def pluginSpecCache = new PluginSpecCache('https://registry.nextflow.io/api/')
29+
def psc = pluginSpecCache()
3030

3131
when:
32-
def spec = pluginSpecCache.get('nf-prov', '1.6.0')
32+
def spec = psc.get('nf-prov', '1.6.0')
3333
then:
3434
spec.configScopes().containsKey('prov')
3535
}
3636

37+
def 'should only track plugin versions for main config files' () {
38+
given:
39+
def psc = pluginSpecCache()
40+
def refs = [new PluginRef('nf-hello', '0.9.0')]
41+
42+
when:
43+
psc.setCurrentVersions(uri('/project/other.config'), refs)
44+
then:
45+
psc.getCurrent(uri('/project/main.nf'), 'nf-hello') == null
46+
}
47+
48+
def 'should return current plugin versions for a given script file' () {
49+
given:
50+
def ref1 = new PluginRef('nf-hello', '0.9.0')
51+
def ref2 = new PluginRef('nf-hello', '1.0.0')
52+
def spec1 = new PluginSpec([:], [], [], [])
53+
def spec2 = new PluginSpec([:], [], [], [])
54+
def psc = pluginSpecCache([(ref1): spec1, (ref2): spec2])
55+
56+
// return nothing when no config files are registered
57+
expect:
58+
psc.getCurrent(uri('/project/main.nf'), 'nf-hello') == null
59+
60+
// return nothing when script has no parent config file
61+
when:
62+
psc.setCurrentVersions(uri('/project/nextflow.config'), [new PluginRef('nf-hello', '0.9.0')])
63+
then:
64+
psc.getCurrent(uri('/other-project/main.nf'), 'nf-hello') == null
65+
66+
// return nothing when requested plugin is not enabled
67+
when:
68+
psc.setCurrentVersions(uri('/project/nextflow.config'), [new PluginRef('nf-other', '1.0.0')])
69+
then:
70+
psc.getCurrent(uri('/project/main.nf'), 'nf-hello') == null
71+
72+
// return plugin spec from nearest parent config
73+
when:
74+
psc.setCurrentVersions(uri('/project/nextflow.config'), [ref1])
75+
then:
76+
psc.getCurrent(uri('/project/main.nf'), 'nf-hello') == spec2
77+
psc.getCurrent(uri('/project/subdir/module.nf'), 'nf-hello') == spec2
78+
79+
when:
80+
psc.setCurrentVersions(uri('/project/nextflow.config'), [ref1])
81+
psc.setCurrentVersions(uri('/project/subdir/nextflow.config'), [ref2])
82+
then:
83+
psc.getCurrent(uri('/project/main.nf'), 'nf-hello') == spec1
84+
psc.getCurrent(uri('/project/subdir/module.nf'), 'nf-hello') == spec2
85+
}
86+
87+
def pluginSpecCache(Map entries = null) {
88+
def result = new PluginSpecCache('https://registry.nextflow.io/api/')
89+
if( entries ) {
90+
def field = PluginSpecCache.getDeclaredField('cache')
91+
field.setAccessible(true)
92+
field.set(result, entries)
93+
}
94+
return result
95+
}
96+
97+
def uri(String path) {
98+
return URI.create('file://' + path)
99+
}
100+
37101
}

0 commit comments

Comments
 (0)