Skip to content

Commit c136da4

Browse files
authored
Keep footnote and reference names (hellt#14)
1 parent ec7f3ea commit c136da4

File tree

6 files changed

+169
-16
lines changed

6 files changed

+169
-16
lines changed

.github/workflows/cicd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
python-version: ${{ matrix.python-version }}
3636

3737
- name: Run Python unit tests
38-
run: python -m unittest discover -s ${{ env.test_dir }}
38+
run: python -m unittest discover -v -s ${{ env.test_dir }}
3939

4040
docker:
4141
runs-on: ubuntu-latest

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,12 @@ This option adds spacing between those inline references so they are properly id
3838

3939
`fnsort.py path/to/doc.md --adjacent`
4040

41+
### --keepnames
42+
Retain or keep inline reference and footnote names.
43+
This prevents the default behavior of replacing the names with numbers.
44+
Footnotes at the end of the markdown are **still sorted**.
45+
46+
`fnsort.py path/to/doc.md --keepnames`
47+
4148
## Contributing
4249
For information about contributing to this project, see the [contributing guidelines](CONTRIBUTING.md).

fnsort.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ def parse_arguments():
3939
help="Fix adjacent footnotes by adding a space between them",
4040
)
4141

42+
parser.add_argument(
43+
"--keepnames",
44+
action="store_true",
45+
help="Keep footnote names and do not replace with numeric ones",
46+
)
47+
4248
return parser.parse_args()
4349

4450

@@ -85,7 +91,7 @@ def space_adjacent_references(text):
8591
return text
8692

8793

88-
def sort_footnotes(text):
94+
def sort_footnotes(text, args):
8995
# removes the last newline from EOF so there is no EOL on the last line
9096
text = text.rstrip()
9197

@@ -105,20 +111,29 @@ def sort_footnotes(text):
105111
# Make a list of the footnote-references in order of appearance the original footnotes in text.
106112
# this is not the order of the footnote contents, but the order of the footnote references in the text.
107113
try:
108-
newlabels = [f"[^{i+1}]: {labels[j]}" for (i, j) in enumerate(order)]
114+
if args.keepnames:
115+
# Retain ref/footnote names (don't replace with numeric ones)
116+
newlabels = [f"[^{i}]: {labels[i]}" for i in order]
117+
else:
118+
# Make a list of the footnote-references in order of appearance in the original footnotes in text.
119+
# This is not the order of the footnote contents, but the order of the footnote references in the text.
120+
newlabels = [f"[^{i+1}]: {labels[j]}" for (i, j) in enumerate(order)]
109121
except KeyError as e:
110122
# add custom exception to improve error output
111123
raise MissingFootnoteError(
112124
f"Missing footnote or inline reference = {repr(e)}"
113125
)
126+
114127
# print(f"newlabels: {newlabels}")
115128

116129
# Remove the old footnote-references and put the new ones at the end of the text.
130+
# https://docs.python.org/3/library/re.html#re.Pattern.sub
117131
text = label.sub("", text).strip() + "\n" * 2 + "\n".join(newlabels)
118132
# print(f"text: {text}")
119133

120-
# Rewrite the footnote-links with the new footnote-reference numbers.
121-
text = link.sub(lambda m: replace_reference(m, order), text)
134+
if not args.keepnames:
135+
# Rewrite the footnote-links with the new footnote-reference numbers.
136+
text = link.sub(lambda m: replace_reference(m, order), text)
122137

123138
return text
124139

@@ -131,7 +146,7 @@ def main():
131146
if args.adjacent:
132147
text = space_adjacent_references(text)
133148

134-
processed_text = sort_footnotes(text)
149+
processed_text = sort_footnotes(text, args)
135150
file.seek(0)
136151
file.write(processed_text)
137152
file.truncate()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Brown Bears
2+
3+
Brown bears [^brn_bear] are majestic mammals best seen at a distance!
4+
There are several sub-species [^brn_bear] across Asia, Europe, and North America. Unfortunately mankind has endangered or pushed some species to extinction due to fur trade or human encroachment.
5+
6+
![Grizzly Bear Sow and her cubs](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Grizzly_Bear-_Sow_and_cubs_%285728173840%29.jpg/320px-Grizzly_Bear-_Sow_and_cubs_%285728173840%29.jpg "Grizzly Bear Sow and her cubs")
7+
8+
## Eurasian Brown Bear
9+
10+
### Asia
11+
* East Siberian brown bear [^e_sib]
12+
* Gobi bear [^gobi]
13+
* Himalayan brown bear [^hima]
14+
* Kamchatkan brown bear [^kamch]
15+
* Syrian brown bear [^syr]
16+
* Tibetan brown bear [^tib]
17+
* Ussuri brown bear [^ussuri]
18+
19+
### Europe
20+
* Cantabrian brown bear [^cant_bear]
21+
* Marsican brown bear [^mars_bear] - critically endangered
22+
23+
## Grizzly Bear
24+
25+
### North America
26+
* Californian grizzly bear[^cali] - extinct
27+
* Dall Island brown bear[^dall]
28+
* Kodiak bear [^kodiak]
29+
* Mexican grizzly bear[^mex] - extinct
30+
* Peninsular giant bear [^pgb]
31+
* Sitka brown bear[^sitka]
32+
* Stickeen brown bear[^stick]
33+
* Ungava brown bear[^ungava] - extinct
34+
35+
[^kamch]:https://en.wikipedia.org/wiki/Kamchatka_brown_bear
36+
[^brn_bear]:https://en.wikipedia.org/wiki/Brown_bear
37+
[^tib]:https://en.wikipedia.org/wiki/Tibetan_blue_bear
38+
[^syr]:https://en.wikipedia.org/wiki/Syrian_brown_bear
39+
[^cant_bear]: https://en.wikipedia.org/wiki/Cantabrian_brown_bear
40+
[^e_sib]: https://en.wikipedia.org/wiki/East_Siberian_brown_bear
41+
[^hima]: https://en.wikipedia.org/wiki/Himalayan_brown_bear
42+
[^mars_bear]: https://en.wikipedia.org/wiki/Marsican_brown_bear
43+
[^gobi]: https://en.wikipedia.org/wiki/Gobi_bear
44+
[^ussuri]: https://en.wikipedia.org/wiki/Ussuri_brown_bear
45+
[^ungava]: https://en.wikipedia.org/wiki/Ungava_brown_bear
46+
[^stick]: https://en.wikipedia.org/wiki/Stickeen_brown_bear
47+
[^sitka]: https://en.wikipedia.org/wiki/ABC_Islands_bear
48+
[^pgb]: https://en.wikipedia.org/wiki/Alaska_Peninsula_brown_bear
49+
[^mex]: https://en.wikipedia.org/wiki/Mexican_grizzly_bear
50+
[^cali]: https://en.wikipedia.org/wiki/California_grizzly_bear
51+
[^kodiak]: https://en.wikipedia.org/wiki/Kodiak_bear
52+
[^dall]: https://en.wikipedia.org/wiki/Dall_Island
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Brown Bears
2+
3+
Brown bears [^brn_bear] are majestic mammals best seen at a distance!
4+
There are several sub-species [^brn_bear] across Asia, Europe, and North America. Unfortunately mankind has endangered or pushed some species to extinction due to fur trade or human encroachment.
5+
6+
![Grizzly Bear Sow and her cubs](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Grizzly_Bear-_Sow_and_cubs_%285728173840%29.jpg/320px-Grizzly_Bear-_Sow_and_cubs_%285728173840%29.jpg "Grizzly Bear Sow and her cubs")
7+
8+
## Eurasian Brown Bear
9+
10+
### Asia
11+
* East Siberian brown bear [^e_sib]
12+
* Gobi bear [^gobi]
13+
* Himalayan brown bear [^hima]
14+
* Kamchatkan brown bear [^kamch]
15+
* Syrian brown bear [^syr]
16+
* Tibetan brown bear [^tib]
17+
* Ussuri brown bear [^ussuri]
18+
19+
### Europe
20+
* Cantabrian brown bear [^cant_bear]
21+
* Marsican brown bear [^mars_bear] - critically endangered
22+
23+
## Grizzly Bear
24+
25+
### North America
26+
* Californian grizzly bear[^cali] - extinct
27+
* Dall Island brown bear[^dall]
28+
* Kodiak bear [^kodiak]
29+
* Mexican grizzly bear[^mex] - extinct
30+
* Peninsular giant bear [^pgb]
31+
* Sitka brown bear[^sitka]
32+
* Stickeen brown bear[^stick]
33+
* Ungava brown bear[^ungava] - extinct
34+
35+
[^brn_bear]: https://en.wikipedia.org/wiki/Brown_bear
36+
[^e_sib]: https://en.wikipedia.org/wiki/East_Siberian_brown_bear
37+
[^gobi]: https://en.wikipedia.org/wiki/Gobi_bear
38+
[^hima]: https://en.wikipedia.org/wiki/Himalayan_brown_bear
39+
[^kamch]: https://en.wikipedia.org/wiki/Kamchatka_brown_bear
40+
[^syr]: https://en.wikipedia.org/wiki/Syrian_brown_bear
41+
[^tib]: https://en.wikipedia.org/wiki/Tibetan_blue_bear
42+
[^ussuri]: https://en.wikipedia.org/wiki/Ussuri_brown_bear
43+
[^cant_bear]: https://en.wikipedia.org/wiki/Cantabrian_brown_bear
44+
[^mars_bear]: https://en.wikipedia.org/wiki/Marsican_brown_bear
45+
[^cali]: https://en.wikipedia.org/wiki/California_grizzly_bear
46+
[^dall]: https://en.wikipedia.org/wiki/Dall_Island
47+
[^kodiak]: https://en.wikipedia.org/wiki/Kodiak_bear
48+
[^mex]: https://en.wikipedia.org/wiki/Mexican_grizzly_bear
49+
[^pgb]: https://en.wikipedia.org/wiki/Alaska_Peninsula_brown_bear
50+
[^sitka]: https://en.wikipedia.org/wiki/ABC_Islands_bear
51+
[^stick]: https://en.wikipedia.org/wiki/Stickeen_brown_bear
52+
[^ungava]: https://en.wikipedia.org/wiki/Ungava_brown_bear

tests/test_footnote_sorting.py

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def setUpClass(self):
2424
self.expected_text = fh.read()
2525

2626
# technically there is also a "file" kwarg by default
27-
args = {"adjacent": False}
27+
args = {"adjacent": False, "keepnames": False}
2828
self.args = set_command_line_args(args)
2929
if self.args.adjacent:
3030
self.text = fnsort.space_adjacent_references(self.text)
@@ -50,7 +50,7 @@ def test_replace_reference(self):
5050

5151
def test_footnote_sort(self):
5252
""" Entire footnote sort process """
53-
self.assertEqual(fnsort.sort_footnotes(self.text), self.expected_text)
53+
self.assertEqual(fnsort.sort_footnotes(self.text, self.args), self.expected_text)
5454

5555

5656
class TestDuplicates(unittest.TestCase):
@@ -65,7 +65,7 @@ def setUpClass(self):
6565
self.expected_text = fh.read()
6666

6767
# technically there is also a "file" kwarg by default
68-
args = {"adjacent": False}
68+
args = {"adjacent": False, "keepnames": False}
6969
self.args = set_command_line_args(args)
7070
if self.args.adjacent:
7171
self.text = fnsort.space_adjacent_references(self.text)
@@ -95,7 +95,7 @@ def test_replace_references_with_dups(self):
9595

9696
def test_footnote_sort_with_dups(self):
9797
""" Entire footnote sort process with duplicate tags """
98-
self.assertEqual(fnsort.sort_footnotes(self.text), self.expected_text)
98+
self.assertEqual(fnsort.sort_footnotes(self.text, self.args), self.expected_text)
9999

100100

101101
class TestFootnotesMustBeLast(unittest.TestCase):
@@ -110,7 +110,7 @@ def setUpClass(self):
110110
self.expected_text = fh.read()
111111

112112
# technically there is also a "file" kwarg by default
113-
args = {"adjacent": False}
113+
args = {"adjacent": False, "keepnames": False}
114114
self.args = set_command_line_args(args)
115115
if self.args.adjacent:
116116
self.text = fnsort.space_adjacent_references(self.text)
@@ -130,7 +130,7 @@ def test_footnote_sort_trailing_text(self):
130130
131131
in short this is not expected to return the desired output
132132
"""
133-
self.assertNotEqual(fnsort.sort_footnotes(self.text), self.expected_text)
133+
self.assertNotEqual(fnsort.sort_footnotes(self.text, self.args), self.expected_text)
134134

135135

136136
class TestAdjacentFootnotes(unittest.TestCase):
@@ -148,7 +148,7 @@ def setUpClass(self):
148148
self.expected_text = fh.read()
149149

150150
# technically there is also a "file" kwarg by default
151-
args = {"adjacent": True}
151+
args = {"adjacent": True, "keepnames": False}
152152
self.args = set_command_line_args(args)
153153

154154
# allow for full diff output
@@ -171,7 +171,34 @@ def test_adjacent_footnote_sort(self):
171171
if self.args.adjacent:
172172
self.text = fnsort.space_adjacent_references(self.text)
173173

174-
self.assertEqual(fnsort.sort_footnotes(self.text), self.expected_text)
174+
self.assertEqual(fnsort.sort_footnotes(self.text, self.args), self.expected_text)
175+
176+
177+
class TestKeepFootnoteNames(unittest.TestCase):
178+
@classmethod
179+
def setUpClass(self):
180+
path = "tests/keep_fn_names"
181+
182+
with open(f"{path}/keep_fn_names.md") as fh:
183+
self.text = fh.read()
184+
185+
with open(f"{path}/keep_fn_names_expected.md") as fh:
186+
self.expected_text = fh.read()
187+
188+
# technically there is also a "file" kwarg by default
189+
args = {"adjacent": False, "keepnames": True}
190+
self.args = set_command_line_args(args)
191+
192+
# allow for full diff output
193+
# self.maxDiff = None
194+
195+
196+
def test_keep_footnote_names(self):
197+
""" Entire footnote sort process while retaining footnote names """
198+
if self.args.adjacent:
199+
self.text = fnsort.space_adjacent_references(self.text)
200+
201+
self.assertEqual(fnsort.sort_footnotes(self.text, self.args), self.expected_text)
175202

176203

177204
class TestMissingFootnotes(unittest.TestCase):
@@ -186,7 +213,7 @@ def setUpClass(self):
186213
# sense in "expected" test file
187214

188215
# technically there is also a "file" kwarg by default
189-
args = {"adjacent": False}
216+
args = {"adjacent": False, "keepnames": False}
190217
self.args = set_command_line_args(args)
191218

192219
# allow for full diff output
@@ -201,7 +228,7 @@ def test_missing_footnotes(self):
201228
self.text = fnsort.space_adjacent_references(self.text)
202229

203230
with self.assertRaises(fnsort.MissingFootnoteError):
204-
fnsort.sort_footnotes(self.text)
231+
fnsort.sort_footnotes(self.text, self.args)
205232

206233

207234
if __name__ == "__main__":

0 commit comments

Comments
 (0)