diff --git a/LanguageServer/src/main/java/org/nittalab/dtram/languageserver/DTRAMLanguageServer.java b/LanguageServer/src/main/java/org/nittalab/dtram/languageserver/DTRAMLanguageServer.java index 6f68598..dc95a48 100644 --- a/LanguageServer/src/main/java/org/nittalab/dtram/languageserver/DTRAMLanguageServer.java +++ b/LanguageServer/src/main/java/org/nittalab/dtram/languageserver/DTRAMLanguageServer.java @@ -10,12 +10,12 @@ public class DTRAMLanguageServer implements LanguageServer, LanguageClientAware { private static final Logger logger = Logger.getLogger(DTRAMLanguageServer.class.getName()); - private final WorkspaceService workspaceService; - private final TextDocumentService textDocumentService; + private final DTRAMWorkspaceService workspaceService; + private final DTRAMTextDocumentService textDocumentService; public DTRAMLanguageServer() { - this.workspaceService = new DTRAMWorkspaceService(); this.textDocumentService = new DTRAMTextDocumentService(); + this.workspaceService = new DTRAMWorkspaceService(); } @Override @@ -28,16 +28,26 @@ logger.info("Connected to: " + clientInfo.getName() + " " + clientInfo.getVersion()); ServerCapabilities capabilities = new ServerCapabilities(); - - /* エディタに補完機能が利用できることを伝える **/ - CompletionOptions completionOptions = new CompletionOptions(); - completionOptions.setTriggerCharacters(List.of(".", " ")); // 入力補完を開始する文字 - capabilities.setTextDocumentSync(TextDocumentSyncKind.Full); // ファイルの同期方法を指定(毎回ドキュメント全体を送信してもらう) capabilities.setPositionEncoding(PositionEncodingKind.UTF16); // 文字列符号化方式を指定 + + /* Tells the client that code completion is available **/ + CompletionOptions completionOptions = new CompletionOptions(); + completionOptions.setTriggerCharacters(List.of(".", " ")); capabilities.setCompletionProvider(completionOptions); + /* Fetch all available token types from client and send them to tell the client that the server can analyze the token */ + List availableTokenTypes = params.getCapabilities().getTextDocument().getSemanticTokens().getTokenTypes(); + SemanticTokensWithRegistrationOptions semanticTokensOptions = new SemanticTokensWithRegistrationOptions(); + semanticTokensOptions.setFull(true); + semanticTokensOptions.setRange(false); + semanticTokensOptions.setLegend(new SemanticTokensLegend(availableTokenTypes, List.of())); + capabilities.setSemanticTokensProvider(semanticTokensOptions); + InitializeResult result = new InitializeResult(capabilities); + + textDocumentService.updateSupportedTokenTypes(availableTokenTypes); + return CompletableFuture.completedFuture(result); } diff --git a/LanguageServer/src/main/java/org/nittalab/dtram/languageserver/DTRAMTextDocumentService.java b/LanguageServer/src/main/java/org/nittalab/dtram/languageserver/DTRAMTextDocumentService.java index 2c43b1c..995164b 100644 --- a/LanguageServer/src/main/java/org/nittalab/dtram/languageserver/DTRAMTextDocumentService.java +++ b/LanguageServer/src/main/java/org/nittalab/dtram/languageserver/DTRAMTextDocumentService.java @@ -5,11 +5,27 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.TextDocumentService; import org.nittalab.dtram.languageserver.completion.*; +import org.nittalab.dtram.languageserver.model.SemanticToken; +import org.nittalab.dtram.languageserver.model.Token; +import org.nittalab.dtram.languageserver.utils.SemanticAnalyzer; +import org.nittalab.dtram.languageserver.utils.Tokenizer; +import java.io.BufferedReader; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; public class DTRAMTextDocumentService implements TextDocumentService { + /** + * All supported token types by the client. + */ + private List supportedTokenTypes = new ArrayList<>(); + @Override public void didOpen(DidOpenTextDocumentParams params) { } @@ -28,7 +44,6 @@ @Override public CompletableFuture, CompletionList>> completion(CompletionParams params) { - System.out.println(params.getPosition().getLine() + " " + params.getPosition().getCharacter()); return CompletableFutures.computeAsync(checker -> { CompletionItem append = new AppendCompletionItem(); CompletionItem delete = new DeleteCompletionItem(); @@ -39,4 +54,69 @@ return Either.forLeft(List.of(append, delete, channel, in, ref, out)); }); } + + @Override + public CompletableFuture semanticTokensFull(SemanticTokensParams params) { + return CompletableFutures.computeAsync(checker -> { + /* We don't have to do unnecessary jobs */ + if (checker.isCanceled()) { + return new SemanticTokens(); + } + + /* Analyze and get semantic tokens */ + SemanticTokens semanticTokens = new SemanticTokens(); + + Path filePath = Paths.get(URI.create(params.getTextDocument().getUri())); + try (BufferedReader br = Files.newBufferedReader(filePath)) { + List tokens = SemanticAnalyzer.analyze(Tokenizer.tokenize(br)); + + List tokenData = new ArrayList<>(); + int prevLine = 0; + int columnOffset = 0; + for (Token token : tokens) { + if (token instanceof SemanticToken semanticToken) { + int tokenType = supportedTokenTypes.indexOf(semanticToken.getSemanticType()); + + /* Skip the token if the token type is not supported by the client. */ + if (tokenType == -1) { + continue; + } + + /* Prepare data of the semantic token */ + int tokenLine = semanticToken.getStartPos().getLine() - 1; + int tokenColumn = semanticToken.getStartPos().getColumn() - 1; + if (tokenLine == prevLine) { + tokenLine -= prevLine; + tokenColumn -= columnOffset; + } else { + tokenLine -= prevLine; + prevLine = semanticToken.getStartPos().getLine() - 1; + } + tokenData.add(tokenLine); // line + tokenData.add(tokenColumn); // deltaStartChar + tokenData.add(semanticToken.getText().length()); // length + tokenData.add(tokenType); // tokenType + tokenData.add(0); // tokenModifiers + + columnOffset = semanticToken.getStartPos().getColumn() - 1; + } + } + semanticTokens.setData(tokenData); + } catch (IOException e) { + throw new RuntimeException(e); + } + return semanticTokens; // Send result to the client + }); + } + + /** + * Updates the supported token types by the client. + * + * @param supportedTokenTypes All supported token types by the client. + * @author Shohei Yamagiwa + * @since 0.1 + */ + public void updateSupportedTokenTypes(List supportedTokenTypes) { + this.supportedTokenTypes = supportedTokenTypes; + } }