mirror of
https://github.com/Microsoft/vscode
synced 2024-11-05 18:29:38 +00:00
Fixes #95843
This commit is contained in:
parent
49a164e886
commit
11e96b2da8
2 changed files with 130 additions and 27 deletions
|
@ -2106,10 +2106,42 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||
return this._matchBracket(this.validatePosition(position));
|
||||
}
|
||||
|
||||
private _establishBracketSearchOffsets(position: Position, lineTokens: LineTokens, modeBrackets: RichEditBrackets, tokenIndex: number) {
|
||||
const tokenCount = lineTokens.getCount();
|
||||
const currentLanguageId = lineTokens.getLanguageId(tokenIndex);
|
||||
|
||||
// limit search to not go before `maxBracketLength`
|
||||
let searchStartOffset = Math.max(0, position.column - 1 - modeBrackets.maxBracketLength);
|
||||
for (let i = tokenIndex - 1; i >= 0; i--) {
|
||||
const tokenEndOffset = lineTokens.getEndOffset(i);
|
||||
if (tokenEndOffset <= searchStartOffset) {
|
||||
break;
|
||||
}
|
||||
if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i)) || lineTokens.getLanguageId(i) !== currentLanguageId) {
|
||||
searchStartOffset = tokenEndOffset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// limit search to not go after `maxBracketLength`
|
||||
let searchEndOffset = Math.min(lineTokens.getLineContent().length, position.column - 1 + modeBrackets.maxBracketLength);
|
||||
for (let i = tokenIndex + 1; i < tokenCount; i++) {
|
||||
const tokenStartOffset = lineTokens.getStartOffset(i);
|
||||
if (tokenStartOffset >= searchEndOffset) {
|
||||
break;
|
||||
}
|
||||
if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i)) || lineTokens.getLanguageId(i) !== currentLanguageId) {
|
||||
searchEndOffset = tokenStartOffset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { searchStartOffset, searchEndOffset };
|
||||
}
|
||||
|
||||
private _matchBracket(position: Position): [Range, Range] | null {
|
||||
const lineNumber = position.lineNumber;
|
||||
const lineTokens = this._getLineTokens(lineNumber);
|
||||
const tokenCount = lineTokens.getCount();
|
||||
const lineText = this._buffer.getLineContent(lineNumber);
|
||||
|
||||
const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
|
||||
|
@ -2120,19 +2152,8 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||
|
||||
// check that the token is not to be ignored
|
||||
if (currentModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex))) {
|
||||
// limit search to not go before `maxBracketLength`
|
||||
let searchStartOffset = Math.max(0, position.column - 1 - currentModeBrackets.maxBracketLength);
|
||||
for (let i = tokenIndex - 1; i >= 0; i--) {
|
||||
const tokenEndOffset = lineTokens.getEndOffset(i);
|
||||
if (tokenEndOffset <= searchStartOffset) {
|
||||
break;
|
||||
}
|
||||
if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i))) {
|
||||
searchStartOffset = tokenEndOffset;
|
||||
}
|
||||
}
|
||||
// limit search to not go after `maxBracketLength`
|
||||
const searchEndOffset = Math.min(lineText.length, position.column - 1 + currentModeBrackets.maxBracketLength);
|
||||
|
||||
let { searchStartOffset, searchEndOffset } = this._establishBracketSearchOffsets(position, lineTokens, currentModeBrackets, tokenIndex);
|
||||
|
||||
// it might be the case that [currentTokenStart -> currentTokenEnd] contains multiple brackets
|
||||
// `bestResult` will contain the most right-side result
|
||||
|
@ -2171,18 +2192,9 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||
|
||||
// check that previous token is not to be ignored
|
||||
if (prevModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(prevTokenIndex))) {
|
||||
// limit search in case previous token is very large, there's no need to go beyond `maxBracketLength`
|
||||
const searchStartOffset = Math.max(0, position.column - 1 - prevModeBrackets.maxBracketLength);
|
||||
let searchEndOffset = Math.min(lineText.length, position.column - 1 + prevModeBrackets.maxBracketLength);
|
||||
for (let i = prevTokenIndex + 1; i < tokenCount; i++) {
|
||||
const tokenStartOffset = lineTokens.getStartOffset(i);
|
||||
if (tokenStartOffset >= searchEndOffset) {
|
||||
break;
|
||||
}
|
||||
if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i))) {
|
||||
searchEndOffset = tokenStartOffset;
|
||||
}
|
||||
}
|
||||
|
||||
let { searchStartOffset, searchEndOffset } = this._establishBracketSearchOffsets(position, lineTokens, prevModeBrackets, prevTokenIndex);
|
||||
|
||||
const foundBracket = BracketsUtils.findPrevBracketInRange(prevModeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
|
||||
// check that we didn't hit a bracket too far away from position
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { TokenizationResult2 } from 'vs/editor/common/core/token';
|
||||
|
@ -337,6 +337,97 @@ suite('TextModelWithTokens', () => {
|
|||
registration.dispose();
|
||||
});
|
||||
|
||||
test('issue #95843: Highlighting of closing braces is indicating wrong brace when cursor is behind opening brace', () => {
|
||||
const mode1 = new LanguageIdentifier('testMode1', 3);
|
||||
const mode2 = new LanguageIdentifier('testMode2', 4);
|
||||
const otherMetadata1 = (
|
||||
(mode1.id << MetadataConsts.LANGUAGEID_OFFSET)
|
||||
| (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET)
|
||||
) >>> 0;
|
||||
const otherMetadata2 = (
|
||||
(mode2.id << MetadataConsts.LANGUAGEID_OFFSET)
|
||||
| (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET)
|
||||
) >>> 0;
|
||||
|
||||
const tokenizationSupport: ITokenizationSupport = {
|
||||
getInitialState: () => NULL_STATE,
|
||||
tokenize: undefined!,
|
||||
tokenize2: (line, hasEOL, state) => {
|
||||
switch (line) {
|
||||
case 'function f() {': {
|
||||
const tokens = new Uint32Array([
|
||||
0, otherMetadata1,
|
||||
8, otherMetadata1,
|
||||
9, otherMetadata1,
|
||||
10, otherMetadata1,
|
||||
11, otherMetadata1,
|
||||
12, otherMetadata1,
|
||||
13, otherMetadata1,
|
||||
]);
|
||||
return new TokenizationResult2(tokens, state);
|
||||
}
|
||||
case ' return <p>{true}</p>;': {
|
||||
const tokens = new Uint32Array([
|
||||
0, otherMetadata1,
|
||||
2, otherMetadata1,
|
||||
8, otherMetadata1,
|
||||
9, otherMetadata2,
|
||||
10, otherMetadata2,
|
||||
11, otherMetadata2,
|
||||
12, otherMetadata2,
|
||||
13, otherMetadata1,
|
||||
17, otherMetadata2,
|
||||
18, otherMetadata2,
|
||||
20, otherMetadata2,
|
||||
21, otherMetadata2,
|
||||
22, otherMetadata2,
|
||||
]);
|
||||
return new TokenizationResult2(tokens, state);
|
||||
}
|
||||
case '}': {
|
||||
const tokens = new Uint32Array([
|
||||
0, otherMetadata1
|
||||
]);
|
||||
return new TokenizationResult2(tokens, state);
|
||||
}
|
||||
}
|
||||
throw new Error(`Unexpected`);
|
||||
}
|
||||
};
|
||||
|
||||
const disposableStore = new DisposableStore();
|
||||
|
||||
disposableStore.add(TokenizationRegistry.register(mode1.language, tokenizationSupport));
|
||||
disposableStore.add(LanguageConfigurationRegistry.register(mode1, {
|
||||
brackets: [
|
||||
['{', '}'],
|
||||
['[', ']'],
|
||||
['(', ')']
|
||||
],
|
||||
}));
|
||||
disposableStore.add(LanguageConfigurationRegistry.register(mode2, {
|
||||
brackets: [
|
||||
['{', '}'],
|
||||
['[', ']'],
|
||||
['(', ')']
|
||||
],
|
||||
}));
|
||||
|
||||
const model = disposableStore.add(createTextModel([
|
||||
'function f() {',
|
||||
' return <p>{true}</p>;',
|
||||
'}',
|
||||
].join('\n'), undefined, mode1));
|
||||
|
||||
model.forceTokenization(1);
|
||||
model.forceTokenization(2);
|
||||
model.forceTokenization(3);
|
||||
|
||||
assert.deepStrictEqual(model.matchBracket(new Position(2, 14)), [new Range(2, 13, 2, 14), new Range(2, 18, 2, 19)]);
|
||||
|
||||
disposableStore.dispose();
|
||||
});
|
||||
|
||||
test('issue #88075: TypeScript brace matching is incorrect in `${}` strings', () => {
|
||||
const mode = new LanguageIdentifier('testMode', 3);
|
||||
const otherMetadata = (
|
||||
|
|
Loading…
Reference in a new issue