提交 6819433c 编写于 作者: J Johannes Rieken

better decoration management, more tests, finish at final tab stop

上级 a86bfc7e
......@@ -6,7 +6,7 @@
'use strict';
import { getLeadingWhitespace } from 'vs/base/common/strings';
import { ICommonCodeEditor, IModel, TrackedRangeStickiness, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { ICommonCodeEditor, IModel, IModelDecorationOptions, TrackedRangeStickiness, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { TextmateSnippet, Placeholder, SnippetParser } from '../common/snippetParser';
import { Selection } from 'vs/editor/common/core/selection';
......@@ -24,6 +24,9 @@ class OneSnippet {
private _placeholderGroups: Placeholder[][];
private _placeholderGroupsIdx: number;
private static readonly _growingDecoration: IModelDecorationOptions = { stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges };
private static readonly _fixedDecoration: IModelDecorationOptions = { stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges };
constructor(editor: ICommonCodeEditor, snippet: TextmateSnippet, offset: number) {
this._editor = editor;
this._snippet = snippet;
......@@ -58,16 +61,7 @@ class OneSnippet {
const end = model.getPositionAt(this._offset + placeholderOffset + placeholderLen);
const range = new Range(start.lineNumber, start.column, end.lineNumber, end.column);
let stickiness: TrackedRangeStickiness;
if (placeholder.isFinalTabstop) {
stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
} else if (lastRange && lastRange.getEndPosition().equals(range.getStartPosition())) {
stickiness = TrackedRangeStickiness.GrowsOnlyWhenTypingAfter;
} else {
stickiness = TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges;
}
const handle = accessor.addDecoration(range, { stickiness });
const handle = accessor.addDecoration(range, OneSnippet._fixedDecoration);
this._placeholderDecorations.set(placeholder, handle);
lastRange = range;
......@@ -91,27 +85,50 @@ class OneSnippet {
this._init();
const prevGroupsIdx = this._placeholderGroupsIdx;
if (fwd && this._placeholderGroupsIdx < this._placeholderGroups.length - 1) {
this._placeholderGroupsIdx += 1;
return this._getCurrentPlaceholderSelections();
} else if (!fwd && this._placeholderGroupsIdx > 0) {
this._placeholderGroupsIdx -= 1;
return this._getCurrentPlaceholderSelections();
} else {
return undefined;
return [];
}
return this._editor.getModel().changeDecorations(accessor => {
// change stickness to never grow when typing at its edges
// so that in-active tabstops never grow
if (prevGroupsIdx !== -1) {
for (const placeholder of this._placeholderGroups[prevGroupsIdx]) {
const id = this._placeholderDecorations.get(placeholder);
accessor.changeDecorationOptions(id, OneSnippet._fixedDecoration);
}
}
// change stickiness to always grow when typing at its edges
// because these decorations represent the currently active
// tabstop. Special case: reaching the final tab stop
const selections: Selection[] = [];
for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) {
const id = this._placeholderDecorations.get(placeholder);
const range = this._editor.getModel().getDecorationRange(id);
selections.push(new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn));
accessor.changeDecorationOptions(id, placeholder.isFinalTabstop ? OneSnippet._fixedDecoration : OneSnippet._growingDecoration);
}
return selections;
});
}
private _getCurrentPlaceholderSelections(): Selection[] {
const selections: Selection[] = [];
for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) {
const handle = this._placeholderDecorations.get(placeholder);
const range = this._editor.getModel().getDecorationRange(handle);
selections.push(new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn));
get isAtFinalPlaceholder() {
if (this._placeholderGroupsIdx < 0) {
return false;
} else {
return this._placeholderGroups[this._placeholderGroupsIdx][0].isFinalTabstop;
}
return selections;
}
}
......@@ -161,32 +178,34 @@ export class SnippetSession {
this._editor.setSelections(newSelections);
}
dispose(): void {
dispose(this._snippets);
}
next(): void {
const newSelections = this._move(true);
this._editor.setSelections(newSelections);
if (newSelections.length > 0) {
this._editor.setSelections(newSelections);
}
}
prev(): void {
const newSelections = this._move(false);
this._editor.setSelections(newSelections);
if (newSelections.length > 0) {
this._editor.setSelections(newSelections);
}
}
private _move(fwd: boolean): Selection[] {
const selections: Selection[] = [];
for (const snippet of this._snippets) {
const oneSelection = snippet.move(fwd);
if (!oneSelection) {
if (fwd) {
this.stop();
}
return this._editor.getSelections();
}
selections.push(...oneSelection);
}
return selections;
}
stop(): void {
dispose(this._snippets);
get isAtFinalPlaceholder() {
return this._snippets[0].isAtFinalPlaceholder;
}
}
......@@ -59,6 +59,19 @@ suite('SnippetInsertion', function () {
assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14));
});
test('snippets, repeated tabstops', function () {
const session = new SnippetSession(editor, '${1:abc}foo${1:abc}$0');
assertSelections(editor,
new Selection(1, 1, 1, 4), new Selection(1, 7, 1, 10),
new Selection(2, 5, 2, 8), new Selection(2, 11, 2, 14),
);
session.next();
assertSelections(editor,
new Selection(1, 10, 1, 10),
new Selection(2, 14, 2, 14),
);
});
test('snippets, selections and new text with newlines', () => {
const session = new SnippetSession(editor, 'foo\n\t${1:bar}\n$0');
......@@ -141,5 +154,76 @@ suite('SnippetInsertion', function () {
assertSelections(editor, new Selection(1, 15, 1, 15));
});
test('snippets, don\'t merge touching tabstops 1/2', function () {
const session = new SnippetSession(editor, '$1$2$3$0');
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
session.next();
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
session.next();
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
session.next();
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
session.prev();
session.prev();
session.prev();
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
editor.trigger('test', 'type', { text: '111' });
session.next();
editor.trigger('test', 'type', { text: '222' });
session.next();
editor.trigger('test', 'type', { text: '333' });
session.next();
assert.equal(model.getValue(), '111222333function foo() {\n 111222333console.log(a);\n}');
assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14));
session.prev();
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
session.prev();
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
session.prev();
assertSelections(editor, new Selection(1, 1, 1, 4), new Selection(2, 5, 2, 8));
});
test('snippets, don\'t merge touching tabstops 2/2', function () {
const session = new SnippetSession(editor, '$1$2$3$0');
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
editor.trigger('test', 'type', { text: '111' });
session.next();
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
editor.trigger('test', 'type', { text: '222' });
session.next();
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
editor.trigger('test', 'type', { text: '333' });
session.next();
assert.equal(session.isAtFinalPlaceholder, true);
});
test('snippets, gracefully move over final tabstop', function () {
const session = new SnippetSession(editor, '${1}bar$0');
assert.equal(session.isAtFinalPlaceholder, false);
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
session.next();
assert.equal(session.isAtFinalPlaceholder, true);
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
session.next();
assert.equal(session.isAtFinalPlaceholder, true);
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册