Port over all manifest.yml editr tests (still failing)
This commit is contained in:
@@ -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>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>language-server-test-harness</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
|
||||
@@ -0,0 +1,33 @@
|
||||
<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>language-server-test-harness</artifactId>
|
||||
<name>language-server-test-harness</name>
|
||||
<description>Test harness for testing language server functionaltity implemented in Java</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-commons</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,143 @@
|
||||
package org.springframework.ide.vscode.testharness;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import io.typefox.lsapi.Diagnostic;
|
||||
import io.typefox.lsapi.Position;
|
||||
import io.typefox.lsapi.PublishDiagnosticsParams;
|
||||
import io.typefox.lsapi.Range;
|
||||
|
||||
public class Editor {
|
||||
|
||||
private static final Comparator<Diagnostic> PROBLEM_COMPARATOR = new Comparator<Diagnostic>() {
|
||||
@Override
|
||||
public int compare(Diagnostic o1, Diagnostic o2) {
|
||||
int diff = compare(o1.getRange().getStart(), o2.getRange().getStart());
|
||||
if (diff!=0) return diff;
|
||||
return compare(o1.getRange().getEnd(), o2.getRange().getEnd());
|
||||
}
|
||||
|
||||
private int compare(Position p1, Position p2) {
|
||||
int d = p1.getLine() - p2.getLine();
|
||||
if (d!=0) return d;
|
||||
return p1.getCharacter() - p2.getCharacter();
|
||||
}
|
||||
};
|
||||
|
||||
private LanguageServerHarness harness;
|
||||
private TextDocumentInfo document;
|
||||
|
||||
public Editor(LanguageServerHarness harness, String contents) throws Exception {
|
||||
this.harness = harness;
|
||||
this.document = harness.openDocument(harness.createWorkingCopy(contents));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a 'expectedProblems' are found by the reconciler. Expected problems are
|
||||
* specified by string of the form "${badSnippet}|${messageSnippet}". The badSnippet
|
||||
* is the text expected to be covered by the marker's region and the message snippet must
|
||||
* be found in the error marker's message.
|
||||
* <p>
|
||||
* The expected problems are matched one-to-one in the order given (so markers in the
|
||||
* editor must appear in the expected order for the assert to pass).
|
||||
*
|
||||
* @param editor
|
||||
* @param expectedProblems
|
||||
* @throws BadLocationException
|
||||
*/
|
||||
public void assertProblems(String... expectedProblems) throws Exception {
|
||||
Editor editor = this;
|
||||
List<Diagnostic> actualProblems = new ArrayList<>(editor.reconcile());
|
||||
Collections.sort(actualProblems, PROBLEM_COMPARATOR);
|
||||
String bad = null;
|
||||
if (actualProblems.size()!=expectedProblems.length) {
|
||||
bad = "Wrong number of problems (expecting "+expectedProblems.length+" but found "+actualProblems.size()+")";
|
||||
} else {
|
||||
for (int i = 0; i < expectedProblems.length; i++) {
|
||||
if (!matchProblem(editor, actualProblems.get(i), expectedProblems[i])) {
|
||||
bad = "First mismatch at index "+i+": "+expectedProblems[i]+"\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bad!=null) {
|
||||
fail(bad+problemSumary(editor, actualProblems));
|
||||
}
|
||||
}
|
||||
|
||||
private String problemSumary(Editor editor, List<Diagnostic> actualProblems) throws Exception {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (Diagnostic p : actualProblems) {
|
||||
buf.append("\n----------------------\n");
|
||||
|
||||
String snippet = editor.getText(p.getRange());
|
||||
buf.append("("+p.getRange().getStart().getLine()+", "+p.getRange().getStart().getCharacter()+")["+snippet+"]:\n");
|
||||
buf.append(" "+p.getMessage());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String getText(Range range) {
|
||||
return document.getText(range);
|
||||
}
|
||||
|
||||
public void setText(String newContent) throws Exception {
|
||||
document = harness.changeDocument(document.getUri(), newContent);
|
||||
}
|
||||
|
||||
private boolean matchProblem(Editor editor, Diagnostic problem, String expect) {
|
||||
String[] parts = expect.split("\\|");
|
||||
assertEquals(2, parts.length);
|
||||
String badSnippet = parts[0];
|
||||
String messageSnippet = parts[1];
|
||||
boolean spaceSensitive = badSnippet.trim().length()<badSnippet.length();
|
||||
boolean emptyRange = problem.getRange().getStart().equals(problem.getRange().getEnd());
|
||||
String actualBadSnippet = emptyRange
|
||||
? editor.getCharAt(problem.getRange().getStart())
|
||||
: editor.getText(problem.getRange());
|
||||
if (!spaceSensitive) {
|
||||
actualBadSnippet = actualBadSnippet.trim();
|
||||
}
|
||||
return actualBadSnippet.equals(badSnippet)
|
||||
&& problem.getMessage().contains(messageSnippet);
|
||||
}
|
||||
|
||||
private String getCharAt(Position start) {
|
||||
int offset = document.toOffset(start);
|
||||
return document.getText().substring(offset, offset+1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Diagnostic> reconcile() {
|
||||
// We assume the language server works synchronously for now and it does an immediate reconcile
|
||||
// when the document changes. In the future this is probably not going to be the case though and then this
|
||||
// method will need to somehow ensure the linter is done working before retrieving the problems from the
|
||||
// test harness.
|
||||
PublishDiagnosticsParams diagnostics = harness.getDiagnostics(document);
|
||||
if (diagnostics!=null) {
|
||||
return (List<Diagnostic>) diagnostics.getDiagnostics();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public void assertCompletions(String... specs) {
|
||||
throw new UnsupportedOperationException("Not implemented yet!");
|
||||
}
|
||||
|
||||
public void assertIsHoverRegion(String string) {
|
||||
throw new UnsupportedOperationException("Not implemented yet!");
|
||||
}
|
||||
|
||||
public void assertHoverContains(String string, String string2) {
|
||||
throw new UnsupportedOperationException("Not implemented yet!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package org.springframework.ide.vscode.testharness;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
|
||||
import io.typefox.lsapi.ClientCapabilitiesImpl;
|
||||
import io.typefox.lsapi.CompletionItem;
|
||||
import io.typefox.lsapi.CompletionList;
|
||||
import io.typefox.lsapi.Diagnostic;
|
||||
import io.typefox.lsapi.DidChangeTextDocumentParamsImpl;
|
||||
import io.typefox.lsapi.DidOpenTextDocumentParamsImpl;
|
||||
import io.typefox.lsapi.InitializeParamsImpl;
|
||||
import io.typefox.lsapi.InitializeResult;
|
||||
import io.typefox.lsapi.Position;
|
||||
import io.typefox.lsapi.PositionImpl;
|
||||
import io.typefox.lsapi.PublishDiagnosticsParams;
|
||||
import io.typefox.lsapi.Range;
|
||||
import io.typefox.lsapi.ServerCapabilities;
|
||||
import io.typefox.lsapi.TextDocumentContentChangeEventImpl;
|
||||
import io.typefox.lsapi.TextDocumentItemImpl;
|
||||
import io.typefox.lsapi.TextDocumentPositionParamsImpl;
|
||||
import io.typefox.lsapi.VersionedTextDocumentIdentifierImpl;
|
||||
import io.typefox.lsapi.services.LanguageServer;
|
||||
|
||||
public class LanguageServerHarness {
|
||||
|
||||
//Warning this 'harness' is not very good yet. It just implements bare minimum to
|
||||
// be able to test the MyLanguageServer example.
|
||||
|
||||
private Random random = new Random();
|
||||
|
||||
private Callable<? extends LanguageServer> factory;
|
||||
|
||||
private LanguageServer server;
|
||||
|
||||
private InitializeResult initResult;
|
||||
|
||||
private Map<String,TextDocumentInfo> documents = new HashMap<>();
|
||||
private Map<String, PublishDiagnosticsParams> diagnostics = new HashMap<>();
|
||||
|
||||
public LanguageServerHarness(Callable<? extends LanguageServer> factory) throws Exception {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public synchronized TextDocumentInfo getOrReadFile(File file) throws Exception {
|
||||
String uri = file.toURI().toString();
|
||||
TextDocumentInfo d = documents.get(uri);
|
||||
if (d==null) {
|
||||
documents.put(uri, d = readFile(file));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public TextDocumentInfo readFile(File file) throws Exception {
|
||||
byte[] encoded = Files.readAllBytes(file.toPath());
|
||||
String content = new String(encoded, getEncoding());
|
||||
TextDocumentItemImpl document = new TextDocumentItemImpl();
|
||||
document.setText(content);
|
||||
document.setUri(file.toURI().toString());
|
||||
document.setVersion(getFirstVersion());
|
||||
document.setLanguageId(getLanguageId());
|
||||
return new TextDocumentInfo(document);
|
||||
}
|
||||
|
||||
private synchronized TextDocumentItemImpl setDocumentContent(String uri, String newContent) {
|
||||
TextDocumentInfo o = documents.get(uri);
|
||||
TextDocumentItemImpl n = new TextDocumentItemImpl();
|
||||
n.setLanguageId(o.getLanguageId());
|
||||
n.setText(newContent);
|
||||
n.setVersion(o.getVersion()+1);
|
||||
n.setUri(o.getUri());
|
||||
documents.put(uri, new TextDocumentInfo(n));
|
||||
return n;
|
||||
}
|
||||
|
||||
protected Charset getEncoding() {
|
||||
return Charset.forName("utf8");
|
||||
}
|
||||
|
||||
protected String getLanguageId() {
|
||||
return "plaintext";
|
||||
}
|
||||
|
||||
protected String getFileExtension() {
|
||||
return ".txt";
|
||||
}
|
||||
|
||||
private synchronized void receiveDiagnostics(PublishDiagnosticsParams diags) {
|
||||
this.diagnostics.put(diags.getUri(), diags);
|
||||
}
|
||||
|
||||
public InitializeResult intialize(File workspaceRoot) throws Exception {
|
||||
server = factory.call();
|
||||
int parentPid = random.nextInt(40000)+1000;
|
||||
InitializeParamsImpl initParams = new InitializeParamsImpl();
|
||||
initParams.setRootPath(workspaceRoot== null?null:workspaceRoot.toString());
|
||||
initParams.setProcessId(parentPid);
|
||||
ClientCapabilitiesImpl clientCap = new ClientCapabilitiesImpl();
|
||||
initParams.setCapabilities(clientCap);
|
||||
initResult = server.initialize(initParams).get();
|
||||
|
||||
server.getTextDocumentService().onPublishDiagnostics(this::receiveDiagnostics);
|
||||
return initResult;
|
||||
}
|
||||
|
||||
public TextDocumentInfo openDocument(TextDocumentInfo documentInfo) throws Exception {
|
||||
DidOpenTextDocumentParamsImpl didOpen = new DidOpenTextDocumentParamsImpl();
|
||||
didOpen.setTextDocument(documentInfo.getDocument());
|
||||
didOpen.setText(documentInfo.getText());
|
||||
didOpen.setUri(documentInfo.getUri());
|
||||
server.getTextDocumentService().didOpen(didOpen);
|
||||
return documentInfo;
|
||||
}
|
||||
|
||||
public TextDocumentInfo openDocument(File file) throws Exception {
|
||||
return openDocument(getOrReadFile(file));
|
||||
}
|
||||
|
||||
public TextDocumentInfo changeDocument(String uri, String newContent) throws Exception {
|
||||
TextDocumentItemImpl textDocument = setDocumentContent(uri, newContent);
|
||||
DidChangeTextDocumentParamsImpl didChange = new DidChangeTextDocumentParamsImpl();
|
||||
VersionedTextDocumentIdentifierImpl version = new VersionedTextDocumentIdentifierImpl();
|
||||
version.setUri(uri);
|
||||
version.setVersion(textDocument.getVersion());
|
||||
didChange.setTextDocument(version);
|
||||
switch (getDocumentSyncMode()) {
|
||||
case ServerCapabilities.SYNC_NONE:
|
||||
break; //nothing todo
|
||||
case ServerCapabilities.SYNC_INCREMENTAL:
|
||||
throw new IllegalStateException("Incremental sync not yet supported by this test harness");
|
||||
case ServerCapabilities.SYNC_FULL:
|
||||
TextDocumentContentChangeEventImpl change = new TextDocumentContentChangeEventImpl();
|
||||
change.setText(newContent);
|
||||
didChange.setContentChanges(Collections.singletonList(change));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unkown SYNC mode: "+getDocumentSyncMode());
|
||||
}
|
||||
server.getTextDocumentService().didChange(didChange);
|
||||
return documents.get(uri);
|
||||
}
|
||||
|
||||
private int getDocumentSyncMode() {
|
||||
Integer mode = initResult.getCapabilities().getTextDocumentSync();
|
||||
return mode==null ? ServerCapabilities.SYNC_NONE : mode;
|
||||
}
|
||||
|
||||
public PublishDiagnosticsParams getDiagnostics(TextDocumentInfo doc) {
|
||||
return diagnostics.get(doc.getUri());
|
||||
}
|
||||
|
||||
public static Condition<Diagnostic> isDiagnosticWithSeverity(int severity) {
|
||||
return new Condition<>(
|
||||
(d) -> d.getSeverity()==severity,
|
||||
"Diagnostic with severity '"+severity+"'"
|
||||
);
|
||||
}
|
||||
|
||||
public static Condition<Diagnostic> isDiagnosticCovering(TextDocumentInfo doc, String string) {
|
||||
return new Condition<>(
|
||||
(d) -> isDiagnosticCovering(d, doc, string),
|
||||
"Diagnostic covering '"+string+"'"
|
||||
);
|
||||
}
|
||||
|
||||
public static final Condition<Diagnostic> isWarning = isDiagnosticWithSeverity(Diagnostic.SEVERITY_WARNING);
|
||||
|
||||
public static boolean isDiagnosticCovering(Diagnostic diag, TextDocumentInfo doc, String string) {
|
||||
Range rng = diag.getRange();
|
||||
String actualText = doc.getText(rng);
|
||||
return string.equals(actualText);
|
||||
}
|
||||
|
||||
public static Condition<Diagnostic> isDiagnosticOnLine(int line) {
|
||||
return new Condition<>(
|
||||
(d) -> d.getRange().getStart().getLine()==line,
|
||||
"Diagnostic on line "+line
|
||||
);
|
||||
}
|
||||
|
||||
public CompletionList getCompletions(TextDocumentInfo doc, Position cursor) throws Exception {
|
||||
TextDocumentPositionParamsImpl params = new TextDocumentPositionParamsImpl();
|
||||
params.setPosition(toImpl(cursor));
|
||||
params.setTextDocument(doc.getId());
|
||||
return server.getTextDocumentService().completion(params).get();
|
||||
}
|
||||
|
||||
private PositionImpl toImpl(Position pos) {
|
||||
if (pos instanceof PositionImpl) {
|
||||
return (PositionImpl) pos;
|
||||
} else {
|
||||
PositionImpl imp = new PositionImpl();
|
||||
imp.setCharacter(pos.getCharacter());
|
||||
imp.setLine(pos.getLine());
|
||||
return imp;
|
||||
}
|
||||
}
|
||||
|
||||
private CompletionItem resolveCompletionItem(CompletionItem unresolved) {
|
||||
try {
|
||||
return server.getTextDocumentService().resolveCompletionItem(unresolved).get();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<CompletionItem> resolveCompletions(CompletionList completions) {
|
||||
return completions.getItems().stream()
|
||||
.map(this::resolveCompletionItem)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Editor newEditor(String contents) throws Exception {
|
||||
return new Editor(this, contents);
|
||||
}
|
||||
|
||||
public synchronized TextDocumentInfo createWorkingCopy(String contents) throws Exception {
|
||||
TextDocumentItemImpl doc = new TextDocumentItemImpl();
|
||||
doc.setLanguageId(getLanguageId());
|
||||
doc.setText(contents);
|
||||
doc.setUri(createTempUri());
|
||||
doc.setVersion(getFirstVersion());
|
||||
TextDocumentInfo docinfo = new TextDocumentInfo(doc);
|
||||
documents.put(docinfo.getUri(), docinfo);
|
||||
return docinfo;
|
||||
}
|
||||
|
||||
protected int getFirstVersion() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected String createTempUri() throws Exception {
|
||||
return File.createTempFile("workingcopy", getFileExtension()).toURI().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.springframework.ide.vscode.testharness;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.typefox.lsapi.Position;
|
||||
import io.typefox.lsapi.PositionImpl;
|
||||
import io.typefox.lsapi.Range;
|
||||
import io.typefox.lsapi.TextDocumentIdentifierImpl;
|
||||
import io.typefox.lsapi.TextDocumentItemImpl;
|
||||
|
||||
public class TextDocumentInfo {
|
||||
|
||||
Pattern NEWLINE = Pattern.compile("\\r|\\n|\\r\\n|\\n\\r");
|
||||
|
||||
private final TextDocumentItemImpl document;
|
||||
|
||||
private int[] _lineStarts;
|
||||
|
||||
public TextDocumentInfo(TextDocumentItemImpl document) {
|
||||
this.document = document;
|
||||
}
|
||||
|
||||
public String getLanguageId() {
|
||||
return getDocument().getLanguageId();
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return getDocument().getVersion();
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return getDocument().getText();
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return getDocument().getUri();
|
||||
}
|
||||
|
||||
public TextDocumentItemImpl getDocument() {
|
||||
return document;
|
||||
}
|
||||
|
||||
public String getText(Range rng) {
|
||||
int start = toOffset(rng.getStart());
|
||||
int end = toOffset(rng.getEnd());
|
||||
return getText().substring(start, end);
|
||||
}
|
||||
|
||||
public int toOffset(Position p) {
|
||||
int startOfLine = startOfLine(p.getLine());
|
||||
return startOfLine+p.getCharacter();
|
||||
}
|
||||
|
||||
private int startOfLine(int line) {
|
||||
return lineStarts()[line];
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the (first) position of a given text snippet in the
|
||||
* document.
|
||||
*
|
||||
* @return The position, or null if the snippet can't be found.
|
||||
*/
|
||||
public Position positionOf(String snippet) {
|
||||
int offset = getText().indexOf(snippet);
|
||||
if (offset>=0) {
|
||||
return toPosition(offset);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Position 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Could use binary search which is faster
|
||||
int lineNumber = 0;
|
||||
for (int i = 0; i < lineStarts.length; i++) {
|
||||
if (lineStarts[i]<=offset) {
|
||||
lineNumber = i;
|
||||
} else {
|
||||
return lineNumber;
|
||||
}
|
||||
}
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public TextDocumentIdentifierImpl getId() {
|
||||
TextDocumentIdentifierImpl id = new TextDocumentIdentifierImpl();
|
||||
id.setUri(getUri());
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,12 +7,18 @@
|
||||
<artifactId>commons-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>Parent pom for headless services written in Java</name>
|
||||
<name>commons-parent</name>
|
||||
|
||||
<modules>
|
||||
<module>language-server-commons</module>
|
||||
<module>language-server-test-harness</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<junit-version>4.11</junit-version>
|
||||
<assertj-version>3.5.2</assertj-version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
@@ -32,13 +38,13 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<version>${junit-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.5.2</version>
|
||||
<version>${assertj-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
Reference in New Issue
Block a user