提交 223e0aab 编写于 作者: A Alex Dima

Fixes #2717: Be smart about where to insert the keybindings snippet

上级 b9a0b950
......@@ -26,6 +26,7 @@ import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/edito
import {ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference} from 'vs/editor/browser/editorBrowser';
import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions';
import {CodeSnippet, getSnippetController} from 'vs/editor/contrib/snippet/common/snippet';
import {SmartSnippetInserter} from 'vs/editor/contrib/defineKeybinding/common/smartSnippetInserter';
const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding");
const NLS_DEFINE_MESSAGE = nls.localize('defineKeybinding.initial', "Press desired key combination and ENTER");
......@@ -112,6 +113,10 @@ export class DefineKeybindingController implements editorCommon.IEditorContribut
'}{{}}'
].join('\n');
let smartInsertInfo = SmartSnippetInserter.insertSnippet(this._editor.getModel(), this._editor.getPosition());
snippetText = smartInsertInfo.prepend + snippetText + smartInsertInfo.append;
this._editor.setPosition(smartInsertInfo.position);
getSnippetController(this._editor).run(new CodeSnippet(snippetText), 0, 0);
}
......
/*---------------------------------------------------------------------------------------------
* 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 {JSONScanner, createScanner as createJSONScanner, SyntaxKind as JSONSyntaxKind} from 'vs/base/common/json';
import * as editorCommon from 'vs/editor/common/editorCommon';
import {Position} from 'vs/editor/common/core/position';
import {Range} from 'vs/editor/common/core/range';
export interface InsertSnippetResult {
position: Position;
prepend: string;
append: string;
}
export class SmartSnippetInserter {
private static hasOpenBrace(scanner:JSONScanner): boolean {
while (scanner.scan() !== JSONSyntaxKind.EOF) {
let kind = scanner.getToken();
if (kind === JSONSyntaxKind.OpenBraceToken) {
return true;
}
}
return false;
}
private static offsetToPosition(model:editorCommon.ITextModel, offset:number): Position {
let offsetBeforeLine = 0;
let eolLength = model.getEOL().length;
let lineCount = model.getLineCount();
for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) {
let lineTotalLength = model.getLineContent(lineNumber).length + eolLength;
let offsetAfterLine = offsetBeforeLine + lineTotalLength;
if (offsetAfterLine > offset) {
return new Position(
lineNumber,
offset - offsetBeforeLine + 1
);
}
offsetBeforeLine = offsetAfterLine;
}
return new Position(
lineCount,
model.getLineMaxColumn(lineCount)
);
}
public static insertSnippet(model:editorCommon.ITextModel, _position:Position): InsertSnippetResult {
let desiredPosition = model.getValueLengthInRange(new Range(1, 1, _position.lineNumber, _position.column));
// <INVALID> [ <BEFORE_OBJECT> { <INVALID> } <AFTER_OBJECT>, <BEFORE_OBJECT> { <INVALID> } <AFTER_OBJECT> ] <INVALID>
enum State {
INVALID = 0,
AFTER_OBJECT = 1,
BEFORE_OBJECT = 2,
}
let currentState = State.INVALID;
let lastValidPos = -1;
let lastValidState = State.INVALID;
let scanner = createJSONScanner(model.getValue());
let arrayLevel = 0;
let objLevel = 0;
let checkRangeStatus = (pos:number, state:State) => {
if (state !== State.INVALID && arrayLevel === 1 && objLevel === 0) {
currentState = state;
lastValidPos = pos;
lastValidState = state;
} else {
if (currentState !== State.INVALID) {
currentState = State.INVALID;
lastValidPos = scanner.getTokenOffset();
}
}
};
while (scanner.scan() !== JSONSyntaxKind.EOF) {
let currentPos = scanner.getPosition();
let kind = scanner.getToken();
let goodKind = false;
switch (kind) {
case JSONSyntaxKind.OpenBracketToken:
goodKind = true;
arrayLevel++;
checkRangeStatus(currentPos, State.BEFORE_OBJECT);
break;
case JSONSyntaxKind.CloseBracketToken:
goodKind = true;
arrayLevel--;
checkRangeStatus(currentPos, State.INVALID);
break;
case JSONSyntaxKind.CommaToken:
goodKind = true;
checkRangeStatus(currentPos, State.BEFORE_OBJECT);
break;
case JSONSyntaxKind.OpenBraceToken:
goodKind = true;
objLevel++;
checkRangeStatus(currentPos, State.INVALID);
break;
case JSONSyntaxKind.CloseBraceToken:
goodKind = true;
objLevel--;
checkRangeStatus(currentPos, State.AFTER_OBJECT);
break;
case JSONSyntaxKind.Trivia:
case JSONSyntaxKind.LineBreakTrivia:
goodKind = true;
}
if (currentPos >= desiredPosition && (currentState !== State.INVALID || lastValidPos !== -1)) {
let acceptPosition: number;
let acceptState: State;
if (currentState !== State.INVALID) {
acceptPosition = (goodKind ? currentPos : scanner.getTokenOffset());
acceptState = currentState;
} else {
acceptPosition = lastValidPos;
acceptState = lastValidState;
}
if (acceptState === State.AFTER_OBJECT) {
return {
position: this.offsetToPosition(model, acceptPosition),
prepend: ',',
append: ''
};
} else {
scanner.setPosition(acceptPosition);
return {
position: this.offsetToPosition(model, acceptPosition),
prepend: '',
append: this.hasOpenBrace(scanner) ? ',' : ''
};
}
}
}
// no valid position found!
let modelLineCount = model.getLineCount();
return {
position: new Position(modelLineCount, model.getLineMaxColumn(modelLineCount)),
prepend: '\n[',
append: ']'
};
}
}
/*---------------------------------------------------------------------------------------------
* 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 {SmartSnippetInserter} from 'vs/editor/contrib/defineKeybinding/common/smartSnippetInserter';
import {TextModel} from 'vs/editor/common/model/textModel';
import {Position} from 'vs/editor/common/core/position';
suite('SmartSnippetInserter', () => {
function testSmartSnippetInserter(text:string[], runner:(assert:(desiredPos:Position, pos:Position, prepend:string, append:string)=>void)=>void): void {
let model = new TextModel([], TextModel.toRawText(text.join('\n'), TextModel.DEFAULT_CREATION_OPTIONS));
runner((desiredPos, pos, prepend, append) => {
let actual = SmartSnippetInserter.insertSnippet(model, desiredPos);
let expected = {
position: pos,
prepend,
append
};
assert.deepEqual(actual, expected);
});
model.dispose();
}
test('empty text', () => {
testSmartSnippetInserter([
], (assert) => {
assert(new Position(1,1), new Position(1,1), '\n[', ']');
});
testSmartSnippetInserter([
' '
], (assert) => {
assert(new Position(1,1), new Position(1,2), '\n[', ']');
assert(new Position(1,2), new Position(1,2), '\n[', ']');
});
testSmartSnippetInserter([
'// just some text'
], (assert) => {
assert(new Position(1,1), new Position(1,18), '\n[', ']');
assert(new Position(1,18), new Position(1,18), '\n[', ']');
});
testSmartSnippetInserter([
'// just some text',
''
], (assert) => {
assert(new Position(1,1), new Position(2,1), '\n[', ']');
assert(new Position(1,18), new Position(2,1), '\n[', ']');
assert(new Position(2,1), new Position(2,1), '\n[', ']');
});
});
test('empty array 1', () => {
testSmartSnippetInserter([
'// just some text',
'[]'
], (assert) => {
assert(new Position(1,1), new Position(2,2), '', '');
assert(new Position(2,1), new Position(2,2), '', '');
assert(new Position(2,2), new Position(2,2), '', '');
assert(new Position(2,3), new Position(2,2), '', '');
});
});
test('empty array 2', () => {
testSmartSnippetInserter([
'// just some text',
'[',
']'
], (assert) => {
assert(new Position(1,1), new Position(2,2), '', '');
assert(new Position(2,1), new Position(2,2), '', '');
assert(new Position(2,2), new Position(2,2), '', '');
assert(new Position(3,1), new Position(3,1), '', '');
assert(new Position(3,2), new Position(3,1), '', '');
});
});
test('empty array 3', () => {
testSmartSnippetInserter([
'// just some text',
'[',
'// just some text',
']'
], (assert) => {
assert(new Position(1,1), new Position(2,2), '', '');
assert(new Position(2,1), new Position(2,2), '', '');
assert(new Position(2,2), new Position(2,2), '', '');
assert(new Position(3,1), new Position(3,1), '', '');
assert(new Position(3,2), new Position(3,1), '', '');
assert(new Position(4,1), new Position(4,1), '', '');
assert(new Position(4,2), new Position(4,1), '', '');
});
});
test('one element array 1', () => {
testSmartSnippetInserter([
'// just some text',
'[',
'{}',
']'
], (assert) => {
assert(new Position(1,1), new Position(2,2), '', ',');
assert(new Position(2,1), new Position(2,2), '', ',');
assert(new Position(2,2), new Position(2,2), '', ',');
assert(new Position(3,1), new Position(3,1), '', ',');
assert(new Position(3,2), new Position(3,1), '', ',');
assert(new Position(3,3), new Position(3,3), ',', '');
assert(new Position(4,1), new Position(4,1), ',', '');
assert(new Position(4,2), new Position(4,1), ',', '');
});
});
test('two elements array 1', () => {
testSmartSnippetInserter([
'// just some text',
'[',
'{},',
'{}',
']'
], (assert) => {
assert(new Position(1,1), new Position(2,2), '', ',');
assert(new Position(2,1), new Position(2,2), '', ',');
assert(new Position(2,2), new Position(2,2), '', ',');
assert(new Position(3,1), new Position(3,1), '', ',');
assert(new Position(3,2), new Position(3,1), '', ',');
assert(new Position(3,3), new Position(3,3), ',', '');
assert(new Position(3,4), new Position(3,4), '', ',');
assert(new Position(4,1), new Position(4,1), '', ',');
assert(new Position(4,2), new Position(4,1), '', ',');
assert(new Position(4,3), new Position(4,3), ',', '');
assert(new Position(5,1), new Position(5,1), ',', '');
assert(new Position(5,2), new Position(5,1), ',', '');
});
});
test('two elements array 2', () => {
testSmartSnippetInserter([
'// just some text',
'[',
'{},{}',
']'
], (assert) => {
assert(new Position(1,1), new Position(2,2), '', ',');
assert(new Position(2,1), new Position(2,2), '', ',');
assert(new Position(2,2), new Position(2,2), '', ',');
assert(new Position(3,1), new Position(3,1), '', ',');
assert(new Position(3,2), new Position(3,1), '', ',');
assert(new Position(3,3), new Position(3,3), ',', '');
assert(new Position(3,4), new Position(3,4), '', ',');
assert(new Position(3,5), new Position(3,4), '', ',');
assert(new Position(3,6), new Position(3,6), ',', '');
assert(new Position(4,1), new Position(4,1), ',', '');
assert(new Position(4,2), new Position(4,1), ',', '');
});
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册