Add quickfix for missing properties in concourse editor
This commit is contained in:
@@ -69,7 +69,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer {
|
||||
documents.onHover(hoverInfoProvider);
|
||||
|
||||
ReferencesHandler referencesHandler = new BootJavaReferencesHandler(this, javaProjectFinder);
|
||||
documents.onRefeences(referencesHandler);
|
||||
documents.onReferences(referencesHandler);
|
||||
}
|
||||
|
||||
public void setMaxCompletionsNumber(int number) {
|
||||
|
||||
@@ -20,14 +20,10 @@ import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.references.ValuePropertyReferencesProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
|
||||
@@ -11,12 +11,11 @@
|
||||
|
||||
package org.springframework.ide.vscode.boot.metadata.hints;
|
||||
|
||||
import java.util.List;
|
||||
import static org.springframework.ide.vscode.commons.util.Renderables.bold;
|
||||
import static org.springframework.ide.vscode.commons.util.Renderables.concat;
|
||||
import static org.springframework.ide.vscode.commons.util.Renderables.paragraph;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.util.Renderables.*;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
|
||||
@@ -13,13 +13,12 @@ package org.springframework.ide.vscode.boot.properties.quickfix;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.ProblemFixer;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
|
||||
public class ReplaceDeprecatedPropertyQuickfix implements ICompletionProposal {
|
||||
|
||||
public static ProblemFixer FIXER = (context, problem, proposals) -> {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
// public static ProblemFixer FIXER = (context, problem, proposals) -> {
|
||||
// throw new UnsupportedOperationException("Not yet implemented");
|
||||
// PropertyInfo metadata = problem.getMetadata();
|
||||
// if (metadata!=null) {
|
||||
// String replacement = metadata.getDeprecationReplacement();
|
||||
@@ -28,7 +27,7 @@ public class ReplaceDeprecatedPropertyQuickfix implements ICompletionProposal {
|
||||
// proposals.add(new ReplaceDeprecatedYamlQuickfix(context, problem));
|
||||
// }
|
||||
// }
|
||||
};
|
||||
// };
|
||||
|
||||
@Override
|
||||
public ICompletionProposal deemphasize() {
|
||||
@@ -57,7 +56,7 @@ public class ReplaceDeprecatedPropertyQuickfix implements ICompletionProposal {
|
||||
|
||||
// private final QuickfixContext context;
|
||||
// private final SpringPropertyProblem problem;
|
||||
//
|
||||
//
|
||||
// private LazyProposalApplier applier = new LazyProposalApplier() {
|
||||
// protected ProposalApplier create() throws Exception {
|
||||
// String newName = problem.getMetadata().getDeprecationReplacement();
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.springframework.ide.vscode.boot.metadata.types.Type;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeParser;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider;
|
||||
import org.springframework.ide.vscode.boot.properties.quickfix.ReplaceDeprecatedPropertyQuickfix;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
@@ -72,26 +71,27 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine {
|
||||
this.fIndexProvider = provider;
|
||||
this.typeUtilProvider = typeUtilProvider;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void reconcile(IDocument doc, IProblemCollector problemCollector) {
|
||||
FuzzyMap<PropertyInfo> index = fIndexProvider.getIndex(doc);
|
||||
problemCollector.beginCollecting();
|
||||
try {
|
||||
ParseResults results = parser.parse(doc.get());
|
||||
DuplicateNameChecker duplicateNameChecker = new DuplicateNameChecker(problemCollector);
|
||||
|
||||
|
||||
results.syntaxErrors.forEach(syntaxError -> {
|
||||
problemCollector.accept(problem(PROP_SYNTAX_ERROR, syntaxError.getMessage(), syntaxError.getOffset(),
|
||||
syntaxError.getLength()));
|
||||
});
|
||||
|
||||
|
||||
if (index==null || index.isEmpty()) {
|
||||
//don't report errors when index is empty, simply don't check (otherwise we will just reprot
|
||||
// all properties as errors, but this not really useful information since the cause is
|
||||
// some problem putting information about properties into the index.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
results.ast.getNodes(KeyValuePair.class).forEach(pair -> {
|
||||
try {
|
||||
DocumentRegion propertyNameRegion = createRegion(doc, pair.getKey());
|
||||
@@ -127,7 +127,7 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine {
|
||||
problemCollector.endCollecting();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected SpringPropertyProblem problemDeprecated(DocumentRegion region, PropertyInfo property) {
|
||||
SpringPropertyProblem p = problem(PROP_DEPRECATED,
|
||||
TypeUtil.deprecatedPropertyMessage(
|
||||
@@ -139,7 +139,7 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine {
|
||||
);
|
||||
p.setPropertyName(property.getId());
|
||||
p.setMetadata(property);
|
||||
p.setProblemFixer(ReplaceDeprecatedPropertyQuickfix.FIXER);
|
||||
// p.setProblemFixer(ReplaceDeprecatedPropertyQuickfix.FIXER);
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -167,10 +167,10 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine {
|
||||
length = doc.get(value.getOffset(), value.getLength()).length();
|
||||
} catch (BadLocationException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return new DocumentRegion(doc, value.getOffset(), value.getOffset() + length);
|
||||
}
|
||||
|
||||
|
||||
private void reconcileType(DocumentRegion region, Type expectType, IProblemCollector problems) {
|
||||
TypeUtil typeUtil = typeUtilProvider.getTypeUtil(region.getDocument());
|
||||
ValueParser parser = typeUtil.getValueParser(expectType);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
package org.springframework.ide.vscode.boot.properties.reconcile;
|
||||
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.ProblemFixer;
|
||||
//import org.springframework.ide.vscode.commons.languageserver.quickfix.ProblemFixer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
@@ -22,7 +22,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion
|
||||
public class SpringPropertyProblem extends ReconcileProblemImpl {
|
||||
|
||||
private PropertyInfo property = null;
|
||||
private ProblemFixer fixer;
|
||||
// private ProblemFixer fixer;
|
||||
private String propertyName;
|
||||
|
||||
public SpringPropertyProblem(ProblemType type, String msg, int offset, int len) {
|
||||
@@ -39,14 +39,14 @@ public class SpringPropertyProblem extends ReconcileProblemImpl {
|
||||
}
|
||||
return new SpringPropertyProblem(type, msg, region.getStart(), region.getLength());
|
||||
}
|
||||
|
||||
|
||||
public void setMetadata(PropertyInfo property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public void setProblemFixer(ProblemFixer fixer) {
|
||||
this.fixer = fixer;
|
||||
}
|
||||
// public void setProblemFixer(ProblemFixer fixer) {
|
||||
// this.fixer = fixer;
|
||||
// }
|
||||
|
||||
public void setPropertyName(String name) {
|
||||
propertyName = name;
|
||||
|
||||
@@ -13,13 +13,12 @@ package org.springframework.ide.vscode.boot.yaml.quickfix;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.ProblemFixer;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
|
||||
public class ReplaceDeprecatedYamlQuickfix implements ICompletionProposal {
|
||||
|
||||
public static ProblemFixer FIXER = (context, problem, proposals) -> {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
// public static ProblemFixer FIXER = (context, problem, proposals) -> {
|
||||
// throw new UnsupportedOperationException("Not yet implemented");
|
||||
// PropertyInfo metadata = problem.getMetadata();
|
||||
// if (metadata!=null) {
|
||||
// String replacement = metadata.getDeprecationReplacement();
|
||||
@@ -28,7 +27,7 @@ public class ReplaceDeprecatedYamlQuickfix implements ICompletionProposal {
|
||||
// proposals.add(new ReplaceDeprecatedYamlQuickfix(context, problem));
|
||||
// }
|
||||
// }
|
||||
};
|
||||
// };
|
||||
|
||||
@Override
|
||||
public ICompletionProposal deemphasize() {
|
||||
@@ -57,7 +56,7 @@ public class ReplaceDeprecatedYamlQuickfix implements ICompletionProposal {
|
||||
|
||||
// private final QuickfixContext context;
|
||||
// private final SpringPropertyProblem problem;
|
||||
//
|
||||
//
|
||||
// private LazyProposalApplier applier = new LazyProposalApplier() {
|
||||
// protected ProposalApplier create() throws Exception {
|
||||
// String newName = problem.getMetadata().getDeprecationReplacement();
|
||||
|
||||
@@ -26,18 +26,17 @@ import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.Type;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeParser;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypedProperty;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.BeanPropertyNameMode;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.EnumCaseMode;
|
||||
import org.springframework.ide.vscode.boot.yaml.quickfix.ReplaceDeprecatedYamlQuickfix;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypedProperty;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.Kind;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.TupleValueRef;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlASTReconciler;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
@@ -350,7 +349,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
SpringPropertyProblem problem = deprecatedPropertyProblem(property.getId(), null, keyNode,
|
||||
property.getDeprecationReplacement(), property.getDeprecationReason());
|
||||
problem.setMetadata(property);
|
||||
problem.setProblemFixer(ReplaceDeprecatedYamlQuickfix.FIXER);
|
||||
//problem.setProblemFixer(ReplaceDeprecatedYamlQuickfix.FIXER);
|
||||
problems.accept(problem);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,13 @@
|
||||
package org.springframework.ide.vscode.boot.yaml.reconcile;
|
||||
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.ProblemFixer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
|
||||
public class SpringPropertyProblem extends ReconcileProblemImpl {
|
||||
|
||||
private PropertyInfo property = null;
|
||||
private ProblemFixer fixer;
|
||||
// private ProblemFixer fixer;
|
||||
private String propertyName;
|
||||
|
||||
public SpringPropertyProblem(ProblemType type, String msg, int offset, int len) {
|
||||
@@ -34,9 +33,9 @@ public class SpringPropertyProblem extends ReconcileProblemImpl {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public void setProblemFixer(ProblemFixer fixer) {
|
||||
this.fixer = fixer;
|
||||
}
|
||||
// public void setProblemFixer(ProblemFixer fixer) {
|
||||
// this.fixer = fixer;
|
||||
// }
|
||||
|
||||
public void setPropertyName(String name) {
|
||||
propertyName = name;
|
||||
|
||||
@@ -21,31 +21,30 @@ import org.springframework.ide.vscode.commons.maven.MavenBuilder;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.maven.java.classpathfile.JavaProjectWithClasspathFile;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
/**
|
||||
* Test projects harness
|
||||
*
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public class ProjectsHarness {
|
||||
|
||||
public static final ProjectsHarness INSTANCE = new ProjectsHarness();;
|
||||
|
||||
|
||||
public static final ProjectsHarness INSTANCE = new ProjectsHarness();;
|
||||
|
||||
public Cache<String, IJavaProject> cache = CacheBuilder.newBuilder().concurrencyLevel(1).build();
|
||||
|
||||
|
||||
private enum ProjectType {
|
||||
MAVEN,
|
||||
CLASSPATH_TXT
|
||||
}
|
||||
|
||||
|
||||
private ProjectsHarness() {
|
||||
}
|
||||
|
||||
|
||||
public IJavaProject project(ProjectType type, String name) throws Exception {
|
||||
return cache.get(type + "/" + name, () -> {
|
||||
Path testProjectPath = getProjectPath(name);
|
||||
@@ -82,7 +81,7 @@ public class ProjectsHarness {
|
||||
return getProjectPathFromClasspath(name);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
private Path getProjectPathFromClasspath(String name) throws URISyntaxException, IOException {
|
||||
URI resource = ProjectsHarness.class.getResource("/test-projects/" + name).toURI();
|
||||
// if (resource.getScheme().equalsIgnoreCase("jar")) {
|
||||
@@ -91,7 +90,7 @@ public class ProjectsHarness {
|
||||
return Paths.get(resource);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
// private Path getProjectPathFromJar(URI jar) throws IOException {
|
||||
// final String[] array = jar.toString().split("!");
|
||||
// URI firstHalf = URI.create(array[0]);
|
||||
@@ -110,10 +109,10 @@ public class ProjectsHarness {
|
||||
// fs.close();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private static void recursiveCopy(Path source, Path target, CopyOption... options) throws IOException {
|
||||
// Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
|
||||
//
|
||||
//
|
||||
// Path destination = target;
|
||||
//
|
||||
// @Override
|
||||
@@ -135,13 +134,13 @@ public class ProjectsHarness {
|
||||
// destination = destination.getParent();
|
||||
// return super.postVisitDirectory(dir, exc);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// });
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private static void recursiveDelete(Path path) throws IOException {
|
||||
// Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
// Files.delete(file);
|
||||
@@ -153,10 +152,10 @@ public class ProjectsHarness {
|
||||
// Files.delete(dir);
|
||||
// return super.postVisitDirectory(dir, exc);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
public MavenJavaProject mavenProject(String name) throws Exception {
|
||||
return (MavenJavaProject) project(ProjectType.MAVEN, name);
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.quickfix;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
|
||||
/**
|
||||
* Represents a strategy for computing potential quickfixes for a given problem.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ProblemFixer {
|
||||
|
||||
/**
|
||||
* Implementor can inspect the problem and quickfix context provided as parameters.
|
||||
* <p>
|
||||
* If the problem is deemed fixable, the strategy can contribute one or more fixes by
|
||||
* adding them to the list of proposals (provided as third parameter).
|
||||
*/
|
||||
void contributeFixes(
|
||||
QuickfixContext context, ReconcileProblem problem,
|
||||
List<ICompletionProposal> proposals
|
||||
);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.quickfix;
|
||||
|
||||
import org.eclipse.lsp4j.CodeActionContext;
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class Quickfix<T> {
|
||||
|
||||
public static class QuickfixData<T> {
|
||||
public final QuickfixType type;
|
||||
public final T params;
|
||||
public final String title;
|
||||
public QuickfixData(QuickfixType type, T params, String title) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.params = params;
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
private final Range range;
|
||||
private final QuickfixData<T> data;
|
||||
|
||||
public Quickfix(Range range, QuickfixData<T> data) {
|
||||
super();
|
||||
this.range = range;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Range getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
public Command getCodeAction() {
|
||||
return new Command(
|
||||
data.title,
|
||||
"sts.quickfix",
|
||||
ImmutableList.of(data.type.getId(), data.params)
|
||||
);
|
||||
}
|
||||
|
||||
public boolean appliesTo(Range range, CodeActionContext context) {
|
||||
return range.equals(this.range);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015, 2016 Pivotal, Inc.
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
@@ -10,19 +10,9 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.quickfix;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
|
||||
/**
|
||||
* Provides access to additional context info and objects that quickfixes might
|
||||
* need in order to be able to apply themselves.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public interface QuickfixContext {
|
||||
// IProject getProject();
|
||||
// IPreferenceStore getWorkspacePreferences();
|
||||
// IPreferenceStore getProjectPreferences();
|
||||
// IJavaProject getJavaProject();
|
||||
// UserInteractions getUI();
|
||||
IDocument getDocument();
|
||||
@FunctionalInterface
|
||||
public interface QuickfixHandler {
|
||||
WorkspaceEdit createEdits(Object params);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.quickfix;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Registry keeping track of quickfix types for a {@link SimpleLanguageServer}.
|
||||
* <p>
|
||||
* Each type must be associated with a handler. The handler accepts a parameter object
|
||||
* and computes workspace edits to be applied when the quickfix is executed.
|
||||
* <p>
|
||||
* The parameters object must be convertible to json since it is sent over the
|
||||
* wire to the client and later sent back when the quickfix is selected.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class QuickfixRegistry {
|
||||
|
||||
private Map<String, QuickfixHandler> registry = new HashMap<>();
|
||||
|
||||
public synchronized QuickfixType register(String typeName, QuickfixHandler handler) {
|
||||
Assert.isLegal(!registry.containsKey(typeName), "Quickfix type already registered: '"+typeName);
|
||||
registry.put(typeName, handler);
|
||||
return new QuickfixType() {
|
||||
|
||||
@Override
|
||||
public WorkspaceEdit createEdits(Object params) {
|
||||
return handler.createEdits(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return typeName;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public CompletableFuture<WorkspaceEdit> handle(QuickfixResolveParams params) {
|
||||
QuickfixHandler handler = registry.get(params.getType());
|
||||
return Mono.fromSupplier(() -> {
|
||||
return handler.createEdits(params.getParams());
|
||||
}).toFuture();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.quickfix;
|
||||
|
||||
public class QuickfixResolveParams {
|
||||
|
||||
private String type;
|
||||
private Object params;
|
||||
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
public Object getParams() {
|
||||
return params;
|
||||
}
|
||||
public void setParams(Object params) {
|
||||
this.params = params;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QuickfixResolveParams [type=" + type + ", params=" + params + "]";
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((params == null) ? 0 : params.hashCode());
|
||||
result = prime * result + ((type == null) ? 0 : type.hashCode());
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
QuickfixResolveParams other = (QuickfixResolveParams) obj;
|
||||
if (params == null) {
|
||||
if (other.params != null)
|
||||
return false;
|
||||
} else if (!params.equals(other.params))
|
||||
return false;
|
||||
if (type == null) {
|
||||
if (other.type != null)
|
||||
return false;
|
||||
} else if (!type.equals(other.type))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.quickfix;
|
||||
|
||||
public interface QuickfixType extends QuickfixHandler {
|
||||
String getId();
|
||||
}
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
package org.springframework.ide.vscode.commons.languageserver.reconcile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
|
||||
|
||||
/**
|
||||
* Minamal interface that objects representing a reconciler problem must
|
||||
* implement.
|
||||
@@ -23,4 +27,5 @@ public interface ReconcileProblem {
|
||||
int getOffset();
|
||||
int getLength();
|
||||
String getCode();
|
||||
List<QuickfixData<?>> getQuickfixes();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
package org.springframework.ide.vscode.commons.languageserver.reconcile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
|
||||
/**
|
||||
@@ -24,6 +28,7 @@ public class ReconcileProblemImpl implements ReconcileProblem {
|
||||
final private String msg;
|
||||
final private int offset;
|
||||
final private int len;
|
||||
private List<QuickfixData<?>> fixes = new ArrayList<>();
|
||||
|
||||
public ReconcileProblemImpl(ProblemType type, String msg, int offset, int len) {
|
||||
super();
|
||||
@@ -78,4 +83,14 @@ public class ReconcileProblemImpl implements ReconcileProblem {
|
||||
return c!='\n'&&c!='\r';
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QuickfixData<?>> getQuickfixes() {
|
||||
return fixes;
|
||||
}
|
||||
|
||||
public ReconcileProblemImpl addQuickfix(QuickfixData<?> command) {
|
||||
fixes.add(command);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.springframework.ide.vscode.commons.languageserver.util;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@@ -25,18 +24,26 @@ import org.eclipse.lsp4j.InitializeParams;
|
||||
import org.eclipse.lsp4j.InitializeResult;
|
||||
import org.eclipse.lsp4j.MessageParams;
|
||||
import org.eclipse.lsp4j.MessageType;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.ServerCapabilities;
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
|
||||
import org.eclipse.lsp4j.services.LanguageClient;
|
||||
import org.eclipse.lsp4j.services.LanguageClientAware;
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressParams;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixResolveParams;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.CollectionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -72,11 +79,20 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl
|
||||
|
||||
private CompletableFuture<Void> busyReconcile = CompletableFuture.completedFuture(null);
|
||||
|
||||
private QuickfixRegistry quickfixRegistry;
|
||||
|
||||
@Override
|
||||
public void connect(LanguageClient _client) {
|
||||
this.client = (STS4LanguageClient) _client;
|
||||
}
|
||||
|
||||
protected synchronized QuickfixRegistry getQuickfixRegistry() {
|
||||
if (quickfixRegistry==null) {
|
||||
quickfixRegistry = new QuickfixRegistry();
|
||||
}
|
||||
return quickfixRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
|
||||
// LOG.info("Initializing");
|
||||
@@ -166,9 +182,11 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl
|
||||
IProblemCollector problems = new IProblemCollector() {
|
||||
|
||||
private List<Diagnostic> diagnostics = new ArrayList<>();
|
||||
private List<Quickfix> quickfixes = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void endCollecting() {
|
||||
documents.setQuickfixes(doc, quickfixes);
|
||||
documents.publishDiagnostics(doc, diagnostics);
|
||||
}
|
||||
|
||||
@@ -185,8 +203,15 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl
|
||||
Diagnostic d = new Diagnostic();
|
||||
d.setCode(problem.getCode());
|
||||
d.setMessage(problem.getMessage());
|
||||
d.setRange(doc.toRange(problem.getOffset(), problem.getLength()));
|
||||
Range rng = doc.toRange(problem.getOffset(), problem.getLength());
|
||||
d.setRange(rng);
|
||||
d.setSeverity(severity);
|
||||
List<QuickfixData<?>> fixes = problem.getQuickfixes();
|
||||
if (CollectionUtil.hasElements(fixes)) {
|
||||
for (QuickfixData<?> fix : fixes) {
|
||||
quickfixes.add(new Quickfix<>(rng, fix));
|
||||
}
|
||||
}
|
||||
diagnostics.add(d);
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
@@ -235,4 +260,11 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl
|
||||
return progressService;
|
||||
}
|
||||
|
||||
@JsonRequest("sts/quickfix")
|
||||
public CompletableFuture<WorkspaceEdit> quickfixResolve(QuickfixResolveParams params) {
|
||||
QuickfixRegistry quickfixes = getQuickfixRegistry();
|
||||
return quickfixes.handle(params);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
package org.springframework.ide.vscode.commons.languageserver.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -21,6 +20,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.lsp4j.CodeActionParams;
|
||||
import org.eclipse.lsp4j.CodeLens;
|
||||
@@ -55,6 +55,7 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.eclipse.lsp4j.services.LanguageClient;
|
||||
import org.eclipse.lsp4j.services.TextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.LanguageIds;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
@@ -62,12 +63,14 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class SimpleTextDocumentService implements TextDocumentService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(SimpleTextDocumentService.class.getName());
|
||||
|
||||
final private SimpleLanguageServer server;
|
||||
private Map<String, TextDocument> documents = new HashMap<>();
|
||||
private Map<String, TrackedDocument> documents = new HashMap<>();
|
||||
private ListenerList<TextDocumentContentChange> documentChangeListeners = new ListenerList<>();
|
||||
private CompletionHandler completionHandler = null;
|
||||
private CompletionResolveHandler completionResolveHandler = null;
|
||||
@@ -100,7 +103,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
this.definitionHandler = h;
|
||||
}
|
||||
|
||||
public synchronized void onRefeences(ReferencesHandler h) {
|
||||
public synchronized void onReferences(ReferencesHandler h) {
|
||||
Assert.isNull("A references handler is already set, multiple handlers not supported yet", referencesHandler);
|
||||
this.referencesHandler = h;
|
||||
}
|
||||
@@ -110,7 +113,9 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
* and not yet closed.
|
||||
*/
|
||||
public synchronized Collection<TextDocument> getAll() {
|
||||
return new ArrayList<>(documents.values());
|
||||
return documents.values().stream()
|
||||
.map((td) -> td.getDocument())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,7 +156,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
String languageId = params.getTextDocument().getLanguageId();
|
||||
if (url!=null) {
|
||||
String text = params.getTextDocument().getText();
|
||||
TextDocument doc = createDocument(url, languageId);
|
||||
TextDocument doc = createDocument(url, languageId).getDocument();
|
||||
doc.setText(text);
|
||||
TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent() {
|
||||
@Override
|
||||
@@ -191,20 +196,20 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
documentChangeListeners.add(l);
|
||||
}
|
||||
|
||||
private synchronized TextDocument getDocument(String url) {
|
||||
TextDocument doc = documents.get(url);
|
||||
public synchronized TextDocument getDocument(String url) {
|
||||
TrackedDocument doc = documents.get(url);
|
||||
if (doc==null) {
|
||||
LOG.warning("Trying to get document ["+url+"] but it did not exists. Creating it with language-id 'plaintext'");
|
||||
doc = createDocument(url, LanguageIds.PLAINTEXT);
|
||||
}
|
||||
return doc;
|
||||
return doc.getDocument();
|
||||
}
|
||||
|
||||
private synchronized TextDocument createDocument(String url, String languageId) {
|
||||
private synchronized TrackedDocument createDocument(String url, String languageId) {
|
||||
if (documents.get(url)!=null) {
|
||||
LOG.warning("Creating document ["+url+"] but it already exists. Existing document discarded!");
|
||||
}
|
||||
TextDocument doc = new TextDocument(url, languageId);
|
||||
TrackedDocument doc = new TrackedDocument(new TextDocument(url, languageId));
|
||||
documents.put(url, doc);
|
||||
return doc;
|
||||
}
|
||||
@@ -273,7 +278,17 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends Command>> codeAction(CodeActionParams params) {
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
TrackedDocument doc = documents.get(params.getTextDocument().getUri());
|
||||
if (doc!=null) {
|
||||
return Flux.fromIterable(doc.getQuickfixes())
|
||||
.filter((fix) -> fix.appliesTo(params.getRange(), params.getContext()))
|
||||
.map(Quickfix::getCodeAction)
|
||||
.collectList()
|
||||
.toFuture()
|
||||
.thenApply(l -> (List<? extends Command>) l);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(ImmutableList.of());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -320,12 +335,21 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
}
|
||||
}
|
||||
|
||||
public void setQuickfixes(TextDocument doc, List<Quickfix> quickfixes) {
|
||||
TrackedDocument td = documents.get(doc.getUri());
|
||||
if (td!=null) {
|
||||
td.setQuickfixes(quickfixes);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized TextDocument get(TextDocumentPositionParams params) {
|
||||
return documents.get(params.getTextDocument().getUri());
|
||||
TrackedDocument td = documents.get(params.getTextDocument().getUri());
|
||||
return td == null ? null : td.getDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends DocumentHighlight>> documentHighlight(TextDocumentPositionParams position) {
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class TrackedDocument {
|
||||
|
||||
private final TextDocument doc;
|
||||
private List<Quickfix> quickfixes = ImmutableList.of();
|
||||
|
||||
public TrackedDocument(TextDocument doc) {
|
||||
this.doc = doc;
|
||||
}
|
||||
|
||||
public TextDocument getDocument() {
|
||||
return doc;
|
||||
}
|
||||
|
||||
public void setQuickfixes(List<Quickfix> quickfixes) {
|
||||
this.quickfixes = quickfixes;
|
||||
}
|
||||
|
||||
public List<Quickfix> getQuickfixes() {
|
||||
return quickfixes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
package org.springframework.ide.vscode.commons.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class Futures {
|
||||
@@ -22,5 +23,17 @@ public class Futures {
|
||||
public static <T> CompletableFuture<T> of(T value) {
|
||||
return CompletableFuture.completedFuture(value);
|
||||
}
|
||||
|
||||
|
||||
public static <T> CompletableFuture<T> error(String message) {
|
||||
CompletableFuture<T> f = new CompletableFuture<T>();
|
||||
f.completeExceptionally(new IOException(message));
|
||||
return f;
|
||||
}
|
||||
|
||||
public static <T> CompletableFuture<T> error(Throwable e) {
|
||||
CompletableFuture<T> f = new CompletableFuture<T>();
|
||||
f.completeExceptionally(e);
|
||||
return f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -252,8 +252,13 @@ public class TextDocument implements IDocument {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageId() {
|
||||
return languageId;
|
||||
}
|
||||
|
||||
public Range toRange(IRegion region) throws BadLocationException {
|
||||
return toRange(region.getOffset(), region.getLength());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ package org.springframework.ide.vscode.commons.yaml.ast;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeId;
|
||||
@@ -125,4 +127,8 @@ public class NodeUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DocumentRegion region(IDocument doc, Node node) {
|
||||
return new DocumentRegion(doc, node.getStartMark().getIndex(), node.getEndMark().getIndex());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.YamlPathSegmentType;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
@@ -301,4 +303,12 @@ public class YamlPath {
|
||||
return new YamlPath(common);
|
||||
}
|
||||
|
||||
public static YamlPath decode(List<String> encodedSegments) {
|
||||
return Flux.fromIterable(encodedSegments)
|
||||
.map(YamlPathSegment::decode)
|
||||
.collectList()
|
||||
.map(YamlPath::new)
|
||||
.block();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,21 @@ package org.springframework.ide.vscode.commons.yaml.path;
|
||||
*/
|
||||
public abstract class YamlPathSegment {
|
||||
|
||||
public static YamlPathSegment decode(String code) {
|
||||
switch (code.charAt(0)) {
|
||||
case '*':
|
||||
return anyChild();
|
||||
case '.':
|
||||
return valueAt(code.substring(1));
|
||||
case '&':
|
||||
return keyAt(code.substring(1));
|
||||
case '[':
|
||||
return valueAt(Integer.parseInt(code.substring(1)));
|
||||
default:
|
||||
throw new IllegalArgumentException("Can't decode YamlPathSegment from '"+code+"'");
|
||||
}
|
||||
}
|
||||
|
||||
public static enum YamlPathSegmentType {
|
||||
VAL_AT_KEY, //Go to value associate with given key in a map.
|
||||
KEY_AT_KEY, //Go to the key node associated with a given key in a map.
|
||||
@@ -50,7 +65,17 @@ public abstract class YamlPathSegment {
|
||||
@Override
|
||||
public YamlPathSegmentType getType() {
|
||||
return YamlPathSegmentType.ANY_CHILD;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected char getTypeCode() {
|
||||
return '*';
|
||||
};
|
||||
|
||||
@Override
|
||||
protected String getValueCode() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static class AtIndex extends YamlPathSegment {
|
||||
@@ -102,6 +127,16 @@ public abstract class YamlPathSegment {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected char getTypeCode() {
|
||||
return '[';
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getValueCode() {
|
||||
return ""+index;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ValAtKey extends YamlPathSegment {
|
||||
@@ -161,6 +196,16 @@ public abstract class YamlPathSegment {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected char getTypeCode() {
|
||||
return '.';
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getValueCode() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
public static class KeyAtKey extends ValAtKey {
|
||||
@@ -174,6 +219,10 @@ public abstract class YamlPathSegment {
|
||||
return YamlPathSegmentType.KEY_AT_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected char getTypeCode() {
|
||||
return '&';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -202,4 +251,11 @@ public abstract class YamlPathSegment {
|
||||
return AnyChild.INSTANCE;
|
||||
}
|
||||
|
||||
public String encode() {
|
||||
return getTypeCode() + getValueCode();
|
||||
}
|
||||
|
||||
protected abstract String getValueCode();
|
||||
|
||||
protected abstract char getTypeCode();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.reconcile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MissingPropertiesData {
|
||||
|
||||
private String uri;
|
||||
private List<String> path;
|
||||
private List<String> props;
|
||||
|
||||
public MissingPropertiesData(String uri, List<String> path, List<String> props) {
|
||||
super();
|
||||
this.uri = uri;
|
||||
this.path = path;
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public MissingPropertiesData() {
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
public List<String> getPath() {
|
||||
return path;
|
||||
}
|
||||
public void setPath(List<String> path) {
|
||||
this.path = path;
|
||||
}
|
||||
public List<String> getProps() {
|
||||
return props;
|
||||
}
|
||||
public void setProps(List<String> props) {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MissingPropertiesData [uri=" + uri + ", path=" + path + ", props=" + props + "]";
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import static org.springframework.ide.vscode.commons.util.ExceptionUtil.getSimpl
|
||||
import static org.springframework.ide.vscode.commons.yaml.ast.NodeUtil.asScalar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -60,14 +59,17 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
private final YamlSchema schema;
|
||||
private final YTypeUtil typeUtil;
|
||||
private final ITypeCollector typeCollector;
|
||||
private final YamlQuickfixes quickfixes;
|
||||
|
||||
public SchemaBasedYamlASTReconciler(IProblemCollector problems, YamlSchema schema, ITypeCollector typeCollector) {
|
||||
public SchemaBasedYamlASTReconciler(IProblemCollector problems, YamlSchema schema, ITypeCollector typeCollector, YamlQuickfixes quickfixes) {
|
||||
this.problems = problems;
|
||||
this.schema = schema;
|
||||
this.typeCollector = typeCollector;
|
||||
this.typeUtil = schema.getTypeUtil();
|
||||
this.quickfixes = quickfixes;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void reconcile(YamlFileAST ast) {
|
||||
if (typeCollector!=null) typeCollector.beginCollecting(ast);
|
||||
@@ -205,7 +207,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getMessage(Exception _e) {
|
||||
Throwable e = ExceptionUtil.getDeepestCause(_e);
|
||||
|
||||
@@ -247,7 +249,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
} else {
|
||||
message = "Properties "+missingProps+" are required for '"+type+"'";
|
||||
}
|
||||
problems.accept(YamlSchemaProblems.missingProperty(message, dc.getDocument(), parent, map));
|
||||
problems.accept(YamlSchemaProblems.missingProperties(message, dc, missingProps, parent, map, quickfixes.MISSING_PROP_FIX));
|
||||
}
|
||||
|
||||
//Check for other constraints attached to the type
|
||||
@@ -347,7 +349,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
}
|
||||
problem(region, parseErrorMsg, problemType);
|
||||
}
|
||||
|
||||
|
||||
private void unknownBeanProperty(Node keyNode, YType type, String name) {
|
||||
problem(keyNode, "Unknown property '"+name+"' for type '"+typeUtil.niceTypeName(type)+"'");
|
||||
}
|
||||
@@ -408,7 +410,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
private void problem(DocumentRegion region, String msg, ProblemType problemType) {
|
||||
problems.accept(YamlSchemaProblems.problem(problemType, msg, region));
|
||||
}
|
||||
|
||||
|
||||
private void problem(DocumentRegion region, String msg) {
|
||||
problems.accept(YamlSchemaProblems.schemaProblem(msg, region));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.reconcile;
|
||||
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits.TextReplace;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlPathEdits;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SChildBearingNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class YamlQuickfixes {
|
||||
|
||||
public final QuickfixType MISSING_PROP_FIX;
|
||||
|
||||
public YamlQuickfixes(QuickfixRegistry r, SimpleTextDocumentService textDocumentService, YamlStructureProvider structureProvider) {
|
||||
MISSING_PROP_FIX = r.register("MISSING_PROP_FIX", (Object _params) -> {
|
||||
MissingPropertiesData params = new ObjectMapper().convertValue(_params, MissingPropertiesData.class);
|
||||
try {
|
||||
TextDocument _doc = textDocumentService.getDocument(params.getUri());
|
||||
if (_doc!=null) {
|
||||
YamlDocument doc = new YamlDocument(_doc, structureProvider);
|
||||
SNode root = doc.getStructure();
|
||||
if (root!=null) {
|
||||
YamlPath path = YamlPath.decode(params.getPath());
|
||||
SNode _target = path.traverse(root);
|
||||
if (_target instanceof SChildBearingNode) {
|
||||
YamlPathEdits edits = new YamlPathEdits(doc);
|
||||
SChildBearingNode target = (SChildBearingNode) _target;
|
||||
for (String prop : params.getProps()) {
|
||||
edits.createPath(target, new YamlPath(YamlPathSegment.valueAt(prop)), " ");
|
||||
}
|
||||
TextReplace replaceEdit = edits.asReplacement(_doc);
|
||||
if (replaceEdit!=null) {
|
||||
WorkspaceEdit wsEdits = new WorkspaceEdit();
|
||||
wsEdits.setChanges(ImmutableMap.of(
|
||||
params.getUri(),
|
||||
ImmutableList.of(new TextEdit(_doc.toRange(replaceEdit.getRegion()), replaceEdit.newText))
|
||||
));
|
||||
return wsEdits;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
//Something went wrong. Return empty edit object.
|
||||
return new WorkspaceEdit(ImmutableMap.of(), null);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,9 +29,12 @@ public final class YamlSchemaBasedReconcileEngine extends YamlReconcileEngine {
|
||||
*/
|
||||
private ITypeCollector typeCollector;
|
||||
|
||||
public YamlSchemaBasedReconcileEngine(YamlASTProvider parser, YamlSchema schema) {
|
||||
private YamlQuickfixes quickfixes;
|
||||
|
||||
public YamlSchemaBasedReconcileEngine(YamlASTProvider parser, YamlSchema schema, YamlQuickfixes quickfixes) {
|
||||
super(parser);
|
||||
this.schema = schema;
|
||||
this.quickfixes = quickfixes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,7 +44,7 @@ public final class YamlSchemaBasedReconcileEngine extends YamlReconcileEngine {
|
||||
|
||||
@Override
|
||||
protected YamlASTReconciler getASTReconciler(IDocument doc, IProblemCollector problems) {
|
||||
return new SchemaBasedYamlASTReconciler(problems, schema, typeCollector);
|
||||
return new SchemaBasedYamlASTReconciler(problems, schema, typeCollector, quickfixes);
|
||||
}
|
||||
|
||||
public ITypeCollector getTypeCollector() {
|
||||
|
||||
@@ -10,8 +10,13 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.reconcile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
@@ -19,9 +24,9 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.Reconcile
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.NodeCursor;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
import org.yaml.snakeyaml.error.Mark;
|
||||
@@ -30,6 +35,7 @@ import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
import org.yaml.snakeyaml.nodes.SequenceNode;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
@@ -45,6 +51,7 @@ public class YamlSchemaProblems {
|
||||
public static final ProblemType MISSING_PROPERTY = problemType("MissingProperty", ProblemSeverity.ERROR);
|
||||
public static final ProblemType EXTRA_PROPERTY = problemType("ExtraProperty", ProblemSeverity.ERROR);
|
||||
|
||||
|
||||
public static final Set<ProblemType> PROPERTY_CONSTRAINT = ImmutableSet.of(
|
||||
MISSING_PROPERTY, EXTRA_PROPERTY
|
||||
);
|
||||
@@ -93,7 +100,7 @@ public class YamlSchemaProblems {
|
||||
return deprecatedProperty("Property '"+property.getName()+"' of '"+bean+"' is Deprecated", node);
|
||||
}
|
||||
|
||||
public static ReconcileProblem problem(ProblemType problemType, String msg, DocumentRegion node) {
|
||||
public static ReconcileProblemImpl problem(ProblemType problemType, String msg, DocumentRegion node) {
|
||||
int start = node.getStart();
|
||||
int end = node.getEnd();
|
||||
return new ReconcileProblemImpl(problemType, msg, start, end-start);
|
||||
@@ -105,26 +112,46 @@ public class YamlSchemaProblems {
|
||||
return new ReconcileProblemImpl(problemType, msg, start, end-start);
|
||||
}
|
||||
|
||||
public static ReconcileProblem missingProperty(String msg, IDocument doc, Node parent, MappingNode map) {
|
||||
public static ReconcileProblemImpl missingProperty(String msg, IDocument doc, Node parent, MappingNode map) {
|
||||
DocumentRegion underline = NodeUtil.region(doc, map);
|
||||
if (parent instanceof MappingNode) {
|
||||
for (NodeTuple prop : ((MappingNode) parent).getValue()) {
|
||||
if (prop.getValueNode()==map) {
|
||||
return problem(MISSING_PROPERTY, msg, prop.getKeyNode());
|
||||
underline = NodeUtil.region(doc, prop.getKeyNode());
|
||||
}
|
||||
}
|
||||
} else if (parent instanceof SequenceNode) {
|
||||
Boolean flowStyle = ((SequenceNode) parent).getFlowStyle();
|
||||
if (flowStyle!=null && !flowStyle) {
|
||||
Mark nodeStart = map.getStartMark();
|
||||
DocumentRegion underline = new DocumentRegion(doc, 0, nodeStart.getIndex());
|
||||
underline = new DocumentRegion(doc, 0, nodeStart.getIndex());
|
||||
underline = underline.trimEnd();
|
||||
if (underline.endsWith("-")) {
|
||||
underline = underline.subSequence(underline.length()-1, underline.length());
|
||||
return problem(MISSING_PROPERTY, msg, underline);
|
||||
}
|
||||
}
|
||||
}
|
||||
return problem(MISSING_PROPERTY, msg, map);
|
||||
return problem(MISSING_PROPERTY, msg, underline);
|
||||
}
|
||||
|
||||
public static ReconcileProblem missingProperties(String msg, DynamicSchemaContext dc, Set<String> missingProps, Node parent, MappingNode map, QuickfixType quickfixType) {
|
||||
YamlPath contextPath = dc.getPath();
|
||||
List<String> segments = Stream.of(contextPath.getSegments())
|
||||
.map(YamlPathSegment::encode)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
QuickfixData<MissingPropertiesData> fix = new QuickfixData<MissingPropertiesData>(
|
||||
quickfixType,
|
||||
new MissingPropertiesData(
|
||||
dc.getDocument().getUri(),
|
||||
segments,
|
||||
ImmutableList.copyOf(missingProps)
|
||||
),
|
||||
"Add properties: "+missingProps
|
||||
);
|
||||
|
||||
return missingProperty(msg, dc.getDocument(), parent, map)
|
||||
.addQuickfix(fix);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
@@ -18,9 +17,6 @@ import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* Adapts a SnakeYaml ast node as a {@link DynamicSchemaContext} (so it
|
||||
|
||||
@@ -705,7 +705,7 @@ public class YTypeFactory {
|
||||
t.addHintProvider((dc) -> () -> values.withContext(dc));
|
||||
t.parseWith((DynamicSchemaContext dc) -> {
|
||||
Collection<String> strings = YTypeFactory.values(values.withContext(dc));
|
||||
return new EnumValueParser("blah", strings) {
|
||||
return new EnumValueParser(name, strings) {
|
||||
@Override
|
||||
protected String createErrorMessage(String parseString, Collection<String> values) {
|
||||
return errorMessageFormatter.apply(parseString, values);
|
||||
|
||||
@@ -14,7 +14,8 @@ package org.springframework.ide.vscode.languageserver.testharness;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.*;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertContains;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertDoesNotContain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -43,7 +44,6 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.junit.Assert;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.eclipse.lsp4j.DiagnosticSeverity;
|
||||
import org.eclipse.lsp4j.ServerCapabilities;
|
||||
import org.eclipse.lsp4j.TextDocumentSyncKind;
|
||||
import org.springframework.ide.vscode.commons.languageserver.LanguageIds;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
|
||||
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngineAdapter;
|
||||
@@ -32,6 +31,7 @@ import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.SchemaBasedYamlAssistContextProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.yaml.hover.YamlHoverInfoProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlQuickfixes;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaBasedReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
@@ -47,6 +47,7 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
|
||||
YamlASTProvider currentAsts = models.getAstProvider(false);
|
||||
private SchemaSpecificPieces forPipelines;
|
||||
private SchemaSpecificPieces forTasks;
|
||||
private final YamlQuickfixes yamlQuickfixes;
|
||||
|
||||
private class SchemaSpecificPieces {
|
||||
|
||||
@@ -62,7 +63,7 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
|
||||
HoverInfoProvider infoProvider = new YamlHoverInfoProvider(currentAsts, structureProvider, contextProvider);
|
||||
this.hoverEngine = new VscodeHoverEngineAdapter(ConcourseLanguageServer.this, infoProvider);
|
||||
|
||||
this.reconcileEngine = new YamlSchemaBasedReconcileEngine(currentAsts, schema);
|
||||
this.reconcileEngine = new YamlSchemaBasedReconcileEngine(currentAsts, schema, yamlQuickfixes);
|
||||
reconcileEngine.setTypeCollector(models.getAstTypeCache());
|
||||
}
|
||||
|
||||
@@ -73,6 +74,7 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
|
||||
|
||||
public ConcourseLanguageServer() {
|
||||
PipelineYmlSchema pipelineSchema = new PipelineYmlSchema(models);
|
||||
this.yamlQuickfixes = new YamlQuickfixes(getQuickfixRegistry(), documents, structureProvider);
|
||||
|
||||
this.forPipelines = new SchemaSpecificPieces(pipelineSchema);
|
||||
this.forTasks = new SchemaSpecificPieces(pipelineSchema.getTaskSchema());
|
||||
@@ -158,6 +160,8 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
|
||||
|
||||
c.setDefinitionProvider(true);
|
||||
|
||||
c.setCodeActionProvider(true);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ package org.springframework.ide.vscode.concourse;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.RegexpParser;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParseException;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
|
||||
@@ -53,13 +55,24 @@ public class ConcourseValueParsers {
|
||||
public static SchemaContextAware<ValueParser> acceptOnlyUniqueNames(
|
||||
Function<IDocument, Multiset<String>> getDefinedNameCounts,
|
||||
String typeName
|
||||
) {
|
||||
return acceptOnlyUniqueNames(getDefinedNameCounts, typeName, false);
|
||||
}
|
||||
|
||||
public static SchemaContextAware<ValueParser> acceptOnlyUniqueNames(
|
||||
Function<IDocument, Multiset<String>> getDefinedNameCounts,
|
||||
String typeName,
|
||||
boolean allowEmptyName
|
||||
) {
|
||||
return (dc) -> {
|
||||
Multiset<String> resourceNames = getDefinedNameCounts.apply(dc.getDocument());
|
||||
return (String input) -> {
|
||||
if (!allowEmptyName && !StringUtil.hasText(input)) {
|
||||
throw new ValueParseException("'"+typeName +"' should not be blank");
|
||||
}
|
||||
Multiset<String> resourceNames = getDefinedNameCounts.apply(dc.getDocument());
|
||||
if (resourceNames.count(input)<=1) {
|
||||
//okay
|
||||
return resourceNames;
|
||||
return input;
|
||||
}
|
||||
throw new IllegalArgumentException("Duplicate "+typeName+" '"+input+"'");
|
||||
};
|
||||
|
||||
@@ -28,11 +28,11 @@ export interface ActivatorOptions {
|
||||
jvmHeap?: string;
|
||||
}
|
||||
|
||||
export function activate(options: ActivatorOptions, context: VSCode.ExtensionContext) {
|
||||
export function activate(options: ActivatorOptions, context: VSCode.ExtensionContext): Promise<LanguageClient> {
|
||||
let DEBUG = options.DEBUG;
|
||||
let jvmHeap = options.jvmHeap;
|
||||
if (options.CONNECT_TO_LS) {
|
||||
connectToLS(context, options);
|
||||
return connectToLS(context, options);
|
||||
} else {
|
||||
let clientOptions = options.clientOptions;
|
||||
let fatJarFile = Path.resolve(context.extensionPath, options.fatJarFile);
|
||||
@@ -61,7 +61,7 @@ export function activate(options: ActivatorOptions, context: VSCode.ExtensionCon
|
||||
log("Found java exe: " + javaExecutablePath);
|
||||
|
||||
|
||||
isJava8(javaExecutablePath).then(eight => {
|
||||
return isJava8(javaExecutablePath).then(eight => {
|
||||
if (!eight) {
|
||||
VSCode.window.showErrorMessage('Java-based Language Server requires Java 8 (using ' + javaExecutablePath + ')');
|
||||
return;
|
||||
@@ -109,12 +109,12 @@ export function activate(options: ActivatorOptions, context: VSCode.ExtensionCon
|
||||
});
|
||||
}
|
||||
|
||||
setupLanguageClient(context, createServer, options);
|
||||
return Promise.resolve(setupLanguageClient(context, createServer, options));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function connectToLS(context: VSCode.ExtensionContext, options: ActivatorOptions) {
|
||||
function connectToLS(context: VSCode.ExtensionContext, options: ActivatorOptions): Promise<LanguageClient> {
|
||||
let connectionInfo = {
|
||||
port: 5007
|
||||
};
|
||||
@@ -128,10 +128,10 @@ function connectToLS(context: VSCode.ExtensionContext, options: ActivatorOptions
|
||||
return Promise.resolve(result);
|
||||
};
|
||||
|
||||
setupLanguageClient(context, serverOptions, options);
|
||||
return Promise.resolve(setupLanguageClient(context, serverOptions, options));
|
||||
}
|
||||
|
||||
function setupLanguageClient(context: VSCode.ExtensionContext, createServer: ServerOptions, options: ActivatorOptions) {
|
||||
function setupLanguageClient(context: VSCode.ExtensionContext, createServer: ServerOptions, options: ActivatorOptions): LanguageClient {
|
||||
// Create the language client and start the client.
|
||||
let client = new LanguageClient(options.extensionId, options.extensionId,
|
||||
createServer, options.clientOptions
|
||||
@@ -150,6 +150,7 @@ function setupLanguageClient(context: VSCode.ExtensionContext, createServer: Ser
|
||||
// client can be deactivated on extension deactivation
|
||||
context.subscriptions.push(disposable);
|
||||
context.subscriptions.push(progressService);
|
||||
return client
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Launch Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
|
||||
"stopOnEntry": false,
|
||||
"stopOnEntry": true,
|
||||
"sourceMaps": true,
|
||||
"outDir": "${workspaceRoot}/out/lib",
|
||||
"preLaunchTask": "npm"
|
||||
|
||||
@@ -8,8 +8,10 @@ import * as Path from 'path';
|
||||
import * as FS from 'fs';
|
||||
import * as Net from 'net';
|
||||
import * as ChildProcess from 'child_process';
|
||||
import {LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo} from 'vscode-languageclient';
|
||||
import {LanguageClient, RequestType, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo} from 'vscode-languageclient';
|
||||
import {TextDocument, OutputChannel} from 'vscode';
|
||||
import {WorkspaceEdit} from 'vscode-languageserver-types';
|
||||
import * as p2c from 'vscode-languageclient/lib/protocolConverter';
|
||||
|
||||
var log_output : OutputChannel = null;
|
||||
|
||||
@@ -28,8 +30,14 @@ function error(msg : string) {
|
||||
}
|
||||
}
|
||||
|
||||
interface QuickfixRequest {
|
||||
type: string;
|
||||
params: any;
|
||||
}
|
||||
|
||||
/** Called when extension is activated */
|
||||
export function activate(context: VSCode.ExtensionContext) {
|
||||
let commands = VSCode.commands
|
||||
let options : commons.ActivatorOptions = {
|
||||
DEBUG : false,
|
||||
CONNECT_TO_LS: false,
|
||||
@@ -47,6 +55,21 @@ export function activate(context: VSCode.ExtensionContext) {
|
||||
}
|
||||
}
|
||||
};
|
||||
commons.activate(options, context);
|
||||
let clientPromise = commons.activate(options, context);
|
||||
commands.registerCommand("sts.quickfix", (fixType, fixParams) => {
|
||||
return clientPromise.then(client => {
|
||||
let type : RequestType<QuickfixRequest, WorkspaceEdit, void> = {method : "sts/quickfix"};
|
||||
let params : QuickfixRequest = {type: fixType, params: fixParams};
|
||||
return client.sendRequest(type, params)
|
||||
.then(
|
||||
(edit) => {
|
||||
return VSCode.workspace.applyEdit(p2c.asWorkspaceEdit(edit))
|
||||
},
|
||||
(error) => {
|
||||
return VSCode.window.showErrorMessage(""+error)
|
||||
}
|
||||
);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
"onLanguage:concourse-task-yaml"
|
||||
],
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "mycommand.sayHello",
|
||||
"title": "Say Hello"
|
||||
}
|
||||
],
|
||||
"languages": [
|
||||
{
|
||||
"id": "concourse-pipeline-yaml",
|
||||
@@ -84,7 +90,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"vsce": "^1.17.0",
|
||||
"typescript": "^2.0.x",
|
||||
"typescript": "^2.2.x",
|
||||
"@types/node": "^6.0.40",
|
||||
"vscode": "^1.0.0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user