You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jl...@apache.org on 2019/08/04 18:24:40 UTC
[netbeans] branch master updated: Assorted language server protocol
client improvements
This is an automated email from the ASF dual-hosted git repository.
jlahoda pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 39e97b9 Assorted language server protocol client improvements
39e97b9 is described below
commit 39e97b900a8d604f3968e07605ee0d7709f37977
Author: Jan Lahoda <jl...@netbeans.org>
AuthorDate: Sun Aug 4 20:24:30 2019 +0200
Assorted language server protocol client improvements
---
ide/lsp.client/nbproject/project.xml | 55 ++++++++
.../netbeans/modules/lsp/client/LSPBindings.java | 8 +-
.../src/org/netbeans/modules/lsp/client/Utils.java | 103 ++++++++++----
.../lsp/client/bindings/LanguageClientImpl.java | 4 +-
.../TextDocumentSyncServerCapabilityHandler.java | 6 +-
.../org/netbeans/modules/lsp/client/UtilsTest.java | 157 +++++++++++++++++++++
6 files changed, 301 insertions(+), 32 deletions(-)
diff --git a/ide/lsp.client/nbproject/project.xml b/ide/lsp.client/nbproject/project.xml
index 00ad85f..f897849 100644
--- a/ide/lsp.client/nbproject/project.xml
+++ b/ide/lsp.client/nbproject/project.xml
@@ -172,6 +172,14 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.openide.loaders</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <specification-version>7.72</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.openide.modules</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -220,6 +228,53 @@
</run-dependency>
</dependency>
</module-dependencies>
+ <test-dependencies>
+ <test-type>
+ <name>unit</name>
+ <test-dependency>
+ <code-name-base>org.netbeans.libs.junit4</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.libs.junit5</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.modules.editor</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.modules.editor.lib2</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.modules.editor.plain</code-name-base>
+ <recursive/>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.modules.masterfs</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.modules.nbjunit</code-name-base>
+ <recursive/>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.modules.projectapi.nb</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.modules.settings</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.openide.loaders</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ </test-type>
+ </test-dependencies>
<public-packages>
<package>org.netbeans.modules.lsp.client.spi</package>
</public-packages>
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java
index a0eb7be..5a4d60a 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java
@@ -38,8 +38,10 @@ import org.eclipse.lsp4j.ClientCapabilities;
import org.eclipse.lsp4j.DocumentSymbolCapabilities;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
+import org.eclipse.lsp4j.ResourceOperationKind;
import org.eclipse.lsp4j.TextDocumentClientCapabilities;
import org.eclipse.lsp4j.WorkspaceClientCapabilities;
+import org.eclipse.lsp4j.WorkspaceEditCapabilities;
import org.eclipse.lsp4j.jsonrpc.Launcher;
import org.eclipse.lsp4j.launch.LSPLauncher;
import org.eclipse.lsp4j.services.LanguageServer;
@@ -169,7 +171,11 @@ public class LSPBindings {
DocumentSymbolCapabilities dsc = new DocumentSymbolCapabilities();
dsc.setHierarchicalDocumentSymbolSupport(true);
tdcc.setDocumentSymbol(dsc);
- initParams.setCapabilities(new ClientCapabilities(new WorkspaceClientCapabilities(), tdcc, null));
+ WorkspaceClientCapabilities wcc = new WorkspaceClientCapabilities();
+ wcc.setWorkspaceEdit(new WorkspaceEditCapabilities());
+ wcc.getWorkspaceEdit().setDocumentChanges(true);
+ wcc.getWorkspaceEdit().setResourceOperations(Arrays.asList(ResourceOperationKind.Create, ResourceOperationKind.Delete, ResourceOperationKind.Rename));
+ initParams.setCapabilities(new ClientCapabilities(wcc, tdcc, null));
return server.initialize(initParams).get();
}
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java
index b924944..206e97e 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java
@@ -18,6 +18,7 @@
*/
package org.netbeans.modules.lsp.client;
+import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -29,8 +30,14 @@ import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.CreateFile;
+import org.eclipse.lsp4j.DeleteFile;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.RenameFile;
+import org.eclipse.lsp4j.ResourceOperation;
+import org.eclipse.lsp4j.ResourceOperationKind;
+import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -38,7 +45,10 @@ import org.netbeans.api.editor.document.LineDocument;
import org.netbeans.api.editor.document.LineDocumentUtils;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
+import org.openide.loaders.DataFolder;
+import org.openide.loaders.DataObject;
import org.openide.text.NbDocument;
import org.openide.util.Exceptions;
@@ -61,36 +71,77 @@ public class Utils {
return LineDocumentUtils.getLineStartFromIndex((LineDocument) doc, pos.getLine()) + pos.getCharacter();
}
- public static void applyWorkspaceEditor(WorkspaceEdit edit) {
- for (Map.Entry<String, List<TextEdit>> e : edit.getChanges().entrySet()) {
- try {
- FileObject file = URLMapper.findFileObject(new URI(e.getKey()).toURL());
- EditorCookie ec = file.getLookup().lookup(EditorCookie.class);
- Document doc = ec != null ? ec.openDocument() : null;
- if (doc == null) {
- continue;
+ public static void applyWorkspaceEdit(WorkspaceEdit edit) {
+ if (edit.getDocumentChanges() != null) {
+ for (Either<TextDocumentEdit, ResourceOperation> change : edit.getDocumentChanges()) {
+ if (change.isLeft()) {
+ applyEdits(change.getLeft().getTextDocument().getUri(), change.getLeft().getEdits());
+ } else {
+ switch (change.getRight().getKind()) {
+ case ResourceOperationKind.Create:
+ try {
+ FileUtil.createData(new File(new URI(((CreateFile) change.getRight()).getUri())));
+ } catch (IOException | URISyntaxException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ break;
+ case ResourceOperationKind.Delete:
+ try {
+ URLMapper.findFileObject(new URI(((DeleteFile) change.getRight()).getUri()).toURL()).delete();
+ } catch (IOException | URISyntaxException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ break;
+ case ResourceOperationKind.Rename:
+ try {
+ File target = new File(new URI(((RenameFile) change.getRight()).getNewUri()));
+ FileObject targetFolder = FileUtil.createFolder(target.getParentFile());
+ FileObject source = URLMapper.findFileObject(new URI(((RenameFile) change.getRight()).getOldUri()).toURL());
+ DataObject od = DataObject.find(source);
+ //XXX: should move and rename in one go!
+ od.move(DataFolder.findFolder(targetFolder));
+ od.rename(target.getName());
+ } catch (IOException | URISyntaxException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ break;
+ }
}
- NbDocument.runAtomic((StyledDocument) doc, () -> {
- e.getValue()
- .stream()
- .sorted((te1, te2) -> te1.getRange().getEnd().getLine() == te2.getRange().getEnd().getLine() ? te1.getRange().getEnd().getCharacter() - te2.getRange().getEnd().getCharacter() : te1.getRange().getEnd().getLine() - te2.getRange().getEnd().getLine())
- .forEach(te -> {
- try {
- int start = Utils.getOffset(doc, te.getRange().getStart());
- int end = Utils.getOffset(doc, te.getRange().getEnd());
- doc.remove(start, end - start);
- doc.insertString(start, te.getNewText(), null);
- } catch (BadLocationException ex) {
- Exceptions.printStackTrace(ex);
- }
- });
- });
- } catch (URISyntaxException | IOException ex) {
- Exceptions.printStackTrace(ex);
+ }
+ } else {
+ for (Map.Entry<String, List<TextEdit>> e : edit.getChanges().entrySet()) {
+ applyEdits(e.getKey(), e.getValue());
}
}
}
+ private static void applyEdits(String uri, List<TextEdit> edits) {
+ try {
+ FileObject file = URLMapper.findFileObject(new URI(uri).toURL());
+ EditorCookie ec = file.getLookup().lookup(EditorCookie.class);
+ Document doc = ec != null ? ec.openDocument() : null;
+ if (doc == null) {
+ return ;
+ }
+ NbDocument.runAtomic((StyledDocument) doc, () -> {
+ edits
+ .stream()
+ .sorted((te1, te2) -> te1.getRange().getEnd().getLine() == te2.getRange().getEnd().getLine() ? te1.getRange().getEnd().getCharacter() - te2.getRange().getEnd().getCharacter() : te1.getRange().getEnd().getLine() - te2.getRange().getEnd().getLine())
+ .forEach(te -> {
+ try {
+ int start = Utils.getOffset(doc, te.getRange().getStart());
+ int end = Utils.getOffset(doc, te.getRange().getEnd());
+ doc.remove(start, end - start);
+ doc.insertString(start, te.getNewText(), null);
+ } catch (BadLocationException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ });
+ });
+ } catch (URISyntaxException | IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
public static void applyCodeAction(LSPBindings server, Either<Command, CodeAction> cmd) {
try {
Command command;
@@ -98,7 +149,7 @@ public class Utils {
if (cmd.isLeft()) {
command = cmd.getLeft();
} else {
- Utils.applyWorkspaceEditor(cmd.getRight().getEdit());
+ Utils.applyWorkspaceEdit(cmd.getRight().getEdit());
command = cmd.getRight().getCommand();
}
if (command != null) {
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java
index bb4abce..f655fa0 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java
@@ -124,12 +124,12 @@ public class LanguageClientImpl implements LanguageClient {
severityMap.put(DiagnosticSeverity.Error, Severity.ERROR);
severityMap.put(DiagnosticSeverity.Hint, Severity.HINT);
severityMap.put(DiagnosticSeverity.Information, Severity.HINT);
- severityMap.put(DiagnosticSeverity.Warning, Severity.HINT);
+ severityMap.put(DiagnosticSeverity.Warning, Severity.WARNING);
}
@Override
public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
- Utils.applyWorkspaceEditor(params.getEdit());
+ Utils.applyWorkspaceEdit(params.getEdit());
return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(true));
}
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
index 3c59ffa..5057e37 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
@@ -68,9 +68,9 @@ public class TextDocumentSyncServerCapabilityHandler {
newOpened.removeAll(lastOpened);
Set<JTextComponent> newClosed = Collections.newSetFromMap(new IdentityHashMap<>());
newClosed.addAll(lastOpened);
- newClosed.removeAll(newOpened);
- lastOpened.removeAll(newClosed);
- lastOpened.addAll(newOpened);
+ newClosed.removeAll(currentOpened);
+ lastOpened.clear();
+ lastOpened.addAll(currentOpened);
for (JTextComponent opened : newOpened) {
FileObject file = NbEditorUtilities.getFileObject(opened.getDocument());
diff --git a/ide/lsp.client/test/unit/src/org/netbeans/modules/lsp/client/UtilsTest.java b/ide/lsp.client/test/unit/src/org/netbeans/modules/lsp/client/UtilsTest.java
new file mode 100644
index 0000000..bfc833e
--- /dev/null
+++ b/ide/lsp.client/test/unit/src/org/netbeans/modules/lsp/client/UtilsTest.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.lsp.client;
+
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.swing.text.StyledDocument;
+import org.eclipse.lsp4j.CreateFile;
+import org.eclipse.lsp4j.DeleteFile;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.RenameFile;
+import org.eclipse.lsp4j.TextDocumentEdit;
+import org.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.eclipse.lsp4j.jsonrpc.messages.Either;
+import org.netbeans.api.editor.mimelookup.MimePath;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.editor.plain.PlainKit;
+import org.netbeans.spi.editor.mimelookup.MimeDataProvider;
+import org.openide.LifecycleManager;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ServiceProvider;
+
+public class UtilsTest extends NbTestCase {
+
+ public UtilsTest(String name) {
+ super(name);
+ }
+
+ public void testApplyTextEdit() throws Exception {
+ clearWorkDir();
+ FileObject wd = FileUtil.toFileObject(getWorkDir());
+ FileObject sourceFile1 = wd.createData("Test1.txt");
+ try (OutputStream out = sourceFile1.getOutputStream()) {
+ out.write(("0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n").getBytes("UTF-8"));
+ }
+ FileObject sourceFile2 = wd.createData("Test2.txt");
+ try (OutputStream out = sourceFile2.getOutputStream()) {
+ out.write(("0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n").getBytes("UTF-8"));
+ }
+ Map<String, List<TextEdit>> changes = new HashMap<>();
+ changes.put(Utils.toURI(sourceFile1), Arrays.asList(new TextEdit(new Range(new Position(2, 3), new Position(2, 6)), "a"),
+ new TextEdit(new Range(new Position(1, 2), new Position(1, 6)), "b"),
+ new TextEdit(new Range(new Position(3, 1), new Position(4, 4)), "c")));
+ changes.put(Utils.toURI(sourceFile2), Arrays.asList(new TextEdit(new Range(new Position(2, 3), new Position(2, 6)), "a"),
+ new TextEdit(new Range(new Position(1, 2), new Position(1, 6)), "b"),
+ new TextEdit(new Range(new Position(3, 1), new Position(4, 4)), "c")));
+ WorkspaceEdit edit = new WorkspaceEdit(changes);
+ Utils.applyWorkspaceEdit(edit);
+ assertContent("0123456789\n" +
+ "01b6789\n" +
+ "012a6789\n" +
+ "0c456789\n",
+ sourceFile1);
+ assertContent("0123456789\n" +
+ "01b6789\n" +
+ "012a6789\n" +
+ "0c456789\n",
+ sourceFile2);
+ LifecycleManager.getDefault().saveAll();
+ }
+
+ public void testApplyChanges() throws Exception {
+ clearWorkDir();
+ FileObject wd = FileUtil.toFileObject(getWorkDir());
+ FileObject sourceFile1 = wd.createData("Test1.txt");
+ try (OutputStream out = sourceFile1.getOutputStream()) {
+ out.write(("0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n").getBytes("UTF-8"));
+ }
+ FileObject sourceFile2 = wd.createData("Test2.txt");
+ try (OutputStream out = sourceFile2.getOutputStream()) {
+ out.write(("0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n" +
+ "0123456789\n").getBytes("UTF-8"));
+ }
+ FileObject sourceFile3 = wd.createData("Test3.txt");
+ WorkspaceEdit edit = new WorkspaceEdit(Arrays.asList(Either.forLeft(new TextDocumentEdit(new VersionedTextDocumentIdentifier(Utils.toURI(sourceFile1), -1), Arrays.asList(new TextEdit(new Range(new Position(2, 3), new Position(2, 6)), "a"),
+ new TextEdit(new Range(new Position(1, 2), new Position(1, 6)), "b"),
+ new TextEdit(new Range(new Position(3, 1), new Position(4, 4)), "c")))),
+ Either.forLeft(new TextDocumentEdit(new VersionedTextDocumentIdentifier(Utils.toURI(sourceFile2), -1), Arrays.asList(new TextEdit(new Range(new Position(2, 3), new Position(2, 6)), "a"),
+ new TextEdit(new Range(new Position(1, 2), new Position(1, 6)), "b"),
+ new TextEdit(new Range(new Position(3, 1), new Position(4, 4)), "c")))),
+ Either.forRight(new CreateFile(Utils.toURI(sourceFile2).replace("Test2", "Test4"))),
+ Either.forLeft(new TextDocumentEdit(new VersionedTextDocumentIdentifier(Utils.toURI(sourceFile2).replace("Test2", "Test4"), -1), Arrays.asList(new TextEdit(new Range(new Position(1, 1), new Position(1, 1)), "new content")))),
+ Either.forRight(new DeleteFile(Utils.toURI(sourceFile3))),
+ Either.forRight(new RenameFile(Utils.toURI(sourceFile1), Utils.toURI(sourceFile1).replace("Test1", "Test1a")))));
+ Utils.applyWorkspaceEdit(edit);
+ assertContent("0123456789\n" +
+ "01b6789\n" +
+ "012a6789\n" +
+ "0c456789\n",
+ wd.getFileObject("Test1a.txt"));
+ assertContent("0123456789\n" +
+ "01b6789\n" +
+ "012a6789\n" +
+ "0c456789\n",
+ wd.getFileObject("Test2.txt"));
+ assertContent("new content", wd.getFileObject("Test4.txt"));
+ assertNull(wd.getFileObject("Test3.txt"));
+ LifecycleManager.getDefault().saveAll();
+ }
+
+ private void assertContent(String expectedContent, FileObject sourceFile) throws Exception {
+ EditorCookie ec = sourceFile.getLookup().lookup(EditorCookie.class);
+ StyledDocument doc = ec.openDocument();
+ assertEquals(expectedContent,
+ doc.getText(0, doc.getLength()));
+ }
+ @ServiceProvider(service=MimeDataProvider.class)
+ public static final class MimeDataProviderImpl implements MimeDataProvider {
+ public Lookup getLookup(MimePath mimePath) {
+ if (mimePath.getPath().equals("text/plain")) {
+ return Lookups.singleton(new PlainKit());
+ }
+ return Lookup.EMPTY;
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists