Skip to content

Commit b8c5d47

Browse files
committed
🐛 Fix texcat
1 parent 8be8978 commit b8c5d47

9 files changed

Lines changed: 121 additions & 53 deletions

File tree

packages/texcat/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,58 @@ It supports color themes:
1414
- [x] textmate/VS Code theme XML/json
1515
- [ ] [syncat themes](https://github.com/foxfriends/syncat-themes)
1616

17+
## Bench
18+
19+
### Terminal output
20+
21+
```sh
22+
$ hyperfine -Nw10 'texcat test.lua' 'texcat --syntax-type=textmate test.lua' 'pygmentize test.lua' 'bat test.lua'
23+
Benchmark 1: texcat test.lua
24+
Time (mean ± σ): 56.8 ms ± 1.9 ms [User: 49.8 ms, System: 6.3 ms]
25+
Range (min … max): 54.4 ms … 64.2 ms 50 runs
26+
27+
Benchmark 2: texcat --syntax-type=textmate test.lua
28+
Time (mean ± σ): 73.1 ms ± 11.2 ms [User: 70.7 ms, System: 7.4 ms]
29+
Range (min … max): 62.0 ms … 89.7 ms 47 runs
30+
31+
Benchmark 3: pygmentize test.lua
32+
Time (mean ± σ): 154.4 ms ± 19.9 ms [User: 140.0 ms, System: 12.8 ms]
33+
Range (min … max): 137.4 ms … 217.3 ms 21 runs
34+
35+
Benchmark 4: bat test.lua
36+
Time (mean ± σ): 8.0 ms ± 0.5 ms [User: 7.4 ms, System: 4.2 ms]
37+
Range (min … max): 7.0 ms … 10.0 ms 429 runs
38+
39+
Summary
40+
bat test.lua ran
41+
7.14 ± 0.50 times faster than texcat test.lua
42+
9.18 ± 1.52 times faster than texcat --syntax-type=textmate test.lua
43+
19.40 ± 2.77 times faster than pygmentize test.lua
44+
```
45+
46+
### LaTeX output
47+
48+
```sh
49+
$ hyperfine -Nw10 'texcat --output-format=latex test.lua' 'texcat --output-format=latex --syntax-type=textmate test.lua' 'pygmentize -f latex -O full=True test.lua'
50+
51+
Benchmark 1: texcat --output-format=latex test.lua
52+
Time (mean ± σ): 56.3 ms ± 1.2 ms [User: 49.6 ms, System: 6.1 ms]
53+
Range (min … max): 54.9 ms … 60.9 ms 53 runs
54+
55+
Benchmark 2: texcat --output-format=latex --syntax-type=textmate test.lua
56+
Time (mean ± σ): 63.8 ms ± 0.8 ms [User: 61.5 ms, System: 7.0 ms]
57+
Range (min … max): 62.5 ms … 66.5 ms 45 runs
58+
59+
Benchmark 3: pygmentize -f latex -O full=True test.lua
60+
Time (mean ± σ): 110.7 ms ± 1.2 ms [User: 99.2 ms, System: 10.7 ms]
61+
Range (min … max): 109.1 ms … 114.3 ms 27 runs
62+
63+
Summary
64+
texcat --output-format=latex test.lua ran
65+
1.13 ± 0.03 times faster than texcat --output-format=latex --syntax-type=textmate test.lua
66+
1.97 ± 0.05 times faster than pygmentize -f latex -O full=True test.lua
67+
```
68+
1769
## Dependencies
1870

1971
### TextMate

packages/texcat/lua/texcat.lua

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
---@copyright 2025
44
---@diagnostic disable: undefined-global
55
-- luacheck: ignore 111 113
6-
local lfs = require 'lfs'
6+
local lfs = require 'texrocks.lfs'
77
local argparse = require 'argparse'
88
local T = require 'texcat.themes'
9-
local TMTheme = require 'texcat.themes.tmtheme'.TMTheme
10-
local TMLanguage = require 'texcat.syntaxes.tmlanguage'.TMLanguage
11-
local Treesitter = require 'texcat.syntaxes.treesitter'.Treesitter
129
local R = require 'texcat.renderer'
1310
local Renderer = require 'texcat.renderer'.Renderer
1411
local M = {
@@ -81,25 +78,25 @@ end
8178
---@param theme_names string[] theme names
8279
function M.get_list(list_type, theme_type, syntax_type, theme_names, extensions_dir)
8380
local class
84-
local theme
85-
if theme_type == 'textmate' then
86-
class = TMTheme
87-
end
88-
theme = class { extensions_dir = extensions_dir }
8981
local syntax
9082
if syntax_type == 'textmate' then
91-
class = Treesitter
83+
class = require 'texcat.syntaxes.tmlanguage'.TMLanguage
9284
elseif syntax_type == 'tree-sitter' then
93-
class = TMLanguage
85+
class = require 'texcat.syntaxes.treesitter'.Treesitter
9486
end
9587
syntax = class { id = 0, extensions_dir = extensions_dir }
88+
local theme
89+
if theme_type == 'textmate' then
90+
class = require 'texcat.themes.tmtheme'.TMTheme
91+
end
92+
theme = class { extensions_dir = extensions_dir }
9693
local list
9794
if list_type == 'extensions_dirs' then
9895
list = table.concat(extensions_dir, "\n")
9996
elseif list_type == 'themes' then
100-
list = theme.list()
97+
list = theme:list()
10198
elseif list_type == 'syntaxes' then
102-
list = syntax.list()
99+
list = syntax:list()
103100
elseif list_type == 'links' then
104101
list = T.list_links()
105102
elseif list_type == 'colors' then
@@ -108,7 +105,7 @@ function M.get_list(list_type, theme_type, syntax_type, theme_names, extensions_
108105
theme = M.themes[theme_name]
109106
if theme == nil then
110107
if theme_type == 'textmate' then
111-
theme = TMTheme { extensions_dir = extensions_dir, name = theme_name }
108+
theme = class { extensions_dir = extensions_dir, name = theme_name }
112109
M.themes[theme_name] = theme
113110
else
114111
return ''
@@ -157,7 +154,7 @@ function M.render(cfg)
157154
-- theme
158155
local class
159156
if cfg.theme_type == 'textmate' then
160-
class = TMTheme
157+
class = require 'texcat.themes.tmtheme'.TMTheme
161158
end
162159
cfg.theme = cfg.theme or 'auto'
163160
-- no theme auto
@@ -182,9 +179,9 @@ function M.render(cfg)
182179

183180
-- syntax
184181
if cfg.syntax_type == 'textmate' then
185-
class = TMLanguage
182+
class = require 'texcat.syntaxes.tmlanguage'.TMLanguage
186183
elseif cfg.syntax_type == 'tree-sitter' then
187-
class = Treesitter
184+
class = require 'texcat.syntaxes.treesitter'.Treesitter
188185
end
189186
cfg.syntax = cfg.syntax or 'auto'
190187
-- no syntax auto
@@ -244,11 +241,7 @@ function M.output(out, filename)
244241
else
245242
local dir = filename:match('(.*)/[^/]+$')
246243
if dir then
247-
if lfs.mkdirp then
248-
lfs.mkdirp(dir)
249-
else
250-
lfs.mkdir(dir)
251-
end
244+
lfs.mkdirp(dir)
252245
end
253246
local f = io.open(filename, 'w')
254247
if f then

packages/texcat/lua/texcat/renderer.lua

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
---@copyright 2025
44
---@diagnostic disable: undefined-global
55
-- luacheck: ignore 111 113
6+
local split = require 'vim.shared'.split
67
local template = require 'template'
78
local warna = require 'warna'
89
warna.options.level = 3
@@ -57,28 +58,34 @@ end
5758
---@param str string
5859
---@return string[] tokens
5960
function M.split(str)
60-
local tokens = {}
61-
local token
62-
for i = 1, #str do
63-
local char = str:sub(i, i)
64-
if char == "\n" then
65-
if token then
66-
table.insert(tokens, token)
67-
token = nil
68-
end
69-
table.insert(tokens, "\n")
70-
elseif token then
71-
token = token .. char
72-
else
73-
token = char
74-
end
61+
local tokens = split(str, "\n")
62+
for i = 1, #tokens - 1 do
63+
tokens[i] = tokens[i] .. "\n"
7564
end
76-
if token then
77-
table.insert(tokens, token)
65+
if tokens[#tokens] == "" then
66+
tokens[#tokens] = nil
7867
end
7968
return tokens
8069
end
8170

71+
---add child scopes to color map
72+
---@param color_map color_map
73+
---@param captures capture[]
74+
function M.add_scope(color_map, captures)
75+
for _, capture in ipairs(captures) do
76+
local scope = capture.scope
77+
while color_map[scope] == nil do
78+
-- get parent scope: punctuation.bracket -> punctuation
79+
scope = scope:match('(.*)%.[^.]+$')
80+
if scope == nil then
81+
scope = 'source'
82+
end
83+
end
84+
color_map[capture.scope] = color_map[scope]
85+
end
86+
return color_map
87+
end
88+
8289
---get highlights
8390
---@param text string
8491
---@param captures capture[]
@@ -142,6 +149,7 @@ function M.escape(text, prefix, math_escape)
142149
return table.concat(texts, '$')
143150
end
144151

152+
---@diagnostic disable-next-line: undefined-doc-name
145153
---@type Renderer
146154

147155
---@param renderer table?
@@ -170,14 +178,16 @@ function M.Renderer:get_opts(text, format, opts)
170178
if format:match 'tex' and opts.prefix == nil then
171179
opts.prefix = "PY" .. self.theme.name:gsub(" ", "")
172180
end
181+
local captures = self.syntax:capture(text, self.theme)
173182
opts.color_map = opts.color_map or self.theme.get_full_color_map()
174-
opts.hls = opts.hls or M.get_hls(text, self.syntax:capture(text, self.theme))
183+
opts.color_map = M.add_scope(opts.color_map, captures)
184+
opts.hls = opts.hls or M.get_hls(text, captures)
175185
opts.ipairs = ipairs
176186
opts.table = table
177187
opts.warna = warna
178188
opts.escape = M.escape
179-
opts.preamble = M.get_path('texcat/main.preamble.tex')
180-
opts.tex = M.get_path('texcat/main.tex')
189+
opts.preamble = M.get_path('templates/main.preamble.tex')
190+
opts.tex = M.get_path('templates/main.tex')
181191
opts.scopes = T.get_sorted_keys(opts.color_map)
182192
return opts
183193
end

packages/texcat/lua/texcat/syntaxes/tmlanguage.lua

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ local M = {
77
TMLanguage = {}
88
}
99

10+
---@diagnostic disable-next-line: undefined-doc-name
1011
---@type TMLanguage
1112

1213
---@param tmlanguage table?
1314
---@return table language
1415
function M.TMLanguage:new(tmlanguage)
1516
tmlanguage = tmlanguage or {}
1617
tmlanguage.name = tmlanguage.name or 'lua'
17-
tmlanguage.id = tmlanguage.id or textmate.highlight_load_syntax(tmlanguage.name)
18+
tmlanguage.id = tmlanguage.id or textmate.highlight_load_language(tmlanguage.name)
1819
setmetatable(tmlanguage, {
1920
__index = self
2021
})
@@ -27,7 +28,7 @@ setmetatable(M.TMLanguage, {
2728

2829
---list all languages
2930
---@return string information
30-
function M.TMLanguage.list()
31+
function M.TMLanguage:list()
3132
return T.list(textmate.highlight_languages())
3233
end
3334

@@ -39,7 +40,7 @@ end
3940
function M.TMLanguage:capture(text, theme)
4041
local captures = {}
4142
textmate.highlight_set_theme(theme.id)
42-
textmate.highlight_set_syntax(self.id)
43+
textmate.highlight_set_language(self.id)
4344
local results = textmate.highlight_line(text, theme.id, self.id, 0)
4445
for _, result in ipairs(results) do
4546
local idx, len, _, _, _, scope = result[1] + 1, result[2], result[3], result[4], result[5], result

packages/texcat/lua/texcat/syntaxes/treesitter.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ function M.cut_captures(captures, len)
148148
return ranges
149149
end
150150

151+
---@diagnostic disable-next-line: undefined-doc-name
151152
---@type Treesitter
152153

153154
---@param treesitter table?
@@ -176,10 +177,10 @@ function M.Treesitter:capture(text, theme)
176177
local tree = self.parser:parse_string(text)
177178
local node = tree:root()
178179
local captures = {}
179-
for capture, name in self.query:capture(node) do
180+
for capture, scope in self.query:capture(node) do
180181
-- lua index start from 1
181182
table.insert(captures,
182-
{ start_index = capture:start_byte() + 1, end_index = capture:end_byte(), scope = name })
183+
{ start_index = capture:start_byte() + 1, end_index = capture:end_byte(), scope = scope })
183184
end
184185
local color_map = theme.get_full_color_map()
185186
color_map = M.add_scope(color_map, captures)

packages/texcat/lua/texcat/themes.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ local M = {
1212
constant = 'type',
1313
operator = 'control',
1414
constructor = 'function',
15+
['punctuation.definition.comment'] = 'comment',
16+
['meta.function'] = 'function',
17+
['entity.name.function'] = 'function',
18+
['support.function'] = 'function',
19+
['punctuation.definition.parameters'] = 'control',
1520
}
1621
}
1722

packages/texcat/lua/texcat/themes/tmtheme.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ function M.load_extensions_dir(extensions_dir)
1515
end
1616
end
1717

18+
---@diagnostic disable-next-line: undefined-doc-name
1819
---@type TMTheme
1920

2021
---@param tmtheme table?
@@ -66,7 +67,7 @@ end
6667

6768
---list all themes
6869
---@return string information
69-
function M.TMTheme.list()
70+
function M.TMTheme:list()
7071
return T.list(textmate.highlight_themes())
7172
end
7273

packages/texcat/lux.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package = "texcat"
2-
version = "0.3.12"
2+
version = "0.3.13"
33
lua = ">=5.1"
44

55
[description]
@@ -22,7 +22,7 @@ warna = ">=0.3.5"
2222
ltreesitter = ">=0.1.0"
2323
nvim-textmate = ">=0.0.1"
2424
luafilesystem = ">=1.9.0"
25-
texrocks = ">=0.3.12"
25+
texrocks = ">=0.3.13"
2626
vscode-extensions = ">=1.102.3"
2727

2828
[test_dependencies]

scripts/publish.sh

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
#!/usr/bin/env bash
22
set -e
33
cd "$(dirname "$(dirname "$(readlink -f "$0")")")"
4-
54
cd packages
5+
6+
dirs=("$1")
7+
if (( ${#dirs} == 0 )); then
8+
dirs=(./*/)
9+
fi
10+
611
rm -f ./*/*.rock{,spec}
7-
for dir in ./*/; do
12+
for dir in "${dirs[@]}"; do
813
if [[ "$dir" = ./demo-*/ ]]; then
914
continue
1015
fi
1116
cd "$dir"
1217
lx generate-rockspec
13-
perl -pi -e's/>=/ >= /' ./*.rockspec
18+
perl -pi -e's/[>=]=/ >= /' ./*.rockspec
1419
luarocks upload --force ./*.rockspec
1520
cd ..
1621
done

0 commit comments

Comments
 (0)