Skip to content

Commit 2cf01f4

Browse files
committed
Merge branch 'develop'
2 parents e1ad96d + 7913d65 commit 2cf01f4

9 files changed

Lines changed: 67582 additions & 38700 deletions

main.js

Lines changed: 66141 additions & 38582 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 1194 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@
2828
"devDependencies": {
2929
"@types/js-yaml": "^4.0.5",
3030
"@types/lodash-es": "^4.17.6",
31-
"@types/marked": "^4.0.1",
31+
"@types/markdown-it": "^12.2.3",
3232
"@types/node": "^16.11.6",
33-
"@types/wpapi": "^1.1.1",
3433
"@types/xmlrpc": "^1.3.7",
3534
"@typescript-eslint/eslint-plugin": "^5.2.0",
3635
"@typescript-eslint/parser": "^5.2.0",
@@ -39,8 +38,9 @@
3938
"esbuild": "0.14.47",
4039
"gray-matter": "^4.0.3",
4140
"js-yaml": "^4.1.0",
41+
"juice": "^9.0.0",
4242
"lodash-es": "^4.17.21",
43-
"marked": "^4.0.6",
43+
"markdown-it": "^13.0.1",
4444
"mathjax-full": "^3.2.2",
4545
"obsidian": "latest",
4646
"standard-version": "^9.3.2",

src/abstract-wp-client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
WordPressPostParams,
1010
WordPressPublishParams
1111
} from './wp-client';
12-
import { marked } from 'marked';
1312
import { WpPublishModal } from './wp-publish-modal';
1413
import { Term } from './wp-api';
1514
import { ERROR_NOTICE_TIMEOUT } from './consts';
@@ -18,6 +17,7 @@ import yaml from 'js-yaml';
1817
import { isPromiseFulfilledResult, openWithBrowser, SafeAny } from './utils';
1918
import { PostPublishedModal } from './post-published-modal';
2019
import { WpProfile } from './wp-profile';
20+
import { AppState } from './app-state';
2121

2222

2323
const matterOptions = {
@@ -182,7 +182,7 @@ export abstract class AbstractWordPressClient implements WordPressClient {
182182
postParams.tags = tagTerms.map(term => term.id);
183183
const result = await this.publish(
184184
postParams.title ?? 'A post from Obsidian!',
185-
marked.parse(postParams.content) ?? '',
185+
AppState.getInstance().markdownParser.render(postParams.content) ?? '',
186186
postParams,
187187
{
188188
username,

src/app-state.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import { Events } from 'obsidian';
2+
import MarkdownIt from 'markdown-it';
23

34
export class AppState {
45
private static instance: AppState;
56

7+
markdownParser = new MarkdownIt();
8+
69
events = new Events();
710

811
/**
912
* Code verifier between classes.
1013
*/
1114
codeVerifier: string | undefined;
1215

13-
private constructor() { }
16+
private constructor() {
17+
}
1418

1519
static getInstance(): AppState {
1620
if (!AppState.instance) {

src/main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { addIcons } from './icons';
44
import { WordPressPostParams } from './wp-client';
55
import { getWordPressClient } from './wp-clients';
66
import { I18n } from './i18n';
7-
import { buildMarked } from './utils';
87
import { ERROR_NOTICE_TIMEOUT, EventType, WP_OAUTH2_REDIRECT_URI, WP_OAUTH2_URL_ACTION } from './consts';
98
import { OAuth2Client } from './oauth2-client';
109
import { CommentStatus, PostStatus } from './wp-api';
@@ -13,6 +12,7 @@ import { WpProfileChooserModal } from './wp-profile-chooser-modal';
1312
import { AppState } from './app-state';
1413
import { DEFAULT_SETTINGS, SettingsVersion, upgradeSettings, WordpressPluginSettings } from './plugin-settings';
1514
import { PassCrypto } from './pass-crypto';
15+
import { setupMarkdownParser } from './utils';
1616

1717
export default class WordpressPlugin extends Plugin {
1818

@@ -37,7 +37,7 @@ export default class WordpressPlugin extends Plugin {
3737
// lang should be load early, but after settings
3838
this.#i18n = new I18n(this.#settings?.lang);
3939

40-
buildMarked(this.settings);
40+
setupMarkdownParser(this.settings);
4141

4242
addIcons();
4343

src/markdown-it-mathjax3-plugin.ts

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import MarkdownIt from 'markdown-it';
2+
import StateInline from 'markdown-it/lib/rules_inline/state_inline';
3+
import StateBlock from 'markdown-it/lib/rules_block/state_block';
4+
import { TeX } from 'mathjax-full/js/input/tex';
5+
import { AllPackages } from 'mathjax-full/js/input/tex/AllPackages';
6+
import { SVG } from 'mathjax-full/js/output/svg';
7+
import Token from 'markdown-it/lib/token';
8+
import { liteAdaptor } from 'mathjax-full/js/adaptors/liteAdaptor';
9+
import { RegisterHTMLHandler } from 'mathjax-full/js/handlers/html';
10+
import { AssistiveMmlHandler } from 'mathjax-full/js/a11y/assistive-mml';
11+
import { mathjax } from 'mathjax-full/js/mathjax';
12+
import juice from 'juice';
13+
import { SafeAny } from './utils';
14+
import { MathJaxOutputType } from './plugin-settings';
15+
16+
17+
interface MarkdownItMathJax3PluginOptions {
18+
outputType: MathJaxOutputType;
19+
}
20+
21+
interface ConvertOptions {
22+
display: boolean
23+
}
24+
25+
export default function MarkdownItMathJax3Plugin(md: MarkdownIt, options: MarkdownItMathJax3PluginOptions): void {
26+
// set MathJax as the renderer for markdown-it-simplemath
27+
md.inline.ruler.after('escape', 'math_inline', mathInline);
28+
md.block.ruler.after('blockquote', 'math_block', mathBlock, {
29+
alt: ['paragraph', 'reference', 'blockquote', 'list'],
30+
});
31+
md.renderer.rules.math_inline = (tokens: Token[], idx: number) => {
32+
return renderMath(tokens[idx].content, {
33+
display: false
34+
}, options);
35+
};
36+
md.renderer.rules.math_block = (tokens: Token[], idx: number) => {
37+
return renderMath(tokens[idx].content, {
38+
display: true
39+
}, options);
40+
};
41+
}
42+
43+
function renderMath(content: string, convertOptions: ConvertOptions, options: MarkdownItMathJax3PluginOptions): string {
44+
if (options.outputType === MathJaxOutputType.SVG) {
45+
const documentOptions = {
46+
InputJax: new TeX({ packages: AllPackages }),
47+
OutputJax: new SVG({ fontCache: 'none' })
48+
};
49+
const adaptor = liteAdaptor();
50+
const handler = RegisterHTMLHandler(adaptor);
51+
AssistiveMmlHandler(handler);
52+
const mathDocument = mathjax.document(content, documentOptions);
53+
const html = adaptor.outerHTML(mathDocument.convert(content, convertOptions));
54+
const stylesheet = adaptor.outerHTML(documentOptions.OutputJax.styleSheet(mathDocument) as SafeAny);
55+
return juice(html + stylesheet);
56+
} else {
57+
if (convertOptions.display) {
58+
return `$$\n${content}$$\n`;
59+
} else {
60+
return `$${content}$`;
61+
}
62+
}
63+
}
64+
65+
// Test if potential opening or closing delimiter
66+
// Assumes that there is a '$' at state.src[pos]
67+
function isValidDelimiter(state: StateInline, pos: number) {
68+
const max = state.posMax;
69+
let canOpen = true;
70+
let canClose = true;
71+
72+
const prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
73+
const nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
74+
75+
// Check non-whitespace conditions for opening and closing, and
76+
// check that closing delimiter isn't followed by a number
77+
if (prevChar === 0x20 /* ' ' */
78+
|| prevChar === 0x09 /* \t */
79+
|| (nextChar >= 0x30 /* '0' */ && nextChar <= 0x39) /* '9' */
80+
) {
81+
canClose = false;
82+
}
83+
if (nextChar === 0x20 /* ' ' */ || nextChar === 0x09 /* \t */) {
84+
canOpen = false;
85+
}
86+
87+
return {
88+
canOpen,
89+
canClose
90+
};
91+
}
92+
93+
function mathInline(state: StateInline, silent: boolean) {
94+
if (state.src[state.pos] !== '$') {
95+
return false;
96+
}
97+
98+
let res = isValidDelimiter(state, state.pos);
99+
if (!res.canOpen) {
100+
if (!silent) {
101+
state.pending += '$';
102+
}
103+
state.pos += 1;
104+
return true;
105+
}
106+
107+
// First check for and bypass all properly escaped delimiters
108+
// This loop will assume that the first leading backtick can not
109+
// be the first character in state.src, which is known since
110+
// we have found an opening delimiter already.
111+
const start = state.pos + 1;
112+
let match = start;
113+
while ((match = state.src.indexOf('$', match)) !== -1) {
114+
// Found potential $, look for escapes, pos will point to
115+
// first non escape when complete
116+
let pos = match - 1;
117+
while (state.src[pos] === '\\') {
118+
pos -= 1;
119+
}
120+
121+
// Even number of escapes, potential closing delimiter found
122+
if ((match - pos) % 2 == 1) {
123+
break;
124+
}
125+
match += 1;
126+
}
127+
128+
// No closing delimter found. Consume $ and continue.
129+
if (match === -1) {
130+
if (!silent) {
131+
state.pending += '$';
132+
}
133+
state.pos = start;
134+
return true;
135+
}
136+
137+
// Check if we have empty content, ie: $$. Do not parse.
138+
if (match - start === 0) {
139+
if (!silent) {
140+
state.pending += '$$';
141+
}
142+
state.pos = start + 1;
143+
return true;
144+
}
145+
146+
// Check for valid closing delimiter
147+
res = isValidDelimiter(state, match);
148+
if (!res.canClose) {
149+
if (!silent) {
150+
state.pending += '$';
151+
}
152+
state.pos = start;
153+
return true;
154+
}
155+
156+
if (!silent) {
157+
const token = state.push('math_inline', 'math', 0);
158+
token.markup = '$';
159+
token.content = state.src.slice(start, match);
160+
}
161+
162+
state.pos = match + 1;
163+
return true;
164+
}
165+
166+
function mathBlock(state: StateBlock, start: number, end: number, silent: boolean) {
167+
let next: number;
168+
let lastPos: number;
169+
let found = false;
170+
let pos = state.bMarks[start] + state.tShift[start];
171+
let max = state.eMarks[start];
172+
let lastLine = '';
173+
174+
if (pos + 2 > max) {
175+
return false;
176+
}
177+
if (state.src.slice(pos, pos + 2) !== '$$') {
178+
return false;
179+
}
180+
181+
pos += 2;
182+
let firstLine = state.src.slice(pos, max);
183+
184+
if (silent) {
185+
return true;
186+
}
187+
if (firstLine.trim().slice(-2) === '$$') {
188+
// Single line expression
189+
firstLine = firstLine.trim().slice(0, -2);
190+
found = true;
191+
}
192+
193+
for (next = start; !found; ) {
194+
next++;
195+
196+
if (next >= end) {
197+
break;
198+
}
199+
200+
pos = state.bMarks[next] + state.tShift[next];
201+
max = state.eMarks[next];
202+
203+
if (pos < max && state.tShift[next] < state.blkIndent) {
204+
// non-empty line with negative indent should stop the list:
205+
break;
206+
}
207+
208+
if (state.src.slice(pos, max).trim().slice(-2) === '$$') {
209+
lastPos = state.src.slice(0, max).lastIndexOf('$$');
210+
lastLine = state.src.slice(pos, lastPos);
211+
found = true;
212+
}
213+
}
214+
215+
state.line = next + 1;
216+
217+
const token = state.push('math_block', 'math', 0);
218+
token.block = true;
219+
token.content =
220+
(firstLine && firstLine.trim() ? firstLine + '\n' : '') +
221+
state.getLines(start + 1, next, state.tShift[start], true) +
222+
(lastLine && lastLine.trim() ? lastLine : '');
223+
token.map = [start, state.line];
224+
token.markup = '$$';
225+
return true;
226+
}

src/settings.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { App, PluginSettingTab, Setting } from 'obsidian';
22
import WordpressPlugin from './main';
33
import { CommentStatus, PostStatus } from './wp-api';
44
import { TranslateKey } from './i18n';
5-
import { buildMarked } from './utils';
65
import { WpProfileManageModal } from './wp-profile-manage-modal';
76
import { MathJaxOutputType } from './plugin-settings';
87
import { WpProfile } from './wp-profile';
8+
import { setupMarkdownParser } from './utils';
99

1010

1111
export class WordpressSettingTab extends PluginSettingTab {
@@ -140,7 +140,7 @@ export class WordpressSettingTab extends PluginSettingTab {
140140
await this.plugin.saveSettings();
141141
this.display();
142142

143-
buildMarked(this.plugin.settings);
143+
setupMarkdownParser(this.plugin.settings);
144144
});
145145
});
146146
containerEl.createEl('p', {

0 commit comments

Comments
 (0)