提交 d5610587 编写于 作者: A Alex Dima

Better handling of cross-mode bracket matching

上级 568c9e51
......@@ -1377,6 +1377,7 @@ export interface ITextModel {
}
export interface IRichEditBracket {
modeId: string;
open: string;
close: string;
forwardRegex: RegExp;
......
......@@ -998,9 +998,8 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke
private _findMatchingBracketUp(bracket:EditorCommon.IRichEditBracket, position:EditorCommon.IEditorPosition): Range {
// console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
let modeId = bracket.modeId;
let tokensMap = this._tokensInflatorMap;
// TODO@Alex: account for mode transitions
let reversedBracketRegex = bracket.reversedRegex;
let count = -1;
......@@ -1008,12 +1007,20 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke
let lineTokens = this._lines[lineNumber - 1].getTokens();
let lineText = this._lines[lineNumber - 1].text;
let tokens = lineTokens.getBinaryEncodedTokens();
let modeTransitions = this._lines[lineNumber - 1].getModeTransitions().toArray(this._mode);
let currentModeIndex = modeTransitions.length - 1;
let currentModeStart = modeTransitions[currentModeIndex].startIndex;
let currentModeId = modeTransitions[currentModeIndex].mode.getId();
let tokensLength = tokens.length - 1;
let currentTokenEnd = lineText.length;
if (lineNumber === position.lineNumber) {
tokensLength = lineTokens.findIndexOfOffset(position.column - 1);
currentTokenEnd = position.column - 1;
currentModeIndex = Arrays.findIndexInSegmentsArray(modeTransitions, position.column - 1);
currentModeStart = modeTransitions[currentModeIndex].startIndex;
currentModeId = modeTransitions[currentModeIndex].mode.getId();
}
for (let tokenIndex = tokensLength; tokenIndex >= 0; tokenIndex--) {
......@@ -1021,7 +1028,13 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke
let currentTokenType = getType(tokensMap, currentToken);
let currentTokenStart = getStartIndex(currentToken);
if (!ignoreBracketsInToken(currentTokenType)) {
if (currentTokenStart < currentModeStart) {
currentModeIndex--;
currentModeStart = modeTransitions[currentModeIndex].startIndex;
currentModeId = modeTransitions[currentModeIndex].mode.getId();
}
if (currentModeId === modeId && !ignoreBracketsInToken(currentTokenType)) {
while (true) {
let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd);
......@@ -1055,8 +1068,8 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke
private _findMatchingBracketDown(bracket:EditorCommon.IRichEditBracket, position:EditorCommon.IEditorPosition): Range {
// console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
let modeId = bracket.modeId;
let tokensMap = this._tokensInflatorMap;
// TODO@Alex: account for mode transitions
let bracketRegex = bracket.forwardRegex;
let count = 1;
......@@ -1064,12 +1077,20 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke
let lineTokens = this._lines[lineNumber - 1].getTokens();
let lineText = this._lines[lineNumber - 1].text;
let tokens = lineTokens.getBinaryEncodedTokens();
let modeTransitions = this._lines[lineNumber - 1].getModeTransitions().toArray(this._mode);
let currentModeIndex = 0;
let nextModeStart = (currentModeIndex + 1 < modeTransitions.length ? modeTransitions[currentModeIndex + 1].startIndex : lineText.length);
let currentModeId = modeTransitions[currentModeIndex].mode.getId();
let startTokenIndex = 0;
let currentTokenStart = getStartIndex(startTokenIndex);
if (lineNumber === position.lineNumber) {
startTokenIndex = lineTokens.findIndexOfOffset(position.column - 1);
currentTokenStart = Math.max(currentTokenStart, position.column - 1);
currentModeIndex = Arrays.findIndexInSegmentsArray(modeTransitions, position.column - 1);
nextModeStart = (currentModeIndex + 1 < modeTransitions.length ? modeTransitions[currentModeIndex + 1].startIndex : lineText.length);
currentModeId = modeTransitions[currentModeIndex].mode.getId();
}
for (let tokenIndex = startTokenIndex, tokensLength = tokens.length; tokenIndex < tokensLength; tokenIndex++) {
......@@ -1077,8 +1098,13 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke
let currentTokenType = getType(tokensMap, currentToken);
let currentTokenEnd = tokenIndex + 1 < tokensLength ? getStartIndex(tokens[tokenIndex + 1]) : lineText.length;
if (currentTokenStart >= nextModeStart) {
currentModeIndex++;
nextModeStart = (currentModeIndex + 1 < modeTransitions.length ? modeTransitions[currentModeIndex + 1].startIndex : lineText.length);
currentModeId = modeTransitions[currentModeIndex].mode.getId();
}
if (!ignoreBracketsInToken(currentTokenType)) {
if (currentModeId === modeId && !ignoreBracketsInToken(currentTokenType)) {
while (true) {
let r = BracketsUtils.findNextBracketInToken(bracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd);
if (!r) {
......
......@@ -84,6 +84,7 @@ export class Brackets {
this._modeId = modeId;
this._brackets = brackets.map((b) => {
return {
modeId: modeId,
open: b.open,
close: b.close,
forwardRegex: getRegexForBracketPair({ open: b.open, close: b.close }),
......
......@@ -347,11 +347,15 @@ export class HTMLMode<W extends htmlWorker.HTMLWorker> extends AbstractMode<W> i
},
brackets: [
['<!--', '-->']
['<!--', '-->'],
['<', '>'],
],
__electricCharacterSupport: {
brackets: [],
brackets: [
{ tokenType: 'bla', open: '<!--', close: '-->', isElectric: true },
{ tokenType: 'bla', open: '<', close: '>', isElectric: true }
],
caseInsensitive: true,
embeddedElectricCharacters: ['*', '}', ']', ')']
},
......
......@@ -17,6 +17,7 @@ import {getTag, DELIM_END, DELIM_START, DELIM_ASSIGN, ATTRIB_NAME, ATTRIB_VALUE,
import {getRawEnterActionAtPosition} from 'vs/editor/common/modes/supports/onEnter';
import {TextModelWithTokens} from 'vs/editor/common/model/textModelWithTokens';
import {TextModel} from 'vs/editor/common/model/textModel';
import {Range} from 'vs/editor/common/core/range';
suite('Colorizing - HTML', () => {
......@@ -653,5 +654,51 @@ suite('Colorizing - HTML', () => {
assertOnEnter('<span> </span>', 6, Modes.IndentAction.Indent);
assertOnEnter('<span> </span>', 7, Modes.IndentAction.IndentOutdent);
});
});
test('matchBracket', () => {
function toString(brackets:EditorCommon.IEditorRange[]): string[] {
if (!brackets) {
return null;
}
brackets.sort(Range.compareRangesUsingStarts);
return brackets.map(b => b.toString());
}
function assertBracket(lines:string[], lineNumber:number, column:number, expected:EditorCommon.IEditorRange[]): void {
let model = new TextModelWithTokens([], TextModel.toRawText(lines.join('\n')), false, _mode);
// force tokenization
model.getLineContext(model.getLineCount());
let actual = model.matchBracket({
lineNumber: lineNumber,
column: column
});
let actualStr = actual ? toString(actual.brackets) : null;
let expectedStr = toString(expected);
assert.deepEqual(actualStr, expectedStr, 'TEXT <<' + lines.join('\n') + '>>, POS: ' + lineNumber + ', ' + column);
}
assertBracket(['<p></p>'], 1, 1, [new Range(1, 1, 1, 2), new Range(1, 3, 1, 4)]);
assertBracket(['<p></p>'], 1, 2, [new Range(1, 1, 1, 2), new Range(1, 3, 1, 4)]);
assertBracket(['<p></p>'], 1, 3, [new Range(1, 1, 1, 2), new Range(1, 3, 1, 4)]);
assertBracket(['<p></p>'], 1, 4, [new Range(1, 1, 1, 2), new Range(1, 3, 1, 4)]);
assertBracket(['<p></p>'], 1, 5, [new Range(1, 4, 1, 5), new Range(1, 7, 1, 8)]);
assertBracket(['<p></p>'], 1, 6, null);
assertBracket(['<p></p>'], 1, 7, [new Range(1, 4, 1, 5), new Range(1, 7, 1, 8)]);
assertBracket(['<p></p>'], 1, 8, [new Range(1, 4, 1, 5), new Range(1, 7, 1, 8)]);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 10, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 11, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 22, null);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 23, null);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 33, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 34, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 10, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 11, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 22, null);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 23, null);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 33, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 34, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册