Skip to content

Commit 0b856f7

Browse files
committed
WIP LinkedString
1 parent 5eeceb2 commit 0b856f7

File tree

1 file changed

+57
-61
lines changed

1 file changed

+57
-61
lines changed

src/components/composer/composer/prettifier_content.ts

Lines changed: 57 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -119,74 +119,72 @@ function flatten(doc: Doc): Doc {
119119
// ---------------------------------------
120120

121121
/**
122-
* The `SubString` represents a reduced version of the `Doc` structure,
123-
* representing a single way to format an AST into a human-readable string.
122+
* A linked list for string segments.
124123
*/
125-
type SubString = SubLine | SubText | null;
126-
127-
type SubLine = {
128-
type: "subLine";
129-
indent: number;
130-
subString: SubString;
131-
};
132-
type SubText = {
133-
type: "subText";
134-
text: string;
135-
subString: SubString;
136-
};
124+
interface LinkedString {
125+
subString: string;
126+
next: LinkedString | null;
127+
}
137128

138129
/**
139-
* `PrettifyFitState` represents the current state of the formatting process,
140-
* including the remaining `Doc` to process and the current indentation level.
130+
* Memoized helper to build indentation strings.
141131
*/
142-
type PrettifyFitState = {
143-
indentLevel: number;
144-
doc: Doc;
145-
next: PrettifyFitState | null;
146-
};
132+
const memoizedIndentation: Record<number, string> = {};
133+
function getIndentation(indentLevel: number): string {
134+
if (!(indentLevel in memoizedIndentation)) {
135+
memoizedIndentation[indentLevel] = "\n" + "\t".repeat(indentLevel);
136+
}
137+
return memoizedIndentation[indentLevel];
138+
}
147139

148140
/**
149141
* Converts a `Doc` structure into a string representation that fits within
150142
* the specified width.
151143
*/
152144
function print(doc: Doc, width: number): string {
153-
return stringify(selectBestSubString(width, doc));
145+
return stringify(selectBestLinkedString(width, doc));
154146
}
155147

156148
/**
157-
* Recursively converts a `SubString` object into a human-readable string.
149+
* Recursively converts a `LinkedString` object into a human-readable string.
158150
* This is the final step of the prettifier process.
159151
*/
160-
function stringify(x: SubString): string {
161-
if (x && "type" in x) {
162-
if (x.type === "subLine") {
163-
return "\n" + "\t".repeat(x.indent) + stringify(x.subString);
164-
}
165-
if (x.type === "subText") {
166-
return x.text + stringify(x.subString);
167-
}
152+
function stringify(linkedString: LinkedString | null): string {
153+
let result = "";
154+
while (linkedString) {
155+
result += linkedString.subString;
156+
linkedString = linkedString.next;
168157
}
169-
return "";
158+
return result;
170159
}
171160

172161
/**
173-
* Selects the best `SubString` representation of a `Doc` that fits within
162+
* Selects the best `LinkedString` representation of a `Doc` that fits within
174163
* the given width.
175164
*/
176-
function selectBestSubString(width: number, doc: Doc): SubString {
177-
const head: PrettifyFitState = {
165+
function selectBestLinkedString(width: number, doc: Doc): LinkedString | null {
166+
const head: RestToFitNode = {
178167
indentLevel: 0,
179168
doc,
180169
next: null,
181170
};
182-
return _selectBestSubString(width, 0, head);
171+
return _selectBestLinkedString(width, 0, head);
183172
}
184173

185-
function _selectBestSubString(
174+
/**
175+
* A specialized linked list node for tracking the remaining `Doc` to fit.
176+
*/
177+
interface RestToFitNode {
178+
indentLevel: number;
179+
doc: Doc;
180+
next: RestToFitNode | null;
181+
}
182+
183+
function _selectBestLinkedString(
186184
width: number,
187185
currentIndentLevel: number,
188-
head: PrettifyFitState | null
189-
): SubString {
186+
head: RestToFitNode | null
187+
): LinkedString | null {
190188
if (head === null) {
191189
return null;
192190
}
@@ -195,58 +193,56 @@ function _selectBestSubString(
195193

196194
if (typeof doc === "string") {
197195
return {
198-
type: "subText",
199-
text: doc,
200-
subString: _selectBestSubString(width, currentIndentLevel + doc.length, next),
196+
subString: doc,
197+
next: _selectBestLinkedString(width, currentIndentLevel + doc.length, next),
201198
};
202199
}
203200
if (doc.type === "concat") {
204201
let newHead = next;
205202
for (let i = doc.docs.length - 1; i >= 0; i--) {
206203
newHead = { indentLevel, doc: doc.docs[i], next: newHead };
207204
}
208-
return _selectBestSubString(width, currentIndentLevel, newHead);
205+
return _selectBestLinkedString(width, currentIndentLevel, newHead);
209206
}
210207
if (doc.type === "nest") {
211-
return _selectBestSubString(width, currentIndentLevel, {
208+
return _selectBestLinkedString(width, currentIndentLevel, {
212209
indentLevel: indentLevel + doc.indentLevel,
213210
doc: doc.doc,
214211
next,
215212
});
216213
}
217214
if (doc.type === "insertLine") {
218215
return {
219-
type: "subLine",
220-
indent: indentLevel,
221-
subString: _selectBestSubString(width, indentLevel, next),
216+
subString: getIndentation(indentLevel),
217+
next: _selectBestLinkedString(width, indentLevel, next),
222218
};
223219
}
224220
if (doc.type === "chooseBetween") {
225221
const head1 = { indentLevel, doc: doc.doc1, next };
226-
const possibleSubString = _selectBestSubString(width, currentIndentLevel, head1);
227-
if (fits(width - currentIndentLevel, possibleSubString)) {
228-
return possibleSubString;
222+
const possibleLinkedString = _selectBestLinkedString(width, currentIndentLevel, head1);
223+
if (fits(width - currentIndentLevel, possibleLinkedString)) {
224+
return possibleLinkedString;
229225
}
230226

231227
const head2 = { indentLevel, doc: doc.doc2, next };
232-
return _selectBestSubString(width, currentIndentLevel, head2);
228+
return _selectBestLinkedString(width, currentIndentLevel, head2);
233229
}
234230
return null;
235231
}
236232

237233
/**
238-
* Checks whether a given `SubString` fits within the specified width.
234+
* Checks whether a given `LinkedString` fits within the specified width.
239235
*/
240-
function fits(width: number, x: SubString): boolean {
241-
if (width < 0) return false;
242-
if (x === null) {
243-
return true;
244-
} else if (x.type === "subLine") {
245-
return true;
246-
} else if (x.type === "subText") {
247-
return fits(width - x.text.length, x.subString);
236+
function fits(width: number, linkedString: LinkedString | null): boolean {
237+
while (linkedString) {
238+
if (linkedString.subString[0] === "\n") {
239+
return true;
240+
}
241+
width -= linkedString.subString.length;
242+
if (width < 0) return false;
243+
linkedString = linkedString.next;
248244
}
249-
return false;
245+
return true;
250246
}
251247

252248
// ---------------------------------------

0 commit comments

Comments
 (0)