From 69573b3b6db0f497c8d0e1795f8d7efc3bcf0eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Thu, 26 Oct 2023 09:18:10 +0200 Subject: [PATCH] Add very basic code completion and server structure --- features/org.eclipse.pde-feature/feature.xml | 7 + ui/org.eclipse.pde.ls/META-INF/MANIFEST.MF | 7 +- ui/org.eclipse.pde.ls/build.properties | 3 +- ui/org.eclipse.pde.ls/plugin.xml | 17 +++ .../pde/ls/BNDLanguageServerClient.java | 78 +++++++++++ .../eclipse/pde/ls/bnd/BNDLanguageServer.java | 92 +++++++++++++ .../pde/ls/bnd/BNDTextDocumentService.java | 126 ++++++++++++++++++ .../pde/ls/bnd/BNDWorkspaceService.java | 37 +++++ 8 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 ui/org.eclipse.pde.ls/plugin.xml create mode 100644 ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/BNDLanguageServerClient.java create mode 100644 ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDLanguageServer.java create mode 100644 ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDTextDocumentService.java create mode 100644 ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDWorkspaceService.java diff --git a/features/org.eclipse.pde-feature/feature.xml b/features/org.eclipse.pde-feature/feature.xml index 0d32f23454..583f28d356 100644 --- a/features/org.eclipse.pde-feature/feature.xml +++ b/features/org.eclipse.pde-feature/feature.xml @@ -159,4 +159,11 @@ version="0.0.0" unpack="false"/> + + diff --git a/ui/org.eclipse.pde.ls/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.ls/META-INF/MANIFEST.MF index bb5e0e8f55..bb0556277b 100644 --- a/ui/org.eclipse.pde.ls/META-INF/MANIFEST.MF +++ b/ui/org.eclipse.pde.ls/META-INF/MANIFEST.MF @@ -1,7 +1,12 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: PDE Language Server -Bundle-SymbolicName: org.eclipse.pde.ls +Bundle-SymbolicName: org.eclipse.pde.ls;singleton:=true Bundle-Version: 1.0.0.qualifier +Import-Package: aQute.bnd.help;version="2.0.0", + aQute.bnd.properties +Require-Bundle: org.eclipse.lsp4e;bundle-version="0.18.0", + org.eclipse.lsp4j;bundle-version="0.21.1", + org.eclipse.lsp4j.jsonrpc;bundle-version="0.21.1" Automatic-Module-Name: org.eclipse.pde.ls Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/ui/org.eclipse.pde.ls/build.properties b/ui/org.eclipse.pde.ls/build.properties index 34d2e4d2da..e9863e281e 100644 --- a/ui/org.eclipse.pde.ls/build.properties +++ b/ui/org.eclipse.pde.ls/build.properties @@ -1,4 +1,5 @@ source.. = src/ output.. = bin/ bin.includes = META-INF/,\ - . + .,\ + plugin.xml diff --git a/ui/org.eclipse.pde.ls/plugin.xml b/ui/org.eclipse.pde.ls/plugin.xml new file mode 100644 index 0000000000..b4c2f9b607 --- /dev/null +++ b/ui/org.eclipse.pde.ls/plugin.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/BNDLanguageServerClient.java b/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/BNDLanguageServerClient.java new file mode 100644 index 0000000000..a29ff78cb2 --- /dev/null +++ b/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/BNDLanguageServerClient.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.ls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.concurrent.Future; + +import org.eclipse.lsp4e.server.StreamConnectionProvider; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.launch.LSPLauncher; +import org.eclipse.lsp4j.services.LanguageClient; +import org.eclipse.pde.ls.bnd.BNDLanguageServer; + +public class BNDLanguageServerClient implements StreamConnectionProvider { + + private PipedInputStream input; + private PipedOutputStream output; + private Future listening; + + @Override + public InputStream getErrorStream() { + return null; + } + + @Override + public InputStream getInputStream() { + return input; + } + + @Override + public OutputStream getOutputStream() { + return output; + } + + @Override + public void start() throws IOException { + System.out.println("BNDLanguageServerClient.start()"); + BNDLanguageServer server = new BNDLanguageServer(); + PipedOutputStream pipedOutputStream = new PipedOutputStream(); + PipedInputStream pipedInputStream = new PipedInputStream(); + Launcher launcher = LSPLauncher.createServerLauncher(server, pipedInputStream, + pipedOutputStream); + input = new PipedInputStream(pipedOutputStream); + output = new PipedOutputStream(pipedInputStream); + listening = launcher.startListening(); + } + + @Override + public void stop() { + System.out.println("BNDLanguageServerClient.stop()"); + // TODO how to properly shutdown?!? + listening.cancel(true); + try { + input.close(); + } catch (IOException e) { + } + try { + output.close(); + } catch (IOException e) { + } + } + +} diff --git a/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDLanguageServer.java b/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDLanguageServer.java new file mode 100644 index 0000000000..0e98aba072 --- /dev/null +++ b/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDLanguageServer.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.ls.bnd; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.eclipse.lsp4j.CompletionOptions; +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4j.InitializeResult; +import org.eclipse.lsp4j.SemanticTokenTypes; +import org.eclipse.lsp4j.SemanticTokensLegend; +import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.TextDocumentSyncKind; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.lsp4j.services.TextDocumentService; +import org.eclipse.lsp4j.services.WorkspaceService; + +//See +// https://github.com/eclipse-lsp4j/lsp4j/blob/main/documentation/README.md#implement-your-language-server +// https://github.com/AlloyTools/org.alloytools.alloy/tree/master/org.alloytools.alloy.lsp +// https://github.com/bndtools/bnd/issues/5833 +// https://www.vogella.com/tutorials/EclipseLanguageServer/article.html +// https://github.com/mickaelistria/eclipse-languageserver-demo/tree/master/Le%20LanguageServer%20de%20Chamrousse +public class BNDLanguageServer implements LanguageServer { + + private BNDTextDocumentService documentService; + private BNDWorkspaceService workspaceService; + private ExecutorService executorService; + + public BNDLanguageServer() { + executorService = Executors.newWorkStealingPool(); + documentService = new BNDTextDocumentService(executorService); + workspaceService = new BNDWorkspaceService(); + } + + @Override + public CompletableFuture initialize(InitializeParams params) { + final InitializeResult res = new InitializeResult(new ServerCapabilities()); + res.getCapabilities().setCompletionProvider(new CompletionOptions(false, List.of())); + res.getCapabilities().setTextDocumentSync(TextDocumentSyncKind.Full); + res.getCapabilities().setSemanticTokensProvider( + new SemanticTokensWithRegistrationOptions( + new SemanticTokensLegend(List.of(SemanticTokenTypes.Keyword), List.of()), + Boolean.TRUE)); + return CompletableFuture.completedFuture(res); + } + + @Override + public CompletableFuture shutdown() { + return CompletableFuture.supplyAsync(() -> { + executorService.shutdown(); + return Boolean.TRUE; + }); + } + + @Override + public void exit() { + executorService.shutdownNow(); + try { + executorService.awaitTermination(30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + @Override + public TextDocumentService getTextDocumentService() { + return documentService; + } + + @Override + public WorkspaceService getWorkspaceService() { + return workspaceService; + } + +} diff --git a/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDTextDocumentService.java b/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDTextDocumentService.java new file mode 100644 index 0000000000..531ff910db --- /dev/null +++ b/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDTextDocumentService.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.ls.bnd; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; + +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4j.CompletionList; +import org.eclipse.lsp4j.CompletionParams; +import org.eclipse.lsp4j.DidChangeTextDocumentParams; +import org.eclipse.lsp4j.DidCloseTextDocumentParams; +import org.eclipse.lsp4j.DidOpenTextDocumentParams; +import org.eclipse.lsp4j.DidSaveTextDocumentParams; +import org.eclipse.lsp4j.SemanticTokens; +import org.eclipse.lsp4j.SemanticTokensParams; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.services.TextDocumentService; + +import aQute.bnd.help.Syntax; +import aQute.bnd.properties.Document; +import aQute.bnd.properties.IDocument; +import aQute.bnd.properties.LineType; +import aQute.bnd.properties.PropertiesLineReader; + +public class BNDTextDocumentService implements TextDocumentService { + + private final Map docs = new ConcurrentHashMap<>(); + private Executor executor; + + BNDTextDocumentService(Executor executor) { + this.executor = executor; + } + + @Override + public void didOpen(DidOpenTextDocumentParams params) { + System.out.println("BNDTextDocumentService.didOpen()"); + IDocument model = new Document(params.getTextDocument().getText()); + this.docs.put(params.getTextDocument().getUri(), model); + } + + @Override + public void didChange(DidChangeTextDocumentParams params) { + IDocument model = new Document(params.getContentChanges().get(0).getText()); + this.docs.put(params.getTextDocument().getUri(), model); + } + + @Override + public void didClose(DidCloseTextDocumentParams params) { + System.out.println("BNDTextDocumentService.didClose()"); + this.docs.remove(params.getTextDocument().getUri()); + } + + @Override + public void didSave(DidSaveTextDocumentParams params) { + // TODO anything to do here? + System.out.println("BNDTextDocumentService.didSave()"); + } + + @Override + public CompletableFuture semanticTokensFull(SemanticTokensParams params) { + System.out.println("BNDTextDocumentService.semanticTokensFull()"); + return withDocument(params.getTextDocument(), document -> { + SemanticTokens tokens = new SemanticTokens(); + PropertiesLineReader reader = new PropertiesLineReader(document); + LineType type; + while ((type = reader.next()) != LineType.eof) { + // TODO how to use SemanticTokens ?!? + } + // TODO https://github.com/eclipse/lsp4e/issues/861 + return null; + }); + } + + @Override + public CompletableFuture, CompletionList>> completion(CompletionParams params) { + System.out.println("BNDTextDocumentService.completion()"); + return withDocument(params.getTextDocument(), document -> { + // TODO prefix + return Either.forRight(new CompletionList(false, Syntax.HELP.values().stream().map(syntax -> { + CompletionItem item = new CompletionItem(); + item.setLabel(syntax.getHeader()); + item.setInsertText(syntax.getHeader() + ": "); + return item; + }).toList())); + }); + } + + private CompletableFuture withDocument(TextDocumentIdentifier identifier, DocumentCallable callable) { + IDocument document = docs.get(identifier.getUri()); + if (document == null) { + return CompletableFuture.failedFuture(new IllegalStateException()); + } + CompletableFuture future = new CompletableFuture<>(); + future.completeAsync(() -> { + try { + return callable.call(document); + } catch (Exception e) { + future.completeExceptionally(e); + return null; + } + + }, executor); + return future; + } + + private static interface DocumentCallable { + V call(IDocument document) throws Exception; + } + +} diff --git a/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDWorkspaceService.java b/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDWorkspaceService.java new file mode 100644 index 0000000000..c9d72b424a --- /dev/null +++ b/ui/org.eclipse.pde.ls/src/org/eclipse/pde/ls/bnd/BNDWorkspaceService.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.ls.bnd; + +import org.eclipse.lsp4j.DidChangeConfigurationParams; +import org.eclipse.lsp4j.DidChangeWatchedFilesParams; +import org.eclipse.lsp4j.services.WorkspaceService; + +public class BNDWorkspaceService implements WorkspaceService { + + BNDWorkspaceService() { + } + + @Override + public void didChangeConfiguration(DidChangeConfigurationParams params) { + // TODO Auto-generated method stub + System.out.println("BNDWorkspaceService.didChangeConfiguration()"); + } + + @Override + public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { + // TODO Auto-generated method stub + System.out.println("BNDWorkspaceService.didChangeWatchedFiles()"); + } + +}