diff --git a/vscode-extensions/commons/language-server-commons/pom.xml b/vscode-extensions/commons/language-server-commons/pom.xml index 70623cdea..6fe6e7d2a 100644 --- a/vscode-extensions/commons/language-server-commons/pom.xml +++ b/vscode-extensions/commons/language-server-commons/pom.xml @@ -31,6 +31,11 @@ + + org.springframework.ide.vscode + util-commons + ${project.version} + io.typefox.lsapi @@ -74,6 +79,4 @@ ${jackson-2-version} - - \ No newline at end of file diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/IDocument.java b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/IDocument.java index 8279929d6..eadcc2578 100644 --- a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/IDocument.java +++ b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/IDocument.java @@ -2,4 +2,6 @@ package org.springframework.ide.vscode.commons.reconcile; public interface IDocument { + String get(); + } diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/IReconcileEngine.java b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/IReconcileEngine.java index 9cf1c2661..0d57ffabf 100644 --- a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/IReconcileEngine.java +++ b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/IReconcileEngine.java @@ -10,8 +10,6 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.reconcile; -import org.springframework.ide.vscode.util.TextDocument; - public interface IReconcileEngine { public void reconcile(IDocument doc, IProblemCollector problemCollector); } diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ProblemType.java b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ProblemType.java index 8e5e97987..45a6fdbe8 100644 --- a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ProblemType.java +++ b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ProblemType.java @@ -14,4 +14,5 @@ package org.springframework.ide.vscode.commons.reconcile; public interface ProblemType { ProblemSeverity getDefaultSeverity(); String toString(); + String getCode(); } diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ReconcileProblem.java b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ReconcileProblem.java index f1403feb0..ee3d61b6e 100644 --- a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ReconcileProblem.java +++ b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ReconcileProblem.java @@ -11,4 +11,5 @@ public interface ReconcileProblem { String getMessage(); int getOffset(); int getLength(); + String getCode(); } diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ReconcileProblemImpl.java b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ReconcileProblemImpl.java new file mode 100644 index 000000000..f83d4468b --- /dev/null +++ b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/commons/reconcile/ReconcileProblemImpl.java @@ -0,0 +1,48 @@ +package org.springframework.ide.vscode.commons.reconcile; + +/** + * An implementation of {@link ReconcileProblem} that is just a simple data object. + * + * @author Kris De Volder + */ +public class ReconcileProblemImpl implements ReconcileProblem { + + final private ProblemType type; + final private String msg; + final private int offset; + final private int len; + + public ReconcileProblemImpl(ProblemType type, String msg, int offset, int len) { + super(); + this.type = type; + this.msg = msg; + this.offset = offset; + this.len = len; + } + + @Override + public ProblemType getType() { + return type; + } + + @Override + public String getMessage() { + return msg; + } + + @Override + public int getOffset() { + return offset; + } + + @Override + public int getLength() { + return len; + } + + @Override + public String getCode() { + return getType().getCode(); + } + +} diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/SimpleTextDocumentService.java b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/SimpleTextDocumentService.java index bb47c3946..ec50435b7 100644 --- a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/SimpleTextDocumentService.java +++ b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/SimpleTextDocumentService.java @@ -260,7 +260,7 @@ public class SimpleTextDocumentService implements TextDocumentService { } public void publishDiagnostics(TextDocument doc, List diagnostics) { - if (diagnostics!=null && !diagnostics.isEmpty()) { + if (diagnostics!=null) { PublishDiagnosticsParamsImpl params = new PublishDiagnosticsParamsImpl(); params.setUri(doc.getUri()); params.setDiagnostics(diagnostics); diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/StringUtil.java b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/StringUtil.java deleted file mode 100644 index f07a9ef5a..000000000 --- a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/StringUtil.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.springframework.ide.vscode.util; - -public class StringUtil { - public static boolean hasText(String name) { - return name!=null && !name.trim().equals(""); - } -} diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/TextDocument.java b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/TextDocument.java index 4e70b2a3e..50f931d1e 100644 --- a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/TextDocument.java +++ b/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/TextDocument.java @@ -1,12 +1,23 @@ package org.springframework.ide.vscode.util; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import org.springframework.ide.vscode.commons.reconcile.IDocument; +import io.typefox.lsapi.Position; +import io.typefox.lsapi.PositionImpl; import io.typefox.lsapi.Range; +import io.typefox.lsapi.RangeImpl; import io.typefox.lsapi.TextDocumentContentChangeEvent; public class TextDocument implements IDocument { + Pattern NEWLINE = Pattern.compile("\\r|\\n|\\r\\n|\\n\\r"); + private int[] _lineStarts; + private final String uri; private String text = ""; @@ -18,23 +29,96 @@ public class TextDocument implements IDocument { return uri; } - public String getText() { + public String get() { + return getText(); + } + + public synchronized String getText() { return text; } - public void setText(String text) { + public synchronized void setText(String text) { this.text = text; + this._lineStarts = null; } public void apply(TextDocumentContentChangeEvent change) { Range rng = change.getRange(); if (rng==null) { //full sync mode - this.text = change.getText(); + setText(change.getText()); } else { //incremental sync mode throw new IllegalStateException("Incremental sync not yet implemented"); } } + /** + * Convert a simple offset+length pair into a vscode range. This is a method on + * TextDocument because it requires splitting document into lines to determine + * line numbers from offsets. + */ + public RangeImpl toRange(int offset, int length) { + int end = offset + length; + RangeImpl range = new RangeImpl(); + range.setStart(toPosition(offset)); + range.setEnd(toPosition(end)); + return range; + } + + /** + * Determine the line-number a given offset (i.e. what line is the offset inside of?) + */ + private int lineNumber(int offset) { + int[] lineStarts = lineStarts(); + // TODO Should really use binary search here for speed + int lineNumber = 0; + for (int i = 0; i < lineStarts.length; i++) { + if (lineStarts[i]<=offset) { + lineNumber = i; + } else { + return lineNumber; + } + } + return lineNumber; + } + + + public PositionImpl toPosition(int offset) { + int line = lineNumber(offset); + int startOfLine = startOfLine(line); + int column = offset - startOfLine; + PositionImpl pos = new PositionImpl(); + pos.setCharacter(column); + pos.setLine(line); + return pos; + } + + private int startOfLine(int line) { + return lineStarts()[line]; + } + + private synchronized int[] lineStarts() { + if (_lineStarts==null) { + _lineStarts = parseLines(); + } + return _lineStarts; + } + + private int[] parseLines() { + List lineStarts = new ArrayList<>(); + lineStarts.add(0); + Matcher matcher = NEWLINE.matcher(getText()); + int pos = 0; + while (matcher.find(pos)) { + lineStarts.add(pos = matcher.end()); + } + int[] array = new int[lineStarts.size()]; + for (int i = 0; i < array.length; i++) { + array[i] = lineStarts.get(i); + } + return array; + } + + } diff --git a/vscode-extensions/commons/pom.xml b/vscode-extensions/commons/pom.xml index b997db51e..9dff83f23 100644 --- a/vscode-extensions/commons/pom.xml +++ b/vscode-extensions/commons/pom.xml @@ -13,12 +13,14 @@ language-server-commons language-server-test-harness yaml-commons + util-commons 4.11 3.5.2 1.7.21 + 19.0 diff --git a/vscode-extensions/commons/util-commons/.classpath b/vscode-extensions/commons/util-commons/.classpath new file mode 100644 index 000000000..fae1a2b37 --- /dev/null +++ b/vscode-extensions/commons/util-commons/.classpath @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vscode-extensions/commons/util-commons/.project b/vscode-extensions/commons/util-commons/.project new file mode 100644 index 000000000..d33777c9c --- /dev/null +++ b/vscode-extensions/commons/util-commons/.project @@ -0,0 +1,23 @@ + + + util-commons + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/vscode-extensions/commons/util-commons/.settings/org.eclipse.jdt.core.prefs b/vscode-extensions/commons/util-commons/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..714351aec --- /dev/null +++ b/vscode-extensions/commons/util-commons/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/vscode-extensions/commons/util-commons/.settings/org.eclipse.m2e.core.prefs b/vscode-extensions/commons/util-commons/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/vscode-extensions/commons/util-commons/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/vscode-extensions/commons/util-commons/pom.xml b/vscode-extensions/commons/util-commons/pom.xml new file mode 100644 index 000000000..377e63200 --- /dev/null +++ b/vscode-extensions/commons/util-commons/pom.xml @@ -0,0 +1,13 @@ + + 4.0.0 + util-commons + util-commons + + + org.springframework.ide.vscode + commons-parent + 0.0.1-SNAPSHOT + ../pom.xml + + + diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/Assert.java b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/Assert.java similarity index 100% rename from vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/Assert.java rename to vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/Assert.java diff --git a/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/Collector.java b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/Collector.java new file mode 100644 index 000000000..c961c509f --- /dev/null +++ b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/Collector.java @@ -0,0 +1,29 @@ +package org.springframework.ide.vscode.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * {@link IRequestor} that simplies stores all items received into + * a List + * + * @author Kris De Volder + */ +public class Collector implements IRequestor { + + @SuppressWarnings("unchecked") + private List nodes = Collections.EMPTY_LIST; + + @Override + public void accept(T node) { + if (nodes==Collections.EMPTY_LIST) { + nodes = new ArrayList(); + } + nodes.add(node); + } + + public List get() { + return nodes; + } +} diff --git a/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/ExceptionUtil.java b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/ExceptionUtil.java new file mode 100644 index 000000000..3922208bf --- /dev/null +++ b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/ExceptionUtil.java @@ -0,0 +1,68 @@ +package org.springframework.ide.vscode.util; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.concurrent.CancellationException; + +/** + * Utility methods to convert exceptions into other types of exceptions, status + * objects etc. + * + * @author Kris De Volder + */ +public class ExceptionUtil { + + public static Throwable getDeepestCause(Throwable e) { + Throwable cause = e; + Throwable parent = e.getCause(); + while (parent != null && parent != e) { + cause = parent; + parent = cause.getCause(); + } + return cause; + } + + public static String getMessage(Throwable e) { + // The message of nested exception is usually more interesting than the + // one on top. + Throwable cause = getDeepestCause(e); + String msg = cause.getClass().getSimpleName() + ": " + cause.getMessage(); + return msg; + } + + public static IllegalStateException notImplemented(String string) { + return new IllegalStateException("Not implemented: " + string); + } + + public static boolean isCancelation(Throwable e) { + return ( +// e instanceof OperationCanceledException || + e instanceof InterruptedException || + e instanceof CancellationException +// ( +// e instanceof CoreException && +// ((CoreException)e).getStatus().getSeverity()==IStatus.CANCEL +// ) + ); + } + + public static RuntimeException unchecked(Exception e) { + return new RuntimeException(e); + } + + public static String stacktrace() { + return stacktrace(new Exception("Stacktrace")); + } + + public static String stacktrace(Exception exception) { + ByteArrayOutputStream dump = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(dump); + try { + exception.printStackTrace(out); + } finally { + out.close(); + } + return dump.toString(); + } + +} diff --git a/vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/Futures.java b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/Futures.java similarity index 100% rename from vscode-extensions/commons/language-server-commons/src/main/java/org/springframework/ide/vscode/util/Futures.java rename to vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/Futures.java diff --git a/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/IRequestor.java b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/IRequestor.java new file mode 100644 index 000000000..4e7bb38a5 --- /dev/null +++ b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/IRequestor.java @@ -0,0 +1,5 @@ +package org.springframework.ide.vscode.util; + +public interface IRequestor { + void accept(T node); +} diff --git a/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/RememberLast.java b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/RememberLast.java new file mode 100644 index 000000000..746697625 --- /dev/null +++ b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/RememberLast.java @@ -0,0 +1,24 @@ +package org.springframework.ide.vscode.util; + +/** + * Requestor that remembers only the last item received. + * + * @author Kris De Volder + */ +public class RememberLast implements IRequestor { + + private T last = null; + + @Override + public void accept(T node) { + this.last = node; + } + + /** + * @return the last received item, may return null if no items where received. + */ + public T get() { + return last; + } + +} diff --git a/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/StringUtil.java b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/StringUtil.java new file mode 100644 index 000000000..52de63069 --- /dev/null +++ b/vscode-extensions/commons/util-commons/src/main/java/org/springframework/ide/vscode/util/StringUtil.java @@ -0,0 +1,20 @@ +package org.springframework.ide.vscode.util; + +public class StringUtil { + public static boolean hasText(String name) { + return name!=null && !name.trim().equals(""); + } + + public static String collectionToDelimitedString(Iterable strings, String delim) { + StringBuilder b = new StringBuilder(); + boolean first = true; + for (String s : strings) { + if (!first) { + b.append(delim); + } + b.append(s); + first = false; + } + return b.toString(); + } +} diff --git a/vscode-extensions/commons/yaml-commons/pom.xml b/vscode-extensions/commons/yaml-commons/pom.xml index b6a96a6fd..384cfcdd5 100644 --- a/vscode-extensions/commons/yaml-commons/pom.xml +++ b/vscode-extensions/commons/yaml-commons/pom.xml @@ -13,6 +13,21 @@ + + org.springframework.ide.vscode + util-commons + ${project.version} + + + org.springframework.ide.vscode + language-server-commons + ${project.version} + + + org.yaml + snakeyaml + 1.17 + org.yaml snakeyaml @@ -26,7 +41,7 @@ com.google.guava guava - 19.0 + ${guava-version} diff --git a/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/ast/NodeRef.java b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/ast/NodeRef.java new file mode 100644 index 000000000..ac66c83cf --- /dev/null +++ b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/ast/NodeRef.java @@ -0,0 +1,190 @@ +package org.springframework.ide.vscode.yaml.ast; + +import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.NodeTuple; +import org.yaml.snakeyaml.nodes.SequenceNode; + +/** + * A node reference represents a 'pointer' to a location where a Node is stored. + * The concept is useful because when looking for a 'path' inside an Yaml AST a + * NodeRef makes it explicit where the node is with respect to some 'container'. + * For example it allows distinguishing between a reference to Node which is + * obtained from map key versus a map value. + * + * @author Kris De Volder + */ +public abstract class NodeRef { + + public static enum Kind { + ROOT, SEQ, KEY, VAL + } + + private Parent parent; + + public NodeRef(Parent parent) { + this.parent = parent; + } + + public Parent getParent() { + return parent; + } + + public abstract Node get(); + public abstract void put(Node value); + + public abstract String toString(); + + public abstract Kind getKind(); + + /** + * Represents a reference to a root node, contained directly + * inside a {@link YamlFileAST} + */ + public static class RootRef extends NodeRef { + private int index; + + public RootRef(YamlFileAST file, int index) { + super(file); + this.index = index; + } + @Override + public Node get() { + return getParent().get(index); + } + + @Override + public void put(Node value) { + getParent().put(index, value); + } + + @Override + public String toString() { + return "ROOT["+index+"]"; + } + @Override + public Kind getKind() { + return Kind.ROOT; + } + public int getIndex() { + return index; + } + } + + public static class SeqRef extends NodeRef { + private int index; + public SeqRef(SequenceNode seq, int index) { + super(seq); + this.index = index; + } + @Override + public Node get() { + return getParent().getValue().get(index); + } + @Override + public void put(Node value) { + getParent().getValue().set(index, value); + } + @Override + public String toString() { + return "["+index+"]"; + } + @Override + public Kind getKind() { + return Kind.SEQ; + } + public int getIndex() { + return index; + } + } + + /** + * Abstract, represent reference to either a key or + * value inside a map tuple. Concrete subclasses define whether + * key or value is being accessed. + */ + public static abstract class TupleRef extends NodeRef { + protected int index; + public TupleRef(MappingNode map, int index) { + super(map); + this.index = index; + } + + public NodeTuple getTuple() { + return getParent().getValue().get(index); + } + + public void putTuple(NodeTuple value) { + getParent().getValue().set(index, value); + } + } + + /** + * References a key of a map entry + */ + public static class TupleKeyRef extends TupleRef { + public TupleKeyRef(MappingNode parent, int index) { + super(parent, index); + } + + @Override + public Node get() { + return getTuple().getKeyNode(); + } + + @Override + public void put(Node newKey) { + NodeTuple tuple = getTuple(); + putTuple(new NodeTuple(newKey, tuple.getValueNode())); + } + + @Override + public String toString() { + return "@key["+index+"]"; + } + + @Override + public Kind getKind() { + return Kind.KEY; + } + } + + /** + * References a value of a map entry + */ + public static class TupleValueRef extends TupleRef { + public TupleValueRef(MappingNode parent, int index) { + super(parent, index); + } + + @Override + public Node get() { + return getTuple().getValueNode(); + } + + @Override + public void put(Node newValue) { + NodeTuple t = getTuple(); + putTuple(new NodeTuple(t.getKeyNode(), newValue)); + } + + @Override + public String toString() { + String keyString = NodeUtil.asScalar(getTuple().getKeyNode()); + if (keyString!=null) { + //more readable to use the key value than the index of the tuple + return "@val['"+keyString+"']"; + } + return "@val["+index+"]"; + } + + @Override + public Kind getKind() { + return Kind.VAL; + } + + public Node getKey() { + return getTuple().getKeyNode(); + } + } +} diff --git a/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/ast/NodeUtil.java b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/ast/NodeUtil.java new file mode 100644 index 000000000..31451de43 --- /dev/null +++ b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/ast/NodeUtil.java @@ -0,0 +1,50 @@ +package org.springframework.ide.vscode.yaml.ast; + +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.NodeId; +import org.yaml.snakeyaml.nodes.ScalarNode; + +/** + * @author Kris De Volder + */ +public class NodeUtil { + + /** + * Determines whether a node contains the given offset. + * The range a node covers between its begin and end mark is treated + * as a half open interval. The start offset is treated as included + * in the range but the end offset is excluded. + *

+ * This is to avoid ambiguity as node ranges tend to 'join' + * together so that the end region of one node coincides with + * the start region of the next node. By treating node ranges + * as 'half open' intervals every offset is typically only + * part of two different nodes if those nodes effectively + * have overlapping ranges (i.e. only if one node contains + * the other). Thus, an operation like finding the smallest + * node that contains an offset is unambgious. + */ + public static boolean contains(Node node, int offset) { + return getStart(node)<=offset && offset> NO_CHILDREN = Collections.emptyList(); + private List nodes; + + public YamlFileAST(Iterable iter) { + nodes = new ArrayList(); + for (Node node : iter) { + nodes.add(node); + } + } + + public List> findPath(int offset) { + Collector> path = new Collector>(); + findPath(offset, path); + return path.get(); + } + + /** + * Find 'smallest' ast node that contains offset. The pathRequestor will + * be called as the search progresses down the AST on all nodes on the + * path to the smallest node. If no node in the tree contains the offset + * the requestor will not be called at all. + */ + public void findPath(int offset, IRequestor> pathRequestor) { + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + if (contains(node, offset)) { + pathRequestor.accept(new RootRef(this, i) ); + findPath(node, offset, pathRequestor); + return; + } + } + } + + /** + * Find smallest node that is a child of 'n' that contains 'offset'. Each visited + * node containing the offset, from the down to the found node are + * passed to the pathRequestor. + */ + private void findPath(Node n, int offset, IRequestor> pathRequestor) { + //TODO: avoid lots of garbage production by not using 'getChildren' + // but inling getChildren (i.e a switch-case that visits + // the children without putting them into temporary collections.) + // By doing this it should be possible to avoid creaing lots of temporary + // array lists and NodeRef objects and only create NodeRef objects for + // the nodes we actually care about (i.e. the ones on the path). + List> children = getChildren(n); + for (int i = 0; i < children.size(); i++) { + NodeRef c = children.get(i); + if (contains(c.get(), offset)) { + pathRequestor.accept(c); + findPath(c.get(), offset, pathRequestor); + return; + } + } + } + + public static List> getChildren(Node n) { + switch (n.getNodeId()) { + case scalar: + return NO_CHILDREN; + case sequence: + return getChildren((SequenceNode)n); + case mapping: + return getChildren((MappingNode)n); + case anchor: + //TODO: is this right? maybe we should visit down into 'realnode' + // but do we then potentially visit the same node twice? + return NO_CHILDREN; + } + return null; + } + + public List getNodes() { + return nodes; + } + + private static List> getChildren(SequenceNode seq) { + int nodes = seq.getValue().size(); + ArrayList> children = new ArrayList>(nodes); + for (int i = 0; i < nodes; i++) { + children.add(new SeqRef(seq, i)); + } + return children; + } + + private static List> getChildren(MappingNode map) { + int entries = map.getValue().size(); + ArrayList> children = new ArrayList>(entries*2); + for (int i = 0; i < entries; i++) { + children.add(new TupleKeyRef(map, i)); + children.add(new TupleValueRef(map, i)); + } + return children; + } + + public NodeRef findNodeRef(int offset) { + RememberLast> lastNode = new RememberLast>(); + findPath(offset, lastNode); + return lastNode.get(); + } + + public Node findNode(int offset) { + NodeRef ref = findNodeRef(offset); + if (ref!=null) { + return ref.get(); + } + return null; + } + + public Node get(int index) { + return nodes.get(index); + } + + public void put(int index, Node value) { + nodes.set(index, value); + } + + +} diff --git a/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/SchemaBasedYamlASTReconciler.java b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/SchemaBasedYamlASTReconciler.java new file mode 100644 index 000000000..7ea5f5ad0 --- /dev/null +++ b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/SchemaBasedYamlASTReconciler.java @@ -0,0 +1,167 @@ +package org.springframework.ide.vscode.yaml.reconcile; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.springframework.ide.vscode.commons.reconcile.IProblemCollector; +import org.springframework.ide.vscode.util.ExceptionUtil; +import org.springframework.ide.vscode.util.StringUtil; +import org.springframework.ide.vscode.yaml.ast.NodeUtil; +import org.springframework.ide.vscode.yaml.ast.YamlFileAST; +import org.springframework.ide.vscode.yaml.schema.YType; +import org.springframework.ide.vscode.yaml.schema.YTypeUtil; +import org.springframework.ide.vscode.yaml.schema.YTypedProperty; +import org.springframework.ide.vscode.yaml.schema.YamlSchema; +import org.springframework.ide.vscode.yaml.util.ValueParser; +import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.NodeTuple; +import org.yaml.snakeyaml.nodes.ScalarNode; +import org.yaml.snakeyaml.nodes.SequenceNode; + +public class SchemaBasedYamlASTReconciler implements YamlASTReconciler { + + private final IProblemCollector problems; + private final YamlSchema schema; + private final YTypeUtil typeUtil; + + public SchemaBasedYamlASTReconciler(IProblemCollector problems, YamlSchema schema) { + this.problems = problems; + this.schema = schema; + this.typeUtil = schema.getTypeUtil(); + } + + @Override + public void reconcile(YamlFileAST ast) { + List nodes = ast.getNodes(); + if (nodes!=null && !nodes.isEmpty()) { + for (Node node : nodes) { + reconcile(node, schema.getTopLevelType()); + } + } + } + + private void reconcile(Node node, YType type) { + if (type!=null) { + switch (node.getNodeId()) { + case mapping: + MappingNode map = (MappingNode) node; + if (typeUtil.isMap(type)) { + for (NodeTuple entry : map.getValue()) { + reconcile(entry.getKeyNode(), typeUtil.getKeyType(type)); + reconcile(entry.getValueNode(), typeUtil.getDomainType(type)); + } + } else if (typeUtil.isBean(type)) { + Map beanProperties = typeUtil.getPropertiesMap(type); + for (NodeTuple entry : map.getValue()) { + Node keyNode = entry.getKeyNode(); + String key = NodeUtil.asScalar(keyNode); + if (key==null) { + expectScalar(node); + } else { + YTypedProperty prop = beanProperties.get(key); + if (prop==null) { + unknownBeanProperty(keyNode, type, key); + } else { + reconcile(entry.getValueNode(), prop.getType()); + } + } + } + } else { + expectTypeButFoundMap(type, node); + } + break; + case sequence: + SequenceNode seq = (SequenceNode) node; + if (typeUtil.isSequencable(type)) { + for (Node el : seq.getValue()) { + reconcile(el, typeUtil.getDomainType(type)); + } + } else { + expectTypeButFoundSequence(type, node); + } + break; + case scalar: + if (typeUtil.isAtomic(type)) { + ValueParser parser = typeUtil.getValueParser(type); + if (parser!=null) { + try { + parser.parse(NodeUtil.asScalar(node)); + } catch (Exception e) { + String msg = ExceptionUtil.getMessage(e); + valueParseError(type, node, msg); + } + } + } else { + expectTypeButFoundScalar(type, node); + } + break; + default: + // other stuff we don't check + } + } + } + + private void valueParseError(YType type, Node node, String parseErrorMsg) { + String msg= "Couldn't parse as '"+describe(type)+"'"; + if (StringUtil.hasText(parseErrorMsg)) { + msg += " ("+parseErrorMsg+")"; + } + problem(node, msg); + } + + private void unknownBeanProperty(Node keyNode, YType type, String name) { + problem(keyNode, "Unknown property '"+name+"' for type '"+typeUtil.niceTypeName(type)+"'"); + } + + private void expectScalar(Node node) { + problem(node, "Expecting a 'Scalar' node but got "+describe(node)); + } + + private String describe(Node node) { + switch (node.getNodeId()) { + case scalar: + return "'"+((ScalarNode)node).getValue()+"'"; + case mapping: + return "a 'Mapping' node"; + case sequence: + return "a 'Sequence' node"; + case anchor: + return "a 'Anchor' node"; + default: + throw new IllegalStateException("Missing switch case"); + } + } + + private void expectTypeButFoundScalar(YType type, Node node) { + problem(node, "Expecting a '"+describe(type)+"' but found a 'Scalar'"); + } + + private void expectTypeButFoundSequence(YType type, Node node) { + problem(node, "Expecting a '"+describe(type)+"' but found a 'Sequence'"); + } + + private void expectTypeButFoundMap(YType type, Node node) { + problem(node, "Expecting a '"+describe(type)+"' but found a 'Map'"); + } + + private String describe(YType type) { + if (typeUtil.isAtomic(type)) { + return typeUtil.niceTypeName(type); + } + ArrayList expectedNodeTypes = new ArrayList<>(); + if (typeUtil.isBean(type) || typeUtil.isMap(type)) { + expectedNodeTypes.add("Map"); + } + if (typeUtil.isSequencable(type)) { + expectedNodeTypes.add("Sequence"); + } + return StringUtil.collectionToDelimitedString(expectedNodeTypes, " or "); + } + + private void problem(Node node, String msg) { + problems.accept(YamlSchemaProblems.schemaProblem(msg, node)); + } + +} diff --git a/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlASTReconciler.java b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlASTReconciler.java new file mode 100644 index 000000000..89372f362 --- /dev/null +++ b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlASTReconciler.java @@ -0,0 +1,7 @@ +package org.springframework.ide.vscode.yaml.reconcile; + +import org.springframework.ide.vscode.yaml.ast.YamlFileAST; + +public interface YamlASTReconciler { + void reconcile(YamlFileAST ast); +} diff --git a/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlReconcileEngine.java b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlReconcileEngine.java new file mode 100644 index 000000000..862e20146 --- /dev/null +++ b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlReconcileEngine.java @@ -0,0 +1,54 @@ +package org.springframework.ide.vscode.yaml.reconcile; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.reconcile.IDocument; +import org.springframework.ide.vscode.commons.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.reconcile.IReconcileEngine; +import org.springframework.ide.vscode.commons.reconcile.ReconcileProblem; +import org.springframework.ide.vscode.yaml.ast.YamlASTProvider; +import org.springframework.ide.vscode.yaml.ast.YamlFileAST; +import org.yaml.snakeyaml.error.Mark; +import org.yaml.snakeyaml.parser.ParserException; +import org.yaml.snakeyaml.scanner.ScannerException; + +/** + * @author Kris De Volder + */ +public abstract class YamlReconcileEngine implements IReconcileEngine { + + final static Logger logger = LoggerFactory.getLogger(YamlReconcileEngine.class); + + protected final YamlASTProvider parser; + + public YamlReconcileEngine(YamlASTProvider parser) { + this.parser = parser; + } + + @Override + public void reconcile(IDocument doc, IProblemCollector problemCollector) { + problemCollector.beginCollecting(); + try { + YamlFileAST ast = parser.getAST(doc); + YamlASTReconciler reconciler = getASTReconciler(doc, problemCollector); + if (reconciler!=null) { + reconciler.reconcile(ast); + } + } catch (ParserException e) { + String msg = e.getProblem(); + Mark mark = e.getProblemMark(); + problemCollector.accept(syntaxError(msg, mark.getIndex(), 1)); + } catch (ScannerException e) { + String msg = e.getProblem(); + Mark mark = e.getProblemMark(); + problemCollector.accept(syntaxError(msg, mark.getIndex(), 1)); + } catch (Exception e) { + logger.error("unexpected error during reconcile", e); + } finally { + problemCollector.endCollecting(); + } + } + + protected abstract ReconcileProblem syntaxError(String msg, int offset, int length); + protected abstract YamlASTReconciler getASTReconciler(IDocument doc, IProblemCollector problemCollector); +} diff --git a/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlSchemaBasedReconcileEngine.java b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlSchemaBasedReconcileEngine.java new file mode 100644 index 000000000..91209febf --- /dev/null +++ b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlSchemaBasedReconcileEngine.java @@ -0,0 +1,29 @@ +package org.springframework.ide.vscode.yaml.reconcile; + +import org.springframework.ide.vscode.commons.reconcile.IDocument; +import org.springframework.ide.vscode.commons.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.reconcile.ReconcileProblem; +import org.springframework.ide.vscode.yaml.ast.YamlASTProvider; +import org.springframework.ide.vscode.yaml.schema.YamlSchema; + +/** + * @author Kris De Volder + */ +public final class YamlSchemaBasedReconcileEngine extends YamlReconcileEngine { + private final YamlSchema schema; + + public YamlSchemaBasedReconcileEngine(YamlASTProvider parser, YamlSchema schema) { + super(parser); + this.schema = schema; + } + + @Override + protected ReconcileProblem syntaxError(String msg, int offset, int length) { + return YamlSchemaProblems.syntaxProblem(msg, offset, length); + } + + @Override + protected YamlASTReconciler getASTReconciler(IDocument doc, IProblemCollector problems) { + return new SchemaBasedYamlASTReconciler(problems, schema); + } +} \ No newline at end of file diff --git a/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlSchemaProblems.java b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlSchemaProblems.java new file mode 100644 index 000000000..7808d9425 --- /dev/null +++ b/vscode-extensions/commons/yaml-commons/src/main/java/org/springframework/ide/vscode/yaml/reconcile/YamlSchemaProblems.java @@ -0,0 +1,45 @@ +package org.springframework.ide.vscode.yaml.reconcile; + +import org.springframework.ide.vscode.commons.reconcile.ProblemSeverity; +import org.springframework.ide.vscode.commons.reconcile.ProblemType; +import org.springframework.ide.vscode.commons.reconcile.ReconcileProblem; +import org.springframework.ide.vscode.commons.reconcile.ReconcileProblemImpl; +import org.yaml.snakeyaml.nodes.Node; + +/** + * Methods for creating reconciler problems for Schema based reconciler implementation. + * + * @author Kris De Volder + */ +public class YamlSchemaProblems { + + private static final ProblemType SCHEMA_PROBLEM = problemType("YamlSchemaProblem"); + private static final ProblemType SYNTAX_PROBLEM = problemType("YamlSyntaxProblem"); + + private static ProblemType problemType(final String typeName) { + return new ProblemType() { + @Override + public String toString() { + return typeName; + } + @Override + public ProblemSeverity getDefaultSeverity() { + return ProblemSeverity.ERROR; + } + @Override + public String getCode() { + return typeName; + } + }; + } + + public static ReconcileProblem syntaxProblem(String msg, int offset, int len) { + return new ReconcileProblemImpl(SYNTAX_PROBLEM, msg, offset, len); + } + + public static ReconcileProblem schemaProblem(String msg, Node node) { + int start = node.getStartMark().getIndex(); + int end = node.getEndMark().getIndex(); + return new ReconcileProblemImpl(SCHEMA_PROBLEM, msg, start, end-start); + } +} diff --git a/vscode-extensions/vscode-manifest-yaml/lib/Main.ts b/vscode-extensions/vscode-manifest-yaml/lib/Main.ts index dd0e042c7..17e2bafb9 100644 --- a/vscode-extensions/vscode-manifest-yaml/lib/Main.ts +++ b/vscode-extensions/vscode-manifest-yaml/lib/Main.ts @@ -42,6 +42,7 @@ function getClasspath(context: VSCode.ExtensionContext):string { /** Called when extension is activated */ export function activate(context: VSCode.ExtensionContext) { + VSCode.window.showInformationMessage("Activating manifet.yml extension"); let javaExecutablePath = findJavaExecutable('java'); if (javaExecutablePath == null) { @@ -103,7 +104,7 @@ export function activate(context: VSCode.ExtensionContext) { let args = [ '-Dserver.port=' + port, '-cp', classpath, - 'org.springframework.ide.vscode.yaml.Main' + 'org.springframework.ide.vscode.cloudfoundry.manifest.editor.Main' ]; if (DEBUG) { args.unshift(DEBUG_ARG); diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/ManifestYamlEditor.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/ManifestYamlEditor.java deleted file mode 100644 index a64de33d4..000000000 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/ManifestYamlEditor.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.springframework.ide.vscode.cloudfoundry.manifest.editor; -///******************************************************************************* -// * Copyright (c) 2015 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.eclipse.cloudfoundry.manifest.editor; -// -//import org.dadacoalition.yedit.editor.YEditSourceViewerConfiguration; -//import org.springframework.ide.eclipse.editor.support.util.ShellProviders; -//import org.springframework.ide.eclipse.editor.support.yaml.AbstractYamlEditor; -// -// -//public class ManifestYamlEditor extends AbstractYamlEditor { -// -// @Override -// protected YEditSourceViewerConfiguration createSourceViewerConfiguration() { -// return new ManifestYamlSourceViewerConfiguration(ShellProviders.from(this)); -// } -// -// @Override -// protected void initializeEditor() { -// super.initializeEditor(); -//// SpringPropertiesEditorPlugin.getIndexManager().addListener(this); -//// SpringPropertiesEditorPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this); -// } -// -//// @Override -//// public void changed(SpringPropertiesIndexManager info) { -//// if (sourceViewerConf!=null) { -//// sourceViewerConf.forceReconcile(); -//// } -//// } -// -// @Override -// public void dispose() { -// super.dispose(); -//// SpringPropertiesEditorPlugin.getIndexManager().removeListener(this); -//// SpringPropertiesEditorPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this); -// } -// -//// @Override -//// public void propertyChange(PropertyChangeEvent event) { -//// if (event.getProperty().startsWith(ProblemSeverityPreferencesUtil.PREFERENCE_PREFIX)) { -//// if (sourceViewerConf!=null) { -//// sourceViewerConf.forceReconcile(); -//// } -//// } -//// } -//} diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/ManifestYamlLanguageServer.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/ManifestYamlLanguageServer.java index b54fcf4bb..f024d24aa 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/ManifestYamlLanguageServer.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/ManifestYamlLanguageServer.java @@ -2,15 +2,27 @@ package org.springframework.ide.vscode.cloudfoundry.manifest.editor; import java.io.StringReader; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; +import javax.inject.Provider; + +import org.springframework.ide.vscode.commons.reconcile.IDocument; +import org.springframework.ide.vscode.commons.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.reconcile.ReconcileProblem; import org.springframework.ide.vscode.util.Futures; import org.springframework.ide.vscode.util.SimpleLanguageServer; import org.springframework.ide.vscode.util.SimpleTextDocumentService; import org.springframework.ide.vscode.util.TextDocument; import org.springframework.ide.vscode.yaml.ErrorCodes; +import org.springframework.ide.vscode.yaml.ast.YamlASTProvider; +import org.springframework.ide.vscode.yaml.reconcile.SchemaBasedYamlASTReconciler; +import org.springframework.ide.vscode.yaml.reconcile.YamlASTReconciler; +import org.springframework.ide.vscode.yaml.reconcile.YamlSchemaBasedReconcileEngine; +import org.springframework.ide.vscode.yaml.schema.YValueHint; +import org.springframework.ide.vscode.yaml.schema.YamlSchema; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.error.MarkedYAMLException; import org.yaml.snakeyaml.error.YAMLException; @@ -32,10 +44,13 @@ import io.typefox.lsapi.ServerCapabilitiesImpl; public class ManifestYamlLanguageServer extends SimpleLanguageServer { + private static final Provider> NO_BUILDPACKS = () -> ImmutableList.of(); + private Yaml yaml = new Yaml(); public ManifestYamlLanguageServer() { SimpleTextDocumentService documents = getTextDocumentService(); + // SimpleWorkspaceService workspace = getWorkspaceService(); documents.onDidChangeContent(params -> { TextDocument doc = params.getDocument(); @@ -104,55 +119,36 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer { } private void validateDocument(SimpleTextDocumentService documents, TextDocument doc) { - List diagnostics = reconcile(documents, doc); - documents.publishDiagnostics(doc, diagnostics); + IProblemCollector problems = new IProblemCollector() { + + private List diagnostics = new ArrayList<>(); + + @Override + public void endCollecting() { + documents.publishDiagnostics(doc, diagnostics); + } + + @Override + public void beginCollecting() { + diagnostics.clear(); + } + + @Override + public void accept(ReconcileProblem problem) { + DiagnosticImpl d = new DiagnosticImpl(); + d.setCode(problem.getCode()); + d.setMessage(problem.getMessage()); + d.setRange(doc.toRange(problem.getOffset(), problem.getLength())); + diagnostics.add(d); + } + }; + + YamlSchema schema = new ManifestYmlSchema(NO_BUILDPACKS); + YamlASTProvider parser = new YamlParser(yaml); + YamlSchemaBasedReconcileEngine engine = new YamlSchemaBasedReconcileEngine(parser, schema); + engine.reconcile(doc, problems); } - protected List reconcile(SimpleTextDocumentService documents, TextDocument doc) { - try { - Iterator asts = yaml.composeAll(new StringReader(doc.getText())).iterator(); - while (asts.hasNext()) { - asts.next(); - } - return ImmutableList.of(); - } catch (YAMLException e) { - return ImmutableList.of(parseError(e)); - } - } - - private DiagnosticImpl parseError(YAMLException e) { - DiagnosticImpl d = new DiagnosticImpl(); - d.setMessage(getMessage(e)); - d.setRange(getRange(e)); - d.setSeverity(Diagnostic.SEVERITY_ERROR); - d.setCode(ErrorCodes.YAML_SYNTAX_ERROR); - d.setSource("yaml"); - return d; - } - - private String getMessage(YAMLException e) { - if (e instanceof MarkedYAMLException) { - return ((MarkedYAMLException) e).getProblem(); - } - return e.getMessage(); - } - - private RangeImpl getRange(YAMLException _e) { - if (_e instanceof MarkedYAMLException) { - MarkedYAMLException e = (MarkedYAMLException) _e; - - PositionImpl start = new PositionImpl(); - start.setLine(e.getProblemMark().getLine()); - start.setCharacter(e.getProblemMark().getColumn()); - - RangeImpl rng = new RangeImpl(); - rng.setStart(start); - rng.setEnd(start); - return rng; - } - return null; - } - @Override protected ServerCapabilitiesImpl getServerCapabilities() { ServerCapabilitiesImpl c = new ServerCapabilitiesImpl(); diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/YamlParser.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/YamlParser.java new file mode 100644 index 000000000..2ffef0bda --- /dev/null +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/cloudfoundry/manifest/editor/YamlParser.java @@ -0,0 +1,23 @@ +package org.springframework.ide.vscode.cloudfoundry.manifest.editor; + +import java.io.StringReader; + +import org.springframework.ide.vscode.commons.reconcile.IDocument; +import org.springframework.ide.vscode.yaml.ast.YamlASTProvider; +import org.springframework.ide.vscode.yaml.ast.YamlFileAST; +import org.yaml.snakeyaml.Yaml; + +public class YamlParser implements YamlASTProvider { + + private Yaml yaml; + + public YamlParser(Yaml yaml) { + this.yaml = yaml; + } + + @Override + public YamlFileAST getAST(IDocument doc) throws Exception { + return new YamlFileAST(yaml.composeAll(new StringReader(doc.get()))); + } + +} diff --git a/vscode-extensions/vscode-manifest-yaml/test/examples/manifest.yml b/vscode-extensions/vscode-manifest-yaml/test/examples/manifest.yml index 46d24e076..29422dc75 100644 --- a/vscode-extensions/vscode-manifest-yaml/test/examples/manifest.yml +++ b/vscode-extensions/vscode-manifest-yaml/test/examples/manifest.yml @@ -1,2 +1,5 @@ -foo: bar -- asss +applications: +- name: foo + foo: bar + builpack: java +