diff --git a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFCallableContext.java b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFCallableContext.java index f7e7458ee..078fd5891 100644 --- a/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFCallableContext.java +++ b/headless-services/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFCallableContext.java @@ -21,8 +21,7 @@ import reactor.ipc.netty.channel.AbortedException; /** * This is a stateful callable context that is "aware" of CF errors, and is not - * suitable for reuse as it may cache errors - * + * suitable for reuse as it may cache errors. */ public class CFCallableContext { diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEdits.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEdits.java index 98fdbdf79..387830934 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEdits.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEdits.java @@ -135,14 +135,15 @@ public class DocumentEdits implements ProposalApplier { private int offset; private String text; - public Insertion(int offset, String insert) { + public Insertion(boolean grabCursor, int offset, String insert) { + super(grabCursor); this.offset = offset; this.text = insert; } @Override void apply(DocumentState doc) throws BadLocationException { - doc.insert(offset, text); + doc.insert(grabCursor, offset, text); } @Override @@ -162,6 +163,10 @@ public class DocumentEdits implements ProposalApplier { } private abstract class Edit { + protected boolean grabCursor; + protected Edit(boolean grabCursor) { + this.grabCursor = grabCursor; + } public abstract int getStart(); public abstract int getEnd(); abstract void apply(DocumentState doc) throws BadLocationException; @@ -174,14 +179,15 @@ public class DocumentEdits implements ProposalApplier { private int start; private int end; - public Deletion(int start, int end) { + public Deletion(boolean grabCursor, int start, int end) { + super(grabCursor); this.start = start; this.end = end; } @Override void apply(DocumentState doc) throws BadLocationException { - doc.delete(start, end); + doc.delete(grabCursor, start, end); } @Override @@ -227,7 +233,7 @@ public class DocumentEdits implements ProposalApplier { this.doc = doc; } - public void insert(int start, final String text) throws BadLocationException { + public void insert(boolean grabCursor, int start, final String text) throws BadLocationException { final int tStart = org2new.transform(start, Direction.AFTER); if (!text.isEmpty()) { if (doc!=null) { @@ -252,10 +258,14 @@ public class DocumentEdits implements ProposalApplier { } }; } - selection = tStart+text.length(); + if (grabCursor) { + selection = tStart+text.length(); + } else if (selection > tStart) { + selection += text.length(); + } } - public void delete(final int start, final int end) throws BadLocationException { + public void delete(boolean grabCursor, final int start, final int end) throws BadLocationException { final int tStart = org2new.transform(start, Direction.AFTER); if (end>start) { // skip work for 'delete nothing' op final int tEnd = org2new.transform(end, Direction.AFTER); @@ -280,7 +290,14 @@ public class DocumentEdits implements ProposalApplier { }; } } - selection = tStart; + if (grabCursor) { + selection = tStart; + } else if (selection>tStart) { + int len = end - start; + if (len > 0) { + selection = Math.max(tStart, selection-len); + } + } } @Override @@ -299,13 +316,22 @@ public class DocumentEdits implements ProposalApplier { private List edits = new ArrayList(); private IDocument doc; + /** + * When this is true, the cursor is moved after each edit, to be positioned right after the + * edit. + *

+ * When it is false, the cursor only moves to remain in place relative to the surrounding text. + * (I.e. if text is inserted/deleted before the cursor its shifted by the length of the inserted/deleted text). + */ + private boolean grabCursor = true; + public DocumentEdits(IDocument doc) { this.doc = doc; } public void delete(int start, int end) { Assert.isLegal(start<=end); - edits.add(new Deletion(start, end)); + edits.add(new Deletion(grabCursor, start, end)); } public void delete(int offset, String text) { @@ -313,7 +339,7 @@ public class DocumentEdits implements ProposalApplier { } public void insert(int offset, String insert) { - edits.add(new Insertion(offset, insert)); + edits.add(new Insertion(grabCursor, offset, insert)); } @Override @@ -417,7 +443,7 @@ public class DocumentEdits implements ProposalApplier { if (edits.size()>0) { Edit firstEdit = edits.get(0); int offset = firstEdit.getStart(); - edits.add(0, new Insertion(offset, indentString)); + edits.add(0, new Insertion(grabCursor, offset, indentString)); } } @@ -445,4 +471,12 @@ public class DocumentEdits implements ProposalApplier { return null; } + /** + * Stop moving the cursor to the end of edits for successive edits. The cursor will still be + * update to remain 'in place' relative to the surrounding text. + */ + public void freezeCursor() { + this.grabCursor = false; + } + } diff --git a/headless-services/commons/language-server-test-harness/src/test/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEditsTest.java b/headless-services/commons/language-server-test-harness/src/test/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEditsTest.java index c07cce229..70d8f17ba 100644 --- a/headless-services/commons/language-server-test-harness/src/test/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEditsTest.java +++ b/headless-services/commons/language-server-test-harness/src/test/java/org/springframework/ide/vscode/commons/languageserver/completion/DocumentEditsTest.java @@ -96,6 +96,10 @@ public class DocumentEditsTest { edits.deleteLineBackward(0); } + public void freezeCursor() { + edits.freezeCursor(); + } + } @Test public void testDeletes() throws Exception { @@ -194,4 +198,40 @@ public class DocumentEditsTest { } + @Test public void testCursorFreeze() throws Exception { + TestSubject it = new TestSubject( + "something:\n" + + "#end" + ); +// it.insBefore("\n#end", "\n foo: "); +// it.insBefore("\n#end", "\n zoro: "); +// it.insBefore("\n#end", "\n banana: "); +// +// it.expect( +// "something:\n" + +// " foo: \n" + +// " zoro: \n" + +// " banana: <*>\n" + +// "#end" +// ); +// +// it.reset(); + + it.insBefore("\n#end", "\n foo: "); + it.freezeCursor(); + it.insBefore("\n#end", "\n zoro: "); + it.insBefore("\n#end", "\n banana: "); + it.del("#end"); + it.insBefore("something", "replc"); + it.del("something"); + + it.expect( + "replc:\n" + + " foo: <*>\n" + + " zoro: \n" + + " banana: \n" + ); + + } + }