Skip to content

Commit 7c99338

Browse files
committed
Use augmented config schema in hover hints and code completions
1 parent 993e6f3 commit 7c99338

File tree

5 files changed

+41
-38
lines changed

5 files changed

+41
-38
lines changed

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Set;
2222

2323
import groovy.lang.GroovyClassLoader;
24+
import nextflow.config.ast.ConfigNode;
2425
import nextflow.config.control.ConfigResolveVisitor;
2526
import nextflow.config.control.ResolveIncludeVisitor;
2627
import nextflow.config.parser.ConfigParserPluginFactory;
@@ -46,8 +47,6 @@ public class ConfigAstCache extends ASTNodeCache {
4647

4748
private LanguageServerConfiguration configuration;
4849

49-
private SchemaNode.Scope schema = ConfigSchemaFactory.load();
50-
5150
private PluginSpecCache pluginSpecCache;
5251

5352
public ConfigAstCache() {
@@ -95,7 +94,7 @@ protected Set<URI> analyze(Set<URI> uris, FileCache fileCache) {
9594
continue;
9695
// phase 3: name checking
9796
new ConfigResolveVisitor(sourceUnit, compiler().compilationUnit(), Types.DEFAULT_CONFIG_IMPORTS).visit();
98-
new ConfigSchemaVisitor(sourceUnit, schema, pluginSpecCache, configuration.typeChecking()).visit();
97+
new ConfigSchemaVisitor(sourceUnit, pluginSpecCache, configuration.typeChecking()).visit();
9998
if( sourceUnit.getErrorCollector().hasErrors() )
10099
continue;
101100
// phase 4: type checking
@@ -124,4 +123,8 @@ public boolean hasSyntaxErrors(URI uri) {
124123
.isPresent();
125124
}
126125

126+
public ConfigNode getConfigNode(URI uri) {
127+
return (ConfigNode) getSourceUnit(uri).getAST();
128+
}
129+
127130
}

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import nextflow.config.ast.ConfigIncompleteNode;
2626
import nextflow.config.dsl.ConfigDsl;
2727
import nextflow.config.schema.SchemaNode;
28-
import nextflow.lsp.ast.ASTNodeCache;
2928
import nextflow.lsp.ast.CompletionHelper;
3029
import nextflow.lsp.services.CompletionProvider;
3130
import nextflow.lsp.util.Logger;
@@ -66,10 +65,10 @@ public class ConfigCompletionProvider implements CompletionProvider {
6665

6766
private static Logger log = Logger.getInstance();
6867

69-
private ASTNodeCache ast;
68+
private ConfigAstCache ast;
7069
private CompletionHelper ch;
7170

72-
public ConfigCompletionProvider(ASTNodeCache ast, int maxItems) {
71+
public ConfigCompletionProvider(ConfigAstCache ast, int maxItems) {
7372
this.ast = ast;
7473
this.ch = new CompletionHelper(maxItems);
7574
}
@@ -96,7 +95,7 @@ public Either<List<CompletionItem>, CompletionList> completion(TextDocumentIdent
9695
var names = currentConfigScope(nodeStack);
9796
if( names.isEmpty() )
9897
return Either.forLeft(TOPLEVEL_ITEMS);
99-
addConfigOptions(names);
98+
addConfigOptions(names, ast.getConfigNode(uri).getSchema());
10099
}
101100

102101
return ch.isIncomplete()
@@ -179,8 +178,8 @@ private static List<String> currentConfigScope(List<ASTNode> nodeStack) {
179178
return names;
180179
}
181180

182-
private void addConfigOptions(List<String> names) {
183-
var scope = SchemaNode.ROOT.getScope(names);
181+
private void addConfigOptions(List<String> names, SchemaNode.Scope schema) {
182+
var scope = schema.getScope(names);
184183
if( scope == null )
185184
return;
186185
scope.children().forEach((name, child) -> {
@@ -192,8 +191,9 @@ private void addConfigOptions(List<String> names) {
192191
}
193192

194193
private static List<CompletionItem> topLevelItems() {
194+
var defaultScopes = ConfigSchemaFactory.defaultScopes();
195195
var result = new ArrayList<CompletionItem>();
196-
SchemaNode.ROOT.children().forEach((name, child) -> {
196+
defaultScopes.forEach((name, child) -> {
197197
if( child instanceof SchemaNode.Option option ) {
198198
result.add(configOption(name, option.description(), option.type()));
199199
}

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import nextflow.config.ast.ConfigAssignNode;
2424
import nextflow.config.ast.ConfigBlockNode;
2525
import nextflow.config.schema.SchemaNode;
26-
import nextflow.lsp.ast.ASTNodeCache;
2726
import nextflow.lsp.ast.ASTNodeStringUtils;
2827
import nextflow.lsp.ast.LanguageServerASTUtils;
2928
import nextflow.lsp.services.HoverProvider;
@@ -49,9 +48,9 @@ public class ConfigHoverProvider implements HoverProvider {
4948

5049
private static Logger log = Logger.getInstance();
5150

52-
private ASTNodeCache ast;
51+
private ConfigAstCache ast;
5352

54-
public ConfigHoverProvider(ASTNodeCache ast) {
53+
public ConfigHoverProvider(ConfigAstCache ast) {
5554
this.ast = ast;
5655
}
5756

@@ -69,7 +68,7 @@ public Hover hover(TextDocumentIdentifier textDocument, Position position) {
6968

7069
var builder = new StringBuilder();
7170

72-
var content = getHoverContent(nodeStack);
71+
var content = getHoverContent(nodeStack, ast.getConfigNode(uri).getSchema());
7372
if( content != null ) {
7473
builder.append(content);
7574
builder.append('\n');
@@ -94,14 +93,14 @@ public Hover hover(TextDocumentIdentifier textDocument, Position position) {
9493
return new Hover(new MarkupContent(MarkupKind.MARKDOWN, value));
9594
}
9695

97-
protected String getHoverContent(List<ASTNode> nodeStack) {
96+
protected String getHoverContent(List<ASTNode> nodeStack, SchemaNode.Scope schema) {
9897
var offsetNode = nodeStack.get(0);
9998
if( offsetNode instanceof ConfigAssignNode assign ) {
10099
var names = getCurrentScope(nodeStack);
101100
names.addAll(assign.names);
102101

103102
var fqName = String.join(".", names);
104-
var option = SchemaNode.ROOT.getOption(names);
103+
var option = schema.getOption(names);
105104
if( option != null ) {
106105
var description = StringGroovyMethods.stripIndent(option.description(), true).trim();
107106
var builder = new StringBuilder();
@@ -120,7 +119,7 @@ else if( Logger.isDebugEnabled() ) {
120119
if( names.isEmpty() )
121120
return null;
122121

123-
var scope = SchemaNode.ROOT.getChild(names);
122+
var scope = schema.getChild(names);
124123
if( scope != null ) {
125124
return StringGroovyMethods.stripIndent(scope.description(), true).trim();
126125
}

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,18 @@
3434
*/
3535
public class ConfigSchemaFactory {
3636

37+
private static Map<String,SchemaNode> defaultScopes = null;
38+
3739
/**
3840
* Load config scopes from core definitions.
3941
*/
40-
public static SchemaNode.Scope load() {
41-
var scope = SchemaNode.ROOT;
42-
scope.children().putAll(fromCoreDefinitions());
43-
return scope;
42+
public static Map<String,SchemaNode> defaultScopes() {
43+
if( defaultScopes == null ) {
44+
var scope = SchemaNode.ROOT;
45+
scope.children().putAll(fromCoreDefinitions());
46+
defaultScopes = scope.children();
47+
}
48+
return defaultScopes;
4449
}
4550

4651
private static Map<String,SchemaNode> fromCoreDefinitions() {

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

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,14 @@ public class ConfigSchemaVisitor extends ConfigVisitorSupport {
5353

5454
private SourceUnit sourceUnit;
5555

56-
private SchemaNode.Scope schema;
57-
5856
private PluginSpecCache pluginSpecCache;
5957

6058
private boolean typeChecking;
6159

6260
private Stack<String> scopes = new Stack<>();
6361

64-
public ConfigSchemaVisitor(SourceUnit sourceUnit, SchemaNode.Scope schema, PluginSpecCache pluginSpecCache, boolean typeChecking) {
62+
public ConfigSchemaVisitor(SourceUnit sourceUnit, PluginSpecCache pluginSpecCache, boolean typeChecking) {
6563
this.sourceUnit = sourceUnit;
66-
this.schema = schema;
6764
this.pluginSpecCache = pluginSpecCache;
6865
this.typeChecking = typeChecking;
6966
}
@@ -73,26 +70,25 @@ protected SourceUnit getSourceUnit() {
7370
return sourceUnit;
7471
}
7572

73+
private SchemaNode.Scope schema;
74+
7675
public void visit() {
7776
var moduleNode = sourceUnit.getAST();
7877
if( moduleNode instanceof ConfigNode cn ) {
79-
loadPluginScopes(cn);
78+
this.schema = getPluginScopes(cn);
79+
cn.setSchema(schema);
8080
super.visit(cn);
81+
this.schema = null;
8182
}
8283
}
8384

84-
private void loadPluginScopes(ConfigNode cn) {
85-
try {
86-
var defaultScopes = schema.children();
87-
var pluginScopes = pluginConfigScopes(cn);
88-
var children = new HashMap<String, SchemaNode>();
89-
children.putAll(defaultScopes);
90-
children.putAll(pluginScopes);
91-
this.schema = new SchemaNode.Scope(schema.description(), children);
92-
}
93-
catch( Exception e ) {
94-
System.err.println("Failed to load plugin config scopes: " + e.toString());
95-
}
85+
private SchemaNode.Scope getPluginScopes(ConfigNode cn) {
86+
var defaultScopes = ConfigSchemaFactory.defaultScopes();
87+
var pluginScopes = pluginConfigScopes(cn);
88+
var children = new HashMap<String, SchemaNode>();
89+
children.putAll(defaultScopes);
90+
children.putAll(pluginScopes);
91+
return new SchemaNode.Scope("", children);
9692
}
9793

9894
private Map<String, SchemaNode> pluginConfigScopes(ConfigNode cn) {

0 commit comments

Comments
 (0)