Skip to content

Commit

Permalink
Add very basic code completion and server structure
Browse files Browse the repository at this point in the history
  • Loading branch information
laeubi committed Oct 26, 2023
1 parent daa62e3 commit 69573b3
Show file tree
Hide file tree
Showing 8 changed files with 365 additions and 2 deletions.
7 changes: 7 additions & 0 deletions features/org.eclipse.pde-feature/feature.xml
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,11 @@
version="0.0.0"
unpack="false"/>

<plugin
id="org.eclipse.pde.ls"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>

</feature>
7 changes: 6 additions & 1 deletion ui/org.eclipse.pde.ls/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion ui/org.eclipse.pde.ls/build.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
.,\
plugin.xml
17 changes: 17 additions & 0 deletions ui/org.eclipse.pde.ls/plugin.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.eclipse.lsp4e.languageServer">
<server
class="org.eclipse.pde.ls.BNDLanguageServerClient"
id="org.eclipse.pde.ls.bnd"
label="BND Instructions">
</server>
<contentTypeMapping
contentType="org.eclipse.pde.bndInstructions"
id="org.eclipse.pde.ls.bnd">
</contentTypeMapping>
</extension>

</plugin>
Original file line number Diff line number Diff line change
@@ -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<Void> 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<LanguageClient> 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) {
}
}

}
Original file line number Diff line number Diff line change
@@ -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<InitializeResult> 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<Object> 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;
}

}
Original file line number Diff line number Diff line change
@@ -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<String, IDocument> 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<SemanticTokens> 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<Either<List<CompletionItem>, 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 <T> CompletableFuture<T> withDocument(TextDocumentIdentifier identifier, DocumentCallable<T> callable) {
IDocument document = docs.get(identifier.getUri());
if (document == null) {
return CompletableFuture.failedFuture(new IllegalStateException());
}
CompletableFuture<T> 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> {
V call(IDocument document) throws Exception;
}

}
Original file line number Diff line number Diff line change
@@ -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()");
}

}

0 comments on commit 69573b3

Please sign in to comment.