Path: blob/main/src/vs/editor/test/common/model/tokenStore.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';7import { TextModel } from '../../../common/model/textModel.js';8import { TokenQuality, TokenStore } from '../../../common/model/tokens/treeSitter/tokenStore.js';910suite('TokenStore', () => {11let textModel: TextModel;12ensureNoDisposablesAreLeakedInTestSuite();1314setup(() => {15textModel = {16getValueLength: () => 1117} as TextModel;18});1920test('constructs with empty model', () => {21const store = new TokenStore(textModel);22assert.ok(store.root);23assert.strictEqual(store.root.length, textModel.getValueLength());24});2526test('builds store with single token', () => {27const store = new TokenStore(textModel);28store.buildStore([{29startOffsetInclusive: 0,30length: 5,31token: 132}], TokenQuality.Accurate);33assert.strictEqual(store.root.length, 5);34});3536test('builds store with multiple tokens', () => {37const store = new TokenStore(textModel);38store.buildStore([39{ startOffsetInclusive: 0, length: 3, token: 1 },40{ startOffsetInclusive: 3, length: 3, token: 2 },41{ startOffsetInclusive: 6, length: 4, token: 3 }42], TokenQuality.Accurate);43assert.ok(store.root);44assert.strictEqual(store.root.length, 10);45});4647test('creates balanced tree structure', () => {48const store = new TokenStore(textModel);49store.buildStore([50{ startOffsetInclusive: 0, length: 2, token: 1 },51{ startOffsetInclusive: 2, length: 2, token: 2 },52{ startOffsetInclusive: 4, length: 2, token: 3 },53{ startOffsetInclusive: 6, length: 2, token: 4 }54], TokenQuality.Accurate);5556const root = store.root as any;57assert.ok(root.children);58assert.strictEqual(root.children.length, 2);59assert.strictEqual(root.children[0].length, 4);60assert.strictEqual(root.children[1].length, 4);61});6263test('creates deep tree structure', () => {64const store = new TokenStore(textModel);65store.buildStore([66{ startOffsetInclusive: 0, length: 1, token: 1 },67{ startOffsetInclusive: 1, length: 1, token: 2 },68{ startOffsetInclusive: 2, length: 1, token: 3 },69{ startOffsetInclusive: 3, length: 1, token: 4 },70{ startOffsetInclusive: 4, length: 1, token: 5 },71{ startOffsetInclusive: 5, length: 1, token: 6 },72{ startOffsetInclusive: 6, length: 1, token: 7 },73{ startOffsetInclusive: 7, length: 1, token: 8 }74], TokenQuality.Accurate);7576const root = store.root as any;77assert.ok(root.children);78assert.strictEqual(root.children.length, 2);79assert.ok(root.children[0].children);80assert.strictEqual(root.children[0].children.length, 2);81assert.ok(root.children[0].children[0].children);82assert.strictEqual(root.children[0].children[0].children.length, 2);83});8485test('updates single token in middle', () => {86const store = new TokenStore(textModel);87store.buildStore([88{ startOffsetInclusive: 0, length: 3, token: 1 },89{ startOffsetInclusive: 3, length: 3, token: 2 },90{ startOffsetInclusive: 6, length: 3, token: 3 }91], TokenQuality.Accurate);9293store.update(3, [94{ startOffsetInclusive: 3, length: 3, token: 4 }95], TokenQuality.Accurate);9697const tokens = store.root as any;98assert.strictEqual(tokens.children[0].token, 1);99assert.strictEqual(tokens.children[1].token, 4);100assert.strictEqual(tokens.children[2].token, 3);101});102103test('updates multiple consecutive tokens', () => {104const store = new TokenStore(textModel);105store.buildStore([106{ startOffsetInclusive: 0, length: 3, token: 1 },107{ startOffsetInclusive: 3, length: 3, token: 2 },108{ startOffsetInclusive: 6, length: 3, token: 3 }109], TokenQuality.Accurate);110111store.update(6, [112{ startOffsetInclusive: 3, length: 3, token: 4 },113{ startOffsetInclusive: 6, length: 3, token: 5 }114], TokenQuality.Accurate);115116const tokens = store.root as any;117assert.strictEqual(tokens.children[0].token, 1);118assert.strictEqual(tokens.children[1].token, 4);119assert.strictEqual(tokens.children[2].token, 5);120});121122test('updates tokens at start of document', () => {123const store = new TokenStore(textModel);124store.buildStore([125{ startOffsetInclusive: 0, length: 3, token: 1 },126{ startOffsetInclusive: 3, length: 3, token: 2 },127{ startOffsetInclusive: 6, length: 3, token: 3 }128], TokenQuality.Accurate);129130store.update(3, [131{ startOffsetInclusive: 0, length: 3, token: 4 }132], TokenQuality.Accurate);133134const tokens = store.root as any;135assert.strictEqual(tokens.children[0].token, 4);136assert.strictEqual(tokens.children[1].token, 2);137assert.strictEqual(tokens.children[2].token, 3);138});139140test('updates tokens at end of document', () => {141const store = new TokenStore(textModel);142store.buildStore([143{ startOffsetInclusive: 0, length: 3, token: 1 },144{ startOffsetInclusive: 3, length: 3, token: 2 },145{ startOffsetInclusive: 6, length: 3, token: 3 }146], TokenQuality.Accurate);147148store.update(3, [149{ startOffsetInclusive: 6, length: 3, token: 4 }150], TokenQuality.Accurate);151152const tokens = store.root as any;153assert.strictEqual(tokens.children[0].token, 1);154assert.strictEqual(tokens.children[1].token, 2);155assert.strictEqual(tokens.children[2].token, 4);156});157158test('updates length of tokens', () => {159const store = new TokenStore(textModel);160store.buildStore([161{ startOffsetInclusive: 0, length: 3, token: 1 },162{ startOffsetInclusive: 3, length: 3, token: 2 },163{ startOffsetInclusive: 6, length: 3, token: 3 }164], TokenQuality.Accurate);165166store.update(6, [167{ startOffsetInclusive: 3, length: 5, token: 4 }168], TokenQuality.Accurate);169170const tokens = store.root as any;171assert.strictEqual(tokens.children[0].token, 1);172assert.strictEqual(tokens.children[0].length, 3);173assert.strictEqual(tokens.children[1].token, 4);174assert.strictEqual(tokens.children[1].length, 5);175});176177test('update deeply nested tree with new token length in the middle', () => {178const store = new TokenStore(textModel);179store.buildStore([180{ startOffsetInclusive: 0, length: 1, token: 1 },181{ startOffsetInclusive: 1, length: 1, token: 2 },182{ startOffsetInclusive: 2, length: 1, token: 3 },183{ startOffsetInclusive: 3, length: 1, token: 4 },184{ startOffsetInclusive: 4, length: 1, token: 5 },185{ startOffsetInclusive: 5, length: 1, token: 6 },186{ startOffsetInclusive: 6, length: 1, token: 7 },187{ startOffsetInclusive: 7, length: 1, token: 8 }188], TokenQuality.Accurate);189190// Update token in the middle (position 3-4) to span 3-6191store.update(3, [192{ startOffsetInclusive: 3, length: 3, token: 9 }193], TokenQuality.Accurate);194195const root = store.root as any;196// Verify the structure remains balanced197assert.strictEqual(root.children.length, 3);198assert.strictEqual(root.children[0].children.length, 2);199200// Verify the lengths are updated correctly201assert.strictEqual(root.children[0].length, 2); // First 2 tokens202assert.strictEqual(root.children[1].length, 4); // Token 3 + our new longer token203assert.strictEqual(root.children[2].length, 2); // Last 2 tokens204});205206test('update deeply nested tree with a range of tokens that causes tokens to split', () => {207const store = new TokenStore(textModel);208store.buildStore([209{ startOffsetInclusive: 0, length: 3, token: 1 },210{ startOffsetInclusive: 3, length: 3, token: 2 },211{ startOffsetInclusive: 6, length: 4, token: 3 },212{ startOffsetInclusive: 10, length: 5, token: 4 },213{ startOffsetInclusive: 15, length: 4, token: 5 },214{ startOffsetInclusive: 19, length: 3, token: 6 },215{ startOffsetInclusive: 22, length: 5, token: 7 },216{ startOffsetInclusive: 27, length: 3, token: 8 }217], TokenQuality.Accurate);218219// Update token in the middle which causes tokens to split220store.update(8, [221{ startOffsetInclusive: 12, length: 4, token: 9 },222{ startOffsetInclusive: 16, length: 4, token: 10 }223], TokenQuality.Accurate);224225const root = store.root as any;226// Verify the structure remains balanced227assert.strictEqual(root.children.length, 2);228assert.strictEqual(root.children[0].children.length, 2);229230// Verify the lengths are updated correctly231assert.strictEqual(root.children[0].length, 12);232assert.strictEqual(root.children[1].length, 18);233});234235test('getTokensInRange returns tokens in middle of document', () => {236const store = new TokenStore(textModel);237store.buildStore([238{ startOffsetInclusive: 0, length: 3, token: 1 },239{ startOffsetInclusive: 3, length: 3, token: 2 },240{ startOffsetInclusive: 6, length: 3, token: 3 }241], TokenQuality.Accurate);242243const tokens = store.getTokensInRange(3, 6);244assert.deepStrictEqual(tokens, [{ startOffsetInclusive: 3, length: 3, token: 2 }]);245});246247test('getTokensInRange returns tokens at start of document', () => {248const store = new TokenStore(textModel);249store.buildStore([250{ startOffsetInclusive: 0, length: 3, token: 1 },251{ startOffsetInclusive: 3, length: 3, token: 2 },252{ startOffsetInclusive: 6, length: 3, token: 3 }253], TokenQuality.Accurate);254255const tokens = store.getTokensInRange(0, 3);256assert.deepStrictEqual(tokens, [{ startOffsetInclusive: 0, length: 3, token: 1 }]);257});258259test('getTokensInRange returns tokens at end of document', () => {260const store = new TokenStore(textModel);261store.buildStore([262{ startOffsetInclusive: 0, length: 3, token: 1 },263{ startOffsetInclusive: 3, length: 3, token: 2 },264{ startOffsetInclusive: 6, length: 3, token: 3 }265], TokenQuality.Accurate);266267const tokens = store.getTokensInRange(6, 9);268assert.deepStrictEqual(tokens, [{ startOffsetInclusive: 6, length: 3, token: 3 }]);269});270271test('getTokensInRange returns multiple tokens across nodes', () => {272const store = new TokenStore(textModel);273store.buildStore([274{ startOffsetInclusive: 0, length: 1, token: 1 },275{ startOffsetInclusive: 1, length: 1, token: 2 },276{ startOffsetInclusive: 2, length: 1, token: 3 },277{ startOffsetInclusive: 3, length: 1, token: 4 },278{ startOffsetInclusive: 4, length: 1, token: 5 },279{ startOffsetInclusive: 5, length: 1, token: 6 }280], TokenQuality.Accurate);281282const tokens = store.getTokensInRange(2, 5);283assert.deepStrictEqual(tokens, [284{ startOffsetInclusive: 2, length: 1, token: 3 },285{ startOffsetInclusive: 3, length: 1, token: 4 },286{ startOffsetInclusive: 4, length: 1, token: 5 }287]);288});289290test('Realistic scenario one', () => {291// inspired by this snippet, with the update adding a space in the constructor's curly braces:292// /*293// */294// class XY {295// constructor() {}296// }297298const store = new TokenStore(textModel);299store.buildStore([300{ startOffsetInclusive: 0, length: 3, token: 164164 },301{ startOffsetInclusive: 3, length: 1, token: 32836 },302{ startOffsetInclusive: 4, length: 3, token: 164164 },303{ startOffsetInclusive: 7, length: 2, token: 32836 },304{ startOffsetInclusive: 9, length: 5, token: 196676 },305{ startOffsetInclusive: 14, length: 1, token: 32836 },306{ startOffsetInclusive: 15, length: 2, token: 557124 },307{ startOffsetInclusive: 17, length: 4, token: 32836 },308{ startOffsetInclusive: 21, length: 1, token: 32836 },309{ startOffsetInclusive: 22, length: 11, token: 196676 },310{ startOffsetInclusive: 33, length: 7, token: 32836 },311{ startOffsetInclusive: 40, length: 3, token: 32836 }312], TokenQuality.Accurate);313314store.update(33, [315{ startOffsetInclusive: 9, length: 5, token: 196676 },316{ startOffsetInclusive: 14, length: 1, token: 32836 },317{ startOffsetInclusive: 15, length: 2, token: 557124 },318{ startOffsetInclusive: 17, length: 4, token: 32836 },319{ startOffsetInclusive: 21, length: 1, token: 32836 },320{ startOffsetInclusive: 22, length: 11, token: 196676 },321{ startOffsetInclusive: 33, length: 8, token: 32836 },322{ startOffsetInclusive: 41, length: 3, token: 32836 }323], TokenQuality.Accurate);324325});326test('Realistic scenario two', () => {327// inspired by this snippet, with the update deleteing the space in the body of class x328// class x {329//330// }331// class y {332333// }334335const store = new TokenStore(textModel);336store.buildStore([337{ startOffsetInclusive: 0, length: 5, token: 196676 },338{ startOffsetInclusive: 5, length: 1, token: 32836 },339{ startOffsetInclusive: 6, length: 1, token: 557124 },340{ startOffsetInclusive: 7, length: 4, token: 32836 },341{ startOffsetInclusive: 11, length: 3, token: 32836 },342{ startOffsetInclusive: 14, length: 3, token: 32836 },343{ startOffsetInclusive: 17, length: 5, token: 196676 },344{ startOffsetInclusive: 22, length: 1, token: 32836 },345{ startOffsetInclusive: 23, length: 1, token: 557124 },346{ startOffsetInclusive: 24, length: 4, token: 32836 },347{ startOffsetInclusive: 28, length: 2, token: 32836 },348{ startOffsetInclusive: 30, length: 1, token: 32836 }349], TokenQuality.Accurate);350const tokens0 = store.getTokensInRange(0, 16);351assert.deepStrictEqual(tokens0, [352{ token: 196676, startOffsetInclusive: 0, length: 5 },353{ token: 32836, startOffsetInclusive: 5, length: 1 },354{ token: 557124, startOffsetInclusive: 6, length: 1 },355{ token: 32836, startOffsetInclusive: 7, length: 4 },356{ token: 32836, startOffsetInclusive: 11, length: 3 },357{ token: 32836, startOffsetInclusive: 14, length: 2 }358]);359360store.update(14, [361{ startOffsetInclusive: 0, length: 5, token: 196676 },362{ startOffsetInclusive: 5, length: 1, token: 32836 },363{ startOffsetInclusive: 6, length: 1, token: 557124 },364{ startOffsetInclusive: 7, length: 4, token: 32836 },365{ startOffsetInclusive: 11, length: 2, token: 32836 },366{ startOffsetInclusive: 13, length: 3, token: 32836 }367], TokenQuality.Accurate);368369const tokens = store.getTokensInRange(0, 16);370assert.deepStrictEqual(tokens, [371{ token: 196676, startOffsetInclusive: 0, length: 5 },372{ token: 32836, startOffsetInclusive: 5, length: 1 },373{ token: 557124, startOffsetInclusive: 6, length: 1 },374{ token: 32836, startOffsetInclusive: 7, length: 4 },375{ token: 32836, startOffsetInclusive: 11, length: 2 },376{ token: 32836, startOffsetInclusive: 13, length: 3 }377]);378});379test('Realistic scenario three', () => {380// inspired by this snippet, with the update adding a space after the { in the constructor381// /*--382// --*/383// class TreeViewPane {384// constructor(385// options: IViewletViewOptions,386// ) {387// }388// }389390391const store = new TokenStore(textModel);392store.buildStore([393{ startOffsetInclusive: 0, length: 5, token: 164164 },394{ startOffsetInclusive: 5, length: 1, token: 32836 },395{ startOffsetInclusive: 6, length: 5, token: 164164 },396{ startOffsetInclusive: 11, length: 2, token: 32836 },397{ startOffsetInclusive: 13, length: 5, token: 196676 },398{ startOffsetInclusive: 18, length: 1, token: 32836 },399{ startOffsetInclusive: 19, length: 12, token: 557124 },400{ startOffsetInclusive: 31, length: 4, token: 32836 },401{ startOffsetInclusive: 35, length: 1, token: 32836 },402{ startOffsetInclusive: 36, length: 11, token: 196676 },403{ startOffsetInclusive: 47, length: 3, token: 32836 },404{ startOffsetInclusive: 50, length: 2, token: 32836 },405{ startOffsetInclusive: 52, length: 7, token: 327748 },406{ startOffsetInclusive: 59, length: 1, token: 98372 },407{ startOffsetInclusive: 60, length: 1, token: 32836 },408{ startOffsetInclusive: 61, length: 19, token: 557124 },409{ startOffsetInclusive: 80, length: 1, token: 32836 },410{ startOffsetInclusive: 81, length: 2, token: 32836 },411{ startOffsetInclusive: 83, length: 6, token: 32836 },412{ startOffsetInclusive: 89, length: 4, token: 32836 },413{ startOffsetInclusive: 93, length: 3, token: 32836 }414], TokenQuality.Accurate);415const tokens0 = store.getTokensInRange(36, 59);416assert.deepStrictEqual(tokens0, [417{ token: 196676, startOffsetInclusive: 36, length: 11 },418{ token: 32836, startOffsetInclusive: 47, length: 3 },419{ token: 32836, startOffsetInclusive: 50, length: 2 },420{ token: 327748, startOffsetInclusive: 52, length: 7 }421]);422423store.update(82, [424{ startOffsetInclusive: 13, length: 5, token: 196676 },425{ startOffsetInclusive: 18, length: 1, token: 32836 },426{ startOffsetInclusive: 19, length: 12, token: 557124 },427{ startOffsetInclusive: 31, length: 4, token: 32836 },428{ startOffsetInclusive: 35, length: 1, token: 32836 },429{ startOffsetInclusive: 36, length: 11, token: 196676 },430{ startOffsetInclusive: 47, length: 3, token: 32836 },431{ startOffsetInclusive: 50, length: 2, token: 32836 },432{ startOffsetInclusive: 52, length: 7, token: 327748 },433{ startOffsetInclusive: 59, length: 1, token: 98372 },434{ startOffsetInclusive: 60, length: 1, token: 32836 },435{ startOffsetInclusive: 61, length: 19, token: 557124 },436{ startOffsetInclusive: 80, length: 1, token: 32836 },437{ startOffsetInclusive: 81, length: 2, token: 32836 },438{ startOffsetInclusive: 83, length: 7, token: 32836 },439{ startOffsetInclusive: 90, length: 4, token: 32836 },440{ startOffsetInclusive: 94, length: 3, token: 32836 }441], TokenQuality.Accurate);442443const tokens = store.getTokensInRange(36, 59);444assert.deepStrictEqual(tokens, [445{ token: 196676, startOffsetInclusive: 36, length: 11 },446{ token: 32836, startOffsetInclusive: 47, length: 3 },447{ token: 32836, startOffsetInclusive: 50, length: 2 },448{ token: 327748, startOffsetInclusive: 52, length: 7 }449]);450});451test('Realistic scenario four', () => {452// inspired by this snippet, with the update adding a new line after the return true;453// function x() {454// return true;455// }456457// class Y {458// private z = false;459// }460461const store = new TokenStore(textModel);462store.buildStore([463{ startOffsetInclusive: 0, length: 8, token: 196676 },464{ startOffsetInclusive: 8, length: 1, token: 32836 },465{ startOffsetInclusive: 9, length: 1, token: 524356 },466{ startOffsetInclusive: 10, length: 6, token: 32836 },467{ startOffsetInclusive: 16, length: 1, token: 32836 },468{ startOffsetInclusive: 17, length: 6, token: 589892 },469{ startOffsetInclusive: 23, length: 1, token: 32836 },470{ startOffsetInclusive: 24, length: 4, token: 196676 },471{ startOffsetInclusive: 28, length: 1, token: 32836 },472{ startOffsetInclusive: 29, length: 2, token: 32836 },473{ startOffsetInclusive: 31, length: 3, token: 32836 }, // This is the closing curly brace + newline chars474{ startOffsetInclusive: 34, length: 2, token: 32836 },475{ startOffsetInclusive: 36, length: 5, token: 196676 },476{ startOffsetInclusive: 41, length: 1, token: 32836 },477{ startOffsetInclusive: 42, length: 1, token: 557124 },478{ startOffsetInclusive: 43, length: 4, token: 32836 },479{ startOffsetInclusive: 47, length: 1, token: 32836 },480{ startOffsetInclusive: 48, length: 7, token: 196676 },481{ startOffsetInclusive: 55, length: 1, token: 32836 },482{ startOffsetInclusive: 56, length: 1, token: 327748 },483{ startOffsetInclusive: 57, length: 1, token: 32836 },484{ startOffsetInclusive: 58, length: 1, token: 98372 },485{ startOffsetInclusive: 59, length: 1, token: 32836 },486{ startOffsetInclusive: 60, length: 5, token: 196676 },487{ startOffsetInclusive: 65, length: 1, token: 32836 },488{ startOffsetInclusive: 66, length: 2, token: 32836 },489{ startOffsetInclusive: 68, length: 1, token: 32836 }490], TokenQuality.Accurate);491const tokens0 = store.getTokensInRange(36, 59);492assert.deepStrictEqual(tokens0, [493{ startOffsetInclusive: 36, length: 5, token: 196676 },494{ startOffsetInclusive: 41, length: 1, token: 32836 },495{ startOffsetInclusive: 42, length: 1, token: 557124 },496{ startOffsetInclusive: 43, length: 4, token: 32836 },497{ startOffsetInclusive: 47, length: 1, token: 32836 },498{ startOffsetInclusive: 48, length: 7, token: 196676 },499{ startOffsetInclusive: 55, length: 1, token: 32836 },500{ startOffsetInclusive: 56, length: 1, token: 327748 },501{ startOffsetInclusive: 57, length: 1, token: 32836 },502{ startOffsetInclusive: 58, length: 1, token: 98372 }503]);504505// insert a tab + new line after `return true;` (like hitting enter after the ;)506store.update(32, [507{ startOffsetInclusive: 0, length: 8, token: 196676 },508{ startOffsetInclusive: 8, length: 1, token: 32836 },509{ startOffsetInclusive: 9, length: 1, token: 524356 },510{ startOffsetInclusive: 10, length: 6, token: 32836 },511{ startOffsetInclusive: 16, length: 1, token: 32836 },512{ startOffsetInclusive: 17, length: 6, token: 589892 },513{ startOffsetInclusive: 23, length: 1, token: 32836 },514{ startOffsetInclusive: 24, length: 4, token: 196676 },515{ startOffsetInclusive: 28, length: 1, token: 32836 },516{ startOffsetInclusive: 29, length: 2, token: 32836 },517{ startOffsetInclusive: 31, length: 3, token: 32836 }, // This is the new line, which consists of 3 characters: \t\r\n518{ startOffsetInclusive: 34, length: 2, token: 32836 }519], TokenQuality.Accurate);520521const tokens1 = store.getTokensInRange(36, 59);522assert.deepStrictEqual(tokens1, [523{ startOffsetInclusive: 36, length: 2, token: 32836 },524{ startOffsetInclusive: 38, length: 2, token: 32836 },525{ startOffsetInclusive: 40, length: 5, token: 196676 },526{ startOffsetInclusive: 45, length: 1, token: 32836 },527{ startOffsetInclusive: 46, length: 1, token: 557124 },528{ startOffsetInclusive: 47, length: 4, token: 32836 },529{ startOffsetInclusive: 51, length: 1, token: 32836 },530{ startOffsetInclusive: 52, length: 7, token: 196676 }531]);532533// Delete the tab character534store.update(37, [535{ startOffsetInclusive: 0, length: 8, token: 196676 },536{ startOffsetInclusive: 8, length: 1, token: 32836 },537{ startOffsetInclusive: 9, length: 1, token: 524356 },538{ startOffsetInclusive: 10, length: 6, token: 32836 },539{ startOffsetInclusive: 16, length: 1, token: 32836 },540{ startOffsetInclusive: 17, length: 6, token: 589892 },541{ startOffsetInclusive: 23, length: 1, token: 32836 },542{ startOffsetInclusive: 24, length: 4, token: 196676 },543{ startOffsetInclusive: 28, length: 1, token: 32836 },544{ startOffsetInclusive: 29, length: 2, token: 32836 },545{ startOffsetInclusive: 31, length: 2, token: 32836 }, // This is the changed line: \t\r\n to \r\n546{ startOffsetInclusive: 33, length: 3, token: 32836 }547], TokenQuality.Accurate);548549const tokens2 = store.getTokensInRange(36, 59);550assert.deepStrictEqual(tokens2, [551{ startOffsetInclusive: 36, length: 1, token: 32836 },552{ startOffsetInclusive: 37, length: 2, token: 32836 },553{ startOffsetInclusive: 39, length: 5, token: 196676 },554{ startOffsetInclusive: 44, length: 1, token: 32836 },555{ startOffsetInclusive: 45, length: 1, token: 557124 },556{ startOffsetInclusive: 46, length: 4, token: 32836 },557{ startOffsetInclusive: 50, length: 1, token: 32836 },558{ startOffsetInclusive: 51, length: 7, token: 196676 },559{ startOffsetInclusive: 58, length: 1, token: 32836 }560]);561562});563564test('Insert new line and remove tabs (split tokens)', () => {565// class A {566// a() {567// }568// }569//570// interface I {571//572// }573574const store = new TokenStore(textModel);575store.buildStore([576{ startOffsetInclusive: 0, length: 5, token: 196676 },577{ startOffsetInclusive: 5, length: 1, token: 32836 },578{ startOffsetInclusive: 6, length: 1, token: 557124 },579{ startOffsetInclusive: 7, length: 3, token: 32836 },580{ startOffsetInclusive: 10, length: 1, token: 32836 },581{ startOffsetInclusive: 11, length: 1, token: 524356 },582{ startOffsetInclusive: 12, length: 5, token: 32836 },583{ startOffsetInclusive: 17, length: 3, token: 32836 }, // This is the closing curly brace line of a()584{ startOffsetInclusive: 20, length: 2, token: 32836 },585{ startOffsetInclusive: 22, length: 1, token: 32836 },586{ startOffsetInclusive: 23, length: 9, token: 196676 },587{ startOffsetInclusive: 32, length: 1, token: 32836 },588{ startOffsetInclusive: 33, length: 1, token: 557124 },589{ startOffsetInclusive: 34, length: 3, token: 32836 },590{ startOffsetInclusive: 37, length: 1, token: 32836 },591{ startOffsetInclusive: 38, length: 1, token: 32836 }592], TokenQuality.Accurate);593594const tokens0 = store.getTokensInRange(23, 39);595assert.deepStrictEqual(tokens0, [596{ startOffsetInclusive: 23, length: 9, token: 196676 },597{ startOffsetInclusive: 32, length: 1, token: 32836 },598{ startOffsetInclusive: 33, length: 1, token: 557124 },599{ startOffsetInclusive: 34, length: 3, token: 32836 },600{ startOffsetInclusive: 37, length: 1, token: 32836 },601{ startOffsetInclusive: 38, length: 1, token: 32836 }602]);603604// Insert a new line after a() { }, which will add 2 tabs605store.update(21, [606{ startOffsetInclusive: 0, length: 5, token: 196676 },607{ startOffsetInclusive: 5, length: 1, token: 32836 },608{ startOffsetInclusive: 6, length: 1, token: 557124 },609{ startOffsetInclusive: 7, length: 3, token: 32836 },610{ startOffsetInclusive: 10, length: 1, token: 32836 },611{ startOffsetInclusive: 11, length: 1, token: 524356 },612{ startOffsetInclusive: 12, length: 5, token: 32836 },613{ startOffsetInclusive: 17, length: 3, token: 32836 },614{ startOffsetInclusive: 20, length: 3, token: 32836 },615{ startOffsetInclusive: 23, length: 1, token: 32836 }616], TokenQuality.Accurate);617618const tokens1 = store.getTokensInRange(26, 42);619assert.deepStrictEqual(tokens1, [620{ startOffsetInclusive: 26, length: 9, token: 196676 },621{ startOffsetInclusive: 35, length: 1, token: 32836 },622{ startOffsetInclusive: 36, length: 1, token: 557124 },623{ startOffsetInclusive: 37, length: 3, token: 32836 },624{ startOffsetInclusive: 40, length: 1, token: 32836 },625{ startOffsetInclusive: 41, length: 1, token: 32836 }626]);627628// Insert another new line at the cursor, which will also cause the 2 tabs to be deleted629store.update(24, [630{ startOffsetInclusive: 0, length: 5, token: 196676 },631{ startOffsetInclusive: 5, length: 1, token: 32836 },632{ startOffsetInclusive: 6, length: 1, token: 557124 },633{ startOffsetInclusive: 7, length: 3, token: 32836 },634{ startOffsetInclusive: 10, length: 1, token: 32836 },635{ startOffsetInclusive: 11, length: 1, token: 524356 },636{ startOffsetInclusive: 12, length: 5, token: 32836 },637{ startOffsetInclusive: 17, length: 3, token: 32836 },638{ startOffsetInclusive: 20, length: 1, token: 32836 },639{ startOffsetInclusive: 21, length: 2, token: 32836 },640{ startOffsetInclusive: 23, length: 1, token: 32836 }641], TokenQuality.Accurate);642643const tokens2 = store.getTokensInRange(26, 42);644assert.deepStrictEqual(tokens2, [645{ startOffsetInclusive: 26, length: 9, token: 196676 },646{ startOffsetInclusive: 35, length: 1, token: 32836 },647{ startOffsetInclusive: 36, length: 1, token: 557124 },648{ startOffsetInclusive: 37, length: 3, token: 32836 },649{ startOffsetInclusive: 40, length: 1, token: 32836 },650{ startOffsetInclusive: 41, length: 1, token: 32836 }651]);652});653654test('delete removes tokens in the middle', () => {655const store = new TokenStore(textModel);656store.buildStore([657{ startOffsetInclusive: 0, length: 3, token: 1 },658{ startOffsetInclusive: 3, length: 3, token: 2 },659{ startOffsetInclusive: 6, length: 3, token: 3 }660], TokenQuality.Accurate);661store.delete(3, 3); // delete 3 chars starting at offset 3662const tokens = store.getTokensInRange(0, 9);663assert.deepStrictEqual(tokens, [664{ startOffsetInclusive: 0, length: 3, token: 1 },665{ startOffsetInclusive: 3, length: 3, token: 3 }666]);667});668669test('delete merges partially affected token', () => {670const store = new TokenStore(textModel);671store.buildStore([672{ startOffsetInclusive: 0, length: 5, token: 1 },673{ startOffsetInclusive: 5, length: 5, token: 2 }674], TokenQuality.Accurate);675store.delete(3, 4); // removes 4 chars within token 1 and partially token 2676const tokens = store.getTokensInRange(0, 10);677assert.deepStrictEqual(tokens, [678{ startOffsetInclusive: 0, length: 4, token: 1 },679// token 2 is now shifted left by 4680{ startOffsetInclusive: 4, length: 3, token: 2 }681]);682});683684test('replace a token with a slightly larger token', () => {685const store = new TokenStore(textModel);686store.buildStore([687{ startOffsetInclusive: 0, length: 5, token: 1 },688{ startOffsetInclusive: 5, length: 1, token: 2 },689{ startOffsetInclusive: 6, length: 1, token: 2 },690{ startOffsetInclusive: 7, length: 17, token: 2 },691{ startOffsetInclusive: 24, length: 1, token: 2 },692{ startOffsetInclusive: 25, length: 5, token: 2 },693{ startOffsetInclusive: 30, length: 1, token: 2 },694{ startOffsetInclusive: 31, length: 1, token: 2 },695{ startOffsetInclusive: 32, length: 5, token: 2 }696], TokenQuality.Accurate);697store.update(17, [{ startOffsetInclusive: 7, length: 19, token: 0 }], TokenQuality.Accurate); // removes 4 chars within token 1 and partially token 2698const tokens = store.getTokensInRange(0, 39);699assert.deepStrictEqual(tokens, [700{ startOffsetInclusive: 0, length: 5, token: 1 },701{ startOffsetInclusive: 5, length: 1, token: 2 },702{ startOffsetInclusive: 6, length: 1, token: 2 },703{ startOffsetInclusive: 7, length: 19, token: 0 },704{ startOffsetInclusive: 26, length: 1, token: 2 },705{ startOffsetInclusive: 27, length: 5, token: 2 },706{ startOffsetInclusive: 32, length: 1, token: 2 },707{ startOffsetInclusive: 33, length: 1, token: 2 },708{ startOffsetInclusive: 34, length: 5, token: 2 }709]);710});711712test('replace a character from a large token', () => {713const store = new TokenStore(textModel);714store.buildStore([715{ startOffsetInclusive: 0, length: 2, token: 1 },716{ startOffsetInclusive: 2, length: 5, token: 2 },717{ startOffsetInclusive: 7, length: 1, token: 3 }718], TokenQuality.Accurate);719store.delete(1, 3);720const tokens = store.getTokensInRange(0, 7);721assert.deepStrictEqual(tokens, [722{ startOffsetInclusive: 0, length: 2, token: 1 },723{ startOffsetInclusive: 2, length: 1, token: 2 },724{ startOffsetInclusive: 3, length: 3, token: 2 },725{ startOffsetInclusive: 6, length: 1, token: 3 }726]);727});728});729730731732