docrev 0.3.0 → 0.5.0

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.
@@ -0,0 +1,127 @@
1
+ # Bash completion for rev (docrev)
2
+ # Install: source this file or add to ~/.bashrc:
3
+ # eval "$(rev completions bash)"
4
+
5
+ _rev_completions() {
6
+ local cur prev words cword
7
+ _init_completion || return
8
+
9
+ local commands="build new import sections extract review status comments resolve reply strip refs migrate config install doi citations equations figures response anonymize validate merge diff history help init split word-count wc stats search backup export preview watch lint grammar annotate apply comment completions"
10
+ local build_formats="pdf docx tex all"
11
+ local doi_actions="check lookup fetch add"
12
+ local eq_actions="list extract convert from-word"
13
+ local help_topics="workflow syntax commands"
14
+ local preview_formats="pdf docx"
15
+
16
+ case "${prev}" in
17
+ rev)
18
+ COMPREPLY=($(compgen -W "${commands} --help --version" -- "${cur}"))
19
+ return
20
+ ;;
21
+ build)
22
+ COMPREPLY=($(compgen -W "${build_formats} --toc --show-changes --clean" -- "${cur}"))
23
+ return
24
+ ;;
25
+ new)
26
+ COMPREPLY=($(compgen -W "--list --template" -- "${cur}"))
27
+ return
28
+ ;;
29
+ --template)
30
+ COMPREPLY=($(compgen -W "paper minimal thesis proposal" -- "${cur}"))
31
+ return
32
+ ;;
33
+ doi)
34
+ COMPREPLY=($(compgen -W "${doi_actions}" -- "${cur}"))
35
+ return
36
+ ;;
37
+ equations|eq)
38
+ COMPREPLY=($(compgen -W "${eq_actions}" -- "${cur}"))
39
+ return
40
+ ;;
41
+ validate)
42
+ COMPREPLY=($(compgen -W "--journal --list" -- "${cur}"))
43
+ return
44
+ ;;
45
+ --journal)
46
+ COMPREPLY=($(compgen -W "nature science plos cell ecology custom" -- "${cur}"))
47
+ return
48
+ ;;
49
+ help)
50
+ COMPREPLY=($(compgen -W "${help_topics}" -- "${cur}"))
51
+ return
52
+ ;;
53
+ config)
54
+ COMPREPLY=($(compgen -W "user" -- "${cur}"))
55
+ return
56
+ ;;
57
+ word-count|wc)
58
+ COMPREPLY=($(compgen -W "--limit --journal" -- "${cur}"))
59
+ return
60
+ ;;
61
+ preview)
62
+ COMPREPLY=($(compgen -W "${preview_formats}" -- "${cur}"))
63
+ return
64
+ ;;
65
+ watch)
66
+ COMPREPLY=($(compgen -W "pdf docx all --no-open" -- "${cur}"))
67
+ return
68
+ ;;
69
+ lint)
70
+ COMPREPLY=($(compgen -W "--fix" -- "${cur}"))
71
+ return
72
+ ;;
73
+ grammar)
74
+ COMPREPLY=($(compgen -W "--learn --forget --list --rules --no-scientific --severity" -- "${cur}"))
75
+ return
76
+ ;;
77
+ annotate)
78
+ COMPREPLY=($(compgen -f -X '!*.docx' -- "${cur}"))
79
+ return
80
+ ;;
81
+ apply)
82
+ COMPREPLY=($(compgen -f -X '!*.md' -- "${cur}"))
83
+ return
84
+ ;;
85
+ comment)
86
+ COMPREPLY=($(compgen -f -X '!*.docx' -- "${cur}"))
87
+ return
88
+ ;;
89
+ completions)
90
+ COMPREPLY=($(compgen -W "bash zsh" -- "${cur}"))
91
+ return
92
+ ;;
93
+ import|sections|extract|review|status|comments|strip|refs|migrate|figures|response|anonymize|split)
94
+ # Complete with .md and .docx files
95
+ COMPREPLY=($(compgen -f -X '!*.@(md|docx)' -- "${cur}"))
96
+ return
97
+ ;;
98
+ resolve|reply)
99
+ # Complete with -n option or .md files
100
+ if [[ "${cur}" == -* ]]; then
101
+ COMPREPLY=($(compgen -W "-n -m" -- "${cur}"))
102
+ else
103
+ COMPREPLY=($(compgen -f -X '!*.md' -- "${cur}"))
104
+ fi
105
+ return
106
+ ;;
107
+ check|lookup|add|fetch)
108
+ # DOI subcommands - complete with .bib files
109
+ COMPREPLY=($(compgen -f -X '!*.bib' -- "${cur}"))
110
+ return
111
+ ;;
112
+ citations)
113
+ COMPREPLY=($(compgen -f -X '!*.bib' -- "${cur}"))
114
+ return
115
+ ;;
116
+ merge)
117
+ # Complete with .md and .docx files
118
+ COMPREPLY=($(compgen -f -X '!*.@(md|docx)' -- "${cur}"))
119
+ return
120
+ ;;
121
+ esac
122
+
123
+ # Default: complete with files
124
+ COMPREPLY=($(compgen -f -- "${cur}"))
125
+ }
126
+
127
+ complete -F _rev_completions rev
@@ -0,0 +1,207 @@
1
+ #compdef rev
2
+
3
+ # Zsh completion for rev (docrev)
4
+ # Install: add to fpath or run:
5
+ # eval "$(rev completions zsh)"
6
+
7
+ _rev() {
8
+ local -a commands build_formats doi_actions eq_actions help_topics journals preview_formats
9
+
10
+ commands=(
11
+ 'build:Build PDF/DOCX/TEX from sections'
12
+ 'new:Create new project from template'
13
+ 'import:Import Word document'
14
+ 'sections:Import to section files'
15
+ 'extract:Extract text from Word'
16
+ 'review:Interactive review TUI'
17
+ 'status:Show annotation counts'
18
+ 'comments:List comments'
19
+ 'resolve:Mark comment as resolved'
20
+ 'reply:Reply to comments'
21
+ 'strip:Output clean markdown'
22
+ 'refs:Show reference status'
23
+ 'migrate:Convert hardcoded refs'
24
+ 'config:Configure settings'
25
+ 'install:Check dependencies'
26
+ 'doi:DOI validation'
27
+ 'citations:Validate citations'
28
+ 'equations:Extract equations'
29
+ 'figures:List figures'
30
+ 'response:Generate response letter'
31
+ 'anonymize:Prepare for blind review'
32
+ 'validate:Check journal requirements'
33
+ 'merge:Merge reviewer feedback'
34
+ 'diff:Compare against git history'
35
+ 'history:Show revision history'
36
+ 'help:Show help'
37
+ 'init:Initialize project'
38
+ 'split:Split paper.md to sections'
39
+ 'word-count:Show word counts per section'
40
+ 'wc:Show word counts (alias)'
41
+ 'stats:Show project statistics'
42
+ 'search:Search across files'
43
+ 'backup:Create timestamped backup'
44
+ 'export:Export project as zip'
45
+ 'preview:Build and open document'
46
+ 'watch:Watch and auto-rebuild'
47
+ 'lint:Check for issues'
48
+ 'grammar:Check grammar and style'
49
+ 'annotate:Add comments to DOCX'
50
+ 'apply:Apply annotations as track changes'
51
+ 'comment:Interactive comment mode'
52
+ 'completions:Generate shell completions'
53
+ )
54
+
55
+ preview_formats=(
56
+ 'pdf:Preview PDF'
57
+ 'docx:Preview Word document'
58
+ )
59
+
60
+ build_formats=(
61
+ 'pdf:Build PDF'
62
+ 'docx:Build Word document'
63
+ 'tex:Build LaTeX'
64
+ 'all:Build all formats'
65
+ )
66
+
67
+ doi_actions=(
68
+ 'check:Validate DOIs'
69
+ 'lookup:Find missing DOIs'
70
+ 'fetch:Get BibTeX from DOI'
71
+ 'add:Add citation by DOI'
72
+ )
73
+
74
+ eq_actions=(
75
+ 'list:List equations'
76
+ 'extract:Extract to file'
77
+ 'convert:Convert to Word'
78
+ 'from-word:Extract from Word'
79
+ )
80
+
81
+ help_topics=(
82
+ 'workflow:Review workflow'
83
+ 'syntax:CriticMarkup syntax'
84
+ 'commands:All commands'
85
+ )
86
+
87
+ journals=(
88
+ 'nature:Nature journal'
89
+ 'science:Science journal'
90
+ 'plos:PLOS ONE'
91
+ 'cell:Cell journal'
92
+ 'ecology:Ecology journals'
93
+ 'custom:Custom profile'
94
+ )
95
+
96
+ case "$words[2]" in
97
+ build)
98
+ _describe -t formats 'format' build_formats
99
+ _arguments \
100
+ '--toc[Include table of contents]' \
101
+ '--show-changes[Show track changes in DOCX]' \
102
+ '--clean[Clean build files]'
103
+ ;;
104
+ new)
105
+ _arguments \
106
+ '--list[List templates]' \
107
+ '--template[Template name]:template:(paper minimal thesis proposal)'
108
+ ;;
109
+ doi)
110
+ _describe -t actions 'action' doi_actions
111
+ ;;
112
+ equations|eq)
113
+ _describe -t actions 'action' eq_actions
114
+ ;;
115
+ validate)
116
+ _arguments \
117
+ '--list[List journals]' \
118
+ '--journal[Journal name]:journal:($journals)'
119
+ ;;
120
+ help)
121
+ _describe -t topics 'topic' help_topics
122
+ ;;
123
+ config)
124
+ _values 'setting' 'user[Set user name]'
125
+ ;;
126
+ word-count|wc)
127
+ _arguments \
128
+ '--limit[Word limit]:number:' \
129
+ '--journal[Use journal word limit]:journal:'
130
+ ;;
131
+ preview)
132
+ _describe -t formats 'format' preview_formats
133
+ ;;
134
+ watch)
135
+ _arguments \
136
+ '--no-open[Do not open after build]'
137
+ _values 'format' 'pdf' 'docx' 'all'
138
+ ;;
139
+ lint)
140
+ _arguments \
141
+ '--fix[Auto-fix issues]'
142
+ ;;
143
+ grammar)
144
+ _arguments \
145
+ '--learn[Add word to dictionary]:word:' \
146
+ '--forget[Remove from dictionary]:word:' \
147
+ '--list[List dictionary words]' \
148
+ '--rules[List grammar rules]' \
149
+ '--no-scientific[Disable science rules]' \
150
+ '--severity[Minimum severity]:level:(error warning info)'
151
+ _files -g '*.md'
152
+ ;;
153
+ annotate)
154
+ _arguments \
155
+ '-m[Comment message]:text:' \
156
+ '-s[Search text]:text:' \
157
+ '-a[Author name]:name:'
158
+ _files -g '*.docx'
159
+ ;;
160
+ apply)
161
+ _arguments \
162
+ '-a[Author name]:name:'
163
+ _files -g '*.md'
164
+ ;;
165
+ comment)
166
+ _arguments \
167
+ '-a[Author name]:name:'
168
+ _files -g '*.docx'
169
+ ;;
170
+ completions)
171
+ _values 'shell' 'bash' 'zsh'
172
+ ;;
173
+ import|sections|extract|review|status|comments|strip|refs|migrate|figures|response|anonymize|split|search|stats)
174
+ _files -g '*.md' -g '*.docx'
175
+ ;;
176
+ resolve|reply)
177
+ _arguments \
178
+ '-n[Comment number]:number:' \
179
+ '-m[Reply message]:message:'
180
+ _files -g '*.md'
181
+ ;;
182
+ check|lookup|add|citations)
183
+ _files -g '*.bib'
184
+ ;;
185
+ merge)
186
+ _files -g '*.md' -g '*.docx'
187
+ ;;
188
+ backup)
189
+ _arguments \
190
+ '--name[Custom backup name]:name:' \
191
+ '--output[Output directory]:dir:_files -/'
192
+ ;;
193
+ export)
194
+ _arguments \
195
+ '--output[Output filename]:file:' \
196
+ '--include-output[Include built files]'
197
+ ;;
198
+ *)
199
+ _describe -t commands 'command' commands
200
+ _arguments \
201
+ '--help[Show help]' \
202
+ '--version[Show version]'
203
+ ;;
204
+ esac
205
+ }
206
+
207
+ _rev "$@"
package/lib/build.js CHANGED
@@ -14,6 +14,7 @@ import { execSync, spawn } from 'child_process';
14
14
  import yaml from 'js-yaml';
15
15
  import { stripAnnotations } from './annotations.js';
16
16
  import { buildRegistry, labelToDisplay, detectDynamicRefs } from './crossref.js';
17
+ import { processVariables, hasVariables } from './variables.js';
17
18
 
18
19
  /**
19
20
  * Default rev.yaml configuration
@@ -156,6 +157,9 @@ export function combineSections(directory, config, options = {}) {
156
157
  parts.push('---');
157
158
  parts.push('');
158
159
 
160
+ // Read all section contents for variable processing
161
+ const sectionContents = [];
162
+
159
163
  // Combine sections
160
164
  for (const section of sections) {
161
165
  const filePath = path.join(directory, section);
@@ -163,13 +167,20 @@ export function combineSections(directory, config, options = {}) {
163
167
 
164
168
  // Remove any existing frontmatter from section files
165
169
  content = stripFrontmatter(content);
170
+ sectionContents.push(content);
166
171
 
167
172
  parts.push(content.trim());
168
173
  parts.push('');
169
174
  parts.push(''); // Double newline between sections
170
175
  }
171
176
 
172
- const paperContent = parts.join('\n');
177
+ let paperContent = parts.join('\n');
178
+
179
+ // Process template variables if any exist
180
+ if (hasVariables(paperContent)) {
181
+ paperContent = processVariables(paperContent, config, { sectionContents });
182
+ }
183
+
173
184
  const paperPath = path.join(directory, 'paper.md');
174
185
 
175
186
  fs.writeFileSync(paperPath, paperContent, 'utf-8');
package/lib/doi.js CHANGED
@@ -105,9 +105,13 @@ export function parseBibEntries(bibPath) {
105
105
  const skipMatch = entryContent.match(/\bnodoi\s*=\s*[{"]?(true|yes|1)[}""]?/i);
106
106
  const skip = !!skipMatch;
107
107
 
108
- // Check for comment marker before entry: % no-doi
108
+ // Check for comment marker immediately before entry: % no-doi
109
+ // Only look at the text between the last entry end (or start) and this entry
109
110
  const linesBefore = content.slice(Math.max(0, startPos - 200), startPos);
110
- const commentSkip = /% *no-?doi/i.test(linesBefore);
111
+ // Find the last closing brace or start of file to avoid matching comments for previous entries
112
+ const lastEntryEnd = linesBefore.lastIndexOf('}');
113
+ const relevantBefore = lastEntryEnd >= 0 ? linesBefore.slice(lastEntryEnd + 1) : linesBefore;
114
+ const commentSkip = /% *no-?doi/i.test(relevantBefore);
111
115
 
112
116
  entries.push({
113
117
  key,
package/lib/equations.js CHANGED
@@ -52,12 +52,30 @@ export function extractEquations(text, file = '') {
52
52
  // Skip code blocks
53
53
  if (line.trim().startsWith('```')) continue;
54
54
 
55
+ // Handle inline math ($...$) in a segment of text
56
+ // Careful not to match $$ or escaped \$
57
+ const inlinePattern = /(?<![\$\\])\$(?!\$)([^$\n]+)\$(?!\$)/g;
58
+ const extractInline = (segment) => {
59
+ let match;
60
+ inlinePattern.lastIndex = 0;
61
+ while ((match = inlinePattern.exec(segment)) !== null) {
62
+ equations.push({
63
+ type: 'inline',
64
+ content: match[1].trim(),
65
+ line: lineNum + 1,
66
+ file,
67
+ });
68
+ }
69
+ };
70
+
55
71
  // Handle display math blocks ($$...$$)
56
72
  if (line.includes('$$')) {
57
73
  const parts = line.split('$$');
58
74
 
59
75
  if (!inDisplayMath && parts.length >= 3) {
60
76
  // Single-line display math: $$content$$
77
+ // Also extract inline math from surrounding text
78
+ extractInline(parts[0]); // Text before $$
61
79
  for (let i = 1; i < parts.length; i += 2) {
62
80
  if (parts[i].trim()) {
63
81
  equations.push({
@@ -68,8 +86,13 @@ export function extractEquations(text, file = '') {
68
86
  });
69
87
  }
70
88
  }
89
+ // Extract inline from text after the last $$
90
+ if (parts.length % 2 === 1 && parts[parts.length - 1]) {
91
+ extractInline(parts[parts.length - 1]);
92
+ }
71
93
  } else if (!inDisplayMath) {
72
94
  // Start of multi-line display math
95
+ extractInline(parts[0]); // Text before $$
73
96
  inDisplayMath = true;
74
97
  displayMathStart = lineNum + 1;
75
98
  displayMathContent = parts[1] || '';
@@ -86,6 +109,10 @@ export function extractEquations(text, file = '') {
86
109
  });
87
110
  }
88
111
  displayMathContent = '';
112
+ // Text after $$ on closing line
113
+ if (parts[1]) {
114
+ extractInline(parts[1]);
115
+ }
89
116
  }
90
117
  continue;
91
118
  }
@@ -95,18 +122,8 @@ export function extractEquations(text, file = '') {
95
122
  continue;
96
123
  }
97
124
 
98
- // Handle inline math ($...$)
99
- // Careful not to match $$ or escaped \$
100
- const inlinePattern = /(?<!\$)\$(?!\$)([^$\n]+)\$(?!\$)/g;
101
- let match;
102
- while ((match = inlinePattern.exec(line)) !== null) {
103
- equations.push({
104
- type: 'inline',
105
- content: match[1].trim(),
106
- line: lineNum + 1,
107
- file,
108
- });
109
- }
125
+ // No display math on this line - extract inline math
126
+ extractInline(line);
110
127
  }
111
128
 
112
129
  return equations;