Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 126 additions & 13 deletions src/typstToTextlintAst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,85 @@ export const paragraphizeTextlintAstObject = (
return { nodes: collected, nextIndex: i };
};

const isBlankStr = (c: Content): boolean =>
c.type === ASTNodeTypes.Str && c.raw?.trim() === "";
const isTermItem = (c: Content): boolean =>
isAstNode(c) && isTypstType(c.type, /^Marked::TermItem$/);
const createParagraph = (nodes: Content[]): Content => {
const first = nodes[0];
const last = nodes[nodes.length - 1];
return {
type: ASTNodeTypes.Paragraph,
children: nodes,
loc: { start: first.loc.start, end: last.loc.end },
range: [first.range[0], last.range[1]],
raw: nodes.map((c) => c.raw).join(""),
} as Content;
};

const splitParagraphTermlist = (paraNode: AstNode): Content[] => {
if (!hasChildren(paraNode)) return [];

const segments: { isTerm: boolean; nodes: Content[] }[] = [];
let current: { isTerm: boolean; nodes: Content[] } | null = null;
for (const child of paraNode.children) {
if (isBlankStr(child)) {
continue;
}
const isTerm = isTermItem(child);
if (!current || current.isTerm !== isTerm) {
if (current) segments.push(current);
current = { isTerm, nodes: [child] };
} else {
current.nodes.push(child);
}
}
if (current) segments.push(current);

const results: Content[] = [];
for (const seg of segments) {
if (seg.isTerm) {
results.push(...seg.nodes);
continue;
}

const nodes = seg.nodes.flatMap(flattenTypstMarkupChildren);
if (nodes.length === 0) continue;
results.push(createParagraph(nodes));
}
return results;
};

const collectConsecutiveRootTermItems = (
arr: Content[],
startIndex: number,
): { nodes: Content[]; nextIndex: number } => {
const nodes: Content[] = [];
let i = startIndex;
while (i < arr.length) {
const currentNode = arr[i];
if (
isAstNode(currentNode) &&
isTypstType(currentNode.type, /^Marked::TermItem$/)
) {
nodes.push(currentNode);
i++;
continue;
}
if (currentNode.type === ASTNodeTypes.Str && currentNode.raw === "\n") {
if (
i + 1 < arr.length &&
isAstNode(arr[i + 1]) &&
isTypstType(arr[i + 1].type, /^Marked::TermItem$/)
) {
i++;
continue;
}
}
break;
}
return { nodes, nextIndex: i };
};
const sourceChildren = rootNode.children;

const children: Content[] = [];
Expand All @@ -939,6 +1018,16 @@ export const paragraphizeTextlintAstObject = (

const node = sourceChildren[i];

if (isAstNode(node) && isTypstType(node.type, /^Marked::TermItem$/)) {
const { nodes, nextIndex } = collectConsecutiveRootTermItems(
sourceChildren,
i,
);
children.push(...nodes);
i = nextIndex;
continue;
}

// Collect consecutive ListItems into a single List node.
if (node.type === ASTNodeTypes.ListItem) {
const listItems: Content[] = [node];
Expand Down Expand Up @@ -1144,7 +1233,6 @@ export const paragraphizeTextlintAstObject = (
paragraph.push(node);
i++;

// Collect consecutive nodes for paragraph grouping.
while (i < sourceChildren.length) {
const currentNode = sourceChildren[i];

Expand All @@ -1164,23 +1252,48 @@ export const paragraphizeTextlintAstObject = (
if (paragraph.length > 0) {
const headNode = paragraph[0];
const lastNode = paragraph[paragraph.length - 1];

// Special handling for hash symbols.
if (
["Kw::Hash", "Fn::(Hash: &quot;#&quot;)"].includes(headNode.type)
) {
children.push(...paragraph);
} else {
children.push({
loc: {
start: headNode.loc.start,
end: lastNode.loc.end,
},
range: [headNode.range[0], lastNode.range[1]],
raw: paragraph.map((node) => node.raw).join(""),
type: ASTNodeTypes.Paragraph,
children: paragraph,
} as Content);
const isTermItem = (c: Content): boolean =>
isAstNode(c) && isTypstType(c.type, /^Marked::TermItem$/);
const createParagraph = (nodes: Content[]): Content => {
const first = nodes[0];
const last = nodes[nodes.length - 1];
return {
type: ASTNodeTypes.Paragraph,
children: nodes,
loc: { start: first.loc.start, end: last.loc.end },
range: [first.range[0], last.range[1]],
raw: nodes.map((n) => n.raw).join(""),
} as Content;
};
const innerParagraphWithTerms: AstNode | undefined = (() => {
if (paragraph.length !== 1) return undefined;
const only = paragraph[0];
if (only.type !== ASTNodeTypes.Paragraph) return undefined;
if (!isAstNode(only) || !hasChildren(only)) return undefined;
if (!only.children.some(isTermItem)) return undefined;
return only;
})();
if (innerParagraphWithTerms) {
children.push(...splitParagraphTermlist(innerParagraphWithTerms));
continue;
}
if (paragraph.some(isTermItem)) {
const paraNode: AstNode = {
type: ASTNodeTypes.Paragraph,
children: paragraph as Content[],
loc: { start: headNode.loc.start, end: lastNode.loc.end },
range: [headNode.range[0], lastNode.range[1]],
raw: paragraph.map((p: Content) => p.raw).join(""),
};
children.push(...splitParagraphTermlist(paraNode));
continue;
}
children.push(createParagraph(paragraph));
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions test/unit/fixtures/List/input.typ
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
1. x
2. y
3. z

/ Ligature: A merged glyph.
/ Kerning: A spacing adjustment
between two adjacent letters.
Loading