Skip to content

Commit

Permalink
support auto completion
Browse files Browse the repository at this point in the history
  • Loading branch information
CsCherrYY committed Sep 2, 2021
1 parent c14fdb7 commit 85eefed
Show file tree
Hide file tree
Showing 8 changed files with 510 additions and 8 deletions.
36 changes: 34 additions & 2 deletions extension/src/languageServer/languageServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,26 @@
import * as net from 'net';
import * as path from 'path';
import * as vscode from 'vscode';
import { LanguageClientOptions } from 'vscode-languageclient';
import {
DidChangeConfigurationNotification,
LanguageClientOptions,
} from 'vscode-languageclient';
import { LanguageClient, StreamInfo } from 'vscode-languageclient/node';
import { getConfigGradleJavaHome } from '../util/config';
import {
getConfigGradleJavaHome,
getConfigJavaImportGradleHome,
getConfigJavaImportGradleUserHome,
getConfigJavaImportGradleVersion,
getConfigJavaImportGradleWrapperEnabled,
} from '../util/config';
const CHANNEL_NAME = 'Gradle Language Server';

export async function startLanguageServer(
context: vscode.ExtensionContext
): Promise<void> {
void vscode.window.withProgress(
{ location: vscode.ProgressLocation.Window },
// eslint-disable-next-line sonarjs/cognitive-complexity
(progress) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return new Promise<void>(async (resolve, _reject) => {
Expand All @@ -26,6 +36,9 @@ export async function startLanguageServer(
documentSelector: [{ scheme: 'file', language: 'gradle' }],
outputChannel: vscode.window.createOutputChannel(CHANNEL_NAME),
outputChannelName: CHANNEL_NAME,
initializationOptions: {
settings: getGradleSettings(),
},
};
let serverOptions;
if (process.env.VSCODE_DEBUG_LANGUAGE_SERVER === 'true') {
Expand Down Expand Up @@ -82,6 +95,16 @@ export async function startLanguageServer(
});
const disposable = languageClient.start();
context.subscriptions.push(disposable);
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('java.import.gradle')) {
languageClient.sendNotification(
DidChangeConfigurationNotification.type,
{ settings: getGradleSettings() }
);
}
})
);
});
}
);
Expand All @@ -101,3 +124,12 @@ async function awaitServerConnection(port: string): Promise<StreamInfo> {
return server;
});
}

function getGradleSettings(): unknown {
return {
gradleHome: getConfigJavaImportGradleHome(),
gradleVersion: getConfigJavaImportGradleVersion(),
gradleWrapperEnabled: getConfigJavaImportGradleWrapperEnabled(),
gradleUserHome: getConfigJavaImportGradleUserHome(),
};
}
1 change: 1 addition & 0 deletions gradle-language-server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation "org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.12.0"
implementation "org.codehaus.groovy:groovy-eclipse-batch:3.0.8-01"
implementation "com.google.code.gson:gson:2.8.7"
implementation "org.apache.bcel:bcel:6.5.0"
}

ext.mainClass = "com.microsoft.gradle.GradleLanguageServer"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@

import java.io.IOException;
import java.net.Socket;
import java.net.URI;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.microsoft.gradle.semantictokens.TokenModifier;
import com.microsoft.gradle.semantictokens.TokenType;
import com.microsoft.gradle.utils.LSPUtils;

import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.DocumentFilter;
Expand All @@ -32,6 +38,7 @@
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.eclipse.lsp4j.TextDocumentSyncOptions;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.eclipse.lsp4j.jsonrpc.Launcher;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageClientAware;
Expand Down Expand Up @@ -71,6 +78,16 @@ public GradleLanguageServer() {

@Override
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
Map<?, ?> initOptions = new Gson().fromJson((JsonElement) params.getInitializationOptions(), Map.class);
// TODO: support multiple workspace folders
List<WorkspaceFolder> workspaceFolders = params.getWorkspaceFolders();
for (WorkspaceFolder folder : workspaceFolders) {
URI uri = URI.create(folder.getUri());
this.gradleServices.getLibraryResolver().setWorkspacePath(Paths.get(uri));
break;
}
Object settings = initOptions.get("settings");
LSPUtils.applySetting(this.gradleServices, settings);
ServerCapabilities serverCapabilities = new ServerCapabilities();
SemanticTokensWithRegistrationOptions semanticOptions = new SemanticTokensWithRegistrationOptions();
semanticOptions.setFull(new SemanticTokensServerFull(false));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,23 @@
import java.util.Set;
import java.util.concurrent.CompletableFuture;

import com.microsoft.gradle.compile.SemanticTokenVisitor;
import com.microsoft.gradle.compile.DocumentSymbolVisitor;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.microsoft.gradle.compile.CompletionVisitor;
import com.microsoft.gradle.compile.GradleCompilationUnit;
import com.microsoft.gradle.compile.CompletionVisitor.DependencyItem;
import com.microsoft.gradle.compile.DocumentSymbolVisitor;
import com.microsoft.gradle.compile.GradleCompilationUnit;
import com.microsoft.gradle.compile.SemanticTokenVisitor;
import com.microsoft.gradle.handlers.CompletionHandler;
import com.microsoft.gradle.handlers.DependencyCompletionHandler;
import com.microsoft.gradle.manager.GradleFilesManager;
import com.microsoft.gradle.resolver.GradleLibraryResolver;
import com.microsoft.gradle.semantictokens.SemanticToken;
import com.microsoft.gradle.utils.LSPUtils;

import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.ErrorCollector;
import org.codehaus.groovy.control.Phases;
Expand All @@ -50,6 +57,7 @@
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SemanticTokens;
Expand All @@ -69,12 +77,18 @@ public class GradleServices implements TextDocumentService, WorkspaceService, La
private SemanticTokenVisitor semanticTokenVisitor;
private DocumentSymbolVisitor documentSymbolVisitor;
private CompletionVisitor completionVisitor;
private GradleLibraryResolver libraryResolver;

public GradleServices() {
this.gradleFilesManager = new GradleFilesManager();
this.semanticTokenVisitor = new SemanticTokenVisitor();
this.documentSymbolVisitor = new DocumentSymbolVisitor();
this.completionVisitor = new CompletionVisitor();
this.libraryResolver = new GradleLibraryResolver();
}

public GradleLibraryResolver getLibraryResolver() {
return this.libraryResolver;
}

@Override
Expand Down Expand Up @@ -116,7 +130,8 @@ public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {

@Override
public void didChangeConfiguration(DidChangeConfigurationParams params) {
// TODO
Map<?, ?> settings = new Gson().fromJson((JsonElement) params.getSettings(), Map.class);
LSPUtils.applySetting(this, settings);
}

private void compile(URI uri, GradleCompilationUnit unit) {
Expand Down Expand Up @@ -201,13 +216,56 @@ public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completio
}
this.completionVisitor.visitCompilationUnit(uri, unit);
List<DependencyItem> dependencies = this.completionVisitor.getDependencies(uri);
if (dependencies == null) {
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
}
for (DependencyItem dependency : dependencies) {
if (Ranges.containsPosition(dependency.getRange(), params.getPosition())) {
DependencyCompletionHandler handler = new DependencyCompletionHandler();
return CompletableFuture
.completedFuture(Either.forLeft(handler.getDependencyCompletionItems(dependency, params.getPosition())));
}
}
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
// should return empty if in constants
List<Expression> constants = this.completionVisitor.getConstants(uri);
for (Expression constant : constants) {
Range range = LSPUtils.toRange(constant);
if (Ranges.containsPosition(range, params.getPosition())) {
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
}
}
Set<MethodCallExpression> methodCalls = this.completionVisitor.getMethodCalls(uri);
List<MethodCallExpression> containingCalls = new ArrayList<>();
for (MethodCallExpression call : methodCalls) {
Expression expression = call.getArguments();
Range range = LSPUtils.toRange(expression);
if (Ranges.containsPosition(range, params.getPosition())) {
containingCalls.add(call);
}
}
containingCalls.sort((MethodCallExpression a, MethodCallExpression b) -> {
if (Ranges.containsRange(LSPUtils.toRange(a), LSPUtils.toRange(b))) {
return -1;
}
return 1;
});
CompletionHandler handler = new CompletionHandler();
// check again
if (containingCalls.isEmpty() && isGradleRoot(uri, params.getPosition())) {
return CompletableFuture.completedFuture(Either.forLeft(handler.getRootCompletionItems(this.libraryResolver)));
}
return CompletableFuture
.completedFuture(Either.forLeft(handler.getCompletionItems(containingCalls, this.libraryResolver)));
}

private boolean isGradleRoot(URI uri, Position position) {
List<Statement> statements = this.completionVisitor.getStatements(uri);
for (Statement statement : statements) {
Range range = LSPUtils.toRange(statement);
if (Ranges.containsPosition(range, position)) {
return false;
}
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@

import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.microsoft.gradle.utils.LSPUtils;

Expand Down Expand Up @@ -56,11 +57,26 @@ public Range getRange() {

private URI currentUri;
private Map<URI, List<DependencyItem>> dependencies = new HashMap<>();
private Map<URI, Set<MethodCallExpression>> methodCalls = new HashMap<>();
private Map<URI, List<Statement>> statements = new HashMap<>();
private Map<URI, List<Expression>> constants = new HashMap<>();

public List<DependencyItem> getDependencies(URI uri) {
return this.dependencies.get(uri);
}

public Set<MethodCallExpression> getMethodCalls(URI uri) {
return this.methodCalls.get(uri);
}

public List<Statement> getStatements(URI uri) {
return this.statements.get(uri);
}

public List<Expression> getConstants(URI uri) {
return this.constants.get(uri);
}

public void visitCompilationUnit(URI uri, GradleCompilationUnit compilationUnit) {
this.currentUri = uri;
compilationUnit.iterator().forEachRemaining(unit -> visitSourceUnit(uri, unit));
Expand All @@ -70,18 +86,24 @@ public void visitSourceUnit(URI uri, SourceUnit unit) {
ModuleNode moduleNode = unit.getAST();
if (moduleNode != null) {
this.dependencies.put(uri, new ArrayList<>());
this.methodCalls.put(uri, new HashSet<>());
this.statements.put(uri, new ArrayList<>());
this.constants.put(uri, new ArrayList<>());
visitModule(moduleNode);
}
}

public void visitModule(ModuleNode node) {
BlockStatement blockStatement = node.getStatementBlock();
this.statements.put(currentUri, blockStatement.getStatements());
node.getClasses().forEach(classNode -> {
super.visitClass(classNode);
});
}

@Override
public void visitMethodCallExpression(MethodCallExpression node) {
this.methodCalls.get(this.currentUri).add(node);
if (node.getMethodAsString().equals("dependencies")) {
this.dependencies.put(currentUri, getDependencies(node));
}
Expand Down Expand Up @@ -139,4 +161,16 @@ private List<DependencyItem> getDependencies(ExpressionStatement expressionState
}
return Collections.emptyList();
}

@Override
public void visitConstantExpression(ConstantExpression expression) {
this.constants.get(currentUri).add(expression);
super.visitConstantExpression(expression);
}

@Override
public void visitGStringExpression(GStringExpression expression) {
this.constants.get(currentUri).add(expression);
super.visitGStringExpression(expression);
}
}
Loading

0 comments on commit 85eefed

Please sign in to comment.