Skip to content

Commit ef6ce00

Browse files
authored
Merge pull request #63 from wheelercj/fix-bugs
Fix bugs
2 parents 502f32f + 7f3d9c7 commit ef6ce00

File tree

3 files changed

+103
-73
lines changed

3 files changed

+103
-73
lines changed

src/md.js

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { getSetting } from './common.js';
1818
import { TurndownService } from './turndown.js';
19-
import { newTurndownService } from './newTurndownService.js';
19+
import { newTurndownService, replaceBrackets } from './newTurndownService.js';
2020

2121
/**
2222
* turndownService is a TurndownService instance used to convert HTML to markdown. Use
@@ -38,24 +38,6 @@ let currentBulletPoint = '-';
3838
*/
3939
let currentSubBrackets = 'underlined';
4040

41-
/**
42-
* replaceBrackets replaces any square brackets in text with the character or escape
43-
* sequence chosen in settings.
44-
* @param {string} text - the text.
45-
* @param {string} subBrackets - the setting for what to substitute any square brackets
46-
* with.
47-
* @returns {string}
48-
*/
49-
export function replaceBrackets(text, subBrackets) {
50-
if (subBrackets === 'underlined') {
51-
return text.replaceAll('[', '⦋').replaceAll(']', '⦌');
52-
} else if (subBrackets === 'escaped') {
53-
return text.replaceAll('[', '\\[').replaceAll(']', '\\]');
54-
} else {
55-
return text;
56-
}
57-
}
58-
5941
/**
6042
* escape escapes many markdown patterns, but not square brackets.
6143
* @param {string} text - the text to escape markdown characters in.
@@ -91,29 +73,15 @@ export async function htmlToMarkdown(html) {
9173
newSubBrackets !== currentSubBrackets
9274
) {
9375
currentBulletPoint = newBulletPoint;
94-
currentSubBrackets = newSubBrackets
76+
currentSubBrackets = newSubBrackets;
9577
turndownService = newTurndownService(
96-
currentBulletPoint, newTurndownEscape(currentSubBrackets)
78+
currentBulletPoint, currentSubBrackets, escape,
9779
);
9880
}
9981

10082
return turndownService.turndown(html);
10183
}
10284

103-
/**
104-
* newTurndownEscape returns a function that escapes markdown. The returned function is
105-
* intended for use in a Turndown service.
106-
* @param {string} subBrackets - the setting for what to replace square brackets with.
107-
* @returns {Function(string): string}
108-
*/
109-
function newTurndownEscape(subBrackets) {
110-
// Making Turndown's escape function async results in Turndown giving the error
111-
// `TypeError: string.replace is not a function`.
112-
return function turndownEscape(text) {
113-
return replaceBrackets(escape(text), subBrackets);
114-
}
115-
}
116-
11785
/**
11886
* createLink creates a markdown link.
11987
* @param {string} title - the title of the link. Square brackets are replaced, escaped,
@@ -147,6 +115,8 @@ export async function createLink(title, url, subBrackets = null) {
147115
* createAlert creates a markdown alert. GitHub and Obsidian use the same format, but
148116
* GitHub supports only specific alert types: note, tip, important, warning, and
149117
* caution. More details here: https://github.com/orgs/community/discussions/16925.
118+
* Obsidian calls these callouts
119+
* https://help.obsidian.md/Editing+and+formatting/Callouts.
150120
* @param {string} type - the alert's type.
151121
* @param {string} text - the alert's text.
152122
* @returns {Promise<string>}

src/newTurndownService.js

Lines changed: 97 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,100 @@
11
import { TurndownService } from './turndown.js';
22
import { turndownPluginGfm } from './turndown-plugin-gfm.js';
33

4+
/**
5+
* replaceBrackets replaces any square brackets in text with the character or escape
6+
* sequence chosen in settings.
7+
* @param {string} text - the text.
8+
* @param {string} subBrackets - the setting for what to substitute any square brackets
9+
* with.
10+
* @returns {string}
11+
*/
12+
export function replaceBrackets(text, subBrackets) {
13+
if (subBrackets === 'underlined') {
14+
return text.replaceAll('[', '⦋').replaceAll(']', '⦌');
15+
} else if (subBrackets === 'escaped') {
16+
return text.replaceAll('[', '\\[').replaceAll(']', '\\]');
17+
} else {
18+
return text;
19+
}
20+
}
21+
422
/**
523
* newTurndownService creates a new TurndownService instance. The instance has been
624
* customized in a way that depends on the `location` object.
725
* @param {string} bulletPoint - the setting for the bullet point character.
26+
* @param {string} subBrackets - the Stardown setting for what to substitute square
27+
* brackets with.
828
* @param {Function(string): string} turndownEscape - the markdown escape function for
929
* the Turndown service instance to use.
1030
* @returns {TurndownService}
1131
*/
12-
export function newTurndownService(bulletPoint, turndownEscape) {
32+
export function newTurndownService(bulletPoint, subBrackets, turndownEscape) {
1333
// https://github.com/mixmark-io/turndown
1434
const t = new TurndownService({
1535
bulletListMarker: bulletPoint,
1636
headingStyle: 'atx',
37+
emDelimiter: '*',
1738
codeBlockStyle: 'fenced',
18-
}).remove('style').remove('script').remove('noscript').remove('link');
39+
});
1940

2041
t.use(turndownPluginGfm.gfm); // GitHub Flavored Markdown
2142

2243
t.escape = turndownEscape;
2344

24-
addRules(t);
45+
addRules(t, subBrackets);
46+
47+
t.keep(['u', 'dl', 'dt', 'dd']);
48+
49+
// Keep subscript and superscript nodes as HTML if and only if they don't contain
50+
// HTML anchor elements because they are not clickable at least in Obsidian. Also,
51+
// if URLs aren't processed, they can't be made absolute.
52+
t.keep((node) => {
53+
return (
54+
(node.nodeName === 'SUB' || node.nodeName === 'SUP') &&
55+
!node.querySelectorAll('a').length
56+
);
57+
});
58+
59+
t.remove(['style', 'script', 'noscript', 'link']);
2560

2661
return t;
2762
}
2863

2964
/**
3065
* addRules adds custom Turndown rules to a Turndown service instance.
3166
* @param {TurndownService} t - the Turndown service instance.
67+
* @param {string} subBrackets - the Stardown setting for what to substitute square
68+
* brackets with.
3269
* @returns {void}
3370
*/
34-
function addRules(t) {
71+
function addRules(t, subBrackets) {
3572
// Each Turndown rule runs on each yet-unreplaced HTML element. If the element
3673
// matches the rule's filter, the rule's replacement function runs on it.
3774

3875
t.addRule('inlineLink', {
3976
filter: isInlineLink,
40-
replacement: convertLinkToMarkdown,
77+
replacement: newConvertLinkToMarkdown(subBrackets),
4178
});
4279

4380
t.addRule('img', {
4481
filter: 'img',
4582
replacement: convertImageToMarkdown,
4683
});
84+
85+
t.addRule('strikethrough', {
86+
filter: ['del', 's', 'strike'],
87+
replacement: function (content) {
88+
return '~~' + content + '~~';
89+
},
90+
});
91+
92+
t.addRule('highlight', {
93+
filter: 'mark',
94+
replacement: function (content) {
95+
return '==' + content + '==';
96+
},
97+
});
4798
}
4899

49100
/**
@@ -61,40 +112,49 @@ function isInlineLink(node, options) {
61112
}
62113

63114
/**
64-
* convertLinkToMarkdown converts an HTML link to a markdown link.
65-
* @param {string} content - the page's content within the HTML anchor. If the anchor
66-
* contains some elements like inline SVGs, this variable will be falsy.
67-
* @param {*} node - the HTML element node.
68-
* @returns {string}
115+
* newConvertLinkToMarkdown returns a function that converts an HTML link to a markdown
116+
* link.
117+
* @param {string} subBrackets - the Stardown setting for what to substitute square
118+
* brackets with.
119+
* @returns {Function(string, any): string}
69120
*/
70-
function convertLinkToMarkdown(content, node) {
71-
if (!content) { // if the link's title would be empty
72-
return ''; // don't create the link
73-
}
74-
75-
let href = node.getAttribute('href');
76-
if (href) {
77-
// make the URL absolute
78-
if (href.startsWith('/')) {
79-
const url = new URL(location.href);
80-
const base = url.origin;
81-
href = base + href;
82-
} else if (href.startsWith('#')) {
83-
href = location.href + href;
121+
function newConvertLinkToMarkdown(subBrackets) {
122+
/**
123+
* @param {string} content - the page's content within the HTML anchor. If the anchor
124+
* contains some elements like inline SVGs, this variable will be falsy.
125+
* @param {*} node - the HTML element node.
126+
* @returns {string}
127+
*/
128+
return function (content, node) {
129+
if (!content) { // if the link's title would be empty
130+
return ''; // don't create the link
84131
}
85132

86-
// escape parentheses
87-
href = href.replace(/([()])/g, '\\$1');
88-
}
133+
content = replaceBrackets(content, subBrackets);
89134

90-
// remove excess whitespace and escape quotation marks
91-
let title = node.getAttribute('title');
92-
if (title) {
93-
title = cleanAttribute(title);
94-
title = ' "' + title.replace(/"/g, '\\"') + '"';
95-
}
135+
let href = node.getAttribute('href') || '';
136+
if (href) {
137+
href = href.replaceAll(' ', '%20').replaceAll('(', '%28').replaceAll(')', '%29');
138+
139+
// make the URL absolute
140+
if (href.startsWith('/')) {
141+
const url = new URL(location.href);
142+
const base = url.origin;
143+
href = base + href;
144+
} else if (href.startsWith('#')) {
145+
href = location.href + href;
146+
}
147+
}
148+
149+
// remove excess whitespace and escape quotation marks
150+
let title = node.getAttribute('title') || '';
151+
if (title) {
152+
title = cleanAttribute(title);
153+
title = ' "' + title.replace(/"/g, '\\"') + '"';
154+
}
96155

97-
return '[' + content + '](' + href + title + ')';
156+
return '[' + content + '](' + href + title + ')';
157+
};
98158
}
99159

100160
/**
@@ -110,19 +170,19 @@ function convertImageToMarkdown(content, node) {
110170
}
111171

112172
// remove excess whitespace
113-
let alt = cleanAttribute(node.getAttribute('alt'));
173+
let alt = cleanAttribute(node.getAttribute('alt') || '');
114174

115175
// make the URL absolute
116176
if (src.startsWith('//')) {
117-
src = 'https:' + src;
177+
src = 'https:' + src.replaceAll(' ', '%20');
118178
} else if (src.startsWith('/')) {
119179
const url = new URL(location.href);
120180
const base = url.origin;
121181
src = base + src;
122182
}
123183

124184
// remove excess whitespace
125-
let title = cleanAttribute(node.getAttribute('title'));
185+
let title = cleanAttribute(node.getAttribute('title') || '');
126186
let titlePart = title ? ' "' + title + '"' : '';
127187

128188
return '![' + alt + '](' + src + titlePart + ')';

src/options.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ <h3 style="text-align: center">Options</h3>
7878
<br />
7979
<br />
8080
<label for="subBrackets">
81-
Replace square brackets in link titles and in selections that keep formatting with
81+
Replace square brackets in link titles with
8282
</label>
8383
<select id="subBrackets" name="subBrackets">
8484
<option value="underlined">underlined brackets ⦋ ⦌</option>

0 commit comments

Comments
 (0)