Manifest.yml validation works!
This commit is contained in:
@@ -31,6 +31,11 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>util-commons</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- Java implementation of VS Code language server protocol -->
|
||||
<dependency>
|
||||
<groupId>io.typefox.lsapi</groupId>
|
||||
@@ -74,6 +79,4 @@
|
||||
<version>${jackson-2-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -2,4 +2,6 @@ package org.springframework.ide.vscode.commons.reconcile;
|
||||
|
||||
public interface IDocument {
|
||||
|
||||
String get();
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -14,4 +14,5 @@ package org.springframework.ide.vscode.commons.reconcile;
|
||||
public interface ProblemType {
|
||||
ProblemSeverity getDefaultSeverity();
|
||||
String toString();
|
||||
String getCode();
|
||||
}
|
||||
|
||||
@@ -11,4 +11,5 @@ public interface ReconcileProblem {
|
||||
String getMessage();
|
||||
int getOffset();
|
||||
int getLength();
|
||||
String getCode();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -260,7 +260,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
}
|
||||
|
||||
public void publishDiagnostics(TextDocument doc, List<DiagnosticImpl> diagnostics) {
|
||||
if (diagnostics!=null && !diagnostics.isEmpty()) {
|
||||
if (diagnostics!=null) {
|
||||
PublishDiagnosticsParamsImpl params = new PublishDiagnosticsParamsImpl();
|
||||
params.setUri(doc.getUri());
|
||||
params.setDiagnostics(diagnostics);
|
||||
|
||||
@@ -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("");
|
||||
}
|
||||
}
|
||||
@@ -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<Integer> 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
<module>language-server-commons</module>
|
||||
<module>language-server-test-harness</module>
|
||||
<module>yaml-commons</module>
|
||||
<module>util-commons</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<junit-version>4.11</junit-version>
|
||||
<assertj-version>3.5.2</assertj-version>
|
||||
<slf4j-version>1.7.21</slf4j-version>
|
||||
<guava-version>19.0</guava-version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
||||
36
vscode-extensions/commons/util-commons/.classpath
Normal file
36
vscode-extensions/commons/util-commons/.classpath
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
vscode-extensions/commons/util-commons/.project
Normal file
23
vscode-extensions/commons/util-commons/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>util-commons</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
||||
13
vscode-extensions/commons/util-commons/pom.xml
Normal file
13
vscode-extensions/commons/util-commons/pom.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>util-commons</artifactId>
|
||||
<name>util-commons</name>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
</project>
|
||||
@@ -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<T> implements IRequestor<T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<T> nodes = Collections.EMPTY_LIST;
|
||||
|
||||
@Override
|
||||
public void accept(T node) {
|
||||
if (nodes==Collections.EMPTY_LIST) {
|
||||
nodes = new ArrayList<T>();
|
||||
}
|
||||
nodes.add(node);
|
||||
}
|
||||
|
||||
public List<T> get() {
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ide.vscode.util;
|
||||
|
||||
public interface IRequestor<T> {
|
||||
void accept(T node);
|
||||
}
|
||||
@@ -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<T> implements IRequestor<T> {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> 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();
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,21 @@
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>util-commons</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-commons</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.17</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
@@ -26,7 +41,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>19.0</version>
|
||||
<version>${guava-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -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<Parent> {
|
||||
|
||||
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<YamlFileAST> {
|
||||
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<SequenceNode> {
|
||||
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<MappingNode> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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<getEnd(node);
|
||||
}
|
||||
|
||||
public static int getStart(Node node) {
|
||||
return node.getStartMark().getIndex();
|
||||
}
|
||||
|
||||
public static int getEnd(Node node) {
|
||||
return node.getEndMark().getIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve String value of a scalar node.
|
||||
* @return String value or null if node is not a Scalar node.
|
||||
*/
|
||||
public static String asScalar(Node node) {
|
||||
if (node.getNodeId()==NodeId.scalar) {
|
||||
return ((ScalarNode)node).getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.springframework.ide.vscode.yaml.ast;
|
||||
|
||||
import org.springframework.ide.vscode.commons.reconcile.IDocument;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface YamlASTProvider {
|
||||
YamlFileAST getAST(IDocument doc) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package org.springframework.ide.vscode.yaml.ast;
|
||||
|
||||
import static org.springframework.ide.vscode.yaml.ast.NodeUtil.contains;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.util.Collector;
|
||||
import org.springframework.ide.vscode.util.IRequestor;
|
||||
import org.springframework.ide.vscode.util.RememberLast;
|
||||
import org.springframework.ide.vscode.yaml.ast.NodeRef.RootRef;
|
||||
import org.springframework.ide.vscode.yaml.ast.NodeRef.SeqRef;
|
||||
import org.springframework.ide.vscode.yaml.ast.NodeRef.TupleKeyRef;
|
||||
import org.springframework.ide.vscode.yaml.ast.NodeRef.TupleValueRef;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.SequenceNode;
|
||||
|
||||
/**
|
||||
* Represents a parsed yml file.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class YamlFileAST {
|
||||
|
||||
private static final List<NodeRef<?>> NO_CHILDREN = Collections.emptyList();
|
||||
private List<Node> nodes;
|
||||
|
||||
public YamlFileAST(Iterable<Node> iter) {
|
||||
nodes = new ArrayList<Node>();
|
||||
for (Node node : iter) {
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
public List<NodeRef<?>> findPath(int offset) {
|
||||
Collector<NodeRef<?>> path = new Collector<NodeRef<?>>();
|
||||
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<NodeRef<?>> 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<NodeRef<?>> 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<NodeRef<?>> 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<NodeRef<?>> 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<Node> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private static List<NodeRef<?>> getChildren(SequenceNode seq) {
|
||||
int nodes = seq.getValue().size();
|
||||
ArrayList<NodeRef<?>> children = new ArrayList<NodeRef<?>>(nodes);
|
||||
for (int i = 0; i < nodes; i++) {
|
||||
children.add(new SeqRef(seq, i));
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
private static List<NodeRef<?>> getChildren(MappingNode map) {
|
||||
int entries = map.getValue().size();
|
||||
ArrayList<NodeRef<?>> children = new ArrayList<NodeRef<?>>(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<NodeRef<?>> lastNode = new RememberLast<NodeRef<?>>();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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<Node> 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<String, YTypedProperty> 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<String> 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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//}
|
||||
@@ -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<Collection<YValueHint>> 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<DiagnosticImpl> diagnostics = reconcile(documents, doc);
|
||||
documents.publishDiagnostics(doc, diagnostics);
|
||||
IProblemCollector problems = new IProblemCollector() {
|
||||
|
||||
private List<DiagnosticImpl> 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<DiagnosticImpl> reconcile(SimpleTextDocumentService documents, TextDocument doc) {
|
||||
try {
|
||||
Iterator<Node> 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();
|
||||
|
||||
@@ -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())));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +1,5 @@
|
||||
foo: bar
|
||||
- asss
|
||||
applications:
|
||||
- name: foo
|
||||
foo: bar
|
||||
builpack: java
|
||||
|
||||
|
||||
Reference in New Issue
Block a user