@@ -36,26 +36,82 @@ export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory
3636 requests . push ( lineText ) ;
3737 } ,
3838 finalize : ( ) => {
39- return createLineBreaks ( this . _fontInfo , tabSize , wrappingColumn , wrappingIndent , requests ) ;
39+ return createLineBreaks ( requests , this . _fontInfo , tabSize , wrappingColumn , wrappingIndent ) ;
4040 }
4141 } ;
4242 }
4343}
4444
45- function createLineBreaks ( fontInfo : FontInfo , tabSize : number , firstLineBreakColumn : number , wrappingIndent : WrappingIndent , requests : string [ ] ) : ( LineBreakData | null ) [ ] {
46- const width = Math . round ( firstLineBreakColumn * fontInfo . typicalHalfwidthCharacterWidth ) ;
45+ function createLineBreaks ( requests : string [ ] , fontInfo : FontInfo , tabSize : number , firstLineBreakColumn : number , wrappingIndent : WrappingIndent ) : ( LineBreakData | null ) [ ] {
46+ if ( firstLineBreakColumn === - 1 ) {
47+ const result : null [ ] = [ ] ;
48+ for ( let i = 0 , len = requests . length ; i < len ; i ++ ) {
49+ result [ i ] = null ;
50+ }
51+ return result ;
52+ }
53+
54+ const overallWidth = Math . round ( firstLineBreakColumn * fontInfo . typicalHalfwidthCharacterWidth ) ;
55+
56+ // Cannot respect WrappingIndent.Indent and WrappingIndent.DeepIndent because that would require
57+ // two dom layouts, in order to first set the width of the first line, and then set the width of the wrapped lines
58+ if ( wrappingIndent === WrappingIndent . Indent || wrappingIndent === WrappingIndent . DeepIndent ) {
59+ wrappingIndent = WrappingIndent . Same ;
60+ }
4761
4862 const containerDomNode = document . createElement ( 'div' ) ;
4963 Configuration . applyFontInfoSlow ( containerDomNode , fontInfo ) ;
50- containerDomNode . style . width = `${ width } px` ;
5164
5265 const sb = createStringBuilder ( 10000 ) ;
53- const charOffsets : number [ ] [ ] = [ ] ;
54- const visibleColumns : number [ ] [ ] = [ ] ;
66+ const firstNonWhitespaceIndices : number [ ] = [ ] ;
67+ const wrappedTextIndentLengths : number [ ] = [ ] ;
68+ const renderLineContents : string [ ] = [ ] ;
69+ const allCharOffsets : number [ ] [ ] = [ ] ;
70+ const allVisibleColumns : number [ ] [ ] = [ ] ;
5571 for ( let i = 0 ; i < requests . length ; i ++ ) {
56- const r = renderLine ( i , requests [ i ] , tabSize , sb ) ;
57- charOffsets [ i ] = r [ 0 ] ;
58- visibleColumns [ i ] = r [ 1 ] ;
72+ const lineContent = requests [ i ] ;
73+
74+ let firstNonWhitespaceIndex = 0 ;
75+ let wrappedTextIndentLength = 0 ;
76+ let width = overallWidth ;
77+
78+ if ( wrappingIndent !== WrappingIndent . None ) {
79+ firstNonWhitespaceIndex = strings . firstNonWhitespaceIndex ( lineContent ) ;
80+ if ( firstNonWhitespaceIndex === - 1 ) {
81+ // all whitespace line
82+ firstNonWhitespaceIndex = 0 ;
83+
84+ } else {
85+ // Track existing indent
86+
87+ for ( let i = 0 ; i < firstNonWhitespaceIndex ; i ++ ) {
88+ const charWidth = (
89+ lineContent . charCodeAt ( i ) === CharCode . Tab
90+ ? ( tabSize - ( wrappedTextIndentLength % tabSize ) )
91+ : 1
92+ ) ;
93+ wrappedTextIndentLength += charWidth ;
94+ }
95+
96+ const indentWidth = Math . ceil ( fontInfo . spaceWidth * wrappedTextIndentLength ) ;
97+
98+ // Force sticking to beginning of line if no character would fit except for the indentation
99+ if ( indentWidth + fontInfo . typicalFullwidthCharacterWidth > overallWidth ) {
100+ firstNonWhitespaceIndex = 0 ;
101+ wrappedTextIndentLength = 0 ;
102+ } else {
103+ width = overallWidth - indentWidth ;
104+ }
105+ }
106+ }
107+
108+ const renderLineContent = lineContent . substr ( firstNonWhitespaceIndex ) ;
109+ const tmp = renderLine ( renderLineContent , wrappedTextIndentLength , tabSize , width , sb ) ;
110+ firstNonWhitespaceIndices [ i ] = firstNonWhitespaceIndex ;
111+ wrappedTextIndentLengths [ i ] = wrappedTextIndentLength ;
112+ renderLineContents [ i ] = renderLineContent ;
113+ allCharOffsets [ i ] = tmp [ 0 ] ;
114+ allVisibleColumns [ i ] = tmp [ 1 ] ;
59115 }
60116 containerDomNode . innerHTML = sb . build ( ) ;
61117
@@ -71,21 +127,45 @@ function createLineBreaks(fontInfo: FontInfo, tabSize: number, firstLineBreakCol
71127 let result : ( LineBreakData | null ) [ ] = [ ] ;
72128 for ( let i = 0 ; i < requests . length ; i ++ ) {
73129 const lineDomNode = lineDomNodes [ i ] ;
74- result [ i ] = readLineBreaks ( range , lineDomNode , requests [ i ] , charOffsets [ i ] , visibleColumns [ i ] ) ;
130+ const breakOffsets : number [ ] | null = readLineBreaks ( range , lineDomNode , renderLineContents [ i ] , allCharOffsets [ i ] ) ;
131+ if ( breakOffsets === null ) {
132+ result [ i ] = null ;
133+ continue ;
134+ }
135+
136+ const firstNonWhitespaceIndex = firstNonWhitespaceIndices [ i ] ;
137+ const wrappedTextIndentLength = wrappedTextIndentLengths [ i ] ;
138+ const visibleColumns = allVisibleColumns [ i ] ;
139+
140+ const breakOffsetsVisibleColumn : number [ ] = [ ] ;
141+ for ( let j = 0 , len = breakOffsets . length ; j < len ; j ++ ) {
142+ breakOffsetsVisibleColumn [ j ] = visibleColumns [ breakOffsets [ j ] ] ;
143+ }
144+
145+ if ( firstNonWhitespaceIndex !== 0 ) {
146+ // All break offsets are relative to the renderLineContent, make them absolute again
147+ for ( let j = 0 , len = breakOffsets . length ; j < len ; j ++ ) {
148+ breakOffsets [ j ] += firstNonWhitespaceIndex ;
149+ }
150+ }
151+
152+ result [ i ] = new LineBreakData ( breakOffsets , breakOffsetsVisibleColumn , wrappedTextIndentLength ) ;
75153 }
76154
77155 document . body . removeChild ( containerDomNode ) ;
78156 return result ;
79157}
80158
81- function renderLine ( lineIndex : number , lineContent : string , tabSize : number , sb : IStringBuilder ) : [ number [ ] , number [ ] ] {
82- sb . appendASCIIString ( '<div>' ) ;
159+ function renderLine ( lineContent : string , initialVisibleColumn : number , tabSize : number , width : number , sb : IStringBuilder ) : [ number [ ] , number [ ] ] {
160+ sb . appendASCIIString ( '<div style="width:' ) ;
161+ sb . appendASCIIString ( String ( width ) ) ;
162+ sb . appendASCIIString ( 'px;">' ) ;
83163 // if (containsRTL) {
84164 // sb.appendASCIIString('" dir="ltr');
85165 // }
86166
87167 const len = lineContent . length ;
88- let visibleColumn = 0 ;
168+ let visibleColumn = initialVisibleColumn ;
89169 let charOffset = 0 ;
90170 let charOffsets : number [ ] = [ ] ;
91171 let visibleColumns : number [ ] = [ ] ;
@@ -163,7 +243,7 @@ function renderLine(lineIndex: number, lineContent: string, tabSize: number, sb:
163243 return [ charOffsets , visibleColumns ] ;
164244}
165245
166- function readLineBreaks ( range : Range , lineDomNode : HTMLDivElement , lineContent : string , charOffsets : number [ ] , visibleColumns : number [ ] ) : LineBreakData | null {
246+ function readLineBreaks ( range : Range , lineDomNode : HTMLDivElement , lineContent : string , charOffsets : number [ ] ) : number [ ] | null {
167247 if ( lineContent . length <= 1 ) {
168248 return null ;
169249 }
@@ -177,13 +257,7 @@ function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent:
177257 }
178258
179259 breakOffsets . push ( lineContent . length ) ;
180-
181- const breakOffsetsVisibleColumn = [ ] ;
182- for ( let i = 0 , len = breakOffsets . length ; i < len ; i ++ ) {
183- breakOffsetsVisibleColumn [ i ] = visibleColumns [ breakOffsets [ i ] ] ;
184- }
185-
186- return new LineBreakData ( breakOffsets , breakOffsetsVisibleColumn , 0 ) ;
260+ return breakOffsets ;
187261}
188262
189263type MaybeRects = ClientRectList | DOMRectList | null ;
0 commit comments