use of org.eclipse.lsp4j.SemanticTokens in project redmatch by aehrc.
the class SemanticTokeniserTest method testTokeniseInvalid.
@Test
public void testTokeniseInvalid() {
String rule = "SCHEMA: 'src/test/resources/schema.json' (REDCAP)\n" + "\n" + "BALUE!";
printTokens(rule);
// Encoded
List<Integer> expectedTokens = Arrays.asList(0, 0, 6, 0, 0, 0, 8, 32, 2, 0, 0, 34, 6, 2, 0, 2, 0, 5, 2, 0);
SemanticTokens semanticTokens = SemanticTokeniser.tokenise(rule);
List<Integer> actualTokens = semanticTokens.getData();
assertEquals(expectedTokens, actualTokens);
}
use of org.eclipse.lsp4j.SemanticTokens in project redmatch by aehrc.
the class SemanticTokeniserTest method testTokeniseLong.
@Test
public void testTokeniseLong() {
String rule = "SCHEMA: 'schema.csv' (REDCAP)\n" + "\n" + "SERVER: 'local'\n" + "\n" + "RULES: {\n" + " TRUE { \n" + " Patient<p>:\n" + " * identifier.type = CONCEPT_LITERAL(http://hl7.org/fhir/v2/0203|MC)\n" + " * identifier.type.text = 'Medicare Number'\n" + " * identifier.system = 'http://ns.electronichealth.net.au/id/medicare-number'\n" + " * identifier.value = VALUE(pat_medicare)\n" + " }\n" + "\n" + " VALUE(pat_dead) = 1 {\n" + " NOTNULL(pat_dead_date) {\n" + " Patient<p>: * deceasedDateTime = VALUE(pat_dead_date)\n" + " } ELSE {\n" + " Patient<p>: * deceasedBoolean = TRUE\n" + " }\n" + " }\n" + "\n" + " VALUE(pat_dead) = 0 {\n" + " Patient<p>: * deceasedBoolean = FALSE\n" + " }\n" + "\n" + " NOTNULL(pat_sex) {\n" + " Observation<sex>:\n" + " * code = CONCEPT_LITERAL(http://snomed.info/sct|429019009)\n" + " * valueCodeableConcept = CONCEPT_SELECTED(pat_sex)\n" + " * subject = REF(Patient<p>)\n" + " }\n" + "\n" + " VALUE(dx_num) > 0 {\n" + " REPEAT(1..2: x)\n" + " NOTNULL(dx_${x}) {\n" + " VALUE(dx_${x}) = '_NRF_' {\n" + " Condition<c${x}>: \n" + " * code.text = VALUE(dx_text_${x})\n" + " * subject = REF(Patient<p>)\n" + " } ELSE {\n" + " Condition<c${x}>: \n" + " * code = CONCEPT(dx_${x})\n" + " * subject = REF(Patient<p>)\n" + " }\n" + " }\n" + " }\n" + "\n" + " REPEAT(1..4: x)\n" + " NOTNULL(phenotype___${x}) {\n" + " Observation<obs${x}>:\n" + " * status = CODE(final)\n" + " * code = CONCEPT(phenotype___${x})\n" + " * interpretation = CONCEPT_LITERAL(" + "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation|POS)\n" + " }\n" + "}\n" + "\n" + "MAPPINGS: {\n" + " pat_sex___1 -> http://snomed.info/sct|248153007;\n" + " pat_sex___2 -> http://snomed.info/sct|248152002;\n" + " phenotype___1 -> http://purl.obolibrary.org/obo/hp.owl|HP:0001558;\n" + " phenotype___2 -> http://purl.obolibrary.org/obo/hp.owl|HP:0001270;\n" + " phenotype___3 -> http://purl.obolibrary.org/obo/hp.owl|HP:0031910;\n" + " phenotype___4 -> http://purl.obolibrary.org/obo/hp.owl|HP:0012587;\n" + "}";
printTokens(rule);
// Encoded
List<Integer> expectedTokens = Arrays.asList(0, 0, 6, 0, 0, 0, 8, 12, 2, 0, 0, 14, 6, 2, 0, 2, 0, 6, 0, 0, 0, 8, 7, 2, 0, 2, 0, 5, 0, 0, 1, 2, 4, 0, 0, 1, 4, 7, 7, 0, 0, 7, 1, 4, 0, 0, 1, 1, 7, 0, 0, 1, 1, 4, 0, 1, 6, 1, 5, 0, 0, 2, 10, 5, 0, 0, 11, 4, 5, 0, 0, 5, 1, 4, 0, 0, 2, 15, 0, 0, 0, 16, 27, 2, 0, 0, 27, 1, 4, 0, 0, 1, 2, 2, 0, 1, 6, 1, 5, 0, 0, 2, 10, 5, 0, 0, 11, 4, 5, 0, 0, 5, 4, 5, 0, 0, 5, 1, 4, 0, 0, 2, 17, 2, 0, 1, 6, 1, 5, 0, 0, 2, 10, 5, 0, 0, 11, 6, 5, 0, 0, 7, 1, 4, 0, 0, 2, 54, 2, 0, 1, 6, 1, 5, 0, 0, 2, 10, 5, 0, 0, 11, 5, 5, 0, 0, 6, 1, 4, 0, 0, 2, 5, 0, 0, 0, 6, 12, 7, 0, 3, 2, 5, 0, 0, 0, 6, 8, 7, 0, 0, 10, 1, 4, 0, 0, 2, 1, 3, 0, 1, 4, 7, 0, 0, 0, 8, 13, 7, 0, 1, 6, 7, 7, 0, 0, 7, 1, 4, 0, 0, 1, 1, 7, 0, 0, 1, 1, 4, 0, 0, 3, 1, 5, 0, 0, 2, 16, 5, 0, 0, 17, 1, 4, 0, 0, 2, 5, 0, 0, 0, 6, 13, 7, 0, 1, 6, 4, 0, 0, 1, 6, 7, 7, 0, 0, 7, 1, 4, 0, 0, 1, 1, 7, 0, 0, 1, 1, 4, 0, 0, 3, 1, 5, 0, 0, 2, 15, 5, 0, 0, 16, 1, 4, 0, 0, 2, 4, 0, 0, 4, 2, 5, 0, 0, 0, 6, 8, 7, 0, 0, 10, 1, 4, 0, 0, 2, 1, 3, 0, 1, 4, 7, 7, 0, 0, 7, 1, 4, 0, 0, 1, 1, 7, 0, 0, 1, 1, 4, 0, 0, 3, 1, 5, 0, 0, 2, 15, 5, 0, 0, 16, 1, 4, 0, 0, 2, 5, 0, 0, 3, 2, 7, 0, 0, 0, 8, 7, 7, 0, 1, 4, 11, 7, 0, 0, 11, 1, 4, 0, 0, 1, 3, 7, 0, 0, 3, 1, 4, 0, 1, 6, 1, 5, 0, 0, 2, 4, 5, 0, 0, 5, 1, 4, 0, 0, 2, 15, 0, 0, 0, 16, 22, 2, 0, 0, 22, 1, 4, 0, 0, 1, 9, 2, 0, 1, 6, 1, 5, 0, 0, 2, 20, 5, 0, 0, 21, 1, 4, 0, 0, 2, 16, 0, 0, 0, 17, 7, 7, 0, 1, 6, 1, 5, 0, 0, 2, 7, 5, 0, 0, 8, 1, 4, 0, 0, 2, 3, 0, 0, 0, 4, 7, 7, 0, 0, 7, 1, 4, 0, 0, 1, 1, 7, 0, 0, 1, 1, 4, 0, 3, 2, 5, 0, 0, 0, 6, 6, 7, 0, 0, 8, 1, 4, 0, 0, 2, 1, 3, 0, 1, 4, 6, 0, 0, 0, 7, 1, 3, 0, 0, 3, 1, 3, 0, 0, 3, 1, 7, 0, 1, 4, 7, 0, 0, 0, 8, 7, 7, 0, 1, 6, 5, 0, 0, 0, 6, 7, 7, 0, 0, 9, 1, 4, 0, 0, 2, 7, 2, 0, 1, 8, 9, 7, 0, 0, 9, 1, 4, 0, 0, 1, 5, 7, 0, 0, 5, 1, 4, 0, 1, 10, 1, 5, 0, 0, 2, 4, 5, 0, 0, 5, 4, 5, 0, 0, 5, 1, 4, 0, 0, 2, 5, 0, 0, 0, 6, 12, 7, 0, 1, 10, 1, 5, 0, 0, 2, 7, 5, 0, 0, 8, 1, 4, 0, 0, 2, 3, 0, 0, 0, 4, 7, 7, 0, 0, 7, 1, 4, 0, 0, 1, 1, 7, 0, 0, 1, 1, 4, 0, 1, 10, 4, 0, 0, 1, 8, 9, 7, 0, 0, 9, 1, 4, 0, 0, 1, 5, 7, 0, 0, 5, 1, 4, 0, 1, 10, 1, 5, 0, 0, 2, 4, 5, 0, 0, 5, 1, 4, 0, 0, 2, 7, 0, 0, 0, 8, 7, 7, 0, 1, 10, 1, 5, 0, 0, 2, 7, 5, 0, 0, 8, 1, 4, 0, 0, 2, 3, 0, 0, 0, 4, 7, 7, 0, 0, 7, 1, 4, 0, 0, 1, 1, 7, 0, 0, 1, 1, 4, 0, 5, 2, 6, 0, 0, 0, 7, 1, 3, 0, 0, 3, 1, 3, 0, 0, 3, 1, 7, 0, 1, 2, 7, 0, 0, 0, 8, 16, 7, 0, 1, 4, 11, 7, 0, 0, 11, 1, 4, 0, 0, 1, 7, 7, 0, 0, 7, 1, 4, 0, 1, 6, 1, 5, 0, 0, 2, 6, 5, 0, 0, 7, 1, 4, 0, 0, 2, 4, 0, 0, 0, 5, 5, 7, 0, 1, 6, 1, 5, 0, 0, 2, 4, 5, 0, 0, 5, 1, 4, 0, 0, 2, 7, 0, 0, 0, 8, 16, 7, 0, 1, 6, 1, 5, 0, 0, 2, 14, 5, 0, 0, 15, 1, 4, 0, 0, 2, 15, 0, 0, 0, 16, 66, 2, 0, 0, 66, 1, 4, 0, 0, 1, 3, 2, 0, 4, 0, 8, 0, 0, 1, 2, 11, 7, 0, 0, 12, 2, 4, 0, 0, 3, 22, 2, 0, 0, 22, 1, 4, 0, 0, 1, 9, 2, 0, 1, 2, 11, 7, 0, 0, 12, 2, 4, 0, 0, 3, 22, 2, 0, 0, 22, 1, 4, 0, 0, 1, 9, 2, 0, 1, 2, 13, 7, 0, 0, 14, 2, 4, 0, 0, 3, 37, 2, 0, 0, 37, 1, 4, 0, 0, 1, 10, 2, 0, 1, 2, 13, 7, 0, 0, 14, 2, 4, 0, 0, 3, 37, 2, 0, 0, 37, 1, 4, 0, 0, 1, 10, 2, 0, 1, 2, 13, 7, 0, 0, 14, 2, 4, 0, 0, 3, 37, 2, 0, 0, 37, 1, 4, 0, 0, 1, 10, 2, 0, 1, 2, 13, 7, 0, 0, 14, 2, 4, 0, 0, 3, 37, 2, 0, 0, 37, 1, 4, 0, 0, 1, 10, 2, 0);
SemanticTokens semanticTokens = SemanticTokeniser.tokenise(rule);
List<Integer> actualTokens = semanticTokens.getData();
assertEquals(expectedTokens, actualTokens);
}
use of org.eclipse.lsp4j.SemanticTokens in project lxtk by lxtk-org.
the class PresentationDamagerRepairer method createPresentation.
@Override
public final void createPresentation(TextPresentation presentation, ITypedRegion damage) {
LanguageOperationTarget target = targetSupplier.get();
if (target != null) {
URI documentUri = target.getDocumentUri();
LanguageService languageService = target.getLanguageService();
DocumentSemanticTokensProvider provider = languageService.getDocumentMatcher().getBestMatch(languageService.getDocumentSemanticTokensProviders(), DocumentSemanticTokensProvider::getDocumentSelector, documentUri, target.getLanguageId());
if (provider != null) {
Range range = null;
try {
range = DocumentUtil.toRange(document, damage.getOffset(), damage.getLength());
} catch (BadLocationException e) {
Activator.logError(e);
}
if (range != null) {
DocumentRangeSemanticTokensRequest request = newDocumentRangeSemanticTokensRequest();
request.setProvider(provider);
request.setParams(new SemanticTokensRangeParams(DocumentUri.toTextDocumentIdentifier(documentUri), range));
request.setTimeout(getSemanticTokensRequestTimeout());
request.setMayThrow(false);
SemanticTokens tokens = request.sendAndReceive();
if (tokens != null) {
List<Integer> data = tokens.getData();
if (data.size() % 5 == 0) {
createPresentation(presentation, damage, data, provider.getRegistrationOptions().getLegend());
return;
}
}
}
}
}
// fallback
addStyleRange(presentation, damage.getOffset(), damage.getLength(), defaultTextAttribute);
}
use of org.eclipse.lsp4j.SemanticTokens in project eclipse.jdt.ls by eclipse.
the class SemanticTokensHandler method full.
public static SemanticTokens full(IProgressMonitor monitor, SemanticTokensParams params, DocumentMonitor documentMonitor) {
ITypeRoot typeRoot = JDTUtils.resolveTypeRoot(params.getTextDocument().getUri());
documentMonitor.checkChanged();
if (typeRoot == null || monitor.isCanceled()) {
return new SemanticTokens(Collections.emptyList());
}
JobHelpers.waitForJobs(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor);
documentMonitor.checkChanged();
CompilationUnit root = CoreASTProvider.getInstance().getAST(typeRoot, CoreASTProvider.WAIT_YES, monitor);
documentMonitor.checkChanged();
if (root == null || monitor.isCanceled()) {
return new SemanticTokens(Collections.emptyList());
}
SemanticTokensVisitor collector = new SemanticTokensVisitor(root);
root.accept(collector);
return collector.getSemanticTokens();
}
use of org.eclipse.lsp4j.SemanticTokens in project OberonEmulator by schierlm.
the class Server method getTextDocumentService.
@Override
public TextDocumentService getTextDocumentService() {
return new TextDocumentService() {
@Override
public void didSave(DidSaveTextDocumentParams params) {
}
@Override
public void didOpen(DidOpenTextDocumentParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
if (of == null) {
of = new OberonFile(params.getTextDocument().getUri(), params.getTextDocument().getText());
} else {
of.setContent(params.getTextDocument().getText());
}
openFiles.put(params.getTextDocument().getUri(), of);
fileChanged(of);
}
@Override
public void didClose(DidCloseTextDocumentParams params) {
fileClosed(openFiles.remove(params.getTextDocument().getUri()));
}
@Override
public void didChange(DidChangeTextDocumentParams params) {
OberonFile of = openFiles.get(params.getTextDocument().getUri());
if (params.getContentChanges().size() != 1)
throw new IllegalArgumentException("Incremental changes not supported)");
of.setContent(params.getContentChanges().get(0).getText());
fileChanged(of);
}
@Override
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(DocumentSymbolParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
if (of == null)
return CompletableFuture.completedFuture(new ArrayList<>());
return of.waitWhenDirty(backgroundExecutor, f -> new ArrayList<>(f.getOutline()));
}
@Override
public CompletableFuture<SemanticTokens> semanticTokensFull(SemanticTokensParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
return of.waitWhenDirty(backgroundExecutor, f -> new SemanticTokens(IntStream.of(f.getSemanticTokens()).mapToObj(i -> i).collect(Collectors.toList())));
}
@Override
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams position) {
OberonFile of = fileForURI(position.getTextDocument().getUri());
int pos = of.getRawPos(position.getPosition());
if (pos == -1)
return CompletableFuture.completedFuture(Either.forLeft(new ArrayList<>()));
String prefix = of.getContent().substring(0, pos);
CompletableFuture<Either<List<CompletionItem>, CompletionList>> result = new CompletableFuture<>();
backgroundExecutor.submit(() -> {
try {
List<CompletionItem> completions = bridge.complete(prefix);
result.complete(Either.forLeft(completions));
} catch (Throwable ex) {
ex.printStackTrace();
result.completeExceptionally(ex);
}
});
return result;
}
@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> definition(DefinitionParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
return of.waitWhenDirty(backgroundExecutor, ar -> {
if (debug && params.getPosition().getLine() == 0 && params.getPosition().getCharacter() == 0) {
// validate all references in this file!
int[] errors = { 0 };
for (Identifier id : ar.getIdDefinitions().values()) {
if (id.getKind() == SymbolKind.Module) {
continue;
}
try {
findRangeToDelete(id, ar, new HashSet<Integer>(Arrays.asList(id.getEndPos())));
} catch (RuntimeException ex) {
System.err.println("Unable to find delete range for pos " + id.getEndPos());
errors[0]++;
}
}
for (Identifier id : ar.getIdReferences().values()) {
String desc_ = id.getDefinition().toString();
IdentifierReference definition = lookupSymbolRef(id.getDefinition());
if (definition == null) {
System.err.println(desc_ + ": symbol reference lookup failed");
continue;
} else if (!definition.equals(id.getDefinition())) {
desc_ += "->" + definition;
}
desc_ += " '" + of.getContent().substring(id.getStartPos(), id.getEndPos()) + "'";
String desc = desc_;
final boolean[] found = { false };
for (OberonFile dof : allFiles(definition.getModule(), false)) {
try {
dof.waitWhenDirty(backgroundExecutor, dar -> {
if (dar.getModuleName().equals(definition.getModule())) {
found[0] = true;
if (!dar.getIdDefinitions().containsKey(definition.getEndPos())) {
System.err.println(desc + ": identifier not found");
errors[0]++;
}
}
return null;
}).get();
} catch (InterruptedException | ExecutionException ex) {
}
}
if (!found[0]) {
System.err.println(desc + ": module not loaded");
errors[0]++;
}
}
System.err.println("Reference validation finished, " + errors[0] + " errors detected.");
}
int pos = of.getRawPos(params.getPosition());
Identifier id = findAt(ar.getIdReferences(), pos);
return id != null ? id.getDefinition() : null;
}).thenApply((IdentifierReference sref) -> {
List<Location> result = new ArrayList<>();
if (sref != null) {
IdentifierReference definition = lookupSymbolRef(sref);
if (definition != null) {
Location loc = buildLocation(of, of.getCachedModuleName(), definition);
if (loc != null) {
result.add(loc);
}
}
}
return Either.forLeft(result);
});
}
private CallHierarchyItem buildCallHierarchyItem(OberonFile of, AnalysisResult ar, Identifier id) {
CallHierarchyItem chi = new CallHierarchyItem();
chi.setName(of.getContent().substring(id.getStartPos(), id.getEndPos()));
chi.setKind(id.getKind());
chi.setData(new Two<>(ar.getModuleName(), id.getEndPos()));
Range r = new Range(of.getPos(id.getStartPos()), of.getPos(id.getEndPos()));
chi.setSelectionRange(r);
if (id.getKind() == SymbolKind.Function) {
int[] func = findFunction(ar.getFunctionRanges(), id.getEndPos());
if (func != null && func[1] == id.getEndPos()) {
r = new Range(of.getPos(func[0]), of.getPos(func[1]));
}
}
chi.setRange(r);
return chi;
}
private Location buildLocation(OberonFile baseFile, String baseModule, IdentifierReference definition) {
String uri = null;
for (OberonFile of : allFiles(definition.getModule(), false)) {
try {
Either<Location, String> loc = of.<Either<Location, String>>waitWhenDirty(backgroundExecutor, ar -> {
if (ar.getModuleName().equals(definition.getModule())) {
Identifier id = ar.getIdDefinitions().get(definition.getEndPos());
if (id != null) {
return Either.forLeft(new Location(of.getUri(), new Range(of.getPos(id.getStartPos()), of.getPos(id.getEndPos()))));
}
return Either.forRight(of.getUri());
}
return null;
}).get();
if (loc != null && loc.isLeft())
return loc.getLeft();
else if (loc != null && loc.isRight())
uri = loc.getRight();
} catch (InterruptedException | ExecutionException ex) {
}
}
if (uri == null) {
String baseUri = baseFile.getUri();
int pos = baseUri.lastIndexOf("/" + baseModule + ".");
if (pos != -1) {
uri = baseUri.substring(0, pos + 1) + definition.getModule() + baseUri.substring(pos + baseModule.length() + 1);
}
}
Location location = uri == null ? null : new Location(uri, new Range(new Position(0, 0), new Position(0, 0)));
return location;
}
@Override
public CompletableFuture<List<? extends Location>> references(ReferenceParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
return of.waitWhenDirty(backgroundExecutor, ar -> {
int pos = of.getRawPos(params.getPosition());
Identifier def = findAt(ar.getIdDefinitions(), pos);
if (def != null) {
return new IdentifierReference(def.isExported() ? ar.getModuleName() : "<local>", def.getEndPos());
}
Identifier ref = findAt(ar.getIdReferences(), pos);
if (ref != null) {
if (ref.getDefinition().getModule().equals(ar.getModuleName())) {
if (!ar.getIdDefinitions().get(ref.getDefinition().getEndPos()).isExported() && ref.getDefinition().getEndPos() >= 0) {
return new IdentifierReference("<local>", ref.getDefinition().getEndPos());
}
}
return ref.getDefinition();
}
return (IdentifierReference) null;
}).thenApply(ref -> {
List<Location> result = new ArrayList<>();
if (ref == null)
return result;
IdentifierReference pendingRef = lookupSymbolRef(ref);
if (pendingRef == null)
return result;
List<IdentifierReference> pendingRefs = new ArrayList<>();
pendingRefs.add(ref);
while (!pendingRefs.isEmpty()) {
IdentifierReference reference = pendingRefs.remove(0);
if (reference == null)
continue;
Collection<OberonFile> files;
String defModuleLocal = reference.getModule();
if (defModuleLocal.equals("<local>")) {
defModuleLocal = of.getCachedModuleName();
files = Arrays.asList(of);
} else {
files = allFiles(defModuleLocal, true);
}
final String defModule = defModuleLocal;
for (OberonFile of2 : files) {
of2.waitToAddWhenDirty(result, backgroundExecutor, ar2 -> {
List<Location> locations = new ArrayList<>();
if (ar2.getModuleName().equals(defModule)) {
Identifier rid = findAt(ar2.getIdDefinitions(), reference.getEndPos());
if (rid != null) {
locations.add(new Location(of2.getUri(), new Range(of2.getPos(rid.getStartPos()), of2.getPos(rid.getEndPos()))));
}
}
Map<Integer, List<Integer>> modRefs = ar2.getModuleDeps().get(defModule);
if (modRefs != null) {
List<Integer> refs = modRefs.get(reference.getEndPos());
if (refs != null) {
for (Integer rend : refs) {
if (rend < 0) {
pendingRefs.add(new IdentifierReference(ar2.getModuleName(), rend));
} else {
Identifier rid = ar2.getIdReferences().get(rend);
if (rid != null) {
locations.add(new Location(of2.getUri(), new Range(of2.getPos(rid.getStartPos()), of2.getPos(rid.getEndPos()))));
}
}
}
}
}
return locations;
});
}
}
return result;
});
}
@Override
public CompletableFuture<List<? extends DocumentHighlight>> documentHighlight(DocumentHighlightParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
if (of == null)
return CompletableFuture.completedFuture(new ArrayList<>());
return of.waitWhenDirty(backgroundExecutor, ar -> {
int pos = of.getRawPos(params.getPosition());
IdentifierReference defReference;
Identifier def = findAt(ar.getIdDefinitions(), pos);
if (def != null) {
defReference = new IdentifierReference(ar.getModuleName(), def.getEndPos());
} else {
Identifier ref = findAt(ar.getIdReferences(), pos);
if (ref != null) {
defReference = ref.getDefinition();
} else {
defReference = null;
}
}
List<DocumentHighlight> result = new ArrayList<>();
if (defReference == null)
return result;
if (ar.getModuleName().equals(defReference.getModule())) {
Identifier rid = findAt(ar.getIdDefinitions(), defReference.getEndPos());
if (rid != null) {
result.add(new DocumentHighlight(new Range(of.getPos(rid.getStartPos()), of.getPos(rid.getEndPos())), DocumentHighlightKind.Read));
}
}
Map<Integer, List<Integer>> modRefs = ar.getModuleDeps().get(defReference.getModule());
if (modRefs != null) {
List<Integer> refs = modRefs.get(defReference.getEndPos());
if (refs != null) {
for (Integer rend : refs) {
if (rend < 0) {
continue;
}
Identifier rid = ar.getIdReferences().get(rend);
if (rid != null)
result.add(new DocumentHighlight(new Range(of.getPos(rid.getStartPos()), of.getPos(rid.getEndPos())), rid.isWrittenTo() ? DocumentHighlightKind.Write : DocumentHighlightKind.Read));
}
}
}
return result;
});
}
private CompletableFuture<List<Range>> editingRanges(String uri, Position position) {
OberonFile of = fileForURI(uri);
return of.waitWhenDirty(backgroundExecutor, ar -> {
final List<Range> ranges = new ArrayList<>();
int pos = of.getRawPos(position);
IdentifierReference defReference = null;
Identifier definition = findAt(ar.getIdDefinitions(), pos);
if (definition != null) {
defReference = new IdentifierReference(ar.getModuleName(), definition.getEndPos());
} else {
Identifier ref = findAt(ar.getIdReferences(), pos);
if (ref != null) {
defReference = ref.getDefinition();
}
}
if (defReference != null && defReference.getModule().equals(ar.getModuleName())) {
if (ar.getIdReferences().containsKey(defReference.getEndPos())) {
// do not allow to rename unaliased IMPORTs!
return ranges;
}
Identifier id = ar.getIdDefinitions().get(defReference.getEndPos());
if (id.isExported()) {
// do not allow to rename exported definitions
return ranges;
}
ranges.add(new Range(of.getPos(id.getStartPos()), of.getPos(id.getEndPos())));
Map<Integer, List<Integer>> modRefs = ar.getModuleDeps().get(defReference.getModule());
if (modRefs != null) {
List<Integer> refs = modRefs.get(defReference.getEndPos());
if (refs != null) {
for (Integer rend : refs) {
if (rend < 0)
continue;
Identifier ref = ar.getIdReferences().get(rend);
ranges.add(new Range(of.getPos(ref.getStartPos()), of.getPos(ref.getEndPos())));
}
}
}
}
return ranges;
});
}
@Override
public CompletableFuture<LinkedEditingRanges> linkedEditingRange(LinkedEditingRangeParams params) {
return editingRanges(params.getTextDocument().getUri(), params.getPosition()).thenApply(LinkedEditingRanges::new);
}
private boolean positionBefore(Position p1, Position p2) {
return p1.getLine() < p2.getLine() || (p1.getLine() == p2.getLine() && p1.getCharacter() <= p2.getCharacter());
}
@Override
public CompletableFuture<Either<Range, PrepareRenameResult>> prepareRename(PrepareRenameParams params) {
return editingRanges(params.getTextDocument().getUri(), params.getPosition()).thenApply(rs -> {
for (Range r : rs) {
if (positionBefore(r.getStart(), params.getPosition()) && positionBefore(params.getPosition(), r.getEnd()))
return Either.forLeft(r);
}
return null;
});
}
@Override
public CompletableFuture<WorkspaceEdit> rename(RenameParams params) {
return editingRanges(params.getTextDocument().getUri(), params.getPosition()).thenApply(rs -> {
List<TextEdit> edits = new ArrayList<>();
for (Range r : rs) {
edits.add(new TextEdit(r, params.getNewName()));
}
Map<String, List<TextEdit>> changes = new HashMap<>();
changes.put(params.getTextDocument().getUri(), edits);
return new WorkspaceEdit(changes);
});
}
@Override
public CompletableFuture<SignatureHelp> signatureHelp(SignatureHelpParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
CompletableFuture<SignatureHelp> result = new CompletableFuture<>();
backgroundExecutor.submit(new Callable<Void>() {
public Void call() throws Exception {
Thread.sleep(200);
of.waitWhenDirty(backgroundExecutor, ar -> {
int paramIndex = 0, startPos = -1;
{
SortedMap<Integer, ParamTag> tagsBefore = ar.getParamTags().headMap(of.getRawPos(params.getPosition()) + 1);
int depth = 0;
loop: while (!tagsBefore.isEmpty()) {
int p = tagsBefore.lastKey();
ParamTag t = tagsBefore.get(p);
tagsBefore = tagsBefore.headMap(p);
switch(t) {
case CALL_START:
if (depth == 0) {
startPos = p - 1;
break loop;
}
depth--;
break;
case END:
depth++;
break;
case NEXT:
if (depth == 0)
paramIndex++;
break;
case END_LAST:
case PROC_START:
default:
break loop;
}
}
}
if (startPos == -1)
return new Two<Integer, IdentifierReference>(paramIndex, null);
while (startPos > 0 && of.getContent().charAt(startPos - 1) <= ' ') startPos--;
Identifier refid = ar.getIdReferences().get(startPos);
return new Two<>(paramIndex, refid.getDefinition());
}).thenApply(pair -> {
final int paramIndex_ = pair.getFirst();
IdentifierReference ref = lookupSymbolRef(pair.getSecond());
if (ref == null)
return null;
for (OberonFile of2 : allFiles(ref.getModule(), false)) {
try {
SignatureHelp sh = of2.waitWhenDirty(backgroundExecutor, ar2 -> {
if (ar2.getModuleName().equals(ref.getModule())) {
Identifier id = ar2.getIdDefinitions().get(ref.getEndPos());
if (id != null) {
int ssPos = id.getStartPos();
SortedMap<Integer, ParamTag> tagsAfter = ar2.getParamTags().tailMap(ssPos);
int p = tagsAfter.firstKey();
ParamTag t = tagsAfter.get(p);
if (t != ParamTag.PROC_START)
return null;
int psPos = p;
tagsAfter = tagsAfter.tailMap(p + 1);
List<ParameterInformation> pis = new ArrayList<>();
while (!tagsAfter.isEmpty()) {
p = tagsAfter.firstKey();
t = tagsAfter.get(p);
tagsAfter = tagsAfter.tailMap(p + 1);
if (t != ParamTag.NEXT && t != ParamTag.END && t != ParamTag.END_LAST)
return null;
ParameterInformation pi = new ParameterInformation();
pi.setLabel(new Two<Integer, Integer>(psPos - ssPos, p - 1 - ssPos));
pis.add(pi);
psPos = p;
if (t != ParamTag.NEXT)
break;
}
SignatureInformation si = new SignatureInformation(of2.getContent().substring(ssPos, psPos));
si.setParameters(pis);
return new SignatureHelp(Arrays.asList(si), 0, paramIndex_);
}
}
return null;
}).get();
if (sh != null)
return sh;
} catch (InterruptedException | ExecutionException ex) {
}
}
return null;
}).thenApply(r -> result.complete(r));
return null;
}
});
return result;
}
@Override
public CompletableFuture<List<FoldingRange>> foldingRange(FoldingRangeRequestParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
if (of == null)
return CompletableFuture.completedFuture(new ArrayList<>());
return of.waitWhenDirty(backgroundExecutor, ar -> {
List<FoldingRange> result = new ArrayList<>();
Map<Integer, Integer> lastLineForOuterFunc = new HashMap<>();
for (Map.Entry<Integer, int[]> func : ar.getFunctionRanges().entrySet()) {
int outerFunc = func.getValue().length > 2 ? func.getValue()[2] : -1;
int lastLine = lastLineForOuterFunc.getOrDefault(outerFunc, -1);
int startLine = of.getPos(func.getKey()).getLine();
int endLine = of.getPos(func.getValue()[1]).getLine();
if (startLine > lastLine) {
result.add(new FoldingRange(startLine, endLine));
lastLineForOuterFunc.put(outerFunc, endLine);
}
}
return result;
});
}
@Override
public CompletableFuture<Hover> hover(HoverParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
if (of == null)
return CompletableFuture.completedFuture(null);
return of.waitWhenDirty(backgroundExecutor, ar -> {
int pos = of.getRawPos(params.getPosition());
Identifier def = findAt(ar.getIdDefinitions(), pos);
if (def != null) {
return new Two<>(def, new IdentifierReference(ar.getModuleName(), def.getEndPos()));
}
Identifier ref = findAt(ar.getIdReferences(), pos);
if (ref != null) {
return new Two<>(ref, ref.getDefinition());
}
return new Two<>((Identifier) null, (IdentifierReference) null);
}).thenApply(pair -> {
Identifier id = pair.getFirst();
IdentifierReference def = lookupSymbolRef(pair.getSecond());
List<Hover> hovers = new ArrayList<>();
if (def != null) {
for (OberonFile of2 : allFiles(def.getModule(), false)) {
of2.waitToAddWhenDirty(hovers, backgroundExecutor, ar2 -> {
if (ar2.getModuleName() != null && ar2.getModuleName().equals(def.getModule())) {
Identifier rid = findAt(ar2.getIdDefinitions(), def.getEndPos());
if (rid != null) {
String content = of2.getContent();
int startPos = rid.getStartPos();
while (startPos > 0 && content.charAt(startPos - 1) != '\n') startPos--;
int endPos = rid.getEndPos();
while (endPos < content.length() && content.charAt(endPos) != '\n') endPos++;
return Arrays.asList(new Hover(new MarkupContent(MarkupKind.PLAINTEXT, of2.getContent().substring(startPos, endPos)), new Range(of.getPos(id.getStartPos()), of.getPos(id.getEndPos()))));
}
}
return Collections.emptyList();
});
}
}
if (!hovers.isEmpty()) {
Hover hover = hovers.get(0);
hover.setRange(new Range(of.getPos(id.getStartPos()), of.getPos(id.getEndPos())));
return hover;
}
return null;
});
}
@Override
public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormattingParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
if (of == null)
return CompletableFuture.completedFuture(new ArrayList<>());
String content = of.getContent();
Range fullRange = new Range(of.getPos(0), of.getPos(of.getContent().length()));
CompletableFuture<List<? extends TextEdit>> result = new CompletableFuture<>();
backgroundExecutor.submit(() -> {
String newText;
try {
List<FormatTokenInfo> tokens = bridge.format(content);
OberonFormatter ofo = new OberonFormatter();
int pos = 0;
for (FormatTokenInfo token : tokens) {
if (pos < token.getStartPos()) {
ofo.appendWhitespace(content.substring(pos, token.getStartPos()));
}
ofo.appendToken(content.substring(token.getStartPos(), token.getEndPos()), token);
pos = token.getEndPos();
}
if (pos < content.length())
ofo.appendWhitespace(content.substring(pos));
newText = ofo.getResult();
} catch (Exception ex) {
ex.printStackTrace();
result.complete(new ArrayList<>());
return;
}
result.complete(Arrays.asList(new TextEdit(fullRange, newText)));
});
return result;
}
@Override
public CompletableFuture<List<CallHierarchyItem>> prepareCallHierarchy(CallHierarchyPrepareParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
return of.waitWhenDirty(backgroundExecutor, (Function<AnalysisResult, Either<List<CallHierarchyItem>, IdentifierReference>>) ar -> {
int pos = of.getRawPos(params.getPosition());
Identifier id = findAt(ar.getIdDefinitions(), pos);
IdentifierReference def;
if (id != null) {
def = new IdentifierReference(ar.getModuleName(), id.getEndPos());
} else {
id = findAt(ar.getIdReferences(), pos);
if (id != null) {
def = id.getDefinition();
} else {
int[] func = findFunction(ar.getFunctionRanges(), pos);
if (func != null) {
id = ar.getIdDefinitions().get(func[1]);
if (id != null) {
def = new IdentifierReference(ar.getModuleName(), func[1]);
} else {
return Either.forLeft(Collections.emptyList());
}
} else {
return Either.forLeft(Collections.emptyList());
}
}
}
if (def == null || !def.getModule().equals(ar.getModuleName())) {
return Either.forRight(def);
}
CallHierarchyItem chi = buildCallHierarchyItem(of, ar, id);
return Either.forLeft(Arrays.asList(chi));
}).thenApply((Either<List<CallHierarchyItem>, IdentifierReference> either) -> {
if (either.isLeft())
return either.getLeft();
IdentifierReference ref = lookupSymbolRef(either.getRight());
List<CallHierarchyItem> result = new ArrayList<>();
if (ref == null)
return result;
for (OberonFile of2 : allFiles(ref.getModule(), false)) {
of2.waitToAddWhenDirty(result, backgroundExecutor, ar2 -> {
CallHierarchyItem chi = null;
if (ar2.getModuleName().equals(ref.getModule())) {
Identifier id = ar2.getIdDefinitions().get(ref.getEndPos());
if (id != null)
chi = buildCallHierarchyItem(of2, ar2, id);
}
return chi == null ? Collections.emptyList() : Arrays.asList(chi);
});
}
return result;
});
}
@Override
public CompletableFuture<List<CallHierarchyIncomingCall>> callHierarchyIncomingCalls(CallHierarchyIncomingCallsParams params) {
@SuppressWarnings("unchecked") Two<String, Integer> details = (Two<String, Integer>) params.getItem().getData();
String defModule = details.getFirst();
int defEndPos = details.getSecond();
CompletableFuture<List<CallHierarchyIncomingCall>> ret = new CompletableFuture<>();
backgroundExecutor.submit(() -> {
List<CallHierarchyIncomingCall> result = new ArrayList<>();
for (OberonFile of : allFiles(defModule, true)) {
of.waitToAddWhenDirty(result, backgroundExecutor, ar -> {
Map<Integer, List<Range>> functionCallRanges = new TreeMap<>();
Map<Integer, List<Integer>> modRefs = ar.getModuleDeps().get(defModule);
if (modRefs != null) {
List<Integer> rends = modRefs.get(defEndPos);
if (rends != null) {
for (Integer rend : rends) {
if (rend < 0)
continue;
Identifier rid = ar.getIdReferences().get(rend);
if (rid == null)
continue;
int[] func = findFunction(ar.getFunctionRanges(), rend);
if (func != null) {
functionCallRanges.computeIfAbsent(func[1], x -> new ArrayList<>()).add(new Range(of.getPos(rid.getStartPos()), of.getPos(rid.getEndPos())));
}
}
}
}
List<CallHierarchyIncomingCall> calls = new ArrayList<>();
for (int funcPos : functionCallRanges.keySet()) {
Identifier id = ar.getIdDefinitions().get(funcPos);
calls.add(new CallHierarchyIncomingCall(buildCallHierarchyItem(of, ar, id), functionCallRanges.get(funcPos)));
}
return calls;
});
}
ret.complete(result);
});
return ret;
}
@Override
public CompletableFuture<List<CallHierarchyOutgoingCall>> callHierarchyOutgoingCalls(CallHierarchyOutgoingCallsParams params) {
@SuppressWarnings("unchecked") Two<String, Integer> details = (Two<String, Integer>) params.getItem().getData();
String defModule = details.getFirst();
int defEndPos = details.getSecond();
CompletableFuture<List<CallHierarchyOutgoingCall>> ret = new CompletableFuture<>();
backgroundExecutor.submit(() -> {
List<Map.Entry<String, Map<Integer, List<Range>>>> tempResult = new ArrayList<>();
for (OberonFile of : allFiles(defModule, false)) {
of.waitToAddWhenDirty(tempResult, backgroundExecutor, ar -> {
Map<String, Map<Integer, List<Range>>> functionRefRanges = new TreeMap<>();
if (ar.getModuleName().equals(defModule)) {
Identifier rid = findAt(ar.getIdDefinitions(), defEndPos);
if (rid != null && rid.getKind() == SymbolKind.Function) {
int[] func = findFunction(ar.getFunctionRanges(), defEndPos);
if (func != null && func[1] == defEndPos) {
for (Identifier ref : ar.getIdReferences().subMap(defEndPos, func[2]).values()) {
if (ref.getKind() == SymbolKind.Function) {
IdentifierReference def = lookupSymbolRef(ref.getDefinition());
if (def != null) {
functionRefRanges.computeIfAbsent(def.getModule(), x -> new HashMap<>()).computeIfAbsent(def.getEndPos(), x -> new ArrayList<>()).add(new Range(of.getPos(ref.getStartPos()), of.getPos(ref.getEndPos())));
}
}
}
}
}
}
return new ArrayList<>(functionRefRanges.entrySet());
});
}
List<CallHierarchyOutgoingCall> result = new ArrayList<>();
for (Map.Entry<String, Map<Integer, List<Range>>> entry : tempResult) {
for (OberonFile of : allFiles(entry.getKey(), false)) {
of.waitToAddWhenDirty(result, backgroundExecutor, ar -> {
List<CallHierarchyOutgoingCall> calls = new ArrayList<>();
if (ar.getModuleName().equals(entry.getKey())) {
for (Map.Entry<Integer, List<Range>> positions : entry.getValue().entrySet()) {
Identifier id = ar.getIdDefinitions().get(positions.getKey());
calls.add(new CallHierarchyOutgoingCall(buildCallHierarchyItem(of, ar, id), positions.getValue()));
}
}
return calls;
});
}
}
ret.complete(result);
});
return ret;
}
@Override
public CompletableFuture<List<Either<Command, CodeAction>>> codeAction(CodeActionParams params) {
OberonFile of = fileForURI(params.getTextDocument().getUri());
return of.waitWhenDirty(backgroundExecutor, ar -> {
return fillCodeActions(params, of, ar);
});
}
@Override
public CompletableFuture<CodeAction> resolveCodeAction(CodeAction unresolved) {
return fillResolvedCodeAction(unresolved);
}
};
}
Aggregations