Skip to content

Commit c6dec6e

Browse files
committed
Add support for WrappingIndent.Same in DOMLineBreaksComputer
1 parent 02d3992 commit c6dec6e

1 file changed

Lines changed: 95 additions & 21 deletions

File tree

src/vs/editor/browser/view/domLineBreaksComputer.ts

Lines changed: 95 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -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

189263
type MaybeRects = ClientRectList | DOMRectList | null;

0 commit comments

Comments
 (0)