diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 9156baecd316de92cf0ab7293bb301af9d5a24ed..7e933bd0d17b2051bc4f5bd8cdaf5b6a098168c0 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -1161,7 +1161,7 @@ export class OneCursorOp { if (matchOpenBracket) { var match = cursor.model.findMatchingBracketUp(matchOpenBracket, { lineNumber: position.lineNumber, - column: position.column - matchOpenBracket.close.length + column: position.column - matchOpenBracket.length }); if (match) { var matchLineNumber = match.startLineNumber; diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 8175eb77036935cffc02eafafa008fab81c5a675..dc0e9f0609fae81be5ccb96b2745cc4b2dd551db 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -1376,10 +1376,11 @@ export interface ITextModel { isDisposed(): boolean; } -export interface IFindBracketRequest { - modeId: string; +export interface IRichEditBracket { open: string; close: string; + forwardRegex: RegExp; + reversedRegex: RegExp; } export interface IFoundBracket { @@ -1486,7 +1487,7 @@ export interface ITokenizedModel extends ITextModel { * @param position The position at which to start the search. * @return The range of the matching bracket, or null if the bracket match was not found. */ - findMatchingBracketUp(request:IFindBracketRequest, position:IPosition): IEditorRange; + findMatchingBracketUp(bracket:string, position:IPosition): IEditorRange; /** * Find the first bracket in the model before `position`. diff --git a/src/vs/editor/common/model/textModelWithTokens.ts b/src/vs/editor/common/model/textModelWithTokens.ts index ba446078c9288f3bd1f8e9d124ba900d1ffa2b45..c6ab2d6fcb442322c862fee5b52b3aa102f4d995 100644 --- a/src/vs/editor/common/model/textModelWithTokens.ts +++ b/src/vs/editor/common/model/textModelWithTokens.ts @@ -24,6 +24,7 @@ import {TPromise} from 'vs/base/common/winjs.base'; import {Range} from 'vs/editor/common/core/range'; import * as Strings from 'vs/base/common/strings'; import {ignoreBracketsInToken} from 'vs/editor/common/modes/supports'; +import {BracketsUtils} from 'vs/editor/common/modes/supports/electricCharacter'; export class TokensInflatorMap implements EditorCommon.ITokensInflatorMap { @@ -847,12 +848,28 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke return result; } - public findMatchingBracketUp(request:EditorCommon.IFindBracketRequest, _position:EditorCommon.IPosition): EditorCommon.IEditorRange { + public findMatchingBracketUp(bracket:string, _position:EditorCommon.IPosition): EditorCommon.IEditorRange { if (this._isDisposed) { throw new Error('TextModelWithTokens.findMatchingBracketUp: Model is disposed'); } - return this._findMatchingBracketUp(request.open, request.close, this.validatePosition(_position)); + let position = this.validatePosition(_position); + let modeTransitions = this._lines[position.lineNumber - 1].getModeTransitions().toArray(this._mode); + let currentModeIndex = Arrays.findIndexInSegmentsArray(modeTransitions, position.column - 1); + let currentMode = modeTransitions[currentModeIndex]; + let currentModeBrackets = currentMode.mode.richEditSupport ? currentMode.mode.richEditSupport.brackets : null; + + if (!currentModeBrackets) { + return null; + } + + let data = currentModeBrackets.textIsBracket[bracket]; + + if (!data) { + return null; + } + + return this._findMatchingBracketUp(data, position); } public matchBracket(position:EditorCommon.IPosition, inaccurateResultAcceptable:boolean = false): EditorCommon.IMatchBracketResult { @@ -864,19 +881,20 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke } private _matchBracket(position:EditorCommon.IEditorPosition): EditorCommon.IMatchBracketResult { - - let reversedBracketRegex = /[\(\)\[\]\{\}]/; // TODO@Alex: use mode's brackets - let bracketRegex = /[\(\)\[\]\{\}]/; // TODO@Alex: use mode's brackets - let maxBracketLength = 1; // TODO@Alex: use mode's brackets - let tokensMap = this._tokensInflatorMap; let lineNumber = position.lineNumber; - let lineTokens = this._lines[lineNumber - 1].getTokens(); let lineText = this._lines[lineNumber - 1].text; + + let lineTokens = this._lines[lineNumber - 1].getTokens(); let tokens = lineTokens.getBinaryEncodedTokens(); let currentTokenIndex = lineTokens.findIndexOfOffset(position.column - 1); let currentTokenStart = getStartIndex(tokens[currentTokenIndex]); + let modeTransitions = this._lines[lineNumber - 1].getModeTransitions().toArray(this._mode); + let currentModeIndex = Arrays.findIndexInSegmentsArray(modeTransitions, position.column - 1); + let currentMode = modeTransitions[currentModeIndex]; + let currentModeBrackets = currentMode.mode.richEditSupport ? currentMode.mode.richEditSupport.brackets : null; + // If position is in between two tokens, try first looking in the previous token if (currentTokenIndex > 0 && currentTokenStart === position.column - 1) { let prevTokenIndex = currentTokenIndex - 1; @@ -886,18 +904,29 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke if (!ignoreBracketsInToken(prevTokenType)) { let prevTokenStart = getStartIndex(tokens[prevTokenIndex]); - // limit search in case previous token is very large, there's no need to go beyond `maxBracketLength` - prevTokenStart = Math.max(prevTokenStart, position.column - 1 - maxBracketLength); + let prevMode = currentMode; + let prevModeBrackets = currentModeBrackets; + // check if previous token is in a different mode + if (currentModeIndex > 0 && currentMode.startIndex === position.column - 1) { + prevMode = modeTransitions[currentModeIndex - 1]; + prevModeBrackets = prevMode.mode.richEditSupport ? prevMode.mode.richEditSupport.brackets : null; + } - let foundBracket = TextModelWithTokens._findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, prevTokenStart, currentTokenStart); + if (prevModeBrackets) { + // limit search in case previous token is very large, there's no need to go beyond `maxBracketLength` + prevTokenStart = Math.max(prevTokenStart, position.column - 1 - prevModeBrackets.maxBracketLength); - // check that we didn't hit a bracket too far away from position - if (foundBracket && foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) { - let r = this._matchFoundBracket(foundBracket); + let foundBracket = BracketsUtils.findPrevBracketInToken(prevModeBrackets.reversedRegex, lineNumber, lineText, prevTokenStart, currentTokenStart); - // check that we can actually match this bracket - if (r) { - return r; + // check that we didn't hit a bracket too far away from position + if (foundBracket && foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) { + let foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1); + let r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText]); + + // check that we can actually match this bracket + if (r) { + return r; + } } } } @@ -906,32 +935,35 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke // check that the token is not to be ignored if (!ignoreBracketsInToken(getType(tokensMap, tokens[currentTokenIndex]))) { - // limit search to not go before `maxBracketLength` - currentTokenStart = Math.max(currentTokenStart, position.column - 1 - maxBracketLength); + if (currentModeBrackets) { + // limit search to not go before `maxBracketLength` + currentTokenStart = Math.max(currentTokenStart, position.column - 1 - currentModeBrackets.maxBracketLength); - // limit search to not go after `maxBracketLength` - let currentTokenEnd = (currentTokenIndex + 1 < tokens.length ? getStartIndex(tokens[currentTokenIndex + 1]) : lineText.length); - currentTokenEnd = Math.min(currentTokenEnd, position.column - 1 + maxBracketLength); + // limit search to not go after `maxBracketLength` + let currentTokenEnd = (currentTokenIndex + 1 < tokens.length ? getStartIndex(tokens[currentTokenIndex + 1]) : lineText.length); + currentTokenEnd = Math.min(currentTokenEnd, position.column - 1 + currentModeBrackets.maxBracketLength); - // it might still be the case that [currentTokenStart -> currentTokenEnd] contains multiple brackets - while(true) { - let foundBracket = TextModelWithTokens._findNextBracketInText(bracketRegex, lineNumber, lineText.substring(currentTokenStart, currentTokenEnd), currentTokenStart); - if (!foundBracket) { - // there are no brackets in this text - break; - } + // it might still be the case that [currentTokenStart -> currentTokenEnd] contains multiple brackets + while(true) { + let foundBracket = BracketsUtils.findNextBracketInText(currentModeBrackets.forwardRegex, lineNumber, lineText.substring(currentTokenStart, currentTokenEnd), currentTokenStart); + if (!foundBracket) { + // there are no brackets in this text + break; + } - // check that we didn't hit a bracket too far away from position - if (foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) { - let r = this._matchFoundBracket(foundBracket); + // check that we didn't hit a bracket too far away from position + if (foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) { + let foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1); + let r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText]); - // check that we can actually match this bracket - if (r) { - return r; + // check that we can actually match this bracket + if (r) { + return r; + } } - } - currentTokenStart = foundBracket.endColumn - 1; + currentTokenStart = foundBracket.endColumn - 1; + } } } @@ -941,19 +973,17 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke }; } - private _matchFoundBracket(foundBracket:Range): EditorCommon.IMatchBracketResult { - let data = this._toFoundBracket(foundBracket); - - if (data.isOpen) { - let matched = this._findMatchingBracketDown(data.open, data.close, data.range.getEndPosition()); + private _matchFoundBracket(foundBracket:Range, data:EditorCommon.IRichEditBracket, isOpen:boolean): EditorCommon.IMatchBracketResult { + if (isOpen) { + let matched = this._findMatchingBracketDown(data, foundBracket.getEndPosition()); if (matched) { return { brackets: [foundBracket, matched], isAccurate: true }; } - } else if (!data.isOpen) { - let matched = this._findMatchingBracketUp(data.open, data.close, data.range.getStartPosition()); + } else { + let matched = this._findMatchingBracketUp(data, foundBracket.getStartPosition()); if (matched) { return { brackets: [foundBracket, matched], @@ -965,50 +995,13 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke return null; } - private static _getReversedString(str:string): string { - let reversedStr = ''; - for (let i = str.length - 1; i >= 0; i--) { - reversedStr += str.charAt(i); - } - return reversedStr; - } - - private static _REVERSED_REGEX_BRACKET_PAIR_CACHE: {[key:string]: RegExp;} = {}; - private static _getReversedRegexForBracketPair(open:string, close:string): RegExp { - let key = open + ';' + close; - if (!this._REVERSED_REGEX_BRACKET_PAIR_CACHE.hasOwnProperty(key)) { - let reversedOpen = this._getReversedString(open); - let reversedClose = this._getReversedString(close); - - let reversedOpenEscaped = Strings.escapeRegExpCharacters(reversedOpen); - let reversedCloseEscaped = Strings.escapeRegExpCharacters(reversedClose); + private _findMatchingBracketUp(bracket:EditorCommon.IRichEditBracket, position:EditorCommon.IEditorPosition): Range { + // console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); - let regexStr = `(${reversedOpenEscaped})|(${reversedCloseEscaped})`; - - this._REVERSED_REGEX_BRACKET_PAIR_CACHE[key] = Strings.createRegExp(regexStr, true, false, false, false); - } - return this._REVERSED_REGEX_BRACKET_PAIR_CACHE[key]; - } - - private static _REGEX_BRACKET_PAIR_CACHE: {[key:string]: RegExp;} = {}; - private static _getRegexForBracketPair(open:string, close:string): RegExp { - let key = open + ';' + close; - if (!this._REGEX_BRACKET_PAIR_CACHE.hasOwnProperty(key)) { - let openEscaped = Strings.escapeRegExpCharacters(open); - let closeEscaped = Strings.escapeRegExpCharacters(close); - - let regexStr = `(${openEscaped})|(${closeEscaped})`; - - this._REGEX_BRACKET_PAIR_CACHE[key] = Strings.createRegExp(regexStr, true, false, false, false); - } - return this._REGEX_BRACKET_PAIR_CACHE[key]; - } - - private _findMatchingBracketUp(openBracket:string, closeBracket:string, position:EditorCommon.IEditorPosition): Range { - // console.log('_findMatchingBracketUp: ', 'openBracket: ', openBracket, 'closeBracket: ', closeBracket, 'startPosition: ', String(position)); let tokensMap = this._tokensInflatorMap; - let reversedBracketRegex = TextModelWithTokens._getReversedRegexForBracketPair(openBracket, closeBracket); + // TODO@Alex: account for mode transitions + let reversedBracketRegex = bracket.reversedRegex; let count = -1; for (let lineNumber = position.lineNumber; lineNumber >= 1; lineNumber--) { @@ -1031,16 +1024,16 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke if (!ignoreBracketsInToken(currentTokenType)) { while (true) { - let r = TextModelWithTokens._findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd); + let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd); if (!r) { break; } let hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1); - if (hitText === openBracket) { + if (hitText === bracket.open) { count++; - } else if (hitText === closeBracket) { + } else if (hitText === bracket.close) { count--; } @@ -1059,11 +1052,12 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke return null; } - private _findMatchingBracketDown(openBracket:string, closeBracket:string, position:EditorCommon.IEditorPosition): Range { - // console.log('_findMatchingBracketDown: ', 'openBracket: ', openBracket, 'closeBracket: ', closeBracket, 'startPosition: ', String(position)); + private _findMatchingBracketDown(bracket:EditorCommon.IRichEditBracket, position:EditorCommon.IEditorPosition): Range { + // console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); let tokensMap = this._tokensInflatorMap; - let bracketRegex = TextModelWithTokens._getRegexForBracketPair(openBracket, closeBracket); + // TODO@Alex: account for mode transitions + let bracketRegex = bracket.forwardRegex; let count = 1; for (let lineNumber = position.lineNumber, lineCount = this.getLineCount(); lineNumber <= lineCount; lineNumber++) { @@ -1086,16 +1080,16 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke if (!ignoreBracketsInToken(currentTokenType)) { while (true) { - let r = TextModelWithTokens._findNextBracketInToken(bracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd); + let r = BracketsUtils.findNextBracketInToken(bracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd); if (!r) { break; } let hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1); - if (hitText === openBracket) { + if (hitText === bracket.open) { count++; - } else if (hitText === closeBracket) { + } else if (hitText === bracket.close) { count--; } @@ -1114,30 +1108,6 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke return null; } - private static _findPrevBracketInText(reversedBracketRegex:RegExp, lineNumber:number, reversedText:string, offset:number): Range { - let m = reversedText.match(reversedBracketRegex); - - if (!m) { - return null; - } - - let matchOffset = reversedText.length - 1 - m.index; - let matchLength = m[0].length; - let absoluteMatchOffset = offset + matchOffset; - - return new Range(lineNumber, absoluteMatchOffset + 1, lineNumber, absoluteMatchOffset + 1 + matchLength); - } - - private 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); - } - - return this._findPrevBracketInText(reversedBracketRegex, lineNumber, currentTokenReversedText, currentTokenStart); - } - public findPrevBracket(_position:EditorCommon.IPosition): EditorCommon.IFoundBracket { if (this._isDisposed) { throw new Error('TextModelWithTokens.findPrevBracket: Model is disposed'); @@ -1165,7 +1135,7 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke let currentTokenStart = getStartIndex(currentToken); if (!ignoreBracketsInToken(currentTokenType)) { - let r = TextModelWithTokens._findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd); + let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd); if (r) { return this._toFoundBracket(r); } @@ -1178,26 +1148,6 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke return null; } - private static _findNextBracketInText(bracketRegex:RegExp, lineNumber:number, text:string, offset:number): Range { - let m = text.match(bracketRegex); - - if (!m) { - return null; - } - - let matchOffset = m.index; - let matchLength = m[0].length; - let absoluteMatchOffset = offset + matchOffset; - - return new Range(lineNumber, absoluteMatchOffset + 1, lineNumber, absoluteMatchOffset + 1 + matchLength); - } - - private static _findNextBracketInToken(bracketRegex:RegExp, lineNumber:number, lineText:string, currentTokenStart:number, currentTokenEnd:number): Range { - let currentTokenText = lineText.substring(currentTokenStart, currentTokenEnd); - - return this._findNextBracketInText(bracketRegex, lineNumber, currentTokenText, currentTokenStart); - } - public findNextBracket(_position:EditorCommon.IPosition): EditorCommon.IFoundBracket { if (this._isDisposed) { throw new Error('TextModelWithTokens.findNextBracket: Model is disposed'); @@ -1225,7 +1175,7 @@ export class TextModelWithTokens extends TextModel implements EditorCommon.IToke let currentTokenEnd = tokenIndex + 1 < tokensLength ? getStartIndex(tokens[tokenIndex + 1]) : lineText.length; if (!ignoreBracketsInToken(currentTokenType)) { - let r = TextModelWithTokens._findNextBracketInToken(bracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd); + let r = BracketsUtils.findNextBracketInToken(bracketRegex, lineNumber, lineText, currentTokenStart, currentTokenEnd); if (r) { return this._toFoundBracket(r); } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 1618f12eae3b422757d05f59e4722cb8e5c5b966..f1887d8653b903f66defb45e9a6c45fdea73aaef 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -697,7 +697,7 @@ export interface IElectricAction { // The line will be indented at the same level of the line // which contains the matching given bracket type. - matchOpenBracket?:EditorCommon.IFindBracketRequest; + matchOpenBracket?:string; // The text will be appended after the electric character. appendText?:string; @@ -754,6 +754,15 @@ export interface IRichEditCharacterPair { getSurroundingPairs():IAutoClosingPair[]; } +export interface IRichEditBrackets { + maxBracketLength: number; + forwardRegex: RegExp; + reversedRegex: RegExp; + brackets: EditorCommon.IRichEditBracket[]; + textIsBracket: {[text:string]:EditorCommon.IRichEditBracket;}; + textIsOpenBracket: {[text:string]:boolean;}; +} + export interface IRichEditSupport { /** * Optional adapter for electric characters. @@ -779,4 +788,9 @@ export interface IRichEditSupport { * Optional adapter for custom Enter handling. */ onEnter?: IRichEditOnEnter; + + /** + * Optional adapter for brackets. + */ + brackets?: IRichEditBrackets; } diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts index 3dadf912522e94c3e57f5d3234f026e9b0da4e24..665feb9adc6b006482bfa78f7b8afd40c79d1879 100644 --- a/src/vs/editor/common/modes/supports/electricCharacter.ts +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -8,7 +8,7 @@ import * as Modes from 'vs/editor/common/modes'; import {handleEvent, ignoreBracketsInToken} from 'vs/editor/common/modes/supports'; import Strings = require('vs/base/common/strings'); import {Range} from 'vs/editor/common/core/range'; -import {IFoundBracket} from 'vs/editor/common/editorCommon'; +import {IRichEditBracket} from 'vs/editor/common/editorCommon'; import {Arrays} from 'vs/editor/common/core/arrays'; /** @@ -58,31 +58,26 @@ export class BracketElectricCharacterSupport implements Modes.IRichEditElectricC } }); } -} -interface ISimpleInternalBracket { - open: string; - close: string; -} - -interface IInternalBrackets extends ISimpleInternalBracket { - forwardRegex: RegExp; - reversedRegex: RegExp; + public getRichEditBrackets(): Modes.IRichEditBrackets { + return this.brackets.getRichEditBrackets(); + } } -interface ITextBracket { +interface ISimpleInternalBracket { open: string; close: string; - isOpen: boolean; } export class Brackets { private _modeId: string; - private _brackets: IInternalBrackets[]; + private _brackets: IRichEditBracket[]; private _bracketsForwardRegex: RegExp; private _bracketsReversedRegex: RegExp; - private _text2Bracket: {[text:string]:ITextBracket;}; + private _maxBracketLength: number; + private _textIsBracket: {[text:string]:IRichEditBracket;}; + private _textIsOpenBracket: {[text:string]:boolean;}; private _docComment: IDocComment; constructor(modeId: string, brackets: Modes.IBracketPair[], docComment: IDocComment = null, caseInsensitive: boolean = false) { @@ -97,14 +92,35 @@ export class Brackets { }); this._bracketsForwardRegex = getRegexForBrackets(this._brackets); this._bracketsReversedRegex = getReversedRegexForBrackets(this._brackets); - this._text2Bracket = {}; + + this._textIsBracket = {}; + this._textIsOpenBracket = {}; + this._maxBracketLength = 0; this._brackets.forEach((b) => { - this._text2Bracket[b.open] = { open: b.open, close: b.close, isOpen: true }; - this._text2Bracket[b.close] = { open: b.open, close: b.close, isOpen: false }; + this._textIsBracket[b.open] = b; + this._textIsBracket[b.close] = b; + this._textIsOpenBracket[b.open] = true; + this._textIsOpenBracket[b.close] = false; + this._maxBracketLength = Math.max(this._maxBracketLength, b.open.length); + this._maxBracketLength = Math.max(this._maxBracketLength, b.close.length); }); this._docComment = docComment ? docComment : null; } + public getRichEditBrackets(): Modes.IRichEditBrackets { + if (this._brackets.length === 0) { + return null; + } + return { + maxBracketLength: this._maxBracketLength, + forwardRegex: this._bracketsForwardRegex, + reversedRegex: this._bracketsReversedRegex, + brackets: this._brackets, + textIsBracket: this._textIsBracket, + textIsOpenBracket: this._textIsOpenBracket + }; + } + public getElectricCharacters():string[] { var result: string[] = []; @@ -168,15 +184,11 @@ export class Brackets { let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, 1, lineText, tokenStart, tokenEnd); if (r) { let text = lineText.substring(r.startColumn - 1, r.endColumn - 1); - let data = this._text2Bracket[text]; - if (!data.isOpen) { + let isOpen = this._textIsOpenBracket[text]; + if (!isOpen) { return { - matchOpenBracket: { - modeId: this._modeId, - open: data.open, - close: data.close - } - } + matchOpenBracket: text + }; } } } @@ -228,7 +240,6 @@ function once(keyFn:(input:T)=>string, computeFn:(input:T)=>R):(input:T)=> } } -// TODO: dup in textModelWithTokens var getRegexForBracketPair = once( (input) => `${input.open};${input.close}`, (input) => { @@ -236,7 +247,6 @@ var getRegexForBracketPair = once( } ); -// TODO: dup in textModelWithTokens var getReversedRegexForBracketPair = once( (input) => `${input.open};${input.close}`, (input) => { @@ -273,7 +283,6 @@ function createOrRegex(pieces:string[]): RegExp { return Strings.createRegExp(regexStr, true, false, false, false); } -// TODO: dup in textModelWithTokens function toReversedString(str:string): string { let reversedStr = ''; for (let i = str.length - 1; i >= 0; i--) { @@ -284,7 +293,6 @@ function toReversedString(str:string): string { export class BracketsUtils { - // TODO: dup in textModelWithTokens private static _findPrevBracketInText(reversedBracketRegex:RegExp, lineNumber:number, reversedText:string, offset:number): Range { let m = reversedText.match(reversedBracketRegex); @@ -299,7 +307,6 @@ export class BracketsUtils { return new Range(lineNumber, absoluteMatchOffset + 1, lineNumber, absoluteMatchOffset + 1 + matchLength); } - // TODO: dup in textModelWithTokens 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 = ''; @@ -310,4 +317,24 @@ export class BracketsUtils { return this._findPrevBracketInText(reversedBracketRegex, lineNumber, currentTokenReversedText, currentTokenStart); } + public static findNextBracketInText(bracketRegex:RegExp, lineNumber:number, text:string, offset:number): Range { + let m = text.match(bracketRegex); + + if (!m) { + return null; + } + + let matchOffset = m.index; + let matchLength = m[0].length; + let absoluteMatchOffset = offset + matchOffset; + + return new Range(lineNumber, absoluteMatchOffset + 1, lineNumber, absoluteMatchOffset + 1 + matchLength); + } + + public static findNextBracketInToken(bracketRegex:RegExp, lineNumber:number, lineText:string, currentTokenStart:number, currentTokenEnd:number): Range { + let currentTokenText = lineText.substring(currentTokenStart, currentTokenEnd); + + return this.findNextBracketInText(bracketRegex, lineNumber, currentTokenText, currentTokenStart); + } + } diff --git a/src/vs/editor/common/modes/supports/richEditSupport.ts b/src/vs/editor/common/modes/supports/richEditSupport.ts index 4323e42e16fa949559d1290bd1a4c7ff764800c3..3a0dd5096def71d59b9bf8464125e1e86e40b287 100644 --- a/src/vs/editor/common/modes/supports/richEditSupport.ts +++ b/src/vs/editor/common/modes/supports/richEditSupport.ts @@ -30,11 +30,12 @@ export interface IRichEditConfiguration { export class RichEditSupport implements Modes.IRichEditSupport { - public electricCharacter: Modes.IRichEditElectricCharacter; + public electricCharacter: BracketElectricCharacterSupport; public comments: Modes.ICommentsConfiguration; public characterPair: Modes.IRichEditCharacterPair; public wordDefinition: RegExp; public onEnter: Modes.IRichEditOnEnter; + public brackets: Modes.IRichEditBrackets; constructor(modeId:string, conf:IRichEditConfiguration) { @@ -48,6 +49,7 @@ export class RichEditSupport implements Modes.IRichEditSupport { if (conf.__electricCharacterSupport) { this.electricCharacter = new BracketElectricCharacterSupport(modeId, conf.__electricCharacterSupport); + this.brackets = this.electricCharacter.getRichEditBrackets(); } this.wordDefinition = conf.wordPattern || NullMode.DEFAULT_WORD_REGEXP; diff --git a/src/vs/editor/test/common/testModes.ts b/src/vs/editor/test/common/testModes.ts index c0f5577e79b395e3f3cc0f2a46ce8b66894de059..57607805b6c500f973f2f70aed4095dede804d2b 100644 --- a/src/vs/editor/test/common/testModes.ts +++ b/src/vs/editor/test/common/testModes.ts @@ -220,12 +220,22 @@ export class BracketState extends AbstractState { export class BracketMode extends TestingMode { public tokenizationSupport: modes.ITokenizationSupport; + public richEditSupport: modes.IRichEditSupport; constructor() { super(); this.tokenizationSupport = new TokenizationSupport(this, { getInitialState: () => new BracketState(this) }, false, false); + this.richEditSupport = new RichEditSupport(this.getId(), { + __electricCharacterSupport: { + brackets: [ + { tokenType: 'asd', open: '{', close: '}', isElectric: true }, + { tokenType: 'qwe', open: '[', close: ']', isElectric: true }, + { tokenType: 'zxc', open: '(', close: ')', isElectric: true } + ] + } + }); } } diff --git a/src/vs/languages/javascript/test/common/javascript.test.ts b/src/vs/languages/javascript/test/common/javascript.test.ts index 5b3d7f455173e87e17adbb92b11ed665760a9ca0..04175d92f16caf35fda5c8dae779ac68c9b7832a 100644 --- a/src/vs/languages/javascript/test/common/javascript.test.ts +++ b/src/vs/languages/javascript/test/common/javascript.test.ts @@ -68,9 +68,9 @@ suite('JS - Auto Indent', () => { assert.deepEqual(actual, expected, 'LINE <<<' + line + '>>>, OFFSET: <<<' + offset + '>>>'); } - const CURLY = { matchOpenBracket: { modeId:'javascript', open:'{', close:'}' } }; - const ROUND = { matchOpenBracket: { modeId:'javascript', open:'(', close:')' } }; - const SQUARE = { matchOpenBracket: { modeId:'javascript', open:'[', close:']' } }; + const CURLY = { matchOpenBracket: '}' }; + const ROUND = { matchOpenBracket: ')' }; + const SQUARE = { matchOpenBracket: ']' }; testElectricCharacter('var f = function() {}', 20, null); testElectricCharacter('}', 0, CURLY);