-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPandocReferencr.py
183 lines (158 loc) · 7.31 KB
/
PandocReferencr.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import sublime, sublime_plugin, sys, re, operator
## This class self-tests! Kind of. Run the CheckRef command over this file.
## It will then check itself. There are deliberate errors that it should find.
## See the comments next to the pandoc refs in python comments below.
footnote_insert = re.compile(r'(\[\^([a-zA-Z0-9]+?)\])\s*[^:]')
footnote_text = re.compile(r'^\s*(((\[\^([a-zA-Z0-9]+?)\])\s*:).*?)$', re.MULTILINE)
# trailing gumpf to prevent this from matching on the actual footnote.[^test0]
# a test selection.[^test1] this is text of markdown footnotes.[^test2] in a comment
# such that this class can test on itself![^nomatch0]***<-this ref has no match.
class CheckFootnotesCommand(sublime_plugin.TextCommand):
def run(self, edit):
sublime.status_message('running referencr... ')
print("Referencr - looking for markdown reference matches and unmatched references.")
unmatched_fn = [] # list of dicts!
matched_fn = {} # dict of dicts!
all_buffer = self.view.substr(sublime.Region(0, self.view.size()))
## first find the insert values, that should have matching text items (with the colons)
matched_arrays = self.match_fninserts(matched_fn, unmatched_fn, all_buffer);
matched_fn = matched_arrays[0]
unmatched_fn = matched_arrays[1]
## next, find the text values (with the colon), that have no insert value
matched_arrays = self.match_fntexts(matched_fn, unmatched_fn, all_buffer);
unmatched_fn = matched_arrays[1]
## the above function only modifies the unmatched data.
l_len = len(unmatched_fn)
print("There are " + str(l_len) + " errors in total.")
if l_len > 5:
unmatched_fn = unmatched_fn[:4]
unmatched_fn.append({
"line" : len(all_buffer) + 1,
"err_msg" : "THERE ARE " + str(l_len-4) + " MORE ERRORS NOT DISPLAYED HERE DUE TO CONSTRAINTS OF SPACE."})
self.alert(unmatched_fn)
return
def alert(self, unmatched_fn):
if len(unmatched_fn) > 0:
unmatched_msg = ""
## first get the fn_inserts with no fn_text
from operator import itemgetter
newlist = sorted(unmatched_fn, key=itemgetter('line'))
i = 0;
for item in newlist:
i = i+1
unmatched_msg = unmatched_msg + "(" + str(i) + ") " + item['err_msg'] + "\n\n"
sublime.error_message("Unmatched footnotes!\n\n" + str(unmatched_msg))
else :
msg = "All Footnotes OK!"
print(msg)
sublime.status_message(msg)
return
## find the insert values, that should have matching text items (with the colons)
## fully populates the matched_fn dictionary with all the data of the matched footnote pairs.
def match_fninserts(self, matched_fn, unmatched_fn, all_buffer):
print("... looking in all current buffer for matches with " + footnote_insert.pattern)
matches = footnote_insert.finditer(all_buffer)
for m in matches :
grp = m.group(1) # this is the real note. group(0) has trailing gumpf
fn = m.group(2)
## row and column of the footnote in the main text
rowcol = self.view.rowcol(m.start())
line = rowcol[0]+1 # it's zero-based!
col = rowcol[1]+1 # it's zero-based!
fn_textvalue = re.compile(r'^\s*(\[\^' + fn + r'\]\s*:\s*(.*))$\n', re.MULTILINE)
fn_match = fn_textvalue.search(all_buffer)
if fn_match:
## row and column of the matching footnote text.
fntext_rowcol = self.view.rowcol(fn_match.start())
fntext_line = fntext_rowcol[0]+1 # it's zero-based!
fntext_col = fntext_rowcol[1]+1 # it's zero-based!
# all the footnote except the leading space and trailing newline
fntext = fn_match.group(1)
data = {
'fn_insert': grp, 'fn_num':fn, 'line':line, 'col':col,
'fn_text_line':fntext_line, 'fn_text_col':fntext_col,
'fn_text':fntext,
'err_msg':'no error found'
}
matched_fn[grp]=data
print(data['fn_insert'] + " at line " + str(data['line']) + ", col " + str(data['col'])
+ "; match at line " + str(data['fn_text_line']) + ", value: '" + data['fn_text'] + "'")
else:
data = {'fn_insert':grp, 'fn_num': fn, 'line':line,'col':col,
'err_msg':"line "+str(line)+"; col "+str(col)+"; '"+ grp+"' with no matching footnote text entry."
}
unmatched_fn.append( data )
print(data['err_msg'])
#finish
return (matched_fn, unmatched_fn)
## find the text values (with the colons), that should have matching insert items
## this function must be run with a populated matched_fn dict containing the inserts footnote data.
def match_fntexts(self, matched_fn, unmatched_fn, all_buffer):
print("... looking in all current buffer for matches with " + footnote_text.pattern)
matches = footnote_text.finditer(all_buffer)
for m in matches :
grp = m.group(3) # this is the real note. group(0) has leading gumpf, group(1), trailing gumpf
fn = m.group(4)
notetext = m.group(2)
alltext = m.group(1)
## row and column of the footnote text
rowcol = self.view.rowcol(m.start())
line = rowcol[0]+1 # it's zero-based!
col = rowcol[1]+1 # it's zero-based!
if grp in matched_fn:
# this will be found from the earlier run (gah! not independent of order!!!)
# just do nothing here except print success, matched_fn has got this data.
found_fn = matched_fn[grp]
print(found_fn['fn_insert'] + " at line " + str(found_fn['line']) + ", col " + str(found_fn['col'])
+ " got match at line " + str(found_fn['fn_text_line']) + " text value: '" + found_fn['fn_text'] + "'")
else:
data = {'fn_insert' : grp, 'fn_num' : fn, 'line':line,'col':col,
'err_msg':"line "+str(line)+"; col "+str(col)+"; '"+grp+"' fn text with no matching insert. Orphaned footnote is '"+alltext+"'"
}
unmatched_fn.append( data )
print(notetext + " no matching fn insert found!")
#finish
return (matched_fn, unmatched_fn)
class CompileRefCommand(sublime_plugin.TextCommand):
def run(self, edit):
sublime.status_message('running referencr... ')
print("Referencr - compiling markdown reference matches.")
unmatched_fn = {}
matched_fn = {}
all_buffer = self.view.substr(sublime.Region(0, self.view.size()))
print("... looking in all current buffer for matches with " + footnote.pattern)
matches = footnote.finditer(all_buffer)
outview = self.new_view(edit, self.view)
outedit = outview.begin_edit
outview.insert(outedit, 0, "Hello World")
outview.end_edit(outedit)
for m in matches :
grp = m.group(1) # this is the real note. group(0) has trailing gumpf
fn = m.group(2)
fn_textvalue = re.compile(r'\s*(\[\^' + fn + r'\]\s*:\s*(.*))$\n', re.MULTILINE)
#print("... searching now for note " + fn_textvalue.pattern)
fn_match = fn_textvalue.search(all_buffer)
if fn_match:
print(grp + " got match")
matched_fn[fn] = fn_match.group(2)
else :
print(grp + " no match found")
def new_view(self, edit, ownerview):
ownername = ownerview.file_name();
if not ownername:
ownername = ownerview.name()
if not ownername:
ownername = ownerview.id()
newview = ownerview.window().create_output_panel(ownername + ".refs")
##ownerview.window().show_panel("output." + ownername + ".refs")
ownerview.window().focus_view(newview)
return newview
'''
[^test0]: hi, this is a friendly footnote.
this is a comment[^test3]
[^test1]: it exists to match to the test note above.
Footnotes entries can be spread throughout a Pandoc documents, not just at the end.
[^test2]: the second match
[^test3]: this note is indented a bit.
[^NOMATCH1]: this note has no matching footnote insert anywhere.
'''