docrev 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.gitattributes +1 -1
  2. package/CHANGELOG.md +202 -184
  3. package/PLAN-tables-and-postprocess.md +850 -850
  4. package/README.md +433 -431
  5. package/bin/rev.js +11 -11
  6. package/bin/rev.ts +145 -145
  7. package/completions/rev.bash +127 -127
  8. package/completions/rev.ps1 +210 -210
  9. package/completions/rev.zsh +207 -207
  10. package/dist/lib/build.d.ts +80 -0
  11. package/dist/lib/build.d.ts.map +1 -1
  12. package/dist/lib/build.js +207 -4
  13. package/dist/lib/build.js.map +1 -1
  14. package/dist/lib/commands/build.d.ts.map +1 -1
  15. package/dist/lib/commands/build.js +34 -12
  16. package/dist/lib/commands/build.js.map +1 -1
  17. package/dist/lib/commands/utilities.js +164 -164
  18. package/dist/lib/commands/word-tools.js +8 -8
  19. package/dist/lib/grammar.js +3 -3
  20. package/dist/lib/macro-filter.lua +201 -201
  21. package/dist/lib/pdf-comments.js +44 -44
  22. package/dist/lib/plugins.js +57 -57
  23. package/dist/lib/pptx-color-filter.lua +37 -37
  24. package/dist/lib/pptx-themes.js +115 -115
  25. package/dist/lib/schema.d.ts.map +1 -1
  26. package/dist/lib/schema.js +8 -2
  27. package/dist/lib/schema.js.map +1 -1
  28. package/dist/lib/spelling.js +2 -2
  29. package/dist/lib/templates.js +387 -387
  30. package/dist/lib/themes.js +51 -51
  31. package/dist/lib/trackchanges.d.ts +41 -25
  32. package/dist/lib/trackchanges.d.ts.map +1 -1
  33. package/dist/lib/trackchanges.js +90 -151
  34. package/dist/lib/trackchanges.js.map +1 -1
  35. package/dist/lib/types.d.ts +0 -7
  36. package/dist/lib/types.d.ts.map +1 -1
  37. package/docs-src/build.py +113 -113
  38. package/docs-src/extra.css +208 -208
  39. package/docs-src/md-to-html.lua +6 -6
  40. package/docs-src/template.html +116 -116
  41. package/eslint.config.js +27 -27
  42. package/lib/anchor-match.ts +307 -307
  43. package/lib/annotations.ts +664 -664
  44. package/lib/build.ts +2047 -1770
  45. package/lib/citations.ts +160 -160
  46. package/lib/commands/build.ts +885 -860
  47. package/lib/commands/citations.ts +515 -515
  48. package/lib/commands/comments.ts +1050 -1050
  49. package/lib/commands/context.ts +176 -176
  50. package/lib/commands/core.ts +309 -309
  51. package/lib/commands/doi.ts +451 -451
  52. package/lib/commands/file-ops.ts +372 -372
  53. package/lib/commands/history.ts +320 -320
  54. package/lib/commands/index.ts +87 -87
  55. package/lib/commands/init.ts +259 -259
  56. package/lib/commands/merge-resolve.ts +378 -378
  57. package/lib/commands/preview.ts +178 -178
  58. package/lib/commands/project-info.ts +244 -244
  59. package/lib/commands/quality.ts +517 -517
  60. package/lib/commands/response.ts +454 -454
  61. package/lib/commands/section-boundaries.ts +82 -82
  62. package/lib/commands/sections.ts +451 -451
  63. package/lib/commands/sync.ts +689 -689
  64. package/lib/commands/text-ops.ts +449 -449
  65. package/lib/commands/utilities.ts +448 -448
  66. package/lib/commands/verify-anchors.ts +272 -272
  67. package/lib/commands/word-tools.ts +340 -340
  68. package/lib/comment-realign.ts +99 -99
  69. package/lib/config.ts +84 -84
  70. package/lib/crossref.ts +781 -781
  71. package/lib/csl.ts +191 -191
  72. package/lib/dependencies.ts +132 -132
  73. package/lib/diff-engine.ts +465 -465
  74. package/lib/doi-cache.ts +115 -115
  75. package/lib/doi.ts +879 -879
  76. package/lib/equations.ts +506 -506
  77. package/lib/errors.ts +350 -350
  78. package/lib/format.ts +541 -541
  79. package/lib/git.ts +326 -326
  80. package/lib/grammar.ts +303 -303
  81. package/lib/image-registry.ts +180 -180
  82. package/lib/import.ts +906 -906
  83. package/lib/journals.ts +543 -543
  84. package/lib/macro-filter.lua +201 -201
  85. package/lib/macros.ts +273 -273
  86. package/lib/merge.ts +633 -633
  87. package/lib/ooxml.ts +768 -768
  88. package/lib/orcid.ts +144 -144
  89. package/lib/pdf-comments.ts +263 -263
  90. package/lib/pdf-import.ts +524 -524
  91. package/lib/plugins.ts +362 -362
  92. package/lib/postprocess.ts +188 -188
  93. package/lib/pptx-color-filter.lua +37 -37
  94. package/lib/pptx-template.ts +469 -469
  95. package/lib/pptx-themes.ts +483 -483
  96. package/lib/protect-restore.ts +520 -520
  97. package/lib/rate-limiter.ts +127 -127
  98. package/lib/response.ts +197 -197
  99. package/lib/restore-references.ts +240 -240
  100. package/lib/review.ts +327 -327
  101. package/lib/schema.ts +494 -488
  102. package/lib/scientific-words.ts +73 -73
  103. package/lib/sections.ts +428 -428
  104. package/lib/slides.ts +756 -756
  105. package/lib/spelling.ts +334 -334
  106. package/lib/templates.ts +526 -526
  107. package/lib/themes.ts +742 -742
  108. package/lib/trackchanges.ts +166 -247
  109. package/lib/tui.ts +450 -450
  110. package/lib/types.ts +546 -558
  111. package/lib/undo.ts +250 -250
  112. package/lib/utils.ts +69 -69
  113. package/lib/variables.ts +179 -179
  114. package/lib/word-extraction.ts +525 -525
  115. package/lib/word.ts +526 -526
  116. package/lib/wordcomments.ts +789 -789
  117. package/mkdocs.yml +64 -64
  118. package/package.json +137 -137
  119. package/scripts/postbuild.js +47 -47
  120. package/skill/REFERENCE.md +550 -539
  121. package/skill/SKILL.md +302 -295
  122. package/tsconfig.json +26 -26
  123. package/types/index.d.ts +531 -525
@@ -1,201 +1,201 @@
1
- --[[
2
- docrev macro filter.
3
-
4
- Reads a JSON sidecar describing one-argument LaTeX-style macros and expands
5
- them per output FORMAT. Used for the built-in \tofill{X} (bold orange [X]
6
- placeholder) and any user-declared macros from rev.yaml.
7
-
8
- Sidecar path is passed via the DOCREV_MACROS_FILE environment variable, set
9
- by build.ts before spawning pandoc. Env vars (not metadata) because pandoc's
10
- filter traversal runs RawInline/RawBlock BEFORE Meta, so by the time we'd
11
- read metadata the inline expansions have already happened.
12
-
13
- Why raw OpenXML for docx? Pandoc 3.x's docx writer does NOT honor
14
- `Span{style="color: #..."}` — those spans render as plain text with no
15
- <w:color> run property. So for docx we emit raw <w:r> nodes directly. Same
16
- reasoning for the pptx-color-filter.
17
-
18
- For latex/pdf/beamer the markdown source already contains \tofill{X} as a raw
19
- LaTeX inline; we leave it alone because build.ts injects a \providecommand
20
- into header-includes. For html we emit a raw <span> with inline style. For
21
- everything else (markdown, gfm, plain) we degrade to **bold [X]** so the
22
- placeholder never silently disappears.
23
- ]]
24
-
25
- local json = require('pandoc.json')
26
-
27
- local macros_by_name = {}
28
-
29
- local function load_sidecar()
30
- local path = os.getenv('DOCREV_MACROS_FILE')
31
- if not path or path == '' then
32
- return
33
- end
34
- local fh = io.open(path, 'r')
35
- if not fh then
36
- io.stderr:write('docrev macro-filter: cannot read sidecar: ' .. path .. '\n')
37
- return
38
- end
39
- local content = fh:read('*a')
40
- fh:close()
41
- local ok, parsed = pcall(json.decode, content)
42
- if not ok or type(parsed) ~= 'table' or type(parsed.macros) ~= 'table' then
43
- io.stderr:write('docrev macro-filter: malformed sidecar JSON\n')
44
- return
45
- end
46
- for _, m in ipairs(parsed.macros) do
47
- if type(m) == 'table' and type(m.name) == 'string' then
48
- macros_by_name[m.name] = m
49
- end
50
- end
51
- end
52
-
53
- load_sidecar()
54
-
55
- local function xml_escape(s)
56
- return (s:gsub('&', '&amp;'):gsub('<', '&lt;'):gsub('>', '&gt;'))
57
- end
58
-
59
- local function html_escape(s)
60
- return (s
61
- :gsub('&', '&amp;')
62
- :gsub('<', '&lt;')
63
- :gsub('>', '&gt;')
64
- :gsub('"', '&quot;'))
65
- end
66
-
67
- -- Resolve effective style for a macro in the current pandoc format.
68
- -- Per-format entry wins over `default` (replacement, not merge — matches
69
- -- macros.ts semantics).
70
- local function pick_style(macro, format)
71
- if macro.formats and macro.formats[format] then
72
- return macro.formats[format]
73
- end
74
- return macro.default or {}
75
- end
76
-
77
- -- Build the inside of the bracket: [prefix][arg][suffix], optionally without
78
- -- brackets when style.bracket == false.
79
- local function compose_text(style, arg)
80
- local prefix = style.prefix or ''
81
- local suffix = style.suffix or ''
82
- local inner = prefix .. arg .. suffix
83
- if style.bracket == false then
84
- return inner
85
- end
86
- return '[' .. inner .. ']'
87
- end
88
-
89
- local function render_docx_run(style, arg)
90
- local rpr = {}
91
- if style.color then
92
- table.insert(rpr, '<w:color w:val="' .. style.color .. '"/>')
93
- end
94
- if style.bold then
95
- table.insert(rpr, '<w:b/>')
96
- end
97
- if style.italic then
98
- table.insert(rpr, '<w:i/>')
99
- end
100
- local rpr_xml = ''
101
- if #rpr > 0 then
102
- rpr_xml = '<w:rPr>' .. table.concat(rpr) .. '</w:rPr>'
103
- end
104
- local text = xml_escape(compose_text(style, arg))
105
- return '<w:r>' .. rpr_xml ..
106
- '<w:t xml:space="preserve">' .. text .. '</w:t></w:r>'
107
- end
108
-
109
- local function render_html(style, arg)
110
- local css = {}
111
- if style.color then
112
- table.insert(css, 'color:#' .. style.color)
113
- end
114
- if style.bold then
115
- table.insert(css, 'font-weight:bold')
116
- end
117
- if style.italic then
118
- table.insert(css, 'font-style:italic')
119
- end
120
- local text = html_escape(compose_text(style, arg))
121
- if #css == 0 then
122
- return '<span>' .. text .. '</span>'
123
- end
124
- return '<span style="' .. table.concat(css, ';') .. '">' .. text .. '</span>'
125
- end
126
-
127
- -- Fallback path: produce native pandoc inlines so the macro never silently
128
- -- disappears in markdown/gfm/plain output. Used when the current format has
129
- -- no native rich-text path (or we couldn't open the sidecar).
130
- local function fallback_inlines(style, arg)
131
- local doc = pandoc.read(compose_text(style, arg), 'markdown')
132
- local inlines = pandoc.utils.blocks_to_inlines(doc.blocks)
133
- if style.bold then
134
- inlines = { pandoc.Strong(inlines) }
135
- end
136
- if style.italic then
137
- inlines = { pandoc.Emph(inlines) }
138
- end
139
- return inlines
140
- end
141
-
142
- -- Match `\NAME{...}` (with balanced braces inside the argument is NOT
143
- -- supported — the use case is plain placeholder text, mirroring the reference
144
- -- filter; users who need nested braces should use a different mechanism).
145
- local function parse_call(text)
146
- local name, arg = text:match('^\\([A-Za-z][A-Za-z0-9]*)%s*{(.*)}%s*$')
147
- if name and arg and macros_by_name[name] then
148
- return name, arg
149
- end
150
- return nil, nil
151
- end
152
-
153
- local function expand_inline(el)
154
- if el.format ~= 'tex' and el.format ~= 'latex' then
155
- return nil
156
- end
157
- local name, arg = parse_call(el.text)
158
- if not name then return nil end
159
- local macro = macros_by_name[name]
160
- local style = pick_style(macro, FORMAT)
161
-
162
- if FORMAT == 'docx' then
163
- return pandoc.RawInline('openxml', render_docx_run(style, arg))
164
- elseif FORMAT == 'html' or FORMAT == 'html4' or FORMAT == 'html5' or FORMAT == 'chunkedhtml' then
165
- return pandoc.RawInline('html', render_html(style, arg))
166
- elseif FORMAT == 'latex' or FORMAT == 'beamer' or FORMAT == 'context' then
167
- -- Leave the raw LaTeX as-is. build.ts injects \providecommand into
168
- -- header-includes, so the LaTeX engine renders it directly.
169
- return nil
170
- else
171
- return fallback_inlines(style, arg)
172
- end
173
- end
174
-
175
- local function expand_block(el)
176
- if el.format ~= 'tex' and el.format ~= 'latex' then
177
- return nil
178
- end
179
- local name, arg = parse_call(el.text)
180
- if not name then return nil end
181
- local macro = macros_by_name[name]
182
- local style = pick_style(macro, FORMAT)
183
-
184
- if FORMAT == 'docx' then
185
- return pandoc.RawBlock('openxml', '<w:p>' .. render_docx_run(style, arg) .. '</w:p>')
186
- elseif FORMAT == 'html' or FORMAT == 'html4' or FORMAT == 'html5' or FORMAT == 'chunkedhtml' then
187
- return pandoc.RawBlock('html', '<p>' .. render_html(style, arg) .. '</p>')
188
- elseif FORMAT == 'latex' or FORMAT == 'beamer' or FORMAT == 'context' then
189
- return nil
190
- else
191
- return pandoc.Para(fallback_inlines(style, arg))
192
- end
193
- end
194
-
195
- function RawInline(el)
196
- return expand_inline(el)
197
- end
198
-
199
- function RawBlock(el)
200
- return expand_block(el)
201
- end
1
+ --[[
2
+ docrev macro filter.
3
+
4
+ Reads a JSON sidecar describing one-argument LaTeX-style macros and expands
5
+ them per output FORMAT. Used for the built-in \tofill{X} (bold orange [X]
6
+ placeholder) and any user-declared macros from rev.yaml.
7
+
8
+ Sidecar path is passed via the DOCREV_MACROS_FILE environment variable, set
9
+ by build.ts before spawning pandoc. Env vars (not metadata) because pandoc's
10
+ filter traversal runs RawInline/RawBlock BEFORE Meta, so by the time we'd
11
+ read metadata the inline expansions have already happened.
12
+
13
+ Why raw OpenXML for docx? Pandoc 3.x's docx writer does NOT honor
14
+ `Span{style="color: #..."}` — those spans render as plain text with no
15
+ <w:color> run property. So for docx we emit raw <w:r> nodes directly. Same
16
+ reasoning for the pptx-color-filter.
17
+
18
+ For latex/pdf/beamer the markdown source already contains \tofill{X} as a raw
19
+ LaTeX inline; we leave it alone because build.ts injects a \providecommand
20
+ into header-includes. For html we emit a raw <span> with inline style. For
21
+ everything else (markdown, gfm, plain) we degrade to **bold [X]** so the
22
+ placeholder never silently disappears.
23
+ ]]
24
+
25
+ local json = require('pandoc.json')
26
+
27
+ local macros_by_name = {}
28
+
29
+ local function load_sidecar()
30
+ local path = os.getenv('DOCREV_MACROS_FILE')
31
+ if not path or path == '' then
32
+ return
33
+ end
34
+ local fh = io.open(path, 'r')
35
+ if not fh then
36
+ io.stderr:write('docrev macro-filter: cannot read sidecar: ' .. path .. '\n')
37
+ return
38
+ end
39
+ local content = fh:read('*a')
40
+ fh:close()
41
+ local ok, parsed = pcall(json.decode, content)
42
+ if not ok or type(parsed) ~= 'table' or type(parsed.macros) ~= 'table' then
43
+ io.stderr:write('docrev macro-filter: malformed sidecar JSON\n')
44
+ return
45
+ end
46
+ for _, m in ipairs(parsed.macros) do
47
+ if type(m) == 'table' and type(m.name) == 'string' then
48
+ macros_by_name[m.name] = m
49
+ end
50
+ end
51
+ end
52
+
53
+ load_sidecar()
54
+
55
+ local function xml_escape(s)
56
+ return (s:gsub('&', '&amp;'):gsub('<', '&lt;'):gsub('>', '&gt;'))
57
+ end
58
+
59
+ local function html_escape(s)
60
+ return (s
61
+ :gsub('&', '&amp;')
62
+ :gsub('<', '&lt;')
63
+ :gsub('>', '&gt;')
64
+ :gsub('"', '&quot;'))
65
+ end
66
+
67
+ -- Resolve effective style for a macro in the current pandoc format.
68
+ -- Per-format entry wins over `default` (replacement, not merge — matches
69
+ -- macros.ts semantics).
70
+ local function pick_style(macro, format)
71
+ if macro.formats and macro.formats[format] then
72
+ return macro.formats[format]
73
+ end
74
+ return macro.default or {}
75
+ end
76
+
77
+ -- Build the inside of the bracket: [prefix][arg][suffix], optionally without
78
+ -- brackets when style.bracket == false.
79
+ local function compose_text(style, arg)
80
+ local prefix = style.prefix or ''
81
+ local suffix = style.suffix or ''
82
+ local inner = prefix .. arg .. suffix
83
+ if style.bracket == false then
84
+ return inner
85
+ end
86
+ return '[' .. inner .. ']'
87
+ end
88
+
89
+ local function render_docx_run(style, arg)
90
+ local rpr = {}
91
+ if style.color then
92
+ table.insert(rpr, '<w:color w:val="' .. style.color .. '"/>')
93
+ end
94
+ if style.bold then
95
+ table.insert(rpr, '<w:b/>')
96
+ end
97
+ if style.italic then
98
+ table.insert(rpr, '<w:i/>')
99
+ end
100
+ local rpr_xml = ''
101
+ if #rpr > 0 then
102
+ rpr_xml = '<w:rPr>' .. table.concat(rpr) .. '</w:rPr>'
103
+ end
104
+ local text = xml_escape(compose_text(style, arg))
105
+ return '<w:r>' .. rpr_xml ..
106
+ '<w:t xml:space="preserve">' .. text .. '</w:t></w:r>'
107
+ end
108
+
109
+ local function render_html(style, arg)
110
+ local css = {}
111
+ if style.color then
112
+ table.insert(css, 'color:#' .. style.color)
113
+ end
114
+ if style.bold then
115
+ table.insert(css, 'font-weight:bold')
116
+ end
117
+ if style.italic then
118
+ table.insert(css, 'font-style:italic')
119
+ end
120
+ local text = html_escape(compose_text(style, arg))
121
+ if #css == 0 then
122
+ return '<span>' .. text .. '</span>'
123
+ end
124
+ return '<span style="' .. table.concat(css, ';') .. '">' .. text .. '</span>'
125
+ end
126
+
127
+ -- Fallback path: produce native pandoc inlines so the macro never silently
128
+ -- disappears in markdown/gfm/plain output. Used when the current format has
129
+ -- no native rich-text path (or we couldn't open the sidecar).
130
+ local function fallback_inlines(style, arg)
131
+ local doc = pandoc.read(compose_text(style, arg), 'markdown')
132
+ local inlines = pandoc.utils.blocks_to_inlines(doc.blocks)
133
+ if style.bold then
134
+ inlines = { pandoc.Strong(inlines) }
135
+ end
136
+ if style.italic then
137
+ inlines = { pandoc.Emph(inlines) }
138
+ end
139
+ return inlines
140
+ end
141
+
142
+ -- Match `\NAME{...}` (with balanced braces inside the argument is NOT
143
+ -- supported — the use case is plain placeholder text, mirroring the reference
144
+ -- filter; users who need nested braces should use a different mechanism).
145
+ local function parse_call(text)
146
+ local name, arg = text:match('^\\([A-Za-z][A-Za-z0-9]*)%s*{(.*)}%s*$')
147
+ if name and arg and macros_by_name[name] then
148
+ return name, arg
149
+ end
150
+ return nil, nil
151
+ end
152
+
153
+ local function expand_inline(el)
154
+ if el.format ~= 'tex' and el.format ~= 'latex' then
155
+ return nil
156
+ end
157
+ local name, arg = parse_call(el.text)
158
+ if not name then return nil end
159
+ local macro = macros_by_name[name]
160
+ local style = pick_style(macro, FORMAT)
161
+
162
+ if FORMAT == 'docx' then
163
+ return pandoc.RawInline('openxml', render_docx_run(style, arg))
164
+ elseif FORMAT == 'html' or FORMAT == 'html4' or FORMAT == 'html5' or FORMAT == 'chunkedhtml' then
165
+ return pandoc.RawInline('html', render_html(style, arg))
166
+ elseif FORMAT == 'latex' or FORMAT == 'beamer' or FORMAT == 'context' then
167
+ -- Leave the raw LaTeX as-is. build.ts injects \providecommand into
168
+ -- header-includes, so the LaTeX engine renders it directly.
169
+ return nil
170
+ else
171
+ return fallback_inlines(style, arg)
172
+ end
173
+ end
174
+
175
+ local function expand_block(el)
176
+ if el.format ~= 'tex' and el.format ~= 'latex' then
177
+ return nil
178
+ end
179
+ local name, arg = parse_call(el.text)
180
+ if not name then return nil end
181
+ local macro = macros_by_name[name]
182
+ local style = pick_style(macro, FORMAT)
183
+
184
+ if FORMAT == 'docx' then
185
+ return pandoc.RawBlock('openxml', '<w:p>' .. render_docx_run(style, arg) .. '</w:p>')
186
+ elseif FORMAT == 'html' or FORMAT == 'html4' or FORMAT == 'html5' or FORMAT == 'chunkedhtml' then
187
+ return pandoc.RawBlock('html', '<p>' .. render_html(style, arg) .. '</p>')
188
+ elseif FORMAT == 'latex' or FORMAT == 'beamer' or FORMAT == 'context' then
189
+ return nil
190
+ else
191
+ return pandoc.Para(fallback_inlines(style, arg))
192
+ end
193
+ end
194
+
195
+ function RawInline(el)
196
+ return expand_inline(el)
197
+ end
198
+
199
+ function RawBlock(el)
200
+ return expand_block(el)
201
+ end