@@ -8,29 +8,107 @@ import { Range } from 'vs/editor/common/core/range';
88import { LanguageIdentifier } from 'vs/editor/common/modes' ;
99import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration' ;
1010
11- interface ISimpleInternalBracket {
12- open : string ;
13- close : string ;
11+ interface InternalBracket {
12+ open : string [ ] ;
13+ close : string [ ] ;
1414}
1515
1616export class RichEditBracket {
1717 _richEditBracketBrand : void ;
1818
1919 readonly languageIdentifier : LanguageIdentifier ;
20- readonly open : string ;
21- readonly close : string ;
20+ readonly open : string [ ] ;
21+ readonly close : string [ ] ;
2222 readonly forwardRegex : RegExp ;
2323 readonly reversedRegex : RegExp ;
24+ private readonly _openSet : Set < string > ;
25+ private readonly _closeSet : Set < string > ;
2426
25- constructor ( languageIdentifier : LanguageIdentifier , open : string , close : string , forwardRegex : RegExp , reversedRegex : RegExp ) {
27+ constructor ( languageIdentifier : LanguageIdentifier , open : string [ ] , close : string [ ] , forwardRegex : RegExp , reversedRegex : RegExp ) {
2628 this . languageIdentifier = languageIdentifier ;
2729 this . open = open ;
2830 this . close = close ;
2931 this . forwardRegex = forwardRegex ;
3032 this . reversedRegex = reversedRegex ;
33+ this . _openSet = RichEditBracket . _toSet ( this . open ) ;
34+ this . _closeSet = RichEditBracket . _toSet ( this . close ) ;
35+ }
36+
37+ public isOpen ( text : string ) {
38+ return this . _openSet . has ( text ) ;
39+ }
40+
41+ public isClose ( text : string ) {
42+ return this . _closeSet . has ( text ) ;
43+ }
44+
45+ private static _toSet ( arr : string [ ] ) : Set < string > {
46+ const result = new Set < string > ( ) ;
47+ for ( const element of arr ) {
48+ result . add ( element ) ;
49+ }
50+ return result ;
3151 }
3252}
3353
54+ function groupFuzzyBrackets ( brackets : CharacterPair [ ] ) : InternalBracket [ ] {
55+ const N = brackets . length ;
56+
57+ brackets = brackets . map ( b => [ b [ 0 ] . toLowerCase ( ) , b [ 1 ] . toLowerCase ( ) ] ) ;
58+
59+ const group : number [ ] = [ ] ;
60+ for ( let i = 0 ; i < N ; i ++ ) {
61+ group [ i ] = i ;
62+ }
63+
64+ const areOverlapping = ( a : CharacterPair , b : CharacterPair ) => {
65+ const [ aOpen , aClose ] = a ;
66+ const [ bOpen , bClose ] = b ;
67+ return ( aOpen === bOpen || aOpen === bClose || aClose === bOpen || aClose === bClose ) ;
68+ } ;
69+
70+ const mergeGroups = ( g1 : number , g2 : number ) => {
71+ const newG = Math . min ( g1 , g2 ) ;
72+ const oldG = Math . max ( g1 , g2 ) ;
73+ for ( let i = 0 ; i < N ; i ++ ) {
74+ if ( group [ i ] === oldG ) {
75+ group [ i ] = newG ;
76+ }
77+ }
78+ } ;
79+
80+ // group together brackets that have the same open or the same close sequence
81+ for ( let i = 0 ; i < N ; i ++ ) {
82+ const a = brackets [ i ] ;
83+ for ( let j = i + 1 ; j < N ; j ++ ) {
84+ const b = brackets [ j ] ;
85+ if ( areOverlapping ( a , b ) ) {
86+ mergeGroups ( group [ i ] , group [ j ] ) ;
87+ }
88+ }
89+ }
90+
91+ const result : InternalBracket [ ] = [ ] ;
92+ for ( let g = 0 ; g < N ; g ++ ) {
93+ let currentOpen : string [ ] = [ ] ;
94+ let currentClose : string [ ] = [ ] ;
95+ for ( let i = 0 ; i < N ; i ++ ) {
96+ if ( group [ i ] === g ) {
97+ const [ open , close ] = brackets [ i ] ;
98+ currentOpen . push ( open ) ;
99+ currentClose . push ( close ) ;
100+ }
101+ }
102+ if ( currentOpen . length > 0 ) {
103+ result . push ( {
104+ open : currentOpen ,
105+ close : currentClose
106+ } ) ;
107+ }
108+ }
109+ return result ;
110+ }
111+
34112export class RichEditBrackets {
35113 _richEditBracketsBrand : void ;
36114
@@ -41,58 +119,56 @@ export class RichEditBrackets {
41119 public readonly textIsBracket : { [ text : string ] : RichEditBracket ; } ;
42120 public readonly textIsOpenBracket : { [ text : string ] : boolean ; } ;
43121
44- constructor ( languageIdentifier : LanguageIdentifier , brackets : CharacterPair [ ] ) {
45- brackets = brackets . map ( b => [ b [ 0 ] . toLowerCase ( ) , b [ 1 ] . toLowerCase ( ) ] ) ;
122+ constructor ( languageIdentifier : LanguageIdentifier , _brackets : CharacterPair [ ] ) {
123+ const brackets = groupFuzzyBrackets ( _brackets ) ;
124+
46125 this . brackets = brackets . map ( ( b , index ) => {
47126 return new RichEditBracket (
48127 languageIdentifier ,
49- b [ 0 ] ,
50- b [ 1 ] ,
51- getRegexForBracketPair ( b [ 0 ] , b [ 1 ] , brackets , index ) ,
52- getReversedRegexForBracketPair ( b [ 0 ] , b [ 1 ] , brackets , index )
128+ b . open ,
129+ b . close ,
130+ getRegexForBracketPair ( b . open , b . close , brackets , index ) ,
131+ getReversedRegexForBracketPair ( b . open , b . close , brackets , index )
53132 ) ;
54133 } ) ;
134+
55135 this . forwardRegex = getRegexForBrackets ( this . brackets ) ;
56136 this . reversedRegex = getReversedRegexForBrackets ( this . brackets ) ;
57137
58138 this . textIsBracket = { } ;
59139 this . textIsOpenBracket = { } ;
60140
61- let maxBracketLength = 0 ;
62- this . brackets . forEach ( ( b ) => {
63- this . textIsBracket [ b . open ] = b ;
64- this . textIsBracket [ b . close ] = b ;
65- this . textIsOpenBracket [ b . open ] = true ;
66- this . textIsOpenBracket [ b . close ] = false ;
67- maxBracketLength = Math . max ( maxBracketLength , b . open . length ) ;
68- maxBracketLength = Math . max ( maxBracketLength , b . close . length ) ;
69- } ) ;
70- this . maxBracketLength = maxBracketLength ;
71- }
72- }
73-
74- function once < T , R > ( keyFn : ( input : T ) => string , computeFn : ( input : T ) => R ) : ( input : T ) => R {
75- let cache : { [ key : string ] : R ; } = { } ;
76- return ( input : T ) : R => {
77- let key = keyFn ( input ) ;
78- if ( ! cache . hasOwnProperty ( key ) ) {
79- cache [ key ] = computeFn ( input ) ;
141+ this . maxBracketLength = 0 ;
142+ for ( const bracket of this . brackets ) {
143+ for ( const open of bracket . open ) {
144+ this . textIsBracket [ open ] = bracket ;
145+ this . textIsOpenBracket [ open ] = true ;
146+ this . maxBracketLength = Math . max ( this . maxBracketLength , open . length ) ;
147+ }
148+ for ( const close of bracket . close ) {
149+ this . textIsBracket [ close ] = bracket ;
150+ this . textIsOpenBracket [ close ] = false ;
151+ this . maxBracketLength = Math . max ( this . maxBracketLength , close . length ) ;
152+ }
80153 }
81- return cache [ key ] ;
82- } ;
154+ }
83155}
84156
85- function collectSuperstrings ( str : string , brackets : CharacterPair [ ] , currentIndex : number , dest : string [ ] ) : void {
157+ function collectSuperstrings ( str : string , brackets : InternalBracket [ ] , currentIndex : number , dest : string [ ] ) : void {
86158 for ( let i = 0 , len = brackets . length ; i < len ; i ++ ) {
87159 if ( i === currentIndex ) {
88160 continue ;
89161 }
90- const [ open , close ] = brackets [ i ] ;
91- if ( open . indexOf ( str ) >= 0 ) {
92- dest . push ( open ) ;
162+ const bracket = brackets [ i ] ;
163+ for ( const open of bracket . open ) {
164+ if ( open . indexOf ( str ) >= 0 ) {
165+ dest . push ( open ) ;
166+ }
93167 }
94- if ( close . indexOf ( str ) >= 0 ) {
95- dest . push ( close ) ;
168+ for ( const close of bracket . close ) {
169+ if ( close . indexOf ( str ) >= 0 ) {
170+ dest . push ( close ) ;
171+ }
96172 }
97173 }
98174}
@@ -101,49 +177,77 @@ function lengthcmp(a: string, b: string) {
101177 return a . length - b . length ;
102178}
103179
104- function getRegexForBracketPair ( open : string , close : string , brackets : CharacterPair [ ] , currentIndex : number ) : RegExp {
180+ function unique ( arr : string [ ] ) : string [ ] {
181+ if ( arr . length <= 1 ) {
182+ return arr ;
183+ }
184+ const result : string [ ] = [ ] ;
185+ const seen = new Set < string > ( ) ;
186+ for ( const element of arr ) {
187+ if ( seen . has ( element ) ) {
188+ continue ;
189+ }
190+ result . push ( element ) ;
191+ seen . add ( element ) ;
192+ }
193+ return result ;
194+ }
195+
196+ function getRegexForBracketPair ( open : string [ ] , close : string [ ] , brackets : InternalBracket [ ] , currentIndex : number ) : RegExp {
105197 // search in all brackets for other brackets that are a superstring of these brackets
106- const pieces : string [ ] = [ open , close ] ;
107- collectSuperstrings ( open , brackets , currentIndex , pieces ) ;
108- collectSuperstrings ( close , brackets , currentIndex , pieces ) ;
198+ let pieces : string [ ] = [ ] ;
199+ pieces = pieces . concat ( open ) ;
200+ pieces = pieces . concat ( close ) ;
201+ for ( let i = 0 , len = pieces . length ; i < len ; i ++ ) {
202+ collectSuperstrings ( pieces [ i ] , brackets , currentIndex , pieces ) ;
203+ }
204+ pieces = unique ( pieces ) ;
109205 pieces . sort ( lengthcmp ) ;
110206 pieces . reverse ( ) ;
111207 return createBracketOrRegExp ( pieces ) ;
112208}
113209
114- function getReversedRegexForBracketPair ( open : string , close : string , brackets : CharacterPair [ ] , currentIndex : number ) : RegExp {
210+ function getReversedRegexForBracketPair ( open : string [ ] , close : string [ ] , brackets : InternalBracket [ ] , currentIndex : number ) : RegExp {
115211 // search in all brackets for other brackets that are a superstring of these brackets
116- const pieces : string [ ] = [ open , close ] ;
117- collectSuperstrings ( open , brackets , currentIndex , pieces ) ;
118- collectSuperstrings ( close , brackets , currentIndex , pieces ) ;
212+ let pieces : string [ ] = [ ] ;
213+ pieces = pieces . concat ( open ) ;
214+ pieces = pieces . concat ( close ) ;
215+ for ( let i = 0 , len = pieces . length ; i < len ; i ++ ) {
216+ collectSuperstrings ( pieces [ i ] , brackets , currentIndex , pieces ) ;
217+ }
218+ pieces = unique ( pieces ) ;
119219 pieces . sort ( lengthcmp ) ;
120220 pieces . reverse ( ) ;
121221 return createBracketOrRegExp ( pieces . map ( toReversedString ) ) ;
122222}
123223
124- const getRegexForBrackets = once < ISimpleInternalBracket [ ] , RegExp > (
125- ( input ) => input . map ( b => ` ${ b . open } ; ${ b . close } ` ) . join ( ';' ) ,
126- ( input ) => {
127- let pieces : string [ ] = [ ] ;
128- input . forEach ( ( b ) => {
129- pieces . push ( b . open ) ;
130- pieces . push ( b . close ) ;
131- } ) ;
132- return createBracketOrRegExp ( pieces ) ;
224+ function getRegexForBrackets ( brackets : RichEditBracket [ ] ) : RegExp {
225+ let pieces : string [ ] = [ ] ;
226+ for ( const bracket of brackets ) {
227+ for ( const open of bracket . open ) {
228+ pieces . push ( open ) ;
229+ }
230+ for ( const close of bracket . close ) {
231+ pieces . push ( close ) ;
232+ }
133233 }
134- ) ;
234+ pieces = unique ( pieces ) ;
235+ return createBracketOrRegExp ( pieces ) ;
236+ }
135237
136- const getReversedRegexForBrackets = once < ISimpleInternalBracket [ ] , RegExp > (
137- ( input ) => input . map ( b => ` ${ b . open } ; ${ b . close } ` ) . join ( ';' ) ,
138- ( input ) => {
139- let pieces : string [ ] = [ ] ;
140- input . forEach ( ( b ) => {
141- pieces . push ( toReversedString ( b . open ) ) ;
142- pieces . push ( toReversedString ( b . close ) ) ;
143- } ) ;
144- return createBracketOrRegExp ( pieces ) ;
238+ function getReversedRegexForBrackets ( brackets : RichEditBracket [ ] ) : RegExp {
239+ let pieces : string [ ] = [ ] ;
240+ for ( const bracket of brackets ) {
241+ for ( const open of bracket . open ) {
242+ pieces . push ( open ) ;
243+ }
244+ for ( const close of bracket . close ) {
245+ pieces . push ( close ) ;
246+ }
145247 }
146- ) ;
248+ pieces = unique ( pieces ) ;
249+ return createBracketOrRegExp ( pieces . map ( toReversedString ) ) ;
250+ }
147251
148252function prepareBracketForRegExp ( str : string ) : string {
149253 // This bracket pair uses letters like e.g. "begin" - "end"
0 commit comments