提交 e3524b78 编写于 作者: J Johannes Rieken

first sketches of new snippet controller

上级 cdf3c0c6
......@@ -39,9 +39,11 @@ class OneSnippet {
}
dispose(): void {
if (this._placeholderDecorations) {
this._editor.changeDecorations(accessor => this._placeholderDecorations.forEach(handle => accessor.removeDecoration(handle)));
if (!this._placeholderDecorations) {
return;
}
this._editor.changeDecorations(accessor => this._placeholderDecorations.forEach(handle => accessor.removeDecoration(handle)));
this._placeholderGroups.length = 0;
}
private _init(): void {
......@@ -131,6 +133,10 @@ class OneSnippet {
});
}
get isAtFirstPlaceholder() {
return this._placeholderGroupsIdx === 0;
}
get isAtFinalPlaceholder() {
if (this._placeholderGroupsIdx < 0) {
return false;
......@@ -194,17 +200,23 @@ export class SnippetSession {
dispose(this._snippets);
}
next(): void {
next(): boolean {
const newSelections = this._move(true);
if (newSelections.length > 0) {
this._editor.setSelections(newSelections);
return true;
} else {
return false;
}
}
prev(): void {
prev(): boolean {
const newSelections = this._move(false);
if (newSelections.length > 0) {
this._editor.setSelections(newSelections);
return true;
} else {
return false;
}
}
......@@ -217,6 +229,10 @@ export class SnippetSession {
return selections;
}
get isAtFirstPlaceholder() {
return this._snippets[0].isAtFirstPlaceholder;
}
get isAtFinalPlaceholder() {
return this._snippets[0].isAtFinalPlaceholder;
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { commonEditorContribution } from 'vs/editor/common/editorCommonExtensions';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { SnippetSession } from './editorSnippets';
@commonEditorContribution
export class SnippetController2 {
static InSnippetMode = new RawContextKey('inSnippet', false);
static HasNextTabstop = new RawContextKey('hasNextTabstop', false);
static HasPrevTabstop = new RawContextKey('hasPrevTabstop', false);
private readonly _inSnippet: IContextKey<boolean>;
private readonly _hasNextTabstop: IContextKey<boolean>;
private readonly _hasPrevTabstop: IContextKey<boolean>;
private _snippetStack: SnippetSession[] = [];
private _snippetListener: IDisposable[] = [];
constructor(
private readonly _editor: ICommonCodeEditor,
@IContextKeyService contextKeyService: IContextKeyService
) {
this._inSnippet = SnippetController2.InSnippetMode.bindTo(contextKeyService);
this._hasNextTabstop = SnippetController2.HasNextTabstop.bindTo(contextKeyService);
this._hasPrevTabstop = SnippetController2.HasPrevTabstop.bindTo(contextKeyService);
}
dispose(): void {
this._inSnippet.reset();
dispose(this._snippetStack);
}
getId(): string {
return 'snippetController2';
}
insert(template: string, overwriteBefore: number = 0, overwriteAfter: number = 0): void {
const newLen = this._snippetStack.unshift(new SnippetSession(this._editor, template));
if (newLen === 1) {
this._inSnippet.set(true);
this._snippetListener = [this._editor.onDidChangeCursorSelection(() => this._updateState())];
}
this._updateState();
}
private _updateState(): void {
if (!this._snippetStack[0].validateSelections()) {
return this.abort();
}
let prev = false;
let next = false;
for (let i = 0; i < this._snippetStack.length && !(prev && next); i++) {
if (!this._snippetStack[i].isAtFirstPlaceholder) {
prev = true;
}
if (!this._snippetStack[i].isAtFinalPlaceholder) {
next = true;
}
}
this._hasNextTabstop.set(next);
this._hasPrevTabstop.set(prev);
}
abort(): void {
// remove current, check for next
const element = this._snippetStack.shift();
dispose(element);
// clean up if last snippet is gone
// or validate the new active snippet
if (this._snippetStack.length === 0) {
this._inSnippet.set(false);
this._hasNextTabstop.set(false);
this._hasPrevTabstop.set(false);
this._snippetListener = dispose(this._snippetListener);
} else {
//
this._updateState();
}
}
prev(): void {
for (let i = 0; i < this._snippetStack.length; i++) {
if (this._snippetStack[i].prev()) {
return;
}
}
}
next(): void {
for (let i = 0; i < this._snippetStack.length; i++) {
if (this._snippetStack[i].next()) {
return;
}
}
}
}
......@@ -34,6 +34,7 @@ suite('SnippetSession', function () {
teardown(function () {
model.dispose();
editor.dispose();
});
test('normalize whitespace', function () {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { Selection } from 'vs/editor/common/core/selection';
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { mockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { Model } from "vs/editor/common/model/model";
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
suite('SnippetController2', function () {
function assertSelections(editor: ICommonCodeEditor, ...s: Selection[]) {
for (const selection of editor.getSelections()) {
const actual = s.shift();
assert.ok(selection.equalsSelection(actual), `actual=${selection.toString()} <> expected=${actual.toString()}`);
}
assert.equal(s.length, 0);
}
let editor: ICommonCodeEditor;
let model: Model;
let contextKeys: MockContextKeyService;
setup(function () {
contextKeys = new MockContextKeyService();
model = Model.createFromString('if\n $state\nfi');
editor = mockCodeEditor([], { model });
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]);
assert.equal(model.getEOL(), '\n');
});
teardown(function () {
model.dispose();
});
test('creation', function () {
const ctrl = new SnippetController2(editor, contextKeys);
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), false);
ctrl.dispose();
});
test('insert, insert -> abort', function () {
const ctrl = new SnippetController2(editor, contextKeys);
ctrl.insert('foo${1:bar}foo$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
ctrl.abort();
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), false);
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
});
test('insert, insert -> tab, tab, done', function () {
const ctrl = new SnippetController2(editor, contextKeys);
ctrl.insert('${1:one}${2:two}$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assert.equal(SnippetController2.HasNextTabstop.getValue(contextKeys), true);
assert.equal(SnippetController2.HasPrevTabstop.getValue(contextKeys), false);
ctrl.next();
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assert.equal(SnippetController2.HasNextTabstop.getValue(contextKeys), true);
assert.equal(SnippetController2.HasPrevTabstop.getValue(contextKeys), true);
ctrl.next();
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assert.equal(SnippetController2.HasNextTabstop.getValue(contextKeys), false);
assert.equal(SnippetController2.HasPrevTabstop.getValue(contextKeys), true);
// editor.trigger('test', 'type', { text: '\t' });
// assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), false);
// assert.equal(SnippetController2.HasNextTabstop.getValue(contextKeys), false);
// assert.equal(SnippetController2.HasPrevTabstop.getValue(contextKeys), false);
});
test('insert, insert -> cursor moves out', function () {
const ctrl = new SnippetController2(editor, contextKeys);
ctrl.insert('foo${1:bar}foo$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
// bad selection change
editor.setSelections([new Selection(1, 12, 1, 12), new Selection(2, 16, 2, 16)]);
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), false);
});
test('insert, nested -> cursor moves out 1/2', function () {
const ctrl = new SnippetController2(editor, contextKeys);
ctrl.insert('foo${1:bar}foo$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
ctrl.insert('ff$1bb$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assertSelections(editor, new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10));
// bad selection
editor.setSelections([new Selection(3, 1, 3, 1), new Selection(1, 6, 1, 6)]);
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), false);
});
test('insert, nested -> cursor moves out 2/2', function () {
const ctrl = new SnippetController2(editor, contextKeys);
ctrl.insert('foo${1:bar}foo$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
ctrl.insert('ff$1bb$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assertSelections(editor, new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10));
// select outer snippet
editor.setSelections([new Selection(1, 3, 1, 3), new Selection(2, 6, 2, 6)]);
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
ctrl.next();
assert.equal(model.getValue(), 'fooffbbfooif\n fooffbbfoo$state\nfi');
assertSelections(editor, new Selection(1, 11, 1, 11), new Selection(2, 15, 2, 15));
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
});
test('insert, nested -> tab across all', function () {
const ctrl = new SnippetController2(editor, contextKeys);
ctrl.insert('outer$1outer$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assert.equal(SnippetController2.HasNextTabstop.getValue(contextKeys), true);
assert.equal(SnippetController2.HasPrevTabstop.getValue(contextKeys), false);
ctrl.insert('inner$1inner$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assert.equal(SnippetController2.HasNextTabstop.getValue(contextKeys), true);
// assert.equal(SnippetController2.HasPrevTabstop.getValue(contextKeys), true);
});
});
......@@ -38,12 +38,17 @@ class MockKeybindingContextKey<T> implements IContextKey<T> {
}
export class MockContextKeyService implements IContextKeyService {
public _serviceBrand: any;
public dispose(): void { }
public _serviceBrand: any;
private _keys = new Map<string, IContextKey<any>>();
public dispose(): void {
//
}
public createKey<T>(key: string, defaultValue: T): IContextKey<T> {
return new MockKeybindingContextKey(key, defaultValue);
let ret = new MockKeybindingContextKey(key, defaultValue);
this._keys.set(key, ret);
return ret;
}
public contextMatchesRules(rules: ContextKeyExpr): boolean {
return false;
......@@ -52,7 +57,9 @@ export class MockContextKeyService implements IContextKeyService {
return Event.None;
}
public getContextKeyValue(key: string) {
return;
if (this._keys.has(key)) {
return this._keys.get(key).get();
}
}
public getContext(domNode: HTMLElement): any {
return null;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册