@@ -105,6 +105,222 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea
105105 let breakingOffsets : number [ ] = [ ] ;
106106 let breakingOffsetsVisibleColumn : number [ ] = [ ] ;
107107 let breakingOffsetsCount : number = 0 ;
108+
109+ if ( previousBreakingData /* && firstLineBreakingColumn >= 10 && Math.abs(previousBreakingData.breakingColumn - firstLineBreakingColumn) <= 3 */ ) {
110+ const prevBreakingOffsets = previousBreakingData . breakOffsets ;
111+ const prevBreakingOffsetsVisibleColumn = previousBreakingData . breakingOffsetsVisibleColumn ;
112+
113+ let breakingColumn = firstLineBreakingColumn ;
114+ const prevLen = prevBreakingOffsets . length ;
115+ let prevIndex = 0 ;
116+ while ( prevIndex < prevLen ) {
117+
118+ // Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break)
119+ let breakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets [ prevIndex ] ;
120+ let breakOffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn [ prevIndex ] ;
121+
122+ if ( breakOffsetVisibleColumn === breakingColumn ) {
123+ // perfect fit, nothing to do
124+ breakingOffsets [ breakingOffsetsCount ] = breakOffset ;
125+ breakingOffsetsVisibleColumn [ breakingOffsetsCount ] = breakOffsetVisibleColumn ;
126+ breakingOffsetsCount ++ ;
127+ breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn ;
128+ prevIndex ++ ;
129+ } else if ( breakOffsetVisibleColumn < breakingColumn ) {
130+ // try to add more characters
131+ const initialBreakOffset = breakOffset ;
132+ let visibleColumn = breakOffsetVisibleColumn ;
133+ breakOffset = 0 ;
134+
135+ let prevCharCode = lineText . charCodeAt ( initialBreakOffset - 1 ) ;
136+ let prevCharCodeClass = classifier . get ( prevCharCode ) ;
137+ let mustBreak = false ;
138+ for ( let i = initialBreakOffset ; i < len ; i ++ ) {
139+ const charCode = lineText . charCodeAt ( i ) ;
140+ const charCodeClass = classifier . get ( charCode ) ;
141+
142+ if ( strings . isHighSurrogate ( prevCharCode ) ) {
143+ // A surrogate pair must always be considered as a single unit, so it is never to be broken
144+ visibleColumn += 1 ;
145+ prevCharCode = charCode ;
146+ prevCharCodeClass = charCodeClass ;
147+ continue ;
148+ }
149+
150+ if ( canBreak ( prevCharCodeClass , charCodeClass ) ) {
151+ breakOffset = i ;
152+ breakOffsetVisibleColumn = visibleColumn ;
153+ }
154+
155+ const charWidth = computeCharWidth ( charCode , visibleColumn , tabSize , columnsForFullWidthChar ) ;
156+ visibleColumn += charWidth ;
157+
158+ if ( visibleColumn > breakingColumn ) {
159+ // We need to break at least before character at `i`:
160+
161+ if ( breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn ) {
162+ // Cannot break at `breakOffset`, must break at `i`
163+ breakOffset = i ;
164+ breakOffsetVisibleColumn = visibleColumn - charWidth ;
165+ }
166+
167+ mustBreak = true ;
168+ break ;
169+ }
170+
171+ prevCharCode = charCode ;
172+ prevCharCodeClass = charCodeClass ;
173+ }
174+
175+ if ( ! mustBreak ) {
176+ // there is no more need to break => stop the outer loop!
177+ // Add last segment
178+ breakingOffsets [ breakingOffsetsCount ] = prevBreakingOffsets [ prevBreakingOffsets . length - 1 ] ;
179+ breakingOffsetsVisibleColumn [ breakingOffsetsCount ] = prevBreakingOffsetsVisibleColumn [ prevBreakingOffsets . length - 1 ] ;
180+ break ;
181+ }
182+
183+ breakingOffsets [ breakingOffsetsCount ] = breakOffset ;
184+ breakingOffsetsVisibleColumn [ breakingOffsetsCount ] = breakOffsetVisibleColumn ;
185+ breakingOffsetsCount ++ ;
186+ breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn ;
187+ prevIndex ++ ;
188+ } else if ( breakOffsetVisibleColumn > breakingColumn ) {
189+ const initialBreakOffset = breakOffset ;
190+ let visibleColumn = breakOffsetVisibleColumn ;
191+ breakOffset = 0 ;
192+
193+ let charCode = lineText . charCodeAt ( initialBreakOffset ) ;
194+ let charCodeClass = classifier . get ( charCode ) ;
195+ let hitTab = false ;
196+
197+ let firstValidBreakOffset = 0 ;
198+ let firstValidBreakOffsetVisibleColumn = 0 ;
199+ for ( let i = initialBreakOffset - 1 ; i >= 0 ; i -- ) {
200+ let prevCharCode = lineText . charCodeAt ( i ) ;
201+ let prevCharCodeClass = classifier . get ( prevCharCode ) ;
202+
203+ if ( strings . isHighSurrogate ( prevCharCode ) ) {
204+ // A surrogate pair must always be considered as a single unit, so it is never to be broken
205+ visibleColumn -= 1 ;
206+ charCode = prevCharCode ;
207+ charCodeClass = prevCharCodeClass ;
208+ continue ;
209+ }
210+
211+ if ( prevCharCode === CharCode . Tab ) {
212+ // cannot determine the width of a tab when going backwards, so we must go forwards
213+ hitTab = true ;
214+ break ;
215+ }
216+
217+ const charWidth = ( strings . isFullWidthCharacter ( prevCharCode ) ? columnsForFullWidthChar : 1 ) ;
218+
219+ if ( visibleColumn <= breakingColumn ) {
220+ if ( firstValidBreakOffset === 0 ) {
221+ firstValidBreakOffset = i + 1 ;
222+ firstValidBreakOffsetVisibleColumn = visibleColumn ;
223+ }
224+
225+ if ( visibleColumn <= breakingColumn - wrappedLineBreakingColumn ) {
226+ // went too far!
227+ break ;
228+ }
229+
230+ if ( canBreak ( prevCharCodeClass , charCodeClass ) ) {
231+ breakOffset = i + 1 ;
232+ breakOffsetVisibleColumn = visibleColumn ;
233+ break ;
234+ }
235+ }
236+
237+ visibleColumn -= charWidth ;
238+ charCode = prevCharCode ;
239+ charCodeClass = prevCharCodeClass ;
240+ }
241+
242+ if ( hitTab ) {
243+ // cannot determine the width of a tab when going backwards, so we must go forwards
244+ prevIndex -- ;
245+ continue ;
246+ }
247+
248+ if ( breakOffset === 0 ) {
249+ // Could not find a good breaking point
250+ breakOffset = firstValidBreakOffset ;
251+ breakOffsetVisibleColumn = firstValidBreakOffsetVisibleColumn ;
252+ }
253+
254+ breakingOffsets [ breakingOffsetsCount ] = breakOffset ;
255+ breakingOffsetsVisibleColumn [ breakingOffsetsCount ] = breakOffsetVisibleColumn ;
256+ breakingOffsetsCount ++ ;
257+ breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn ;
258+ }
259+
260+ if ( prevIndex < 0 ) {
261+ prevIndex = 0 ;
262+ } else {
263+ let currentDiff = Math . abs ( prevBreakingOffsetsVisibleColumn [ prevIndex ] - breakingColumn ) ;
264+ while ( prevIndex + 1 < prevLen ) {
265+ const potentialDiff = Math . abs ( prevBreakingOffsetsVisibleColumn [ prevIndex + 1 ] - breakingColumn ) ;
266+ if ( potentialDiff >= currentDiff ) {
267+ break ;
268+ }
269+ currentDiff = potentialDiff ;
270+ prevIndex ++ ;
271+ }
272+ }
273+ }
274+
275+ if ( breakingOffsetsCount === 0 ) {
276+ return null ;
277+ }
278+
279+ return new LineBreakingData ( firstLineBreakingColumn , breakingOffsets , breakingOffsetsVisibleColumn , wrappedTextIndentLength ) ;
280+ const expected = createLineMapping ( classifier , null , lineText , tabSize , firstLineBreakingColumn , columnsForFullWidthChar , hardWrappingIndent ) ;
281+ const actual = new LineBreakingData ( firstLineBreakingColumn , breakingOffsets , breakingOffsetsVisibleColumn , wrappedTextIndentLength ) ;
282+ try {
283+ actual . assertEqual ( expected ) ;
284+ } catch ( err ) {
285+ console . log ( `BREAKING!!` ) ;
286+ console . log ( err ) ;
287+ console . log ( `
288+ assertIncrementalLineMapping(
289+ factory, ${ str ( lineText ) } , 4,
290+ ${ previousBreakingData . breakingColumn } , ${ str ( toAnnotatedText ( lineText , previousBreakingData ) ) } ,
291+ ${ expected ! . breakingColumn } , ${ str ( toAnnotatedText ( lineText , expected ) ) }
292+ );
293+ ` ) ;
294+ function str ( strr : string ) {
295+ return `'${ strr . replace ( / ' / g, '\\\'' ) } '` ;
296+ }
297+ function toAnnotatedText ( text : string , lineBreakingData : LineBreakingData | null ) : string {
298+ // Insert line break markers again, according to algorithm
299+ let actualAnnotatedText = '' ;
300+ if ( lineBreakingData ) {
301+ let previousLineIndex = 0 ;
302+ for ( let i = 0 , len = text . length ; i < len ; i ++ ) {
303+ let r = LineBreakingData . getOutputPositionOfInputOffset ( lineBreakingData . breakOffsets , i ) ;
304+ if ( previousLineIndex !== r . outputLineIndex ) {
305+ previousLineIndex = r . outputLineIndex ;
306+ actualAnnotatedText += '|' ;
307+ }
308+ actualAnnotatedText += text . charAt ( i ) ;
309+ }
310+ } else {
311+ // No wrapping
312+ actualAnnotatedText = text ;
313+ }
314+ return actualAnnotatedText ;
315+ }
316+ }
317+ return actual ;
318+
319+ breakingOffsets = [ ] ;
320+ breakingOffsetsVisibleColumn = [ ] ;
321+ breakingOffsetsCount = 0 ;
322+ }
323+
108324 let breakOffset = 0 ;
109325 let breakOffsetVisibleColumn = 0 ;
110326
@@ -165,6 +381,62 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea
165381 return new LineBreakingData ( firstLineBreakingColumn , breakingOffsets , breakingOffsetsVisibleColumn , wrappedTextIndentLength ) ;
166382}
167383
384+ // class BreakSearchResult {
385+
386+ // public static INSTANCE = new BreakSearchResult();
387+
388+ // prevCharCode: number = CharCode.Null;
389+ // prevCharCodeClass: CharacterClass = CharacterClass.NONE;
390+ // breakOffset: number = 0;
391+ // breakOffsetVisibleColumn: number = 0;
392+ // visibleColumn: number = 0;
393+ // }
394+
395+ // function searchForBreak(classifier: WrappingCharacterClassifier, lineText: string, len: number, prevCharCode: number, prevCharCodeClass: number): boolean {
396+ // let breakOffset = 0;
397+ // let breakOffsetVisibleColumn = 0;
398+ // for (let i = 1; i < len; i++) {
399+ // const charCode = lineText.charCodeAt(i);
400+ // const charCodeClass = classifier.get(charCode);
401+
402+ // if (strings.isHighSurrogate(prevCharCode)) {
403+ // // A surrogate pair must always be considered as a single unit, so it is never to be broken
404+ // visibleColumn += 1;
405+ // prevCharCode = charCode;
406+ // prevCharCodeClass = charCodeClass;
407+ // continue;
408+ // }
409+
410+ // if (canBreak(prevCharCodeClass, charCodeClass)) {
411+ // breakOffset = i;
412+ // breakOffsetVisibleColumn = visibleColumn;
413+ // }
414+
415+ // const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar);
416+ // visibleColumn += charWidth;
417+
418+ // // check if adding character at `i` will go over the breaking column
419+ // if (visibleColumn > breakingColumn) {
420+ // // We need to break at least before character at `i`:
421+
422+ // if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) {
423+ // // Cannot break at `breakOffset`, must break at `i`
424+ // breakOffset = i;
425+ // breakOffsetVisibleColumn = visibleColumn - charWidth;
426+ // }
427+
428+ // breakingOffsets[breakingOffsetsCount] = breakOffset;
429+ // breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn;
430+ // breakingOffsetsCount++;
431+ // breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn;
432+ // breakOffset = 0;
433+ // }
434+
435+ // prevCharCode = charCode;
436+ // prevCharCodeClass = charCodeClass;
437+ // }
438+ // }
439+
168440function computeCharWidth ( charCode : number , visibleColumn : number , tabSize : number , columnsForFullWidthChar : number ) : number {
169441 if ( charCode === CharCode . Tab ) {
170442 return ( tabSize - ( visibleColumn % tabSize ) ) ;
0 commit comments