Search in sources :

Example 1 with AnalysisResult

use of lspserver.OberonFile.AnalysisResult 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 2 with AnalysisResult

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

the class CachingServer method handlePendingQueue.

private void handlePendingQueue() {
    try {
        // give some time for initialization
        Thread.sleep(2000);
        int progressPos = 0, progressCount = 0;
        while (true) {
            String nextUri;
            synchronized (pendingUris) {
                while (pendingUris.isEmpty()) {
                    pendingUris.wait();
                }
                if (progressPos == progressCount) {
                    progressPos = 0;
                    progressCount = pendingUris.size();
                }
                nextUri = pendingUris.remove(0);
            }
            sendProgress(progressPos, progressCount);
            if (nextUri.startsWith("-file:")) {
                String deleteUri = nextUri.substring(1);
                File deleteCacheFile = new File(cacheDir, OberonFile.hashText(deleteUri) + ".cache");
                if (deleteCacheFile.exists()) {
                    deleteCacheFile.delete();
                }
                removeFromCache(deleteUri);
                PublishDiagnosticsParams diags = new PublishDiagnosticsParams(deleteUri, new ArrayList<>());
                client.publishDiagnostics(diags);
            } else if (nextUri.startsWith("file:")) {
                File file = new File(new URI(nextUri));
                if (file.exists()) {
                    String content = Files.asCharSource(file, StandardCharsets.UTF_8).read();
                    File cacheFile = new File(cacheDir, OberonFile.hashText(nextUri) + ".cache");
                    OberonFile of = cachedFiles.get(nextUri);
                    if (of != null) {
                        of.setContent(content);
                    } else if (cacheFile.exists()) {
                        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(cacheFile))) {
                            String uri = (String) ois.readObject();
                            if (uri.equals(nextUri)) {
                                AnalysisResult ar = (AnalysisResult) ois.readObject();
                                ar.afterDeserialize();
                                of = new OberonFile(uri, ar, content);
                                if (!of.isDirty()) {
                                    updateDiagnostics(of);
                                }
                            }
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                    if (of == null) {
                        of = new OberonFile(nextUri, content);
                    }
                    if (of.isDirty()) {
                        analyzeFile(of, of.getContentVersion());
                    }
                    if (of.getCachedModuleName() != null) {
                        removeFromCache(nextUri);
                        addToCache(nextUri, of);
                    }
                }
            }
            progressPos++;
            sendProgress(progressPos, progressCount);
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}
Also used : PublishDiagnosticsParams(org.eclipse.lsp4j.PublishDiagnosticsParams) IOException(java.io.IOException) File(java.io.File) URI(java.net.URI) FileInputStream(java.io.FileInputStream) AnalysisResult(lspserver.OberonFile.AnalysisResult) URISyntaxException(java.net.URISyntaxException) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) ObjectInputStream(java.io.ObjectInputStream)

Example 3 with AnalysisResult

use of lspserver.OberonFile.AnalysisResult 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)

Example 4 with AnalysisResult

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

the class Server method addCodeAction.

protected void addCodeAction(List<Either<Command, CodeAction>> actions, CodeActionParams params, AnalysisResult ar, JsonArray data, String message, String what) {
    List<Diagnostic> relevantDiags = new ArrayList<>();
    for (Diagnostic diag : params.getContext().getDiagnostics()) {
        if (diag.getTags() != null && diag.getTags().contains(DiagnosticTag.Unnecessary) && diag.getMessage().equals(message)) {
            relevantDiags.add(diag);
        }
    }
    if (!relevantDiags.isEmpty()) {
        CodeAction ca = new CodeAction(relevantDiags.size() == 1 ? "Remove unused " + what : "Remove " + relevantDiags.size() + " unused " + what + "s");
        ca.setIsPreferred(true);
        ca.setData(data);
        ca.setDiagnostics(relevantDiags);
        ca.setKind(CodeActionKind.QuickFix);
        actions.add(Either.forRight(ca));
        List<Diagnostic> allDiags = ar.getErrors().stream().filter(d -> d.getTags() != null && d.getTags().contains(DiagnosticTag.Unnecessary) && d.getMessage().equals(message)).collect(Collectors.toList());
        if (allDiags.size() > 1) {
            ca = new CodeAction("Remove all unused " + what + "s");
            ca.setData(data);
            ca.setDiagnostics(allDiags);
            ca.setKind(CodeActionKind.QuickFix);
            actions.add(Either.forRight(ca));
        }
    }
}
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) CodeAction(org.eclipse.lsp4j.CodeAction) ArrayList(java.util.ArrayList) Diagnostic(org.eclipse.lsp4j.Diagnostic)

Example 5 with AnalysisResult

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

the class Bridge method analyze.

/**
 * Return whether dependent modules should be analyzed again.
 */
public synchronized boolean analyze(OberonFile file, AnalysisResult ar) throws IOException {
    ar.setModuleName(null);
    ar.getModuleDeps().clear();
    ar.getErrors().clear();
    ar.setSemanticTokens(new int[0]);
    ar.getOutline().clear();
    ar.getIdDefinitions().clear();
    ar.getIdReferences().clear();
    ar.getFunctionDefinitions().clear();
    ar.getFunctionRanges().clear();
    ar.getParamTags().clear();
    ar.getExportedSymbolRefs().clear();
    String content = file.getContent();
    ar.setContentHash(OberonFile.hashText(content));
    if (content.length() == 0) {
        ar.getErrors().add(new Diagnostic(new Range(new Position(0, 0), new Position(0, 1)), "Empty file"));
        return false;
    }
    ProtocolConstant.INST_GetModuleInfo.send(dos);
    writeIntLE(content.length());
    dos.write(content.getBytes(StandardCharsets.ISO_8859_1));
    dos.flush();
    boolean result = false, functionPending = false, importAliasPending = false;
    int prevSymbolEnd = 0, lastSymbolEnd = 0, declarationBlockStart = -1, paramDepth = 0;
    List<List<DocumentSymbol>> outlineStack = new ArrayList<>();
    List<Range> procRangeStack = new ArrayList<>();
    List<Integer> functionNamePosStack = new ArrayList<>();
    outlineStack.add(new ArrayList<>());
    SortedMap<Integer, int[]> semanticTokenInformation = new TreeMap<>();
    List<int[]> definitionLists = new ArrayList<>();
    boolean inProcParam = false;
    Identifier lastUndefinedSymbol = null;
    loop: while (true) {
        ProtocolConstant res = ProtocolConstant.read(dis);
        switch(res) {
            case STATUS_OK:
                break loop;
            case STATUS_Invalid:
                throw new IOException("Invalid status received");
            case ANSWER_ModuleName:
                ar.setModuleName(readCStr());
                break;
            case ANSWER_ModuleImport:
                ar.getModuleDeps().putIfAbsent(readCStr(), new HashMap<>());
                break;
            case ANSWER_Error:
                int pos = adjustPos(readIntLE(), content.length());
                String msg = readCStr();
                ar.getErrors().add(new Diagnostic(new Range(file.getPos(pos == 0 ? 0 : pos - 1), file.getPos(pos + 1)), msg));
                break;
            case ANSWER_Warning:
                int pos_W = adjustPos(readIntLE(), content.length());
                if (pos_W == content.length())
                    pos_W--;
                String msg_W = readCStr();
                Diagnostic diag_W = new Diagnostic(new Range(file.getPos(pos_W == 0 ? 0 : pos_W - 1), file.getPos(pos_W + 1)), msg_W);
                diag_W.setSeverity(DiagnosticSeverity.Warning);
                ar.getErrors().add(diag_W);
                break;
            case ANSWER_Information:
                int pos_I = adjustPos(readIntLE(), content.length());
                String msg_I = readCStr();
                Diagnostic diag_I = new Diagnostic(new Range(file.getPos(pos_I == 0 ? 0 : pos_I - 1), file.getPos(pos_I + 1)), msg_I);
                diag_I.setSeverity(DiagnosticSeverity.Information);
                ar.getErrors().add(diag_I);
                break;
            case ANSWER_SymbolFileChanged:
                result = true;
                break;
            case ANSWER_SymbolFileIndex:
                int symIdx = readIntLE();
                String symMod = readCStr();
                int symEndPos = readIntLE();
                if (ar.getExportedSymbolRefs().containsKey(symIdx))
                    throw new IllegalStateException("Duplicate symbol index " + symIdx);
                ar.getExportedSymbolRefs().put(symIdx, new IdentifierReference(symMod, symEndPos));
                break;
            case ANSWER_SyntaxElement:
                int start = readIntLE();
                int end = readIntLE();
                SyntaxElement synElem = SyntaxElement.read(dis);
                int defEnd = readIntLE();
                String defModName = defEnd == -1 ? null : readCStr();
                prevSymbolEnd = lastSymbolEnd;
                lastSymbolEnd = end;
                if (end > file.getContent().length())
                    end = file.getContent().length();
                String name = file.getContent().substring(start, end);
                IdentifierReference definition = defEnd == -1 ? null : new IdentifierReference(defModName, defEnd);
                boolean declaration = defEnd == end && (defModName.equals(ar.getModuleName()) || ar.getModuleName() == null);
                int[] semanticInfo = new int[] { end - start, synElem.tokenType, synElem.tokenModifiers | (declaration ? (1 << TOKEN_MODIFIERS.indexOf(SemanticTokenModifiers.Declaration)) : 0) };
                if (name.contains("\n")) {
                    int currPos = name.indexOf('\n'), lastPos = currPos + 1;
                    semanticInfo[0] = currPos;
                    semanticTokenInformation.put(start + currPos, new int[] { currPos, semanticInfo[1], semanticInfo[2] });
                    while ((currPos = name.indexOf('\n', lastPos)) != -1) {
                        semanticTokenInformation.put(start + currPos, new int[] { currPos - lastPos, semanticInfo[1], semanticInfo[2] });
                        lastPos = currPos + 1;
                    }
                    semanticInfo[0] = name.length() - lastPos;
                }
                semanticTokenInformation.put(end, semanticInfo);
                if (declaration && synElem.kind != null) {
                    List<DocumentSymbol> outline = outlineStack.get(outlineStack.size() - 1);
                    Range range = new Range(file.getPos(start), file.getPos(end));
                    DocumentSymbol ds = new DocumentSymbol(name, synElem.kind, range, range);
                    if (synElem.kind == SymbolKind.Function && functionPending) {
                        functionPending = false;
                        ds.setRange(procRangeStack.get(procRangeStack.size() - 1));
                        ds.setChildren(new ArrayList<>());
                        ar.getFunctionDefinitions().put(end, new OberonFile.Identifier(start, end, synElem.kind, null));
                        ar.getFunctionRanges().get(functionNamePosStack.get(functionNamePosStack.size() - 1))[0] = end;
                        outlineStack.add(ds.getChildren());
                    }
                    outline.add(ds);
                    OberonFile.Identifier newId = new OberonFile.Identifier(start, end, synElem.kind, null);
                    if (inProcParam)
                        newId.setProcedureParameter(true);
                    ar.getIdDefinitions().put(end, newId);
                    if (synElem == SyntaxElement.SynModule && name.equals(defModName) && ar.getModuleName() == null) {
                        ar.getIdDefinitions().put(1, newId);
                    }
                } else if (defEnd != -1 && synElem.kind != null) {
                    OberonFile.Identifier id = new OberonFile.Identifier(start, end, synElem.kind, definition);
                    ar.getIdReferences().put(end, id);
                    ar.getModuleDeps().computeIfAbsent(defModName, x -> new HashMap<>()).computeIfAbsent(defEnd, x -> new ArrayList<>()).add(end);
                    if (defEnd == 1) {
                        if (!defModName.equals(ar.getModuleName())) {
                            // import statement
                            if (importAliasPending) {
                                importAliasPending = false;
                            } else {
                                ar.getIdDefinitions().put(end, id);
                            }
                        } else {
                            // Module END.
                            ar.getModuleDeps().get(defModName).computeIfAbsent(ar.getIdDefinitions().get(1).getEndPos(), x -> new ArrayList<>()).add(end);
                        }
                    }
                } else if (synElem == SyntaxElement.SynUndefined) {
                    lastUndefinedSymbol = new OberonFile.Identifier(start, end, null, null);
                }
                break;
            case ANSWER_ProcedureStart:
                int procPos = lastSymbolEnd - "PROCEDURE".length();
                procRangeStack.add(new Range(file.getPos(procPos), file.getPos(lastSymbolEnd)));
                if (functionNamePosStack.size() == 0) {
                    ar.getFunctionRanges().put(procPos, new int[] { -1, -1 });
                } else {
                    ar.getFunctionRanges().put(procPos, new int[] { -1, -1, functionNamePosStack.get(functionNamePosStack.size() - 1) });
                }
                functionNamePosStack.add(procPos);
                functionPending = true;
                break;
            case ANSWER_ProcedureEnd:
                int pos0 = adjustPos(readIntLE(), file.getContent().length());
                procRangeStack.remove(procRangeStack.size() - 1).setEnd(file.getPos(pos0));
                ar.getFunctionRanges().get(functionNamePosStack.remove(functionNamePosStack.size() - 1))[1] = lastSymbolEnd;
                if (!functionPending) {
                    outlineStack.remove(outlineStack.size() - 1);
                }
                functionPending = false;
                break;
            case ANSWER_ImportAlias:
                // find last reference and remove it
                int lastPos = ar.getIdReferences().lastKey();
                Identifier id = ar.getIdReferences().remove(lastPos);
                ar.getModuleDeps().get(id.getDefinition().getModule()).remove(id.getDefinition().getEndPos());
                if (id.getKind() != SymbolKind.Module) {
                    throw new IllegalStateException("Last symbol to remove is not an imported module");
                }
                importAliasPending = true;
                break;
            case ANSWER_ProcParamStart:
                int pos1 = readIntLE();
                paramDepth++;
                ar.getParamTags().put(pos1, ParamTag.PROC_START);
                inProcParam = true;
                break;
            case ANSWER_CallParamStart:
                int pos2 = readIntLE();
                paramDepth++;
                ar.getParamTags().put(pos2, ParamTag.CALL_START);
                inProcParam = false;
                break;
            case ANSWER_ParamNext:
                int pos3 = readIntLE();
                ar.getParamTags().put(pos3, ParamTag.NEXT);
                break;
            case ANSWER_ParamEnd:
                int pos4 = readIntLE();
                paramDepth--;
                ar.getParamTags().put(pos4, paramDepth == 0 ? ParamTag.END_LAST : ParamTag.END);
                if (paramDepth == 0)
                    inProcParam = false;
                break;
            case ANSWER_ForwardPointer:
                if (lastUndefinedSymbol != null && lastUndefinedSymbol.getEndPos() == lastSymbolEnd) {
                    semanticTokenInformation.get(lastSymbolEnd)[1] = SyntaxElement.SynType.tokenType;
                    ar.getIdReferences().put(lastSymbolEnd, new OberonFile.Identifier(lastUndefinedSymbol.getStartPos(), lastSymbolEnd, SyntaxElement.SynType.kind, new IdentifierReference(ar.getModuleName(), lastSymbolEnd)));
                }
                break;
            case ANSWER_ForwardPointerFixup:
                int pointerPos = readIntLE();
                int targetPos = readIntLE();
                OberonFile.Identifier pointerId = ar.getIdReferences().get(pointerPos);
                pointerId.setDefinition(new IdentifierReference(pointerId.getDefinition().getModule(), targetPos));
                ar.getModuleDeps().computeIfAbsent(pointerId.getDefinition().getModule(), x -> new HashMap<>()).computeIfAbsent(targetPos, x -> new ArrayList<>()).add(pointerPos);
                break;
            case ANSWER_VarModified:
                Identifier varId = ar.getIdReferences().get(lastSymbolEnd);
                if (varId != null) {
                    int tokenType = semanticTokenInformation.get(lastSymbolEnd)[1];
                    if (tokenType == SyntaxElement.SynVariable.tokenType || tokenType == SyntaxElement.SynParameter.tokenType) {
                        semanticTokenInformation.get(lastSymbolEnd)[2] |= 1 << TOKEN_MODIFIERS.indexOf(SemanticTokenModifiers.Modification);
                    }
                    varId.setWrittenTo(true);
                }
                break;
            case ANSWER_NameExported:
                Identifier expoId = ar.getIdDefinitions().get(prevSymbolEnd);
                if (expoId != null) {
                    expoId.setExportedPos(lastSymbolEnd);
                }
                break;
            case ANSWER_DefinitionRepeat:
                Identifier repeatId = ar.getIdReferences().get(lastSymbolEnd);
                if (repeatId != null) {
                    repeatId.setDefinitionRepeat(true);
                }
                break;
            case ANSWER_DefinitionUsed:
                Identifier usedId = ar.getIdDefinitions().get(lastSymbolEnd);
                if (usedId != null) {
                    usedId.setUsed(true);
                }
                break;
            case ANSWER_DeclarationBlockStart:
                if (declarationBlockStart != -1)
                    throw new IllegalStateException("Nested declaration blocks");
                declarationBlockStart = readIntLE();
                break;
            case ANSWER_DeclarationBlockEnd:
                if (declarationBlockStart == -1)
                    throw new IllegalStateException("No declaration block open");
                int declarationBlockEnd = readIntLE();
                ar.getDeclarationBlocks().put(declarationBlockStart, declarationBlockEnd);
                declarationBlockStart = -1;
                break;
            case ANSWER_DefinitionListStart:
                int[] definitionListS = new int[] { readIntLE(), -1, -1 };
                definitionLists.add(definitionListS);
                ar.getDefinitionLists().put(definitionListS[0], definitionListS);
                break;
            case ANSWER_DefinitionListValue:
                if (definitionLists.isEmpty())
                    throw new IllegalStateException("No definition list open");
                definitionLists.get(definitionLists.size() - 1)[1] = readIntLE();
                break;
            case ANSWER_DefinitionListEnd:
                if (definitionLists.isEmpty())
                    throw new IllegalStateException("No definition list open");
                int[] definitionListE = definitionLists.remove(definitionLists.size() - 1);
                definitionListE[2] = readIntLE();
                if (definitionListE[1] == -1)
                    definitionListE[1] = definitionListE[2];
                break;
            case ANSWER_RecordStart:
                List<DocumentSymbol> newOutline = new ArrayList<>(), oldOutline = outlineStack.get(outlineStack.size() - 1);
                if (!oldOutline.isEmpty()) {
                    oldOutline.get(oldOutline.size() - 1).setChildren(newOutline);
                }
                outlineStack.add(newOutline);
                break;
            case ANSWER_RecordEnd:
                outlineStack.remove(outlineStack.size() - 1);
                break;
            default:
                throw new IOException("Invalid response: " + res);
        }
    }
    for (int[] pendingDef : definitionLists) {
        ar.getErrors().add(new Diagnostic(new Range(file.getPos(pendingDef[0]), file.getPos(pendingDef[0] + 1)), "LSP: Definition list not closed"));
        ar.getDefinitionLists().remove(pendingDef[0]);
    }
    for (Identifier id : ar.getIdDefinitions().values()) {
        if (id.isExported() || id.isUsed())
            continue;
        if (id.getEndPos() == 1 || id.getEndPos() == ar.getIdDefinitions().get(1).getEndPos())
            continue;
        Map<Integer, List<Integer>> deps = ar.getModuleDeps().get(ar.getModuleName());
        if (deps == null || deps.get(id.getEndPos()) == null || deps.get(id.getEndPos()).stream().map(ep -> ar.getIdReferences().get(ep)).allMatch(rid -> rid.isDefinitionRepeat())) {
            Diagnostic diag = new Diagnostic(new Range(file.getPos(id.getStartPos()), file.getPos(id.getEndPos())), id.isProcedureParameter() ? OberonFile.UNUSED_PARAM : OberonFile.UNUSED_DEFINITION);
            diag.setSeverity(DiagnosticSeverity.Hint);
            diag.setTags(Arrays.asList(DiagnosticTag.Unnecessary));
            ar.getErrors().add(diag);
        }
    }
    int[] semanticTokenBuffer = new int[5 * semanticTokenInformation.size()];
    int semanticTokenLength = 0, semanticTokenLine = 0, semanticTokenChar = 0;
    for (Map.Entry<Integer, int[]> entry : semanticTokenInformation.entrySet()) {
        int start = entry.getKey() - entry.getValue()[0];
        Position startPos = file.getPos(start);
        semanticTokenBuffer[semanticTokenLength++] = startPos.getLine() - semanticTokenLine;
        semanticTokenBuffer[semanticTokenLength++] = startPos.getCharacter() - (startPos.getLine() == semanticTokenLine ? semanticTokenChar : 0);
        semanticTokenBuffer[semanticTokenLength++] = entry.getValue()[0];
        semanticTokenBuffer[semanticTokenLength++] = entry.getValue()[1];
        semanticTokenBuffer[semanticTokenLength++] = entry.getValue()[2];
        semanticTokenLine = startPos.getLine();
        semanticTokenChar = startPos.getCharacter();
    }
    if (semanticTokenLength != semanticTokenBuffer.length)
        throw new IllegalStateException();
    ar.setSemanticTokens(semanticTokenBuffer);
    if (outlineStack.size() == 1) {
        for (DocumentSymbol ds : outlineStack.get(0)) {
            ar.getOutline().add(Either.forRight(ds));
        }
    }
    return result;
}
Also used : DataInputStream(java.io.DataInputStream) Arrays(java.util.Arrays) SemanticTokenModifiers(org.eclipse.lsp4j.SemanticTokenModifiers) SemanticTokenTypes(org.eclipse.lsp4j.SemanticTokenTypes) SymbolKind(org.eclipse.lsp4j.SymbolKind) FormatTokenInfo(lspserver.OberonFormatter.FormatTokenInfo) Identifier(lspserver.OberonFile.Identifier) ParamTag(lspserver.OberonFile.ParamTag) HashMap(java.util.HashMap) Callable(java.util.concurrent.Callable) Diagnostic(org.eclipse.lsp4j.Diagnostic) Range(org.eclipse.lsp4j.Range) ArrayList(java.util.ArrayList) DataOutputStream(java.io.DataOutputStream) CompletionItemKind(org.eclipse.lsp4j.CompletionItemKind) Map(java.util.Map) Either(org.eclipse.lsp4j.jsonrpc.messages.Either) Position(org.eclipse.lsp4j.Position) AnalysisResult(lspserver.OberonFile.AnalysisResult) DiagnosticTag(org.eclipse.lsp4j.DiagnosticTag) DiagnosticSeverity(org.eclipse.lsp4j.DiagnosticSeverity) IOException(java.io.IOException) EOFException(java.io.EOFException) StandardCharsets(java.nio.charset.StandardCharsets) CompletionItem(org.eclipse.lsp4j.CompletionItem) IdentifierReference(lspserver.OberonFile.IdentifierReference) List(java.util.List) TreeMap(java.util.TreeMap) DocumentSymbol(org.eclipse.lsp4j.DocumentSymbol) SortedMap(java.util.SortedMap) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Diagnostic(org.eclipse.lsp4j.Diagnostic) Identifier(lspserver.OberonFile.Identifier) Identifier(lspserver.OberonFile.Identifier) ArrayList(java.util.ArrayList) List(java.util.List) Position(org.eclipse.lsp4j.Position) IOException(java.io.IOException) Range(org.eclipse.lsp4j.Range) TreeMap(java.util.TreeMap) IdentifierReference(lspserver.OberonFile.IdentifierReference) DocumentSymbol(org.eclipse.lsp4j.DocumentSymbol) HashMap(java.util.HashMap) Map(java.util.Map) TreeMap(java.util.TreeMap) SortedMap(java.util.SortedMap)

Aggregations

IOException (java.io.IOException)5 AnalysisResult (lspserver.OberonFile.AnalysisResult)5 ArrayList (java.util.ArrayList)4 Arrays (java.util.Arrays)4 HashMap (java.util.HashMap)4 List (java.util.List)4 Map (java.util.Map)4 SortedMap (java.util.SortedMap)4 TreeMap (java.util.TreeMap)4 Callable (java.util.concurrent.Callable)4 ExecutionException (java.util.concurrent.ExecutionException)4 Identifier (lspserver.OberonFile.Identifier)4 IdentifierReference (lspserver.OberonFile.IdentifierReference)4 ParamTag (lspserver.OberonFile.ParamTag)4 FormatTokenInfo (lspserver.OberonFormatter.FormatTokenInfo)4 JsonArray (com.google.gson.JsonArray)3 Collection (java.util.Collection)3 Collections (java.util.Collections)3 Comparator (java.util.Comparator)3 HashSet (java.util.HashSet)3