fe802247502a447cbc017f352b8766a55ccf12f742a0466a72380305193d01a733f36340f479ca765dfb5b0511b27aee0f65f3765b467d22b6eebe0b948546 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  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. import { Emitter } from '../../../../base/common/event.js';
  6. import { Disposable, DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js';
  7. import { Range } from '../../core/range.js';
  8. import { BracketPairsTree } from './bracketPairsTree/bracketPairsTree.js';
  9. import { ignoreBracketsInToken } from '../../languages/supports.js';
  10. import { BracketsUtils } from '../../languages/supports/richEditBrackets.js';
  11. import { compareBy, findLast, findLastMaxBy } from '../../../../base/common/arrays.js';
  12. export class BracketPairsTextModelPart extends Disposable {
  13. constructor(textModel, languageConfigurationService) {
  14. super();
  15. this.textModel = textModel;
  16. this.languageConfigurationService = languageConfigurationService;
  17. this.bracketPairsTree = this._register(new MutableDisposable());
  18. this.onDidChangeEmitter = new Emitter();
  19. this.onDidChange = this.onDidChangeEmitter.event;
  20. this.bracketsRequested = false;
  21. this._register(this.languageConfigurationService.onDidChange(e => {
  22. var _a;
  23. if (!e.languageId || ((_a = this.bracketPairsTree.value) === null || _a === void 0 ? void 0 : _a.object.didLanguageChange(e.languageId))) {
  24. this.bracketPairsTree.clear();
  25. this.updateBracketPairsTree();
  26. }
  27. }));
  28. }
  29. get canBuildAST() {
  30. const maxSupportedDocumentLength = /* max lines */ 50000 * /* average column count */ 100;
  31. return this.textModel.getValueLength() <= maxSupportedDocumentLength;
  32. }
  33. //#region TextModel events
  34. handleDidChangeOptions(e) {
  35. this.bracketPairsTree.clear();
  36. this.updateBracketPairsTree();
  37. }
  38. handleDidChangeLanguage(e) {
  39. this.bracketPairsTree.clear();
  40. this.updateBracketPairsTree();
  41. }
  42. handleDidChangeContent(change) {
  43. var _a;
  44. (_a = this.bracketPairsTree.value) === null || _a === void 0 ? void 0 : _a.object.handleContentChanged(change);
  45. }
  46. handleDidChangeBackgroundTokenizationState() {
  47. var _a;
  48. (_a = this.bracketPairsTree.value) === null || _a === void 0 ? void 0 : _a.object.handleDidChangeBackgroundTokenizationState();
  49. }
  50. handleDidChangeTokens(e) {
  51. var _a;
  52. (_a = this.bracketPairsTree.value) === null || _a === void 0 ? void 0 : _a.object.handleDidChangeTokens(e);
  53. }
  54. //#endregion
  55. updateBracketPairsTree() {
  56. if (this.bracketsRequested && this.canBuildAST) {
  57. if (!this.bracketPairsTree.value) {
  58. const store = new DisposableStore();
  59. this.bracketPairsTree.value = createDisposableRef(store.add(new BracketPairsTree(this.textModel, (languageId) => {
  60. return this.languageConfigurationService.getLanguageConfiguration(languageId);
  61. })), store);
  62. store.add(this.bracketPairsTree.value.object.onDidChange(e => this.onDidChangeEmitter.fire(e)));
  63. this.onDidChangeEmitter.fire();
  64. }
  65. }
  66. else {
  67. if (this.bracketPairsTree.value) {
  68. this.bracketPairsTree.clear();
  69. // Important: Don't call fire if there was no change!
  70. this.onDidChangeEmitter.fire();
  71. }
  72. }
  73. }
  74. /**
  75. * Returns all bracket pairs that intersect the given range.
  76. * The result is sorted by the start position.
  77. */
  78. getBracketPairsInRange(range) {
  79. var _a;
  80. this.bracketsRequested = true;
  81. this.updateBracketPairsTree();
  82. return ((_a = this.bracketPairsTree.value) === null || _a === void 0 ? void 0 : _a.object.getBracketPairsInRange(range, false)) || [];
  83. }
  84. getBracketPairsInRangeWithMinIndentation(range) {
  85. var _a;
  86. this.bracketsRequested = true;
  87. this.updateBracketPairsTree();
  88. return ((_a = this.bracketPairsTree.value) === null || _a === void 0 ? void 0 : _a.object.getBracketPairsInRange(range, true)) || [];
  89. }
  90. getBracketsInRange(range) {
  91. var _a;
  92. this.bracketsRequested = true;
  93. this.updateBracketPairsTree();
  94. return ((_a = this.bracketPairsTree.value) === null || _a === void 0 ? void 0 : _a.object.getBracketsInRange(range)) || [];
  95. }
  96. findMatchingBracketUp(_bracket, _position, maxDuration) {
  97. const position = this.textModel.validatePosition(_position);
  98. const languageId = this.textModel.getLanguageIdAtPosition(position.lineNumber, position.column);
  99. if (this.canBuildAST) {
  100. const closingBracketInfo = this.languageConfigurationService
  101. .getLanguageConfiguration(languageId)
  102. .bracketsNew.getClosingBracketInfo(_bracket);
  103. if (!closingBracketInfo) {
  104. return null;
  105. }
  106. const bracketPair = findLast(this.getBracketPairsInRange(Range.fromPositions(_position, _position)) || [], (b) => closingBracketInfo.closes(b.openingBracketInfo));
  107. if (bracketPair) {
  108. return bracketPair.openingBracketRange;
  109. }
  110. return null;
  111. }
  112. else {
  113. // Fallback to old bracket matching code:
  114. const bracket = _bracket.toLowerCase();
  115. const bracketsSupport = this.languageConfigurationService.getLanguageConfiguration(languageId).brackets;
  116. if (!bracketsSupport) {
  117. return null;
  118. }
  119. const data = bracketsSupport.textIsBracket[bracket];
  120. if (!data) {
  121. return null;
  122. }
  123. return stripBracketSearchCanceled(this._findMatchingBracketUp(data, position, createTimeBasedContinueBracketSearchPredicate(maxDuration)));
  124. }
  125. }
  126. matchBracket(position, maxDuration) {
  127. if (this.canBuildAST) {
  128. const bracketPair = findLastMaxBy(this.getBracketPairsInRange(Range.fromPositions(position, position)).filter((item) => item.closingBracketRange !== undefined &&
  129. (item.openingBracketRange.containsPosition(position) ||
  130. item.closingBracketRange.containsPosition(position))), compareBy((item) => item.openingBracketRange.containsPosition(position)
  131. ? item.openingBracketRange
  132. : item.closingBracketRange, Range.compareRangesUsingStarts));
  133. if (bracketPair) {
  134. return [bracketPair.openingBracketRange, bracketPair.closingBracketRange];
  135. }
  136. return null;
  137. }
  138. else {
  139. // Fallback to old bracket matching code:
  140. const continueSearchPredicate = createTimeBasedContinueBracketSearchPredicate(maxDuration);
  141. return this._matchBracket(this.textModel.validatePosition(position), continueSearchPredicate);
  142. }
  143. }
  144. _establishBracketSearchOffsets(position, lineTokens, modeBrackets, tokenIndex) {
  145. const tokenCount = lineTokens.getCount();
  146. const currentLanguageId = lineTokens.getLanguageId(tokenIndex);
  147. // limit search to not go before `maxBracketLength`
  148. let searchStartOffset = Math.max(0, position.column - 1 - modeBrackets.maxBracketLength);
  149. for (let i = tokenIndex - 1; i >= 0; i--) {
  150. const tokenEndOffset = lineTokens.getEndOffset(i);
  151. if (tokenEndOffset <= searchStartOffset) {
  152. break;
  153. }
  154. if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i)) || lineTokens.getLanguageId(i) !== currentLanguageId) {
  155. searchStartOffset = tokenEndOffset;
  156. break;
  157. }
  158. }
  159. // limit search to not go after `maxBracketLength`
  160. let searchEndOffset = Math.min(lineTokens.getLineContent().length, position.column - 1 + modeBrackets.maxBracketLength);
  161. for (let i = tokenIndex + 1; i < tokenCount; i++) {
  162. const tokenStartOffset = lineTokens.getStartOffset(i);
  163. if (tokenStartOffset >= searchEndOffset) {
  164. break;
  165. }
  166. if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i)) || lineTokens.getLanguageId(i) !== currentLanguageId) {
  167. searchEndOffset = tokenStartOffset;
  168. break;
  169. }
  170. }
  171. return { searchStartOffset, searchEndOffset };
  172. }
  173. _matchBracket(position, continueSearchPredicate) {
  174. const lineNumber = position.lineNumber;
  175. const lineTokens = this.textModel.tokenization.getLineTokens(lineNumber);
  176. const lineText = this.textModel.getLineContent(lineNumber);
  177. const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
  178. if (tokenIndex < 0) {
  179. return null;
  180. }
  181. const currentModeBrackets = this.languageConfigurationService.getLanguageConfiguration(lineTokens.getLanguageId(tokenIndex)).brackets;
  182. // check that the token is not to be ignored
  183. if (currentModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex))) {
  184. let { searchStartOffset, searchEndOffset } = this._establishBracketSearchOffsets(position, lineTokens, currentModeBrackets, tokenIndex);
  185. // it might be the case that [currentTokenStart -> currentTokenEnd] contains multiple brackets
  186. // `bestResult` will contain the most right-side result
  187. let bestResult = null;
  188. while (true) {
  189. const foundBracket = BracketsUtils.findNextBracketInRange(currentModeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  190. if (!foundBracket) {
  191. // there are no more brackets in this text
  192. break;
  193. }
  194. // check that we didn't hit a bracket too far away from position
  195. if (foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) {
  196. const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase();
  197. const r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText], continueSearchPredicate);
  198. if (r) {
  199. if (r instanceof BracketSearchCanceled) {
  200. return null;
  201. }
  202. bestResult = r;
  203. }
  204. }
  205. searchStartOffset = foundBracket.endColumn - 1;
  206. }
  207. if (bestResult) {
  208. return bestResult;
  209. }
  210. }
  211. // If position is in between two tokens, try also looking in the previous token
  212. if (tokenIndex > 0 && lineTokens.getStartOffset(tokenIndex) === position.column - 1) {
  213. const prevTokenIndex = tokenIndex - 1;
  214. const prevModeBrackets = this.languageConfigurationService.getLanguageConfiguration(lineTokens.getLanguageId(prevTokenIndex)).brackets;
  215. // check that previous token is not to be ignored
  216. if (prevModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(prevTokenIndex))) {
  217. const { searchStartOffset, searchEndOffset } = this._establishBracketSearchOffsets(position, lineTokens, prevModeBrackets, prevTokenIndex);
  218. const foundBracket = BracketsUtils.findPrevBracketInRange(prevModeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  219. // check that we didn't hit a bracket too far away from position
  220. if (foundBracket && foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) {
  221. const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase();
  222. const r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText], continueSearchPredicate);
  223. if (r) {
  224. if (r instanceof BracketSearchCanceled) {
  225. return null;
  226. }
  227. return r;
  228. }
  229. }
  230. }
  231. }
  232. return null;
  233. }
  234. _matchFoundBracket(foundBracket, data, isOpen, continueSearchPredicate) {
  235. if (!data) {
  236. return null;
  237. }
  238. const matched = (isOpen
  239. ? this._findMatchingBracketDown(data, foundBracket.getEndPosition(), continueSearchPredicate)
  240. : this._findMatchingBracketUp(data, foundBracket.getStartPosition(), continueSearchPredicate));
  241. if (!matched) {
  242. return null;
  243. }
  244. if (matched instanceof BracketSearchCanceled) {
  245. return matched;
  246. }
  247. return [foundBracket, matched];
  248. }
  249. _findMatchingBracketUp(bracket, position, continueSearchPredicate) {
  250. // console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
  251. const languageId = bracket.languageId;
  252. const reversedBracketRegex = bracket.reversedRegex;
  253. let count = -1;
  254. let totalCallCount = 0;
  255. const searchPrevMatchingBracketInRange = (lineNumber, lineText, searchStartOffset, searchEndOffset) => {
  256. while (true) {
  257. if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) {
  258. return BracketSearchCanceled.INSTANCE;
  259. }
  260. const r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  261. if (!r) {
  262. break;
  263. }
  264. const hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1).toLowerCase();
  265. if (bracket.isOpen(hitText)) {
  266. count++;
  267. }
  268. else if (bracket.isClose(hitText)) {
  269. count--;
  270. }
  271. if (count === 0) {
  272. return r;
  273. }
  274. searchEndOffset = r.startColumn - 1;
  275. }
  276. return null;
  277. };
  278. for (let lineNumber = position.lineNumber; lineNumber >= 1; lineNumber--) {
  279. const lineTokens = this.textModel.tokenization.getLineTokens(lineNumber);
  280. const tokenCount = lineTokens.getCount();
  281. const lineText = this.textModel.getLineContent(lineNumber);
  282. let tokenIndex = tokenCount - 1;
  283. let searchStartOffset = lineText.length;
  284. let searchEndOffset = lineText.length;
  285. if (lineNumber === position.lineNumber) {
  286. tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
  287. searchStartOffset = position.column - 1;
  288. searchEndOffset = position.column - 1;
  289. }
  290. let prevSearchInToken = true;
  291. for (; tokenIndex >= 0; tokenIndex--) {
  292. const searchInToken = (lineTokens.getLanguageId(tokenIndex) === languageId && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
  293. if (searchInToken) {
  294. // this token should be searched
  295. if (prevSearchInToken) {
  296. // the previous token should be searched, simply extend searchStartOffset
  297. searchStartOffset = lineTokens.getStartOffset(tokenIndex);
  298. }
  299. else {
  300. // the previous token should not be searched
  301. searchStartOffset = lineTokens.getStartOffset(tokenIndex);
  302. searchEndOffset = lineTokens.getEndOffset(tokenIndex);
  303. }
  304. }
  305. else {
  306. // this token should not be searched
  307. if (prevSearchInToken && searchStartOffset !== searchEndOffset) {
  308. const r = searchPrevMatchingBracketInRange(lineNumber, lineText, searchStartOffset, searchEndOffset);
  309. if (r) {
  310. return r;
  311. }
  312. }
  313. }
  314. prevSearchInToken = searchInToken;
  315. }
  316. if (prevSearchInToken && searchStartOffset !== searchEndOffset) {
  317. const r = searchPrevMatchingBracketInRange(lineNumber, lineText, searchStartOffset, searchEndOffset);
  318. if (r) {
  319. return r;
  320. }
  321. }
  322. }
  323. return null;
  324. }
  325. _findMatchingBracketDown(bracket, position, continueSearchPredicate) {
  326. // console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
  327. const languageId = bracket.languageId;
  328. const bracketRegex = bracket.forwardRegex;
  329. let count = 1;
  330. let totalCallCount = 0;
  331. const searchNextMatchingBracketInRange = (lineNumber, lineText, searchStartOffset, searchEndOffset) => {
  332. while (true) {
  333. if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) {
  334. return BracketSearchCanceled.INSTANCE;
  335. }
  336. const r = BracketsUtils.findNextBracketInRange(bracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  337. if (!r) {
  338. break;
  339. }
  340. const hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1).toLowerCase();
  341. if (bracket.isOpen(hitText)) {
  342. count++;
  343. }
  344. else if (bracket.isClose(hitText)) {
  345. count--;
  346. }
  347. if (count === 0) {
  348. return r;
  349. }
  350. searchStartOffset = r.endColumn - 1;
  351. }
  352. return null;
  353. };
  354. const lineCount = this.textModel.getLineCount();
  355. for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) {
  356. const lineTokens = this.textModel.tokenization.getLineTokens(lineNumber);
  357. const tokenCount = lineTokens.getCount();
  358. const lineText = this.textModel.getLineContent(lineNumber);
  359. let tokenIndex = 0;
  360. let searchStartOffset = 0;
  361. let searchEndOffset = 0;
  362. if (lineNumber === position.lineNumber) {
  363. tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
  364. searchStartOffset = position.column - 1;
  365. searchEndOffset = position.column - 1;
  366. }
  367. let prevSearchInToken = true;
  368. for (; tokenIndex < tokenCount; tokenIndex++) {
  369. const searchInToken = (lineTokens.getLanguageId(tokenIndex) === languageId && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
  370. if (searchInToken) {
  371. // this token should be searched
  372. if (prevSearchInToken) {
  373. // the previous token should be searched, simply extend searchEndOffset
  374. searchEndOffset = lineTokens.getEndOffset(tokenIndex);
  375. }
  376. else {
  377. // the previous token should not be searched
  378. searchStartOffset = lineTokens.getStartOffset(tokenIndex);
  379. searchEndOffset = lineTokens.getEndOffset(tokenIndex);
  380. }
  381. }
  382. else {
  383. // this token should not be searched
  384. if (prevSearchInToken && searchStartOffset !== searchEndOffset) {
  385. const r = searchNextMatchingBracketInRange(lineNumber, lineText, searchStartOffset, searchEndOffset);
  386. if (r) {
  387. return r;
  388. }
  389. }
  390. }
  391. prevSearchInToken = searchInToken;
  392. }
  393. if (prevSearchInToken && searchStartOffset !== searchEndOffset) {
  394. const r = searchNextMatchingBracketInRange(lineNumber, lineText, searchStartOffset, searchEndOffset);
  395. if (r) {
  396. return r;
  397. }
  398. }
  399. }
  400. return null;
  401. }
  402. findPrevBracket(_position) {
  403. var _a;
  404. const position = this.textModel.validatePosition(_position);
  405. if (this.canBuildAST) {
  406. this.bracketsRequested = true;
  407. this.updateBracketPairsTree();
  408. return ((_a = this.bracketPairsTree.value) === null || _a === void 0 ? void 0 : _a.object.getFirstBracketBefore(position)) || null;
  409. }
  410. let languageId = null;
  411. let modeBrackets = null;
  412. let bracketConfig = null;
  413. for (let lineNumber = position.lineNumber; lineNumber >= 1; lineNumber--) {
  414. const lineTokens = this.textModel.tokenization.getLineTokens(lineNumber);
  415. const tokenCount = lineTokens.getCount();
  416. const lineText = this.textModel.getLineContent(lineNumber);
  417. let tokenIndex = tokenCount - 1;
  418. let searchStartOffset = lineText.length;
  419. let searchEndOffset = lineText.length;
  420. if (lineNumber === position.lineNumber) {
  421. tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
  422. searchStartOffset = position.column - 1;
  423. searchEndOffset = position.column - 1;
  424. const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
  425. if (languageId !== tokenLanguageId) {
  426. languageId = tokenLanguageId;
  427. modeBrackets = this.languageConfigurationService.getLanguageConfiguration(languageId).brackets;
  428. bracketConfig = this.languageConfigurationService.getLanguageConfiguration(languageId).bracketsNew;
  429. }
  430. }
  431. let prevSearchInToken = true;
  432. for (; tokenIndex >= 0; tokenIndex--) {
  433. const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
  434. if (languageId !== tokenLanguageId) {
  435. // language id change!
  436. if (modeBrackets && bracketConfig && prevSearchInToken && searchStartOffset !== searchEndOffset) {
  437. const r = BracketsUtils.findPrevBracketInRange(modeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  438. if (r) {
  439. return this._toFoundBracket(bracketConfig, r);
  440. }
  441. prevSearchInToken = false;
  442. }
  443. languageId = tokenLanguageId;
  444. modeBrackets = this.languageConfigurationService.getLanguageConfiguration(languageId).brackets;
  445. bracketConfig = this.languageConfigurationService.getLanguageConfiguration(languageId).bracketsNew;
  446. }
  447. const searchInToken = (!!modeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
  448. if (searchInToken) {
  449. // this token should be searched
  450. if (prevSearchInToken) {
  451. // the previous token should be searched, simply extend searchStartOffset
  452. searchStartOffset = lineTokens.getStartOffset(tokenIndex);
  453. }
  454. else {
  455. // the previous token should not be searched
  456. searchStartOffset = lineTokens.getStartOffset(tokenIndex);
  457. searchEndOffset = lineTokens.getEndOffset(tokenIndex);
  458. }
  459. }
  460. else {
  461. // this token should not be searched
  462. if (bracketConfig && modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
  463. const r = BracketsUtils.findPrevBracketInRange(modeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  464. if (r) {
  465. return this._toFoundBracket(bracketConfig, r);
  466. }
  467. }
  468. }
  469. prevSearchInToken = searchInToken;
  470. }
  471. if (bracketConfig && modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
  472. const r = BracketsUtils.findPrevBracketInRange(modeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  473. if (r) {
  474. return this._toFoundBracket(bracketConfig, r);
  475. }
  476. }
  477. }
  478. return null;
  479. }
  480. findNextBracket(_position) {
  481. var _a;
  482. const position = this.textModel.validatePosition(_position);
  483. if (this.canBuildAST) {
  484. this.bracketsRequested = true;
  485. this.updateBracketPairsTree();
  486. return ((_a = this.bracketPairsTree.value) === null || _a === void 0 ? void 0 : _a.object.getFirstBracketAfter(position)) || null;
  487. }
  488. const lineCount = this.textModel.getLineCount();
  489. let languageId = null;
  490. let modeBrackets = null;
  491. let bracketConfig = null;
  492. for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) {
  493. const lineTokens = this.textModel.tokenization.getLineTokens(lineNumber);
  494. const tokenCount = lineTokens.getCount();
  495. const lineText = this.textModel.getLineContent(lineNumber);
  496. let tokenIndex = 0;
  497. let searchStartOffset = 0;
  498. let searchEndOffset = 0;
  499. if (lineNumber === position.lineNumber) {
  500. tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
  501. searchStartOffset = position.column - 1;
  502. searchEndOffset = position.column - 1;
  503. const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
  504. if (languageId !== tokenLanguageId) {
  505. languageId = tokenLanguageId;
  506. modeBrackets = this.languageConfigurationService.getLanguageConfiguration(languageId).brackets;
  507. bracketConfig = this.languageConfigurationService.getLanguageConfiguration(languageId).bracketsNew;
  508. }
  509. }
  510. let prevSearchInToken = true;
  511. for (; tokenIndex < tokenCount; tokenIndex++) {
  512. const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
  513. if (languageId !== tokenLanguageId) {
  514. // language id change!
  515. if (bracketConfig && modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
  516. const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  517. if (r) {
  518. return this._toFoundBracket(bracketConfig, r);
  519. }
  520. prevSearchInToken = false;
  521. }
  522. languageId = tokenLanguageId;
  523. modeBrackets = this.languageConfigurationService.getLanguageConfiguration(languageId).brackets;
  524. bracketConfig = this.languageConfigurationService.getLanguageConfiguration(languageId).bracketsNew;
  525. }
  526. const searchInToken = (!!modeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
  527. if (searchInToken) {
  528. // this token should be searched
  529. if (prevSearchInToken) {
  530. // the previous token should be searched, simply extend searchEndOffset
  531. searchEndOffset = lineTokens.getEndOffset(tokenIndex);
  532. }
  533. else {
  534. // the previous token should not be searched
  535. searchStartOffset = lineTokens.getStartOffset(tokenIndex);
  536. searchEndOffset = lineTokens.getEndOffset(tokenIndex);
  537. }
  538. }
  539. else {
  540. // this token should not be searched
  541. if (bracketConfig && modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
  542. const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  543. if (r) {
  544. return this._toFoundBracket(bracketConfig, r);
  545. }
  546. }
  547. }
  548. prevSearchInToken = searchInToken;
  549. }
  550. if (bracketConfig && modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
  551. const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  552. if (r) {
  553. return this._toFoundBracket(bracketConfig, r);
  554. }
  555. }
  556. }
  557. return null;
  558. }
  559. findEnclosingBrackets(_position, maxDuration) {
  560. const position = this.textModel.validatePosition(_position);
  561. if (this.canBuildAST) {
  562. const range = Range.fromPositions(position);
  563. const bracketPair = findLast(this.getBracketPairsInRange(Range.fromPositions(position, position)), (item) => item.closingBracketRange !== undefined && item.range.strictContainsRange(range));
  564. if (bracketPair) {
  565. return [bracketPair.openingBracketRange, bracketPair.closingBracketRange];
  566. }
  567. return null;
  568. }
  569. const continueSearchPredicate = createTimeBasedContinueBracketSearchPredicate(maxDuration);
  570. const lineCount = this.textModel.getLineCount();
  571. const savedCounts = new Map();
  572. let counts = [];
  573. const resetCounts = (languageId, modeBrackets) => {
  574. if (!savedCounts.has(languageId)) {
  575. const tmp = [];
  576. for (let i = 0, len = modeBrackets ? modeBrackets.brackets.length : 0; i < len; i++) {
  577. tmp[i] = 0;
  578. }
  579. savedCounts.set(languageId, tmp);
  580. }
  581. counts = savedCounts.get(languageId);
  582. };
  583. let totalCallCount = 0;
  584. const searchInRange = (modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset) => {
  585. while (true) {
  586. if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) {
  587. return BracketSearchCanceled.INSTANCE;
  588. }
  589. const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
  590. if (!r) {
  591. break;
  592. }
  593. const hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1).toLowerCase();
  594. const bracket = modeBrackets.textIsBracket[hitText];
  595. if (bracket) {
  596. if (bracket.isOpen(hitText)) {
  597. counts[bracket.index]++;
  598. }
  599. else if (bracket.isClose(hitText)) {
  600. counts[bracket.index]--;
  601. }
  602. if (counts[bracket.index] === -1) {
  603. return this._matchFoundBracket(r, bracket, false, continueSearchPredicate);
  604. }
  605. }
  606. searchStartOffset = r.endColumn - 1;
  607. }
  608. return null;
  609. };
  610. let languageId = null;
  611. let modeBrackets = null;
  612. for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) {
  613. const lineTokens = this.textModel.tokenization.getLineTokens(lineNumber);
  614. const tokenCount = lineTokens.getCount();
  615. const lineText = this.textModel.getLineContent(lineNumber);
  616. let tokenIndex = 0;
  617. let searchStartOffset = 0;
  618. let searchEndOffset = 0;
  619. if (lineNumber === position.lineNumber) {
  620. tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
  621. searchStartOffset = position.column - 1;
  622. searchEndOffset = position.column - 1;
  623. const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
  624. if (languageId !== tokenLanguageId) {
  625. languageId = tokenLanguageId;
  626. modeBrackets = this.languageConfigurationService.getLanguageConfiguration(languageId).brackets;
  627. resetCounts(languageId, modeBrackets);
  628. }
  629. }
  630. let prevSearchInToken = true;
  631. for (; tokenIndex < tokenCount; tokenIndex++) {
  632. const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
  633. if (languageId !== tokenLanguageId) {
  634. // language id change!
  635. if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
  636. const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
  637. if (r) {
  638. return stripBracketSearchCanceled(r);
  639. }
  640. prevSearchInToken = false;
  641. }
  642. languageId = tokenLanguageId;
  643. modeBrackets = this.languageConfigurationService.getLanguageConfiguration(languageId).brackets;
  644. resetCounts(languageId, modeBrackets);
  645. }
  646. const searchInToken = (!!modeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
  647. if (searchInToken) {
  648. // this token should be searched
  649. if (prevSearchInToken) {
  650. // the previous token should be searched, simply extend searchEndOffset
  651. searchEndOffset = lineTokens.getEndOffset(tokenIndex);
  652. }
  653. else {
  654. // the previous token should not be searched
  655. searchStartOffset = lineTokens.getStartOffset(tokenIndex);
  656. searchEndOffset = lineTokens.getEndOffset(tokenIndex);
  657. }
  658. }
  659. else {
  660. // this token should not be searched
  661. if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
  662. const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
  663. if (r) {
  664. return stripBracketSearchCanceled(r);
  665. }
  666. }
  667. }
  668. prevSearchInToken = searchInToken;
  669. }
  670. if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
  671. const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
  672. if (r) {
  673. return stripBracketSearchCanceled(r);
  674. }
  675. }
  676. }
  677. return null;
  678. }
  679. _toFoundBracket(bracketConfig, r) {
  680. if (!r) {
  681. return null;
  682. }
  683. let text = this.textModel.getValueInRange(r);
  684. text = text.toLowerCase();
  685. const bracketInfo = bracketConfig.getBracketInfo(text);
  686. if (!bracketInfo) {
  687. return null;
  688. }
  689. return {
  690. range: r,
  691. bracketInfo
  692. };
  693. }
  694. }
  695. function createDisposableRef(object, disposable) {
  696. return {
  697. object,
  698. dispose: () => disposable === null || disposable === void 0 ? void 0 : disposable.dispose(),
  699. };
  700. }
  701. function createTimeBasedContinueBracketSearchPredicate(maxDuration) {
  702. if (typeof maxDuration === 'undefined') {
  703. return () => true;
  704. }
  705. else {
  706. const startTime = Date.now();
  707. return () => {
  708. return (Date.now() - startTime <= maxDuration);
  709. };
  710. }
  711. }
  712. class BracketSearchCanceled {
  713. constructor() {
  714. this._searchCanceledBrand = undefined;
  715. }
  716. }
  717. BracketSearchCanceled.INSTANCE = new BracketSearchCanceled();
  718. function stripBracketSearchCanceled(result) {
  719. if (result instanceof BracketSearchCanceled) {
  720. return null;
  721. }
  722. return result;
  723. }