Skip to content

Commit

Permalink
Merge pull request #1323 from TypeFox/language-server
Browse files Browse the repository at this point in the history
Initial prototype of language server protocol extension for Che
  • Loading branch information
Evgen Vidolob committed May 24, 2016
2 parents a38525f + eaa44ab commit f35a778
Show file tree
Hide file tree
Showing 86 changed files with 2,931 additions and 0 deletions.
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

0 comments on commit f35a778

Please sign in to comment.