Skip to content

Commit a32229d

Browse files
committed
Implement table formatting in Space Lua (close silverbulletmd#12)
Aligns columns as described in issue silverbulletmd#12. Preserves alignment specifiers and takes them into consideration for column width (see also silverbulletmd#847, silverbulletmd#1293)
1 parent 67a29dd commit a32229d

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

plugs/core/Library/Std/Table.md

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#meta
2+
Implements utilities for working with Markdown tables
3+
4+
# Table
5+
## Commands
6+
7+
```space-lua
8+
command.define {
9+
name = "Table: Format",
10+
run = function()
11+
position = editor.getCursor()
12+
tree = markdown.parseMarkdown(editor.getText())
13+
tableNode = nodeParentOfType(tree, position, "Table")
14+
if tableNode then
15+
formatMarkdownTable(tableNode)
16+
else
17+
editor.flashNotification("This command must be run with cursor in a table", "error")
18+
end
19+
end
20+
}
21+
```
22+
23+
## Implementation
24+
25+
```space-lua
26+
-- Find outermost node of given type that contains the position
27+
-- similar to plug-api/lib/tree.ts nodeAtPos
28+
function nodeParentOfType(tree, position, nodeType)
29+
if position < tree.from or position > tree.to then
30+
return nil
31+
end
32+
if tree.type == nodeType then
33+
return tree
34+
end
35+
if tree.children then
36+
for _, child in ipairs(tree.children) do
37+
if position >= child.from and position <= child.to then
38+
return nodeParentOfType(child, position, nodeType)
39+
end
40+
end
41+
end
42+
return nil
43+
end
44+
```
45+
46+
```space-lua
47+
function formatMarkdownTable(tree)
48+
-- First find the desired length for each column
49+
columnLengths = {}
50+
columnAlignments = {}
51+
for _, child in ipairs(tree.children) do
52+
if child.type == "TableDelimiter" then
53+
column = 1
54+
for _, spec in ipairs(string.split(child.children[1].text, "|")) do
55+
if #spec > 0 then
56+
minLength = 1
57+
if #spec >= 2 and string.endsWith(spec, ":") then
58+
if string.startsWith(spec, ":") then
59+
columnAlignments[column] = "center"
60+
minLength = 3
61+
else
62+
columnAlignments[column] = "right"
63+
minLength = 2
64+
end
65+
else
66+
columnAlignments[column] = "left"
67+
end
68+
69+
if minLength > (columnLengths[column] or 0) then
70+
columnLengths[column] = minLength
71+
end
72+
column = column + 1
73+
end
74+
end
75+
elseif child.type == "TableHeader" or child.type == "TableRow" then
76+
column = 1
77+
for i, cell in ipairs(child.children) do
78+
if cell.type == "TableDelimiter" then
79+
next = child.children[i + 1]
80+
if next and next.type == "TableCell" then
81+
len = next.to - next.from
82+
if len > (columnLengths[column] or 0) then
83+
columnLengths[column] = len
84+
end
85+
end
86+
87+
column = column + 1
88+
end
89+
end
90+
end
91+
end
92+
print(columnAlignments)
93+
94+
-- Then print the table using these column lengths
95+
output = ""
96+
for _, child in ipairs(tree.children) do
97+
if child.type == "TableDelimiter" then
98+
output = output .. "\n|"
99+
for column, len in ipairs(columnLengths) do
100+
align = columnAlignments[column]
101+
if align == "left" then
102+
-- This is separate case because could be shorter than 2 characters
103+
output = output .. string.rep("-", len)
104+
else
105+
output = output .. (align == "center" and ":" or "-") .. string.rep("-", len - 2) .. ":"
106+
end
107+
output = output .. "|"
108+
end
109+
elseif child.type == "TableHeader" or child.type == "TableRow" then
110+
if child.type ~= "TableHeader" then
111+
output = output .. "\n"
112+
end
113+
output = output .. "|"
114+
column = 1
115+
for i, cell in ipairs(child.children) do
116+
if cell.type == "TableDelimiter" then
117+
next = child.children[i + 1]
118+
if next and next.type == "TableCell" then
119+
len = next.to - next.from
120+
121+
-- Similar to plugs/index/table.ts:concatChildrenTexts
122+
for _, next_child in ipairs(next.children) do
123+
output = output .. next_child.text
124+
end
125+
126+
output = output .. string.rep(" ", columnLengths[column] - len) .. "|"
127+
end
128+
129+
column = column + 1
130+
end
131+
end
132+
end
133+
end
134+
135+
-- Replace the table node with the formatted content
136+
editor.replaceRange(tree.from, tree.to, output)
137+
end
138+
```

0 commit comments

Comments
 (0)