|
| 1 | +/*--------------------------------------------------------------------------------------------- |
| 2 | + * Copyright (c) Microsoft Corporation. All rights reserved. |
| 3 | + * Licensed under the MIT License. See License.txt in the project root for license information. |
| 4 | + *--------------------------------------------------------------------------------------------*/ |
| 5 | + |
| 6 | +import * as assert from 'assert'; |
| 7 | +import { MultilineTokens2, SparseEncodedTokens } from 'vs/editor/common/model/tokensStore'; |
| 8 | +import { Range } from 'vs/editor/common/core/range'; |
| 9 | +import { TextModel } from 'vs/editor/common/model/textModel'; |
| 10 | +import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; |
| 11 | +import { MetadataConsts, TokenMetadata } from 'vs/editor/common/modes'; |
| 12 | + |
| 13 | +suite('TokensStore', () => { |
| 14 | + |
| 15 | + const SEMANTIC_COLOR = 5; |
| 16 | + |
| 17 | + function parseTokensState(state: string[]): { text: string; tokens: number[]; } { |
| 18 | + let text: string[] = []; |
| 19 | + let tokens: number[] = []; |
| 20 | + for (let i = 0; i < state.length; i++) { |
| 21 | + const line = state[i]; |
| 22 | + |
| 23 | + let startOffset = 0; |
| 24 | + let lineText = ''; |
| 25 | + while (true) { |
| 26 | + const firstPipeOffset = line.indexOf('|', startOffset); |
| 27 | + if (firstPipeOffset === -1) { |
| 28 | + break; |
| 29 | + } |
| 30 | + const secondPipeOffset = line.indexOf('|', firstPipeOffset + 1); |
| 31 | + if (secondPipeOffset === -1) { |
| 32 | + break; |
| 33 | + } |
| 34 | + if (firstPipeOffset + 1 === secondPipeOffset) { |
| 35 | + // skip || |
| 36 | + lineText += line.substring(startOffset, secondPipeOffset + 1); |
| 37 | + startOffset = secondPipeOffset + 1; |
| 38 | + continue; |
| 39 | + } |
| 40 | + |
| 41 | + lineText += line.substring(startOffset, firstPipeOffset); |
| 42 | + const tokenStartCharacter = lineText.length; |
| 43 | + const tokenLength = secondPipeOffset - firstPipeOffset - 1; |
| 44 | + const metadata = (SEMANTIC_COLOR << MetadataConsts.FOREGROUND_OFFSET); |
| 45 | + |
| 46 | + tokens.push(i, tokenStartCharacter, tokenStartCharacter + tokenLength, metadata); |
| 47 | + |
| 48 | + lineText += line.substr(firstPipeOffset + 1, tokenLength); |
| 49 | + startOffset = secondPipeOffset + 1; |
| 50 | + } |
| 51 | + |
| 52 | + lineText += line.substring(startOffset); |
| 53 | + |
| 54 | + text.push(lineText); |
| 55 | + } |
| 56 | + |
| 57 | + return { |
| 58 | + text: text.join('\n'), |
| 59 | + tokens: tokens |
| 60 | + }; |
| 61 | + } |
| 62 | + |
| 63 | + function extractState(model: TextModel): string[] { |
| 64 | + let result: string[] = []; |
| 65 | + for (let lineNumber = 1; lineNumber <= model.getLineCount(); lineNumber++) { |
| 66 | + const lineTokens = model.getLineTokens(lineNumber); |
| 67 | + const lineContent = model.getLineContent(lineNumber); |
| 68 | + |
| 69 | + let lineText = ''; |
| 70 | + for (let i = 0; i < lineTokens.getCount(); i++) { |
| 71 | + const tokenStartCharacter = lineTokens.getStartOffset(i); |
| 72 | + const tokenEndCharacter = lineTokens.getEndOffset(i); |
| 73 | + const metadata = lineTokens.getMetadata(i); |
| 74 | + const color = TokenMetadata.getForeground(metadata); |
| 75 | + const tokenText = lineContent.substring(tokenStartCharacter, tokenEndCharacter); |
| 76 | + if (color === SEMANTIC_COLOR) { |
| 77 | + lineText += `|${tokenText}|`; |
| 78 | + } else { |
| 79 | + lineText += tokenText; |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + result.push(lineText); |
| 84 | + } |
| 85 | + return result; |
| 86 | + } |
| 87 | + |
| 88 | + // function extractState |
| 89 | + |
| 90 | + function testTokensAdjustment(rawInitialState: string[], edits: IIdentifiedSingleEditOperation[], rawFinalState: string[]) { |
| 91 | + const initialState = parseTokensState(rawInitialState); |
| 92 | + const model = TextModel.createFromString(initialState.text); |
| 93 | + model.setSemanticTokens([new MultilineTokens2(1, new SparseEncodedTokens(new Uint32Array(initialState.tokens)))]); |
| 94 | + |
| 95 | + model.applyEdits(edits); |
| 96 | + |
| 97 | + const actualState = extractState(model); |
| 98 | + assert.deepEqual(actualState, rawFinalState); |
| 99 | + |
| 100 | + model.dispose(); |
| 101 | + } |
| 102 | + |
| 103 | + test('issue #86303 - color shifting between different tokens', () => { |
| 104 | + testTokensAdjustment( |
| 105 | + [ |
| 106 | + `import { |URI| } from 'vs/base/common/uri';`, |
| 107 | + `const foo = |URI|.parse('hey');` |
| 108 | + ], |
| 109 | + [ |
| 110 | + { range: new Range(2, 9, 2, 10), text: '' } |
| 111 | + ], |
| 112 | + [ |
| 113 | + `import { |URI| } from 'vs/base/common/uri';`, |
| 114 | + `const fo = |URI|.parse('hey');` |
| 115 | + ] |
| 116 | + ); |
| 117 | + }); |
| 118 | + |
| 119 | +}); |
0 commit comments