Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support compiling and pushing diagnostics #962

Merged
merged 3 commits into from
Aug 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gradle-language-server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ repositories {
dependencies {
implementation "org.eclipse.lsp4j:org.eclipse.lsp4j:0.12.0"
implementation "org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.12.0"
implementation "org.codehaus.groovy:groovy-eclipse-batch:3.0.8-01"
}

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

package com.microsoft.gradle;

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

import com.microsoft.gradle.compile.GradleCompilationUnit;
import com.microsoft.gradle.manager.GradleFilesManager;
import com.microsoft.gradle.utils.LSPUtils;

import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.ErrorCollector;
import org.codehaus.groovy.control.Phases;
import org.codehaus.groovy.control.messages.Message;
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
import org.codehaus.groovy.syntax.SyntaxException;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageClientAware;
import org.eclipse.lsp4j.services.TextDocumentService;
import org.eclipse.lsp4j.services.WorkspaceService;

public class GradleServices implements TextDocumentService, WorkspaceService, LanguageClientAware {

private LanguageClient client;
private GradleFilesManager gradleFilesManager;

public GradleServices() {
Expand All @@ -34,22 +55,29 @@ public GradleServices() {

@Override
public void connect(LanguageClient client) {
// TODO
this.client = client;
}

@Override
public void didOpen(DidOpenTextDocumentParams params) {
gradleFilesManager.didOpen(params);
URI uri = URI.create(params.getTextDocument().getUri());
gradleFilesManager.didOpen(uri, params.getTextDocument().getText());
GradleCompilationUnit unit = this.gradleFilesManager.getCompilationUnit(uri, params.getTextDocument().getVersion());
compile(uri, unit);
}

@Override
public void didChange(DidChangeTextDocumentParams params) {
gradleFilesManager.didChange(params);
URI uri = URI.create(params.getTextDocument().getUri());
gradleFilesManager.didChange(uri, params.getContentChanges().get(0));
GradleCompilationUnit unit = this.gradleFilesManager.getCompilationUnit(uri, params.getTextDocument().getVersion());
compile(uri, unit);
}

@Override
public void didClose(DidCloseTextDocumentParams params) {
gradleFilesManager.didClose(params);
URI uri = URI.create(params.getTextDocument().getUri());
gradleFilesManager.didClose(uri);
}

@Override
Expand All @@ -66,4 +94,49 @@ public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
public void didChangeConfiguration(DidChangeConfigurationParams params) {
// TODO
}

private void compile(URI uri, GradleCompilationUnit unit) {
if (unit == null) {
return;
}
Set<PublishDiagnosticsParams> diagnostics = new HashSet<>();
try {
unit.compile(Phases.CANONICALIZATION);
// Send empty diagnostic if there is no error
diagnostics.add(new PublishDiagnosticsParams(uri.toString(), new ArrayList<>()));
} catch (CompilationFailedException e) {
diagnostics = generateDiagnostics(unit.getErrorCollector());
}
for (PublishDiagnosticsParams diagnostic : diagnostics) {
client.publishDiagnostics(diagnostic);
}
}

private Set<PublishDiagnosticsParams> generateDiagnostics(ErrorCollector collector) {
// URI, List<Diagnostic>
Map<String, List<Diagnostic>> diagnosticsStorage = new HashMap<>();
for (Message error : collector.getErrors()) {
if (error instanceof SyntaxErrorMessage) {
SyntaxException exp = ((SyntaxErrorMessage)error).getCause();
Range range = LSPUtils.toRange(exp);
Diagnostic diagnostic = new Diagnostic();
diagnostic.setRange(range);
diagnostic.setSeverity(DiagnosticSeverity.Error);
diagnostic.setMessage(exp.getMessage());
diagnostic.setSource("Gradle");
if (diagnosticsStorage.containsKey(exp.getSourceLocator())) {
diagnosticsStorage.get(exp.getSourceLocator()).add(diagnostic);
} else {
List<Diagnostic> diagnostics = new ArrayList<>();
diagnostics.add(diagnostic);
diagnosticsStorage.put(exp.getSourceLocator(), diagnostics);
}
}
}
Set<PublishDiagnosticsParams> diagnosticsParams = new HashSet<>();
for (Map.Entry<String, List<Diagnostic>> entry : diagnosticsStorage.entrySet()) {
diagnosticsParams.add(new PublishDiagnosticsParams(entry.getKey(), entry.getValue()));
}
return diagnosticsParams;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package com.microsoft.gradle.compile;

import java.security.CodeSource;

import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;

import groovy.lang.GroovyClassLoader;

public class GradleCompilationUnit extends CompilationUnit {
private Integer version;

public GradleCompilationUnit(CompilerConfiguration configuration, CodeSource codeSource, GroovyClassLoader loader, Integer version) {
super(configuration, codeSource, loader);
this.version = version;
}

public Integer getVersion() {
return this.version;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,27 @@
import java.util.HashMap;
import java.util.Map;

import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import com.microsoft.gradle.compile.GradleCompilationUnit;

import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.io.StringReaderSource;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;

import groovy.lang.GroovyClassLoader;

public class GradleFilesManager {
private Map<URI, String> openFiles = new HashMap<>();
private Map<URI, GradleCompilationUnit> unitStorage = new HashMap<>();

public void didOpen(DidOpenTextDocumentParams params) {
URI uri = URI.create(params.getTextDocument().getUri());
openFiles.put(uri, params.getTextDocument().getText());
public void didOpen(URI uri, String content) {
openFiles.put(uri, content);
}

public void didChange(DidChangeTextDocumentParams params) {
URI uri = URI.create(params.getTextDocument().getUri());
public void didChange(URI uri, TextDocumentContentChangeEvent change) {
String oldText = openFiles.get(uri);
TextDocumentContentChangeEvent change = params.getContentChanges().get(0);
Range range = change.getRange();
if (range == null) {
openFiles.put(uri, change.getText());
Expand All @@ -51,8 +53,7 @@ public void didChange(DidChangeTextDocumentParams params) {
}
}

public void didClose(DidCloseTextDocumentParams params) {
URI uri = URI.create(params.getTextDocument().getUri());
public void didClose(URI uri) {
openFiles.remove(uri);
}

Expand Down Expand Up @@ -91,4 +92,20 @@ public int getOffset(String string, Position position) {
}
return currentIndex + character;
}

public GradleCompilationUnit getCompilationUnit(URI uri, Integer version) {
if (this.unitStorage.containsKey(uri) && this.unitStorage.get(uri).getVersion().equals(version)) {
return this.unitStorage.get(uri);
}
CompilerConfiguration config = new CompilerConfiguration();
GroovyClassLoader classLoader = new GroovyClassLoader(ClassLoader.getSystemClassLoader().getParent(), config, true);
GradleCompilationUnit unit = new GradleCompilationUnit(config, null, classLoader, version);
SourceUnit sourceUnit = new SourceUnit(uri.toString(),
new StringReaderSource(getContents(uri), unit.getConfiguration()),
unit.getConfiguration(), unit.getClassLoader(), unit.getErrorCollector());
unit.addSource(sourceUnit);
this.unitStorage.put(uri, unit);
return unit;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2021 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package com.microsoft.gradle.utils;

import org.codehaus.groovy.syntax.SyntaxException;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;

public class LSPUtils {
public static Range toRange(SyntaxException exp) {
// LSP Range start from 0, while groovy classes start from 1
return new Range(new Position(exp.getStartLine() - 1, exp.getStartColumn() - 1),
new Position(exp.getEndLine() - 1, exp.getEndColumn() - 1));
}
}