diff --git a/src/vs/editor/common/core/lineTokens.ts b/src/vs/editor/common/core/lineTokens.ts index d0fd29ebf6dcd36fd72a8bc7af183ba9beb62f11..9612dbad0ad96485b00d91e4b034843925de096d 100644 --- a/src/vs/editor/common/core/lineTokens.ts +++ b/src/vs/editor/common/core/lineTokens.ts @@ -42,12 +42,34 @@ export class LineToken { if (this._modeIndex === 0) { return new LineToken(this._source, this._tokenIndex - 1, this._modeIndex); } - let modeTransition = this._source.modeTransitions[this._modeIndex]; - if (this.startOffset === modeTransition.startIndex) { + const modeTransitions = this._source.modeTransitions; + const currentModeTransition = modeTransitions[this._modeIndex]; + const prevStartOffset = this._source.getTokenStartOffset(this._tokenIndex - 1); + + if (prevStartOffset < currentModeTransition.startIndex) { + // Going to previous mode transition return new LineToken(this._source, this._tokenIndex - 1, this._modeIndex - 1); } return new LineToken(this._source, this._tokenIndex - 1, this._modeIndex); } + + public next(): LineToken { + if (!this.hasNext) { + return null; + } + const modeTransitions = this._source.modeTransitions; + if (this._modeIndex === modeTransitions.length - 1) { + return new LineToken(this._source, this._tokenIndex + 1, this._modeIndex); + } + const nextModeTransition = modeTransitions[this._modeIndex + 1]; + const nextStartOffset = this._source.getTokenStartOffset(this._tokenIndex + 1); + + if (nextStartOffset >= nextModeTransition.startIndex) { + // Going to next mode transition + return new LineToken(this._source, this._tokenIndex + 1, this._modeIndex + 1); + } + return new LineToken(this._source, this._tokenIndex + 1, this._modeIndex); + } } export class LineTokens { @@ -112,11 +134,15 @@ export class LineTokens { public findTokenAtOffset(offset:number): LineToken { let tokenIndex = this.findTokenIndexAtOffset(offset); let modeIndex = ModeTransition.findIndexInSegmentsArray(this.modeTransitions, offset); - return new LineToken( - this, - tokenIndex, - modeIndex - ); + return new LineToken(this, tokenIndex, modeIndex); + } + + public first(): LineToken { + return new LineToken(this, 0, 0); + } + + public last(): LineToken { + return new LineToken(this, this._tokens.length - 1, this.modeTransitions.length - 1); } public inflate(): ViewLineToken[] { diff --git a/src/vs/editor/common/model/textModelWithTokens.ts b/src/vs/editor/common/model/textModelWithTokens.ts index a770f42bc72699aa62a2b07f6c1e1df51abdcc1f..f86d37e41f1945198434ec57b08528a660341638 100644 --- a/src/vs/editor/common/model/textModelWithTokens.ts +++ b/src/vs/editor/common/model/textModelWithTokens.ts @@ -24,7 +24,7 @@ import {TokensInflatorMap} from 'vs/editor/common/model/tokensBinaryEncoding'; import {Position} from 'vs/editor/common/core/position'; import {LanguageConfigurationRegistry} from 'vs/editor/common/modes/languageConfigurationRegistry'; import {Token} from 'vs/editor/common/core/token'; -import {LineTokens} from 'vs/editor/common/core/lineTokens'; +import {LineTokens, LineToken} from 'vs/editor/common/core/lineTokens'; class Mode implements IMode { @@ -610,43 +610,29 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke private _findMatchingBracketUp(bracket:editorCommon.IRichEditBracket, position:Position): Range { // console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); - let modeId = bracket.modeId; - let reversedBracketRegex = bracket.reversedRegex; + const modeId = bracket.modeId; + const reversedBracketRegex = bracket.reversedRegex; let count = -1; for (let lineNumber = position.lineNumber; lineNumber >= 1; lineNumber--) { - let lineTokens = this._lines[lineNumber - 1].getTokens(this._tokensInflatorMap); - let lineText = this._lines[lineNumber - 1].text; - let modeTransitions = this._lines[lineNumber - 1].getModeTransitions(this.getModeId()); - let currentModeIndex = modeTransitions.length - 1; - let currentModeStart = modeTransitions[currentModeIndex].startIndex; - let currentModeId = modeTransitions[currentModeIndex].modeId; + const lineTokens = this._lines[lineNumber - 1].getTokens(this._tokensInflatorMap); + const lineText = this._lines[lineNumber - 1].text; - let tokensLength = lineTokens.getTokenCount() - 1; - let currentTokenEndOffset = lineText.length; + let currentToken: LineToken; + let searchStopOffset: number; if (lineNumber === position.lineNumber) { - tokensLength = lineTokens.findTokenIndexAtOffset(position.column - 1); - currentTokenEndOffset = position.column - 1; - - currentModeIndex = ModeTransition.findIndexInSegmentsArray(modeTransitions, position.column - 1); - currentModeStart = modeTransitions[currentModeIndex].startIndex; - currentModeId = modeTransitions[currentModeIndex].modeId; + currentToken = lineTokens.findTokenAtOffset(position.column - 1); + searchStopOffset = position.column - 1; + } else { + currentToken = lineTokens.last(); + searchStopOffset = currentToken.endOffset; } - for (let tokenIndex = tokensLength; tokenIndex >= 0; tokenIndex--) { - let currentTokenType = lineTokens.getTokenType(tokenIndex); - let currentTokenStartOffset = lineTokens.getTokenStartOffset(tokenIndex); - - if (currentTokenStartOffset < currentModeStart) { - currentModeIndex--; - currentModeStart = modeTransitions[currentModeIndex].startIndex; - currentModeId = modeTransitions[currentModeIndex].modeId; - } - - if (currentModeId === modeId && !ignoreBracketsInToken(currentTokenType)) { + do { + if (currentToken.modeId === modeId && !ignoreBracketsInToken(currentToken.type)) { while (true) { - let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, currentTokenStartOffset, currentTokenEndOffset); + let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, currentToken.startOffset, searchStopOffset); if (!r) { break; } @@ -664,12 +650,16 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke return r; } - currentTokenEndOffset = r.startColumn - 1; + searchStopOffset = r.startColumn - 1; } } - currentTokenEndOffset = currentTokenStartOffset; - } + currentToken = currentToken.prev(); + if (currentToken) { + searchStopOffset = currentToken.endOffset; + } + + } while(currentToken); } return null; @@ -678,42 +668,28 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke private _findMatchingBracketDown(bracket:editorCommon.IRichEditBracket, position:Position): Range { // console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); - let modeId = bracket.modeId; - let bracketRegex = bracket.forwardRegex; + const modeId = bracket.modeId; + const bracketRegex = bracket.forwardRegex; let count = 1; for (let lineNumber = position.lineNumber, lineCount = this.getLineCount(); lineNumber <= lineCount; lineNumber++) { - let lineTokens = this._lines[lineNumber - 1].getTokens(this._tokensInflatorMap); - let lineText = this._lines[lineNumber - 1].text; - let modeTransitions = this._lines[lineNumber - 1].getModeTransitions(this.getModeId()); - let currentModeIndex = 0; - let nextModeStart = (currentModeIndex + 1 < modeTransitions.length ? modeTransitions[currentModeIndex + 1].startIndex : lineText.length + 1); - let currentModeId = modeTransitions[currentModeIndex].modeId; + const lineTokens = this._lines[lineNumber - 1].getTokens(this._tokensInflatorMap); + const lineText = this._lines[lineNumber - 1].text; - let startTokenIndex = 0; - let currentTokenStartOffset = lineTokens.getTokenStartOffset(startTokenIndex); + let currentToken: LineToken; + let searchStartOffset: number; if (lineNumber === position.lineNumber) { - startTokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1); - currentTokenStartOffset = Math.max(currentTokenStartOffset, position.column - 1); - - currentModeIndex = ModeTransition.findIndexInSegmentsArray(modeTransitions, position.column - 1); - nextModeStart = (currentModeIndex + 1 < modeTransitions.length ? modeTransitions[currentModeIndex + 1].startIndex : lineText.length + 1); - currentModeId = modeTransitions[currentModeIndex].modeId; + currentToken = lineTokens.findTokenAtOffset(position.column - 1); + searchStartOffset = position.column - 1; + } else { + currentToken = lineTokens.first(); + searchStartOffset = currentToken.startOffset; } - for (let tokenIndex = startTokenIndex, tokensLength = lineTokens.getTokenCount(); tokenIndex < tokensLength; tokenIndex++) { - let currentTokenType = lineTokens.getTokenType(tokenIndex); - let currentTokenEndOffset = lineTokens.getTokenEndOffset(tokenIndex); - - if (currentTokenStartOffset >= nextModeStart) { - currentModeIndex++; - nextModeStart = (currentModeIndex + 1 < modeTransitions.length ? modeTransitions[currentModeIndex + 1].startIndex : lineText.length + 1); - currentModeId = modeTransitions[currentModeIndex].modeId; - } - - if (currentModeId === modeId && !ignoreBracketsInToken(currentTokenType)) { + do { + if (currentToken.modeId === modeId && !ignoreBracketsInToken(currentToken.type)) { while (true) { - let r = BracketsUtils.findNextBracketInToken(bracketRegex, lineNumber, lineText, currentTokenStartOffset, currentTokenEndOffset); + let r = BracketsUtils.findNextBracketInToken(bracketRegex, lineNumber, lineText, searchStartOffset, currentToken.endOffset); if (!r) { break; } @@ -731,12 +707,15 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke return r; } - currentTokenStartOffset = r.endColumn - 1; + searchStartOffset = r.endColumn - 1; } } - currentTokenStartOffset = currentTokenEndOffset; - } + currentToken = currentToken.next(); + if (currentToken) { + searchStartOffset = currentToken.startOffset; + } + } while (currentToken); } return null; diff --git a/src/vs/editor/common/modes/supports/richEditBrackets.ts b/src/vs/editor/common/modes/supports/richEditBrackets.ts index 1c671c8069b084e42fe9dd1a160c16beef4adf03..e0ed782193d4374e05f83ad873ebf047d1310ccd 100644 --- a/src/vs/editor/common/modes/supports/richEditBrackets.ts +++ b/src/vs/editor/common/modes/supports/richEditBrackets.ts @@ -104,13 +104,26 @@ function createOrRegex(pieces:string[]): RegExp { return strings.createRegExp(regexStr, true); } -function toReversedString(str:string): string { - let reversedStr = ''; - for (let i = str.length - 1; i >= 0; i--) { - reversedStr += str.charAt(i); +let toReversedString = (function() { + + function reverse(str:string): string { + let reversedStr = ''; + for (let i = str.length - 1; i >= 0; i--) { + reversedStr += str.charAt(i); + } + return reversedStr; } - return reversedStr; -} + + let lastInput: string = null; + let lastOutput: string = null; + return function toReversedString(str:string): string { + if (lastInput !== str) { + lastInput = str; + lastOutput = reverse(lastInput); + } + return lastOutput; + }; +})(); export class BracketsUtils { @@ -130,12 +143,10 @@ export class BracketsUtils { public static findPrevBracketInToken(reversedBracketRegex:RegExp, lineNumber:number, lineText:string, currentTokenStart:number, currentTokenEnd:number): Range { // Because JS does not support backwards regex search, we search forwards in a reversed string with a reversed regex ;) - let currentTokenReversedText = ''; - for (let index = currentTokenEnd - 1; index >= currentTokenStart; index--) { - currentTokenReversedText += lineText.charAt(index); - } + let reversedLineText = toReversedString(lineText); + let reversedTokenText = reversedLineText.substring(lineText.length - currentTokenEnd, lineText.length - currentTokenStart); - return this._findPrevBracketInText(reversedBracketRegex, lineNumber, currentTokenReversedText, currentTokenStart); + return this._findPrevBracketInText(reversedBracketRegex, lineNumber, reversedTokenText, currentTokenStart); } public static findNextBracketInText(bracketRegex:RegExp, lineNumber:number, text:string, offset:number): Range {