Skip to content

Commit c07c60a

Browse files
committed
WIP
1 parent 68bed3e commit c07c60a

File tree

4 files changed

+220
-1
lines changed

4 files changed

+220
-1
lines changed

src/components/composer/composer/abstract_composer_store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ export abstract class AbstractComposerStore extends SpreadsheetStore {
534534
if (isNewCurrentContent || this.editionMode !== "inactive") {
535535
const locale = this.getters.getLocale();
536536
this.currentTokens = isFormula(text) ? composerTokenize(text, locale) : [];
537+
// this._currentContent = prettify(parseTokens(this.currentTokens));
537538
if (this.currentTokens.length > 100) {
538539
if (raise) {
539540
this.notificationStore.raiseError(

src/components/composer/composer/cell_composer_store.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { parseTokens } from "../../../formulas/parser";
12
import { parseLiteral } from "../../../helpers/cells";
23
import {
34
formatValue,
@@ -26,6 +27,7 @@ import {
2627
isMatrix,
2728
} from "../../../types";
2829
import { AbstractComposerStore } from "./abstract_composer_store";
30+
import { prettify } from "./prettifier_content";
2931

3032
const CELL_DELETED_MESSAGE = _t("The cell you are trying to edit has been deleted.");
3133

@@ -202,7 +204,10 @@ export class CellComposerStore extends AbstractComposerStore {
202204
const locale = this.getters.getLocale();
203205
const cell = this.getters.getCell(position);
204206
if (cell?.isFormula) {
205-
return localizeFormula(cell.content, locale);
207+
const pretifiedContent = cell.compiledFormula.isBadExpression
208+
? cell.content
209+
: prettify(parseTokens(cell.compiledFormula.tokens));
210+
return localizeFormula(pretifiedContent, locale);
206211
}
207212
const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
208213
if (spreader) {

src/components/composer/composer/composer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ css/* scss */ `
6060
padding-right: 3px;
6161
outline: none;
6262
63+
tab-size: 4;
64+
6365
p {
6466
margin-bottom: 0px;
6567
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// ---------------------------------------
2+
// DOC part
3+
// ---------------------------------------
4+
5+
import { AST } from "../../../formulas/parser";
6+
7+
type Union = { type: "union"; a: Doc; b: Doc };
8+
type Concat = { type: "concat"; docs: Doc[] };
9+
type Nest = { type: "nest"; indent: number; a: Doc };
10+
type Line = { type: "line" };
11+
12+
type Doc = null | Concat | Nest | Union | string | Line;
13+
14+
// indicates where a newline can be inserted
15+
function line(): Line {
16+
return { type: "line" };
17+
}
18+
19+
// sections the tokens as a single line
20+
function concat(docs: Doc[]): Concat {
21+
return { type: "concat", docs };
22+
}
23+
24+
// indents the tokens
25+
function nest(indent: number, x: Doc): Nest {
26+
return { type: "nest", indent, a: x };
27+
}
28+
29+
// sections tokens into a group
30+
function group(x: Doc): Union {
31+
return union(flatten(x), x);
32+
}
33+
34+
function union(a: Doc, b: Doc): Union {
35+
return { type: "union", a, b };
36+
}
37+
38+
function flatten(x: Doc): Doc {
39+
if (typeof x === "string") {
40+
return x;
41+
}
42+
if (x === null) {
43+
return null;
44+
}
45+
if (x.type === "union") {
46+
return union(flatten(x.a), flatten(x.b));
47+
}
48+
if (x.type === "concat") {
49+
return concat(x.docs.map(flatten));
50+
}
51+
if (x.type === "nest") {
52+
return {
53+
type: "nest",
54+
indent: x.indent,
55+
a: flatten(x.a),
56+
};
57+
}
58+
if (x.type === "line") {
59+
return " ";
60+
}
61+
return x;
62+
}
63+
64+
// ---------------------------------------
65+
// pretty part
66+
// ---------------------------------------
67+
68+
type SubLine = {
69+
type: "subline";
70+
indent: number;
71+
doc: SubDoc;
72+
};
73+
type SubText = {
74+
type: "subtext";
75+
text: string;
76+
doc: SubDoc;
77+
};
78+
type SubDoc = SubLine | SubText | null;
79+
type DocFit = [number, Doc];
80+
81+
function pretty(width: number, x: Doc): string {
82+
return layout(best(width, 0, x));
83+
}
84+
85+
function layout(x: SubDoc): string {
86+
if (x && "type" in x) {
87+
if (x.type === "subline") {
88+
return "\n" + "\t".repeat(x.indent) + layout(x.doc);
89+
}
90+
if (x.type === "subtext") {
91+
return x.text + layout(x.doc);
92+
}
93+
}
94+
return "";
95+
}
96+
97+
function best(width: number, k: number, x: Doc): SubDoc {
98+
return be(width, k, [[0, x]]);
99+
}
100+
101+
function be(width: number, k: number, x: DocFit[]): SubDoc {
102+
if (x.length === 0) return null;
103+
const [first, ...rest] = x;
104+
const [i, doc] = first;
105+
if (doc !== null) {
106+
if (typeof doc === "string") {
107+
return {
108+
type: "subtext",
109+
text: doc,
110+
doc: be(width, k + doc.length, rest),
111+
};
112+
}
113+
if (doc.type === "concat") {
114+
const concatDocs: DocFit[] = doc.docs.map((d) => [i, d]);
115+
const res = be(width, k, [...concatDocs, ...rest]);
116+
return res;
117+
}
118+
if (doc.type === "nest") {
119+
return be(width, k, [[i + doc.indent, doc.a], ...rest]);
120+
}
121+
if (doc.type === "line") {
122+
return {
123+
type: "subline",
124+
indent: i,
125+
doc: be(width, i, rest),
126+
};
127+
}
128+
if (doc.type === "union") {
129+
const a = be(width, k, [[i, doc.a], ...rest]);
130+
const b = be(width, k, [[i, doc.b], ...rest]);
131+
return fits(width - k, a) ? a : b;
132+
}
133+
}
134+
return null;
135+
}
136+
137+
function fits(width: number, x: SubDoc): boolean {
138+
if (width < 0) return false;
139+
if (x === null) {
140+
return true;
141+
} else if (x.type === "subline") {
142+
return true;
143+
} else if (x.type === "subtext") {
144+
return fits(width - x.text.length, x.doc);
145+
}
146+
return false;
147+
}
148+
149+
// ---------------------------------------
150+
// AST part
151+
// ---------------------------------------
152+
153+
export function prettify(ast: AST) {
154+
return "= " + pretty(30, astToDoc(ast));
155+
}
156+
157+
function astToDoc(ast: AST): Doc {
158+
switch (ast.type) {
159+
case "NUMBER":
160+
return String(ast.value);
161+
162+
case "STRING":
163+
return `"${ast.value}"`;
164+
165+
case "BOOLEAN":
166+
return ast.value ? "TRUE" : "FALSE";
167+
168+
case "REFERENCE":
169+
return ast.value;
170+
171+
case "FUNCALL":
172+
const argsDocs = ast.args.map(astToDoc);
173+
return group(
174+
concat([
175+
// line(),
176+
ast.value,
177+
"(",
178+
nest(
179+
1,
180+
concat([
181+
line(),
182+
concat(argsDocs.map((doc, i) => (i > 0 ? concat([",", line(), doc]) : doc))),
183+
])
184+
),
185+
line(),
186+
")",
187+
])
188+
);
189+
190+
case "UNARY_OPERATION":
191+
return ast.postfix
192+
? concat([astToDoc(ast.operand), ast.value])
193+
: concat([ast.value, astToDoc(ast.operand)]);
194+
195+
// case "BIN_OPERATION":
196+
// return concat([astToDoc(ast.left), " ", ast.value, " ", astToDoc(ast.right)]);
197+
198+
case "BIN_OPERATION": {
199+
const leftDoc = astToDoc(ast.left);
200+
const rightDoc = astToDoc(ast.right);
201+
const operator = ` ${ast.value} `;
202+
return group(concat([nest(0, concat([leftDoc, operator, line(), rightDoc]))]));
203+
}
204+
205+
case "SYMBOL":
206+
return ast.value;
207+
208+
case "EMPTY":
209+
return "";
210+
}
211+
}

0 commit comments

Comments
 (0)