Skip to content

Commit d403e00

Browse files
committed
Support formatWithCursor
1 parent 1162380 commit d403e00

File tree

4 files changed

+307
-181
lines changed

4 files changed

+307
-181
lines changed

bin/print.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env node
22

33
import fs from "fs";
4-
import { format } from "prettier";
4+
import { formatWithCursor } from "prettier";
55

66
import plugin from "../src/plugin.js";
77

@@ -10,10 +10,13 @@ const code = fs.existsSync(process.argv[2])
1010
: process.argv.slice(2).join(" ").replace(/\\n/g, "\n");
1111

1212
const options = {
13+
cursorOffset: 10,
1314
parser: "xml",
1415
plugins: [plugin],
1516
xmlWhitespaceSensitivity: "ignore",
1617
embeddedLanguageFormatting: "auto"
1718
};
1819

19-
format(code, options).then((formatted) => console.log(formatted));
20+
formatWithCursor(code, options).then((formatted) =>
21+
console.log(formatted.formatted)
22+
);

src/embed.js

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,29 @@ const {
1515
function getElementTags(path, opts, print) {
1616
const node = path.getValue();
1717
const { OPEN, Name, attribute, START_CLOSE, SLASH_OPEN, END_NAME, END } =
18-
node.children;
18+
node;
1919

20-
const parts = [OPEN[0].image, Name[0].image];
20+
const parts = [OPEN, Name];
2121

22-
if (attribute) {
23-
parts.push(
24-
indent([line, join(line, path.map(print, "children", "attribute"))])
25-
);
22+
if (attribute.length > 0) {
23+
parts.push(indent([line, join(line, path.map(print, "attribute"))]));
2624
}
2725

2826
if (!opts.bracketSameLine) {
2927
parts.push(softline);
3028
}
3129

3230
return {
33-
openTag: group([...parts, START_CLOSE[0].image]),
34-
closeTag: group([SLASH_OPEN[0].image, END_NAME[0].image, END[0].image])
31+
openTag: group([...parts, START_CLOSE]),
32+
closeTag: group([SLASH_OPEN, END_NAME, END])
3533
};
3634
}
3735

3836
// Returns the value of the type tag if there is one, otherwise returns null.
3937
function getTagType(attributes) {
4038
for (const attribute of attributes) {
41-
if (attribute.children.Name[0].image === "type") {
42-
const value = attribute.children.STRING[0].image;
39+
if (attribute.Name === "type") {
40+
const value = attribute.STRING;
4341

4442
if (value.startsWith('"text/') && value.endsWith('"')) {
4543
return value.slice(6, -1);
@@ -53,8 +51,8 @@ function getTagType(attributes) {
5351
// Get the name of the parser that is represented by the given element node,
5452
// return null if a matching parser cannot be found
5553
function getParser(node, opts) {
56-
const { Name, attribute } = node.children;
57-
let parser = Name[0].image.toLowerCase();
54+
const { Name, attribute } = node;
55+
let parser = Name.toLowerCase();
5856

5957
// We don't want to deal with some weird recursive parser situation, so we
6058
// need to explicitly call out the XML parser here and just return null
@@ -64,7 +62,7 @@ function getParser(node, opts) {
6462

6563
// If this is a style tag or a script tag with a text/xxx type then we will
6664
// use xxx as the name of the parser
67-
if ((parser === "style" || parser === "script") && attribute) {
65+
if ((parser === "style" || parser === "script") && attribute.length > 0) {
6866
parser = getTagType(attribute);
6967
}
7068

@@ -96,8 +94,8 @@ function getParser(node, opts) {
9694
function getSource(content) {
9795
return content.chardata
9896
.map((node) => {
99-
const { SEA_WS, TEXT } = node.children;
100-
const [{ image }] = SEA_WS || TEXT;
97+
const { SEA_WS, TEXT } = node;
98+
const image = SEA_WS || TEXT;
10199

102100
return {
103101
offset: node.location.startOffset,
@@ -125,14 +123,21 @@ function embed(path, opts) {
125123
}
126124

127125
// If the node is self-closing, then skip
128-
if (!node.children.content) {
126+
if (!node.content) {
129127
return;
130128
}
131129

132130
// If the node does not actually contain content, or it contains any content
133-
// that is not just plain text, then skip
134-
const content = node.children.content[0].children;
135-
if (Object.keys(content).length !== 1 || !content.chardata) {
131+
// that is not just plain text, then skip.
132+
const content = node.content;
133+
if (
134+
content.chardata.length === 0 ||
135+
content.CData.length > 0 ||
136+
content.Comment.length > 0 ||
137+
content.element.length > 0 ||
138+
content.PROCESSING_INSTRUCTION.length > 0 ||
139+
content.reference.length > 0
140+
) {
136141
return;
137142
}
138143

src/parser.js

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,154 @@ function createError(message, options) {
1616
return Object.assign(error, options);
1717
}
1818

19+
function simplifyCST(node) {
20+
switch (node.name) {
21+
case "attribute": {
22+
const { Name, EQUALS, STRING } = node.children;
23+
24+
return {
25+
name: "attribute",
26+
Name: Name[0].image,
27+
EQUALS: EQUALS[0].image,
28+
STRING: STRING[0].image,
29+
location: node.location
30+
};
31+
}
32+
case "chardata": {
33+
const { SEA_WS, TEXT } = node.children;
34+
35+
return {
36+
name: "chardata",
37+
SEA_WS: SEA_WS ? SEA_WS[0].image : null,
38+
TEXT: TEXT ? TEXT[0].image : null,
39+
location: node.location
40+
};
41+
}
42+
case "content": {
43+
const {
44+
CData,
45+
Comment,
46+
chardata,
47+
element,
48+
PROCESSING_INSTRUCTION,
49+
reference
50+
} = node.children;
51+
52+
return {
53+
name: "content",
54+
CData: CData || [],
55+
Comment: Comment || [],
56+
chardata: (chardata || []).map(simplifyCST),
57+
element: (element || []).map(simplifyCST),
58+
PROCESSING_INSTRUCTION: PROCESSING_INSTRUCTION || [],
59+
reference: (reference || []).map(simplifyCST),
60+
location: node.location
61+
};
62+
}
63+
case "docTypeDecl": {
64+
const { DocType, Name, externalID, CLOSE } = node.children;
65+
66+
return {
67+
name: "docTypeDecl",
68+
DocType: DocType[0].image,
69+
Name: Name[0].image,
70+
externalID: externalID ? simplifyCST(externalID[0]) : null,
71+
CLOSE: CLOSE[0].image,
72+
location: node.location
73+
};
74+
}
75+
case "document": {
76+
const { docTypeDecl, element, misc, prolog } = node.children;
77+
78+
return {
79+
name: "document",
80+
docTypeDecl: docTypeDecl ? simplifyCST(docTypeDecl[0]) : null,
81+
element: element ? simplifyCST(element[0]) : null,
82+
misc: (misc || [])
83+
.filter((child) => !child.children.SEA_WS)
84+
.map(simplifyCST),
85+
prolog: prolog ? simplifyCST(prolog[0]) : null,
86+
location: node.location
87+
};
88+
}
89+
case "element": {
90+
const {
91+
OPEN,
92+
Name,
93+
attribute,
94+
START_CLOSE,
95+
content,
96+
SLASH_OPEN,
97+
END_NAME,
98+
END,
99+
SLASH_CLOSE
100+
} = node.children;
101+
102+
return {
103+
name: "element",
104+
OPEN: OPEN[0].image,
105+
Name: Name[0].image,
106+
attribute: (attribute || []).map(simplifyCST),
107+
START_CLOSE: START_CLOSE ? START_CLOSE[0].image : null,
108+
content: content ? simplifyCST(content[0]) : null,
109+
SLASH_OPEN: SLASH_OPEN ? SLASH_OPEN[0].image : null,
110+
END_NAME: END_NAME ? END_NAME[0].image : null,
111+
END: END ? END[0].image : null,
112+
SLASH_CLOSE: SLASH_CLOSE ? SLASH_CLOSE[0].image : null,
113+
location: node.location
114+
};
115+
}
116+
case "externalID": {
117+
const { Public, PubIDLiteral, System, SystemLiteral } = node.children;
118+
119+
return {
120+
name: "externalID",
121+
Public: Public ? Public[0].image : null,
122+
PubIDLiteral: PubIDLiteral ? PubIDLiteral[0].image : null,
123+
System: System ? System[0].image : null,
124+
SystemLiteral: SystemLiteral ? SystemLiteral[0].image : null,
125+
location: node.location
126+
};
127+
}
128+
case "misc": {
129+
const { Comment, PROCESSING_INSTRUCTION, SEA_WS } = node.children;
130+
131+
return {
132+
name: "misc",
133+
Comment: Comment ? Comment[0].image : null,
134+
PROCESSING_INSTRUCTION: PROCESSING_INSTRUCTION
135+
? PROCESSING_INSTRUCTION[0].image
136+
: null,
137+
SEA_WS: SEA_WS ? SEA_WS[0].image : null,
138+
location: node.location
139+
};
140+
}
141+
case "prolog": {
142+
const { XMLDeclOpen, attribute, SPECIAL_CLOSE } = node.children;
143+
144+
return {
145+
name: "prolog",
146+
XMLDeclOpen: XMLDeclOpen[0].image,
147+
attribute: (attribute || []).map(simplifyCST),
148+
SPECIAL_CLOSE: SPECIAL_CLOSE[0].image,
149+
location: node.location
150+
};
151+
}
152+
case "reference": {
153+
const { CharRef, EntityRef } = node.children;
154+
155+
return {
156+
name: "reference",
157+
CharRef: CharRef ? CharRef[0].image : null,
158+
EntityRef: EntityRef ? EntityRef[0].image : null,
159+
location: node.location
160+
};
161+
}
162+
default:
163+
throw new Error(`Unknown node type: ${node.name}`);
164+
}
165+
}
166+
19167
const parser = {
20168
parse(text) {
21169
const { lexErrors, parseErrors, cst } = xmlToolsParse(text);
@@ -52,7 +200,7 @@ const parser = {
52200
}
53201

54202
// Otherwise return the CST.
55-
return cst;
203+
return simplifyCST(cst);
56204
},
57205
astFormat: "xml",
58206
locStart(node) {

0 commit comments

Comments
 (0)