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

Initial prototype of language server protocol extension for Che #1323

Merged
merged 3 commits into from
May 24, 2016
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
8 changes: 8 additions & 0 deletions assembly/assembly-ide-war/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-java-plain-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-languageserver-ide</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-languageserver-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-machine-ext-client</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<inherits name='org.eclipse.che.plugin.java.plain.PlainJava'/>
<inherits name='org.eclipse.che.plugin.nodejs.NodeJs'/>
<inherits name='org.eclipse.che.plugin.docker.client.dto.Dto'/>
<inherits name='org.eclipse.che.plugin.languageserver.LanguageServer'/>

<inherits name='org.eclipse.che.api.core.Core'/>
<inherits name='org.eclipse.che.api.machine.Machine'/>
Expand Down
8 changes: 8 additions & 0 deletions assembly/assembly-wsagent-war/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-java-plain-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-languageserver-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-languageserver-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-maven-generator-archetype</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2012-2016 TypeFox GmbH. 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: TypeFox GmbH -
initial API and implementation -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>che-plugin-languageserver-parent</artifactId>
<groupId>org.eclipse.che.plugin</groupId>
<version>4.3.0-RC1-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>che-plugin-languageserver-ide</artifactId>
<packaging>jar</packaging>
<name>Che Plugin :: Language Server :: IDE</name>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.gwt.inject</groupId>
<artifactId>gin</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-gwt</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-ide-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-languageserver-shared</artifactId>
</dependency>
<dependency>
<groupId>org.vectomatic</groupId>
<artifactId>lib-gwt-svg</artifactId>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.eclipse.che.plugin.languageserver.ide;

import org.eclipse.che.ide.api.editor.EditorRegistry;
import org.eclipse.che.ide.api.event.FileEvent;
import org.eclipse.che.ide.api.event.FileEventHandler;
import org.eclipse.che.ide.api.extension.Extension;
import org.eclipse.che.ide.api.filetypes.FileType;
import org.eclipse.che.ide.api.filetypes.FileTypeRegistry;
import org.eclipse.che.plugin.languageserver.ide.editor.LanguageServerEditorConfiguration;
import org.eclipse.che.plugin.languageserver.ide.editor.LanguageServerEditorProvider;
import org.eclipse.che.plugin.languageserver.ide.service.TextDocumentServiceClient;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.DidCloseTextDocumentParamsDTOImpl;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.DidOpenTextDocumentParamsDTOImpl;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.DidSaveTextDocumentParamsDTOImpl;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.TextDocumentIdentifierDTOImpl;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.TextDocumentItemDTOImpl;

import com.google.inject.Inject;
import com.google.web.bindery.event.shared.EventBus;

@Extension(title = "LanguageServer")
public class LanguageServerExtension {

@Inject
protected void configureFileTypes(FileTypeRegistry fileTypeRegistry, LanguageServerResources resources,
final EditorRegistry editorRegistry, final LanguageServerEditorProvider editorProvider) {
// TODO the file types need to be retrieved from the server. Ideally we
// would listen on messages when new language servers get registered.
FileType fileType = new FileType(resources.file(), "foo");
fileTypeRegistry.registerFileType(fileType);
// register editor provider
editorRegistry.registerDefaultEditor(fileType, editorProvider);
}

@Inject protected void registerFileEventHandler(EventBus eventBus, final TextDocumentServiceClient serviceClient) {
eventBus.addHandler(FileEvent.TYPE, new FileEventHandler() {

@Override
public void onFileOperation(FileEvent event) {
TextDocumentIdentifierDTOImpl documentId = DtoClientImpls.TextDocumentIdentifierDTOImpl.make();
documentId.setUri(event.getFile().getPath());
switch (event.getOperationType()) {
case OPEN:
DidOpenTextDocumentParamsDTOImpl openEvent = DtoClientImpls.DidOpenTextDocumentParamsDTOImpl.make();
TextDocumentItemDTOImpl documentItem = DtoClientImpls.TextDocumentItemDTOImpl.make();
documentItem.setUri(event.getFile().getPath());
documentItem.setVersion(LanguageServerEditorConfiguration.INITIAL_DOCUMENT_VERSION);
//TODO send text?
openEvent.setTextDocument(documentItem);
serviceClient.didOpen(openEvent);
break;
case CLOSE:
DidCloseTextDocumentParamsDTOImpl closeEvent = DtoClientImpls.DidCloseTextDocumentParamsDTOImpl.make();
closeEvent.setTextDocument(documentId);
serviceClient.didClose(closeEvent);
break;
case SAVE:
DidSaveTextDocumentParamsDTOImpl saveEvent = DtoClientImpls.DidSaveTextDocumentParamsDTOImpl.make();
saveEvent.setTextDocument(documentId);
serviceClient.didSave(saveEvent);
break;
}
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.eclipse.che.plugin.languageserver.ide;

import com.google.gwt.core.client.GWT;
import com.google.gwt.resources.client.ClientBundle;

import org.vectomatic.dom.svg.ui.SVGResource;

public interface LanguageServerResources extends ClientBundle {
LanguageServerResources INSTANCE = GWT.create(LanguageServerResources.class);

@Source("svg/file.svg")
SVGResource file();

@Source("svg/category.svg")
SVGResource category();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package org.eclipse.che.plugin.languageserver.ide.editor;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.eclipse.che.ide.api.editor.annotation.AnnotationModel;
import org.eclipse.che.ide.api.editor.annotation.AnnotationModelImpl;
import org.eclipse.che.ide.api.editor.codeassist.CodeAssistProcessor;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.editorconfig.DefaultTextEditorConfiguration;
import org.eclipse.che.ide.api.editor.events.DocumentChangeEvent;
import org.eclipse.che.ide.api.editor.partition.ConstantPartitioner;
import org.eclipse.che.ide.api.editor.partition.DocumentPartitioner;
import org.eclipse.che.ide.api.editor.partition.DocumentPositionMap;
import org.eclipse.che.ide.api.editor.reconciler.Reconciler;
import org.eclipse.che.ide.api.editor.reconciler.ReconcilerWithAutoSave;
import org.eclipse.che.ide.api.editor.text.TextPosition;
import org.eclipse.che.plugin.languageserver.ide.editor.codeassist.LanguageServerCodeAssistProcessor;
import org.eclipse.che.plugin.languageserver.ide.service.TextDocumentServiceClient;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.DidChangeTextDocumentParamsDTOImpl;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.PositionDTOImpl;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.RangeDTOImpl;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.TextDocumentContentChangeEventDTOImpl;
import org.eclipse.che.plugin.languageserver.shared.dto.DtoClientImpls.VersionedTextDocumentIdentifierDTOImpl;

import com.google.inject.Inject;
import com.google.inject.Provider;

public class LanguageServerEditorConfiguration extends DefaultTextEditorConfiguration {

public static final int INITIAL_DOCUMENT_VERSION = 0;

private final LanguageServerCodeAssistProcessor codeAssistProcessor;
private final AnnotationModel annotationModel;
private final DocumentPositionMap documentPositionMap;
private final AtomicInteger version = new AtomicInteger(INITIAL_DOCUMENT_VERSION);
private final ConstantPartitioner constantPartitioner;
private final ReconcilerWithAutoSave reconciler;

@Inject
public LanguageServerEditorConfiguration(final LanguageServerCodeAssistProcessor codeAssistProcessor, final Provider<DocumentPositionMap> docPositionMapProvider, final TextDocumentServiceClient textDocumentService) {
this.codeAssistProcessor = codeAssistProcessor;
this.documentPositionMap = docPositionMapProvider.get();
this.annotationModel = new AnnotationModelImpl(documentPositionMap);

//HACK hijacked the partitioner to get document change events
this.constantPartitioner = new ConstantPartitioner() {

@Override
public void onDocumentChange(DocumentChangeEvent event) {
super.onDocumentChange(event);

Document document = event.getDocument().getDocument();
TextPosition startPosition = document.getPositionFromIndex(event.getOffset());
TextPosition endPosition = document.getPositionFromIndex(event.getOffset() + event.getLength());

DidChangeTextDocumentParamsDTOImpl changeDTO = DtoClientImpls.DidChangeTextDocumentParamsDTOImpl.make();
String uri = ((Document) document).getFile().getPath();
changeDTO.setUri(uri);
VersionedTextDocumentIdentifierDTOImpl versionedDocId = DtoClientImpls.VersionedTextDocumentIdentifierDTOImpl.make();
versionedDocId.setUri(uri);
versionedDocId.setVersion(version.incrementAndGet());
changeDTO.setTextDocument(versionedDocId);
TextDocumentContentChangeEventDTOImpl actualChange = DtoClientImpls.TextDocumentContentChangeEventDTOImpl.make();
RangeDTOImpl range = DtoClientImpls.RangeDTOImpl.make();
PositionDTOImpl start = DtoClientImpls.PositionDTOImpl.make();
start.setLine(startPosition.getLine());
start.setCharacter(startPosition.getCharacter());
PositionDTOImpl end = DtoClientImpls.PositionDTOImpl.make();
end.setLine(endPosition.getLine());
end.setCharacter(endPosition.getCharacter());
range.setStart(start);
range.setEnd(end);
actualChange.setRange(range);
actualChange.setText(event.getText());
changeDTO.addContentChanges(actualChange);
textDocumentService.didChange(changeDTO);
}
};
this.reconciler = new ReconcilerWithAutoSave(DocumentPartitioner.DEFAULT_CONTENT_TYPE, constantPartitioner);
}

@Override
public Map<String, CodeAssistProcessor> getContentAssistantProcessors() {
Map<String, CodeAssistProcessor> map = new HashMap<>();
map.put(DocumentPartitioner.DEFAULT_CONTENT_TYPE, codeAssistProcessor);
return map;
}

@Override
public AnnotationModel getAnnotationModel() {
return annotationModel;
}

@Override
public DocumentPartitioner getPartitioner() {
return constantPartitioner;
}

@Override
public Reconciler getReconciler() {
return reconciler;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.eclipse.che.plugin.languageserver.ide.editor;

import javax.inject.Inject;

import org.eclipse.che.ide.api.editor.defaulteditor.AbstractTextEditorProvider;
import org.eclipse.che.ide.api.editor.editorconfig.TextEditorConfiguration;

import com.google.inject.Provider;

public class LanguageServerEditorProvider extends AbstractTextEditorProvider {

private Provider<LanguageServerEditorConfiguration> editorConfigurationProvider;

@Inject
public LanguageServerEditorProvider(final Provider<LanguageServerEditorConfiguration> editorConfigurationProvider) {
this.editorConfigurationProvider = editorConfigurationProvider;
}

@Override
public String getId() {
return "LanguageServerEditor";
}

@Override
public String getDescription() {
return "Code Editor";
}

@Override
protected TextEditorConfiguration getEditorConfiguration() {
return editorConfigurationProvider.get();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.eclipse.che.plugin.languageserver.ide.editor;

import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.editor.annotation.AnnotationModel;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.editorconfig.TextEditorConfiguration;
import org.eclipse.che.ide.api.editor.text.Position;
import org.eclipse.che.ide.api.editor.text.TextPosition;
import org.eclipse.che.ide.api.editor.text.annotation.Annotation;
import org.eclipse.che.ide.api.editor.texteditor.TextEditor;
import org.eclipse.che.ide.resource.Path;
import org.eclipse.che.plugin.languageserver.shared.lsapi.DiagnosticDTO;
import org.eclipse.che.plugin.languageserver.shared.lsapi.PublishDiagnosticsParamsDTO;
import org.eclipse.che.plugin.languageserver.shared.lsapi.RangeDTO;

import com.google.inject.Inject;
import com.google.inject.Singleton;

import io.typefox.lsapi.Diagnostic;

@Singleton
public class PublishDiagnosticsProcessor {

private final EditorAgent editorAgent;

@Inject
public PublishDiagnosticsProcessor(EditorAgent editorAgent) {
this.editorAgent = editorAgent;
}

public void processDiagnostics(PublishDiagnosticsParamsDTO diagnosticsMessage) {
EditorPartPresenter openedEditor = editorAgent.getOpenedEditor(new Path(diagnosticsMessage.getUri()));
//TODO add markers
if (openedEditor == null)
return;
if (openedEditor instanceof TextEditor) {
TextEditorConfiguration editorConfiguration = ((TextEditor) openedEditor).getConfiguration();
AnnotationModel annotationModel = editorConfiguration.getAnnotationModel();
if (annotationModel != null) {
annotationModel.clear();
Document document = ((TextEditor) openedEditor).getDocument();
for (DiagnosticDTO diagnostic : diagnosticsMessage.getDiagnostics()) {
Annotation annotation = new Annotation(true);
annotation.setText(diagnostic.getMessage());
// TODO generalize id for error and warning. Needs fix in org.eclipse.che.ide.editor.orion.client.OrionEditorWidget.getSeverity(String)
annotation.setType(diagnostic.getSeverity() == Diagnostic.SEVERITY_ERROR ? "org.eclipse.jdt.ui.error" : "org.eclipse.jdt.ui.warning");

RangeDTO range = diagnostic.getRange();
int startIndex = document.getIndexFromPosition(new TextPosition(range.getStart().getLine(), range.getStart().getCharacter()));
int endIndex = document.getIndexFromPosition(new TextPosition(range.getEnd().getLine(), range.getEnd().getCharacter()));
annotationModel.addAnnotation(annotation, new Position(startIndex, endIndex - startIndex));
}
}
}
}

}
Loading