Search in sources :

Example 1 with Identifier

use of lspserver.OberonFile.Identifier in project OberonEmulator by schierlm.

the class Server method fillResolvedCodeAction.

protected CompletableFuture<CodeAction> fillResolvedCodeAction(CodeAction unresolved) {
    JsonArray data = (JsonArray) unresolved.getData();
    if (data.get(0).getAsString().equals("REMOVE")) {
        OberonFile of = fileForURI(data.get(1).getAsString());
        return of.waitWhenDirty(backgroundExecutor, ar -> {
            CodeAction resolved = new CodeAction(unresolved.getTitle());
            resolved.setIsPreferred(unresolved.getIsPreferred());
            resolved.setData(data);
            resolved.setDiagnostics(unresolved.getDiagnostics());
            resolved.setKind(unresolved.getKind());
            List<int[]> deletionRanges = new ArrayList<>();
            Set<Integer> deleteEndPositions = new HashSet<>();
            for (Diagnostic diag : unresolved.getDiagnostics()) {
                Identifier id = ar.getIdDefinitions().get(of.getRawPos(diag.getRange().getEnd()));
                if (id != null)
                    deleteEndPositions.add(id.getEndPos());
            }
            for (Integer endPos : deleteEndPositions) {
                Identifier id = ar.getIdDefinitions().get(endPos);
                deletionRanges.add(findRangeToDelete(id, ar, deleteEndPositions));
            }
            deletionRanges.sort(Comparator.comparing((int[] e) -> e[0]));
            List<TextEdit> edits = new ArrayList<>();
            if (!deletionRanges.isEmpty()) {
                int[] currentRange = deletionRanges.get(0);
                for (int i = 1; i < deletionRanges.size(); i++) {
                    int[] nextRange = deletionRanges.get(i);
                    if (nextRange[0] > currentRange[1]) {
                        edits.add(new TextEdit(new Range(of.getPos(currentRange[0]), of.getPos(currentRange[1])), ""));
                        currentRange = nextRange;
                    } else {
                        currentRange[1] = Math.max(currentRange[1], nextRange[1]);
                    }
                }
                edits.add(new TextEdit(new Range(of.getPos(currentRange[0]), of.getPos(currentRange[1])), ""));
            }
            Map<String, List<TextEdit>> changes = new HashMap<>();
            changes.put(of.getUri(), edits);
            resolved.setEdit(new WorkspaceEdit(changes));
            return resolved;
        });
    }
    return CompletableFuture.completedFuture(unresolved);
}
Also used : ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) CodeAction(org.eclipse.lsp4j.CodeAction) ArrayList(java.util.ArrayList) Diagnostic(org.eclipse.lsp4j.Diagnostic) WorkspaceEdit(org.eclipse.lsp4j.WorkspaceEdit) FoldingRange(org.eclipse.lsp4j.FoldingRange) Range(org.eclipse.lsp4j.Range) JsonArray(com.google.gson.JsonArray) Identifier(lspserver.OberonFile.Identifier) TextEdit(org.eclipse.lsp4j.TextEdit) ArrayList(java.util.ArrayList) List(java.util.List) CompletionList(org.eclipse.lsp4j.CompletionList) HashSet(java.util.HashSet)

Example 2 with Identifier

use of lspserver.OberonFile.Identifier in project OberonEmulator by schierlm.

the class Server method findRangeToDelete.

protected final int[] findRangeToDelete(Identifier id, AnalysisResult ar, Set<Integer> endPosOfDefinitionsToDelete) {
    if (id.getKind() == SymbolKind.Function) {
        int funcStart = ar.getFunctionRanges().headMap(id.getEndPos()).lastKey();
        int[] funcRange = ar.getFunctionRanges().get(funcStart);
        if (funcRange[0] != id.getEndPos())
            throw new NoSuchElementException();
        return new int[] { funcStart, funcRange[1] + 1 };
    } else {
        int declStart, declEnd;
        int[] defList = ar.getDefinitionLists().get(ar.getDefinitionLists().headMap(id.getEndPos()).lastKey());
        SortedMap<Integer, Integer> declMap = ar.getDeclarationBlocks().headMap(id.getEndPos());
        if (declMap.isEmpty()) {
            declStart = defList[0];
            declEnd = defList[2];
        } else {
            declStart = declMap.lastKey();
            declEnd = declMap.get(declStart);
            if (declStart > id.getStartPos() || declEnd < id.getEndPos()) {
                declStart = defList[0];
                declEnd = defList[2];
            }
        }
        SortedMap<Integer, Identifier> otherIDs = findInRange(ar.getIdDefinitions(), defList[0], defList[1]);
        if (!otherIDs.values().stream().allMatch(oid -> endPosOfDefinitionsToDelete.contains(oid.getEndPos()))) {
            // def list contains more variables, so surgically only remove
            // this one
            SortedMap<Integer, Identifier> laterIDs = otherIDs.tailMap(id.getEndPos() + 1);
            SortedMap<Integer, Identifier> earlierIDs = otherIDs.headMap(id.getStartPos() + 1);
            if (!earlierIDs.isEmpty() && !laterIDs.isEmpty() && endPosOfDefinitionsToDelete.contains(laterIDs.firstKey())) {
                laterIDs = Collections.emptySortedMap();
            }
            if (!laterIDs.isEmpty()) {
                // next symbol
                return new int[] { id.getStartPos(), laterIDs.get(laterIDs.firstKey()).getStartPos() };
            } else {
                // to the end of this symbol
                return new int[] { earlierIDs.lastKey(), id.getEndPos() };
            }
        } else {
            // we need to check if other definition lists still exist
            boolean removeWholeDeclarationBlock = true;
            int pos = declStart;
            while (pos < declEnd && !ar.getDefinitionLists().tailMap(pos).isEmpty()) {
                pos = ar.getDefinitionLists().tailMap(pos).firstKey();
                if (pos >= declEnd)
                    break;
                int[] otherList = ar.getDefinitionLists().get(pos);
                pos = otherList[2];
                SortedMap<Integer, Identifier> otherListIDs = findInRange(ar.getIdDefinitions(), otherList[0], otherList[1]);
                if (!otherListIDs.values().stream().allMatch(oid -> endPosOfDefinitionsToDelete.contains(oid.getEndPos()))) {
                    removeWholeDeclarationBlock = false;
                    break;
                }
            }
            if (removeWholeDeclarationBlock)
                return new int[] { declStart, declEnd };
            else
                return new int[] { defList[0], defList[2] };
        }
    }
}
Also used : Arrays(java.util.Arrays) DidChangeTextDocumentParams(org.eclipse.lsp4j.DidChangeTextDocumentParams) LanguageServer(org.eclipse.lsp4j.services.LanguageServer) DidSaveTextDocumentParams(org.eclipse.lsp4j.DidSaveTextDocumentParams) PublishDiagnosticsParams(org.eclipse.lsp4j.PublishDiagnosticsParams) Map(java.util.Map) SemanticTokensParams(org.eclipse.lsp4j.SemanticTokensParams) CallHierarchyIncomingCallsParams(org.eclipse.lsp4j.CallHierarchyIncomingCallsParams) RenameParams(org.eclipse.lsp4j.RenameParams) LinkedEditingRanges(org.eclipse.lsp4j.LinkedEditingRanges) TextDocumentService(org.eclipse.lsp4j.services.TextDocumentService) SignatureHelpOptions(org.eclipse.lsp4j.SignatureHelpOptions) Set(java.util.Set) WorkspaceService(org.eclipse.lsp4j.services.WorkspaceService) LocationLink(org.eclipse.lsp4j.LocationLink) Executors(java.util.concurrent.Executors) SemanticTokensLegend(org.eclipse.lsp4j.SemanticTokensLegend) JsonArray(com.google.gson.JsonArray) DocumentHighlightKind(org.eclipse.lsp4j.DocumentHighlightKind) FoldingRange(org.eclipse.lsp4j.FoldingRange) CallHierarchyPrepareParams(org.eclipse.lsp4j.CallHierarchyPrepareParams) LanguageClientAware(org.eclipse.lsp4j.services.LanguageClientAware) SymbolKind(org.eclipse.lsp4j.SymbolKind) HoverParams(org.eclipse.lsp4j.HoverParams) Identifier(lspserver.OberonFile.Identifier) Callable(java.util.concurrent.Callable) Diagnostic(org.eclipse.lsp4j.Diagnostic) Hover(org.eclipse.lsp4j.Hover) ArrayList(java.util.ArrayList) TextEdit(org.eclipse.lsp4j.TextEdit) CallHierarchyOutgoingCall(org.eclipse.lsp4j.CallHierarchyOutgoingCall) DocumentFormattingParams(org.eclipse.lsp4j.DocumentFormattingParams) AnalysisResult(lspserver.OberonFile.AnalysisResult) InitializeResult(org.eclipse.lsp4j.InitializeResult) DocumentHighlight(org.eclipse.lsp4j.DocumentHighlight) DidChangeWatchedFilesParams(org.eclipse.lsp4j.DidChangeWatchedFilesParams) DocumentHighlightParams(org.eclipse.lsp4j.DocumentHighlightParams) PrepareRenameResult(org.eclipse.lsp4j.PrepareRenameResult) IOException(java.io.IOException) SignatureInformation(org.eclipse.lsp4j.SignatureInformation) ExecutionException(java.util.concurrent.ExecutionException) SignatureHelp(org.eclipse.lsp4j.SignatureHelp) Two(org.eclipse.lsp4j.jsonrpc.messages.Tuple.Two) TreeMap(java.util.TreeMap) WorkspaceEdit(org.eclipse.lsp4j.WorkspaceEdit) RenameOptions(org.eclipse.lsp4j.RenameOptions) CompletionOptions(org.eclipse.lsp4j.CompletionOptions) SemanticTokensServerFull(org.eclipse.lsp4j.SemanticTokensServerFull) SignatureHelpParams(org.eclipse.lsp4j.SignatureHelpParams) LanguageClient(org.eclipse.lsp4j.services.LanguageClient) MarkupKind(org.eclipse.lsp4j.MarkupKind) MessageType(org.eclipse.lsp4j.MessageType) FoldingRangeRequestParams(org.eclipse.lsp4j.FoldingRangeRequestParams) DidChangeWorkspaceFoldersParams(org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams) LinkedEditingRangeParams(org.eclipse.lsp4j.LinkedEditingRangeParams) Location(org.eclipse.lsp4j.Location) Either(org.eclipse.lsp4j.jsonrpc.messages.Either) DidCloseTextDocumentParams(org.eclipse.lsp4j.DidCloseTextDocumentParams) CodeAction(org.eclipse.lsp4j.CodeAction) DiagnosticTag(org.eclipse.lsp4j.DiagnosticTag) MarkupContent(org.eclipse.lsp4j.MarkupContent) SemanticTokens(org.eclipse.lsp4j.SemanticTokens) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) DocumentSymbolOptions(org.eclipse.lsp4j.DocumentSymbolOptions) CodeActionOptions(org.eclipse.lsp4j.CodeActionOptions) PrepareRenameParams(org.eclipse.lsp4j.PrepareRenameParams) Collectors(java.util.stream.Collectors) DefinitionParams(org.eclipse.lsp4j.DefinitionParams) ServerCapabilities(org.eclipse.lsp4j.ServerCapabilities) CompletionItem(org.eclipse.lsp4j.CompletionItem) List(java.util.List) Command(org.eclipse.lsp4j.Command) CallHierarchyOutgoingCallsParams(org.eclipse.lsp4j.CallHierarchyOutgoingCallsParams) DidOpenTextDocumentParams(org.eclipse.lsp4j.DidOpenTextDocumentParams) SemanticTokensWithRegistrationOptions(org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions) InitializeParams(org.eclipse.lsp4j.InitializeParams) SortedMap(java.util.SortedMap) IntStream(java.util.stream.IntStream) CompletionParams(org.eclipse.lsp4j.CompletionParams) ServerInfo(org.eclipse.lsp4j.ServerInfo) FormatTokenInfo(lspserver.OberonFormatter.FormatTokenInfo) DocumentSymbolParams(org.eclipse.lsp4j.DocumentSymbolParams) ParamTag(lspserver.OberonFile.ParamTag) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Range(org.eclipse.lsp4j.Range) Function(java.util.function.Function) CallHierarchyItem(org.eclipse.lsp4j.CallHierarchyItem) SymbolInformation(org.eclipse.lsp4j.SymbolInformation) HashSet(java.util.HashSet) CallHierarchyIncomingCall(org.eclipse.lsp4j.CallHierarchyIncomingCall) CodeActionParams(org.eclipse.lsp4j.CodeActionParams) MessageParams(org.eclipse.lsp4j.MessageParams) Position(org.eclipse.lsp4j.Position) CompletionList(org.eclipse.lsp4j.CompletionList) NoSuchElementException(java.util.NoSuchElementException) DidChangeConfigurationParams(org.eclipse.lsp4j.DidChangeConfigurationParams) ExecutorService(java.util.concurrent.ExecutorService) TextDocumentSyncKind(org.eclipse.lsp4j.TextDocumentSyncKind) ParameterInformation(org.eclipse.lsp4j.ParameterInformation) IdentifierReference(lspserver.OberonFile.IdentifierReference) CodeActionKind(org.eclipse.lsp4j.CodeActionKind) Comparator(java.util.Comparator) Collections(java.util.Collections) DocumentSymbol(org.eclipse.lsp4j.DocumentSymbol) ReferenceParams(org.eclipse.lsp4j.ReferenceParams) Identifier(lspserver.OberonFile.Identifier) NoSuchElementException(java.util.NoSuchElementException)

Example 3 with Identifier

use of lspserver.OberonFile.Identifier in project OberonEmulator by schierlm.

the class Server method getErrors.

protected List<Diagnostic> getErrors(OberonFile of) throws InterruptedException, ExecutionException {
    // be careful here and watch out for deadlocks locking two different
    // OberonFile instances
    Two<List<Diagnostic>, Range> pair = of.waitWhenDirty(backgroundExecutor, ar -> {
        Range range = new Range(new Position(0, 0), new Position(0, 0));
        Identifier moduleDef = ar.getIdDefinitions().get(1);
        if (moduleDef != null && moduleDef.getKind() == SymbolKind.Module) {
            range = new Range(of.getPos(moduleDef.getStartPos()), of.getPos(moduleDef.getEndPos()));
        }
        return new Two<>(ar.getErrors(), range);
    }).get();
    List<Diagnostic> result = pair.getFirst();
    if (of.getCachedModuleName() != null) {
        if (allFiles(of.getCachedModuleName(), false).stream().anyMatch(of2 -> of.getCachedModuleName().equals(of2.getCachedModuleName()) && !of.getNormalizedUri().equals(of2.getNormalizedUri()))) {
            result = new ArrayList<>(result);
            result.add(new Diagnostic(pair.getSecond(), "Same module name used by multiple files"));
        }
    }
    return result;
}
Also used : Arrays(java.util.Arrays) DidChangeTextDocumentParams(org.eclipse.lsp4j.DidChangeTextDocumentParams) LanguageServer(org.eclipse.lsp4j.services.LanguageServer) DidSaveTextDocumentParams(org.eclipse.lsp4j.DidSaveTextDocumentParams) PublishDiagnosticsParams(org.eclipse.lsp4j.PublishDiagnosticsParams) Map(java.util.Map) SemanticTokensParams(org.eclipse.lsp4j.SemanticTokensParams) CallHierarchyIncomingCallsParams(org.eclipse.lsp4j.CallHierarchyIncomingCallsParams) RenameParams(org.eclipse.lsp4j.RenameParams) LinkedEditingRanges(org.eclipse.lsp4j.LinkedEditingRanges) TextDocumentService(org.eclipse.lsp4j.services.TextDocumentService) SignatureHelpOptions(org.eclipse.lsp4j.SignatureHelpOptions) Set(java.util.Set) WorkspaceService(org.eclipse.lsp4j.services.WorkspaceService) LocationLink(org.eclipse.lsp4j.LocationLink) Executors(java.util.concurrent.Executors) SemanticTokensLegend(org.eclipse.lsp4j.SemanticTokensLegend) JsonArray(com.google.gson.JsonArray) DocumentHighlightKind(org.eclipse.lsp4j.DocumentHighlightKind) FoldingRange(org.eclipse.lsp4j.FoldingRange) CallHierarchyPrepareParams(org.eclipse.lsp4j.CallHierarchyPrepareParams) LanguageClientAware(org.eclipse.lsp4j.services.LanguageClientAware) SymbolKind(org.eclipse.lsp4j.SymbolKind) HoverParams(org.eclipse.lsp4j.HoverParams) Identifier(lspserver.OberonFile.Identifier) Callable(java.util.concurrent.Callable) Diagnostic(org.eclipse.lsp4j.Diagnostic) Hover(org.eclipse.lsp4j.Hover) ArrayList(java.util.ArrayList) TextEdit(org.eclipse.lsp4j.TextEdit) CallHierarchyOutgoingCall(org.eclipse.lsp4j.CallHierarchyOutgoingCall) DocumentFormattingParams(org.eclipse.lsp4j.DocumentFormattingParams) AnalysisResult(lspserver.OberonFile.AnalysisResult) InitializeResult(org.eclipse.lsp4j.InitializeResult) DocumentHighlight(org.eclipse.lsp4j.DocumentHighlight) DidChangeWatchedFilesParams(org.eclipse.lsp4j.DidChangeWatchedFilesParams) DocumentHighlightParams(org.eclipse.lsp4j.DocumentHighlightParams) PrepareRenameResult(org.eclipse.lsp4j.PrepareRenameResult) IOException(java.io.IOException) SignatureInformation(org.eclipse.lsp4j.SignatureInformation) ExecutionException(java.util.concurrent.ExecutionException) SignatureHelp(org.eclipse.lsp4j.SignatureHelp) Two(org.eclipse.lsp4j.jsonrpc.messages.Tuple.Two) TreeMap(java.util.TreeMap) WorkspaceEdit(org.eclipse.lsp4j.WorkspaceEdit) RenameOptions(org.eclipse.lsp4j.RenameOptions) CompletionOptions(org.eclipse.lsp4j.CompletionOptions) SemanticTokensServerFull(org.eclipse.lsp4j.SemanticTokensServerFull) SignatureHelpParams(org.eclipse.lsp4j.SignatureHelpParams) LanguageClient(org.eclipse.lsp4j.services.LanguageClient) MarkupKind(org.eclipse.lsp4j.MarkupKind) MessageType(org.eclipse.lsp4j.MessageType) FoldingRangeRequestParams(org.eclipse.lsp4j.FoldingRangeRequestParams) DidChangeWorkspaceFoldersParams(org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams) LinkedEditingRangeParams(org.eclipse.lsp4j.LinkedEditingRangeParams) Location(org.eclipse.lsp4j.Location) Either(org.eclipse.lsp4j.jsonrpc.messages.Either) DidCloseTextDocumentParams(org.eclipse.lsp4j.DidCloseTextDocumentParams) CodeAction(org.eclipse.lsp4j.CodeAction) DiagnosticTag(org.eclipse.lsp4j.DiagnosticTag) MarkupContent(org.eclipse.lsp4j.MarkupContent) SemanticTokens(org.eclipse.lsp4j.SemanticTokens) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) DocumentSymbolOptions(org.eclipse.lsp4j.DocumentSymbolOptions) CodeActionOptions(org.eclipse.lsp4j.CodeActionOptions) PrepareRenameParams(org.eclipse.lsp4j.PrepareRenameParams) Collectors(java.util.stream.Collectors) DefinitionParams(org.eclipse.lsp4j.DefinitionParams) ServerCapabilities(org.eclipse.lsp4j.ServerCapabilities) CompletionItem(org.eclipse.lsp4j.CompletionItem) List(java.util.List) Command(org.eclipse.lsp4j.Command) CallHierarchyOutgoingCallsParams(org.eclipse.lsp4j.CallHierarchyOutgoingCallsParams) DidOpenTextDocumentParams(org.eclipse.lsp4j.DidOpenTextDocumentParams) SemanticTokensWithRegistrationOptions(org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions) InitializeParams(org.eclipse.lsp4j.InitializeParams) SortedMap(java.util.SortedMap) IntStream(java.util.stream.IntStream) CompletionParams(org.eclipse.lsp4j.CompletionParams) ServerInfo(org.eclipse.lsp4j.ServerInfo) FormatTokenInfo(lspserver.OberonFormatter.FormatTokenInfo) DocumentSymbolParams(org.eclipse.lsp4j.DocumentSymbolParams) ParamTag(lspserver.OberonFile.ParamTag) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Range(org.eclipse.lsp4j.Range) Function(java.util.function.Function) CallHierarchyItem(org.eclipse.lsp4j.CallHierarchyItem) SymbolInformation(org.eclipse.lsp4j.SymbolInformation) HashSet(java.util.HashSet) CallHierarchyIncomingCall(org.eclipse.lsp4j.CallHierarchyIncomingCall) CodeActionParams(org.eclipse.lsp4j.CodeActionParams) MessageParams(org.eclipse.lsp4j.MessageParams) Position(org.eclipse.lsp4j.Position) CompletionList(org.eclipse.lsp4j.CompletionList) NoSuchElementException(java.util.NoSuchElementException) DidChangeConfigurationParams(org.eclipse.lsp4j.DidChangeConfigurationParams) ExecutorService(java.util.concurrent.ExecutorService) TextDocumentSyncKind(org.eclipse.lsp4j.TextDocumentSyncKind) ParameterInformation(org.eclipse.lsp4j.ParameterInformation) IdentifierReference(lspserver.OberonFile.IdentifierReference) CodeActionKind(org.eclipse.lsp4j.CodeActionKind) Comparator(java.util.Comparator) Collections(java.util.Collections) DocumentSymbol(org.eclipse.lsp4j.DocumentSymbol) ReferenceParams(org.eclipse.lsp4j.ReferenceParams) Identifier(lspserver.OberonFile.Identifier) Position(org.eclipse.lsp4j.Position) Diagnostic(org.eclipse.lsp4j.Diagnostic) ArrayList(java.util.ArrayList) List(java.util.List) CompletionList(org.eclipse.lsp4j.CompletionList) FoldingRange(org.eclipse.lsp4j.FoldingRange) Range(org.eclipse.lsp4j.Range)

Example 4 with Identifier

use of lspserver.OberonFile.Identifier in project OberonEmulator by schierlm.

the class Server method findInRange.

protected final SortedMap<Integer, Identifier> findInRange(SortedMap<Integer, Identifier> map, int from, int to) {
    SortedMap<Integer, Identifier> result = new TreeMap<>();
    int pos = from - 1;
    while (pos <= to && !map.tailMap(pos + 1).isEmpty()) {
        pos = map.tailMap(pos + 1).firstKey();
        if (pos > to)
            break;
        Identifier id = map.get(pos);
        if (id.getStartPos() >= from && id.getEndPos() <= to)
            result.put(id.getEndPos(), id);
    }
    return result;
}
Also used : Identifier(lspserver.OberonFile.Identifier) TreeMap(java.util.TreeMap)

Example 5 with Identifier

use of lspserver.OberonFile.Identifier 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);
        }
    };
}
Also used : Arrays(java.util.Arrays) DidChangeTextDocumentParams(org.eclipse.lsp4j.DidChangeTextDocumentParams) LanguageServer(org.eclipse.lsp4j.services.LanguageServer) DidSaveTextDocumentParams(org.eclipse.lsp4j.DidSaveTextDocumentParams) PublishDiagnosticsParams(org.eclipse.lsp4j.PublishDiagnosticsParams) Map(java.util.Map) SemanticTokensParams(org.eclipse.lsp4j.SemanticTokensParams) CallHierarchyIncomingCallsParams(org.eclipse.lsp4j.CallHierarchyIncomingCallsParams) RenameParams(org.eclipse.lsp4j.RenameParams) LinkedEditingRanges(org.eclipse.lsp4j.LinkedEditingRanges) TextDocumentService(org.eclipse.lsp4j.services.TextDocumentService) SignatureHelpOptions(org.eclipse.lsp4j.SignatureHelpOptions) Set(java.util.Set) WorkspaceService(org.eclipse.lsp4j.services.WorkspaceService) LocationLink(org.eclipse.lsp4j.LocationLink) Executors(java.util.concurrent.Executors) SemanticTokensLegend(org.eclipse.lsp4j.SemanticTokensLegend) JsonArray(com.google.gson.JsonArray) DocumentHighlightKind(org.eclipse.lsp4j.DocumentHighlightKind) FoldingRange(org.eclipse.lsp4j.FoldingRange) CallHierarchyPrepareParams(org.eclipse.lsp4j.CallHierarchyPrepareParams) LanguageClientAware(org.eclipse.lsp4j.services.LanguageClientAware) SymbolKind(org.eclipse.lsp4j.SymbolKind) HoverParams(org.eclipse.lsp4j.HoverParams) Identifier(lspserver.OberonFile.Identifier) Callable(java.util.concurrent.Callable) Diagnostic(org.eclipse.lsp4j.Diagnostic) Hover(org.eclipse.lsp4j.Hover) ArrayList(java.util.ArrayList) TextEdit(org.eclipse.lsp4j.TextEdit) CallHierarchyOutgoingCall(org.eclipse.lsp4j.CallHierarchyOutgoingCall) DocumentFormattingParams(org.eclipse.lsp4j.DocumentFormattingParams) AnalysisResult(lspserver.OberonFile.AnalysisResult) InitializeResult(org.eclipse.lsp4j.InitializeResult) DocumentHighlight(org.eclipse.lsp4j.DocumentHighlight) DidChangeWatchedFilesParams(org.eclipse.lsp4j.DidChangeWatchedFilesParams) DocumentHighlightParams(org.eclipse.lsp4j.DocumentHighlightParams) PrepareRenameResult(org.eclipse.lsp4j.PrepareRenameResult) IOException(java.io.IOException) SignatureInformation(org.eclipse.lsp4j.SignatureInformation) ExecutionException(java.util.concurrent.ExecutionException) SignatureHelp(org.eclipse.lsp4j.SignatureHelp) Two(org.eclipse.lsp4j.jsonrpc.messages.Tuple.Two) TreeMap(java.util.TreeMap) WorkspaceEdit(org.eclipse.lsp4j.WorkspaceEdit) RenameOptions(org.eclipse.lsp4j.RenameOptions) CompletionOptions(org.eclipse.lsp4j.CompletionOptions) SemanticTokensServerFull(org.eclipse.lsp4j.SemanticTokensServerFull) SignatureHelpParams(org.eclipse.lsp4j.SignatureHelpParams) LanguageClient(org.eclipse.lsp4j.services.LanguageClient) MarkupKind(org.eclipse.lsp4j.MarkupKind) MessageType(org.eclipse.lsp4j.MessageType) FoldingRangeRequestParams(org.eclipse.lsp4j.FoldingRangeRequestParams) DidChangeWorkspaceFoldersParams(org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams) LinkedEditingRangeParams(org.eclipse.lsp4j.LinkedEditingRangeParams) Location(org.eclipse.lsp4j.Location) Either(org.eclipse.lsp4j.jsonrpc.messages.Either) DidCloseTextDocumentParams(org.eclipse.lsp4j.DidCloseTextDocumentParams) CodeAction(org.eclipse.lsp4j.CodeAction) DiagnosticTag(org.eclipse.lsp4j.DiagnosticTag) MarkupContent(org.eclipse.lsp4j.MarkupContent) SemanticTokens(org.eclipse.lsp4j.SemanticTokens) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) DocumentSymbolOptions(org.eclipse.lsp4j.DocumentSymbolOptions) CodeActionOptions(org.eclipse.lsp4j.CodeActionOptions) PrepareRenameParams(org.eclipse.lsp4j.PrepareRenameParams) Collectors(java.util.stream.Collectors) DefinitionParams(org.eclipse.lsp4j.DefinitionParams) ServerCapabilities(org.eclipse.lsp4j.ServerCapabilities) CompletionItem(org.eclipse.lsp4j.CompletionItem) List(java.util.List) Command(org.eclipse.lsp4j.Command) CallHierarchyOutgoingCallsParams(org.eclipse.lsp4j.CallHierarchyOutgoingCallsParams) DidOpenTextDocumentParams(org.eclipse.lsp4j.DidOpenTextDocumentParams) SemanticTokensWithRegistrationOptions(org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions) InitializeParams(org.eclipse.lsp4j.InitializeParams) SortedMap(java.util.SortedMap) IntStream(java.util.stream.IntStream) CompletionParams(org.eclipse.lsp4j.CompletionParams) ServerInfo(org.eclipse.lsp4j.ServerInfo) FormatTokenInfo(lspserver.OberonFormatter.FormatTokenInfo) DocumentSymbolParams(org.eclipse.lsp4j.DocumentSymbolParams) ParamTag(lspserver.OberonFile.ParamTag) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Range(org.eclipse.lsp4j.Range) Function(java.util.function.Function) CallHierarchyItem(org.eclipse.lsp4j.CallHierarchyItem) SymbolInformation(org.eclipse.lsp4j.SymbolInformation) HashSet(java.util.HashSet) CallHierarchyIncomingCall(org.eclipse.lsp4j.CallHierarchyIncomingCall) CodeActionParams(org.eclipse.lsp4j.CodeActionParams) MessageParams(org.eclipse.lsp4j.MessageParams) Position(org.eclipse.lsp4j.Position) CompletionList(org.eclipse.lsp4j.CompletionList) NoSuchElementException(java.util.NoSuchElementException) DidChangeConfigurationParams(org.eclipse.lsp4j.DidChangeConfigurationParams) ExecutorService(java.util.concurrent.ExecutorService) TextDocumentSyncKind(org.eclipse.lsp4j.TextDocumentSyncKind) ParameterInformation(org.eclipse.lsp4j.ParameterInformation) IdentifierReference(lspserver.OberonFile.IdentifierReference) CodeActionKind(org.eclipse.lsp4j.CodeActionKind) Comparator(java.util.Comparator) Collections(java.util.Collections) DocumentSymbol(org.eclipse.lsp4j.DocumentSymbol) ReferenceParams(org.eclipse.lsp4j.ReferenceParams) FormatTokenInfo(lspserver.OberonFormatter.FormatTokenInfo) DocumentFormattingParams(org.eclipse.lsp4j.DocumentFormattingParams) ArrayList(java.util.ArrayList) CallHierarchyItem(org.eclipse.lsp4j.CallHierarchyItem) DidCloseTextDocumentParams(org.eclipse.lsp4j.DidCloseTextDocumentParams) DidOpenTextDocumentParams(org.eclipse.lsp4j.DidOpenTextDocumentParams) CodeActionParams(org.eclipse.lsp4j.CodeActionParams) Position(org.eclipse.lsp4j.Position) DocumentSymbolParams(org.eclipse.lsp4j.DocumentSymbolParams) WorkspaceEdit(org.eclipse.lsp4j.WorkspaceEdit) Two(org.eclipse.lsp4j.jsonrpc.messages.Tuple.Two) FoldingRangeRequestParams(org.eclipse.lsp4j.FoldingRangeRequestParams) SignatureInformation(org.eclipse.lsp4j.SignatureInformation) Hover(org.eclipse.lsp4j.Hover) SortedMap(java.util.SortedMap) Map(java.util.Map) TreeMap(java.util.TreeMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) SortedMap(java.util.SortedMap) HashMap(java.util.HashMap) ParamTag(lspserver.OberonFile.ParamTag) CompletableFuture(java.util.concurrent.CompletableFuture) Either(org.eclipse.lsp4j.jsonrpc.messages.Either) ExecutionException(java.util.concurrent.ExecutionException) CallHierarchyOutgoingCallsParams(org.eclipse.lsp4j.CallHierarchyOutgoingCallsParams) SemanticTokens(org.eclipse.lsp4j.SemanticTokens) DefinitionParams(org.eclipse.lsp4j.DefinitionParams) DocumentHighlight(org.eclipse.lsp4j.DocumentHighlight) TreeMap(java.util.TreeMap) AnalysisResult(lspserver.OberonFile.AnalysisResult) IdentifierReference(lspserver.OberonFile.IdentifierReference) DocumentSymbol(org.eclipse.lsp4j.DocumentSymbol) DidChangeTextDocumentParams(org.eclipse.lsp4j.DidChangeTextDocumentParams) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) SymbolInformation(org.eclipse.lsp4j.SymbolInformation) SemanticTokensParams(org.eclipse.lsp4j.SemanticTokensParams) Identifier(lspserver.OberonFile.Identifier) CompletionParams(org.eclipse.lsp4j.CompletionParams) DocumentHighlightParams(org.eclipse.lsp4j.DocumentHighlightParams) ArrayList(java.util.ArrayList) List(java.util.List) CompletionList(org.eclipse.lsp4j.CompletionList) HashSet(java.util.HashSet) LinkedEditingRanges(org.eclipse.lsp4j.LinkedEditingRanges) DidSaveTextDocumentParams(org.eclipse.lsp4j.DidSaveTextDocumentParams) SignatureHelp(org.eclipse.lsp4j.SignatureHelp) FoldingRange(org.eclipse.lsp4j.FoldingRange) Range(org.eclipse.lsp4j.Range) FoldingRange(org.eclipse.lsp4j.FoldingRange) CallHierarchyIncomingCall(org.eclipse.lsp4j.CallHierarchyIncomingCall) CompletionItem(org.eclipse.lsp4j.CompletionItem) SignatureHelpParams(org.eclipse.lsp4j.SignatureHelpParams) LinkedEditingRangeParams(org.eclipse.lsp4j.LinkedEditingRangeParams) CallHierarchyIncomingCallsParams(org.eclipse.lsp4j.CallHierarchyIncomingCallsParams) ParameterInformation(org.eclipse.lsp4j.ParameterInformation) TextDocumentService(org.eclipse.lsp4j.services.TextDocumentService) Function(java.util.function.Function) ReferenceParams(org.eclipse.lsp4j.ReferenceParams) HoverParams(org.eclipse.lsp4j.HoverParams) RenameParams(org.eclipse.lsp4j.RenameParams) PrepareRenameParams(org.eclipse.lsp4j.PrepareRenameParams) CodeAction(org.eclipse.lsp4j.CodeAction) PrepareRenameParams(org.eclipse.lsp4j.PrepareRenameParams) MarkupContent(org.eclipse.lsp4j.MarkupContent) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) NoSuchElementException(java.util.NoSuchElementException) CallHierarchyPrepareParams(org.eclipse.lsp4j.CallHierarchyPrepareParams) CallHierarchyOutgoingCall(org.eclipse.lsp4j.CallHierarchyOutgoingCall) Command(org.eclipse.lsp4j.Command) TextEdit(org.eclipse.lsp4j.TextEdit) Location(org.eclipse.lsp4j.Location)

Aggregations

Identifier (lspserver.OberonFile.Identifier)7 ArrayList (java.util.ArrayList)5 HashMap (java.util.HashMap)5 List (java.util.List)5 TreeMap (java.util.TreeMap)5 Diagnostic (org.eclipse.lsp4j.Diagnostic)5 Range (org.eclipse.lsp4j.Range)5 JsonArray (com.google.gson.JsonArray)4 IOException (java.io.IOException)4 Arrays (java.util.Arrays)4 HashSet (java.util.HashSet)4 Map (java.util.Map)4 SortedMap (java.util.SortedMap)4 Callable (java.util.concurrent.Callable)4 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)4 AnalysisResult (lspserver.OberonFile.AnalysisResult)4 IdentifierReference (lspserver.OberonFile.IdentifierReference)4 ParamTag (lspserver.OberonFile.ParamTag)4 FormatTokenInfo (lspserver.OberonFormatter.FormatTokenInfo)4 Collection (java.util.Collection)3