Skip to content

Commit 7465950

Browse files
committed
add settings
1 parent d65debb commit 7465950

File tree

12 files changed

+330
-27
lines changed

12 files changed

+330
-27
lines changed

exampleVault/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,10 @@ echo "Hello"
9191
+ this line will be marked as inserted
9292
- this line will be marked as deleted
9393
this is a regular line
94+
```
95+
96+
```mermaid
97+
pie title NETFLIX
98+
"Time spent looking for movie" : 90
99+
"Time spent watching it" : 10
94100
```

src/LoadedLanguage.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { type BundledLanguage, type SpecialLanguage } from 'shiki';
2+
3+
export class LoadedLanguage {
4+
alias: string;
5+
defaultLanguage: BundledLanguage | SpecialLanguage | undefined;
6+
languages: (BundledLanguage | SpecialLanguage)[];
7+
8+
constructor(alias: string) {
9+
this.alias = alias;
10+
this.defaultLanguage = undefined;
11+
this.languages = [];
12+
}
13+
14+
addLanguage(language: BundledLanguage | SpecialLanguage): void {
15+
if (!this.languages.includes(language)) {
16+
this.languages.push(language);
17+
}
18+
}
19+
20+
setDefaultLanguage(language: BundledLanguage | SpecialLanguage): void {
21+
if (!this.languages.includes(language)) {
22+
throw new Error(`Language ${language} is not included in the loaded languages for ${this.alias}`);
23+
}
24+
25+
this.defaultLanguage = language;
26+
}
27+
28+
getDefaultLanguage(): BundledLanguage | SpecialLanguage {
29+
if (this.defaultLanguage === undefined) {
30+
throw new Error(`No default language set for ${this.alias}`);
31+
}
32+
33+
return this.defaultLanguage;
34+
}
35+
}

src/PrismPlugin.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/* eslint-disable */
2+
3+
/*
4+
* Taken from https://github.com/PrismJS/prism/blob/master/plugins/filter-highlight-all/prism-filter-highlight-all.js
5+
*/
6+
7+
export function filterHighlightAllPlugin(Prism: any): void {
8+
if (typeof Prism === 'undefined' || typeof document === 'undefined') {
9+
return;
10+
}
11+
12+
const script = Prism.util.currentScript();
13+
14+
/**
15+
* @type {Array<(element: HTMLElement) => boolean>}
16+
*/
17+
const filters: any[] = [];
18+
19+
const config: any = (Prism.plugins.filterHighlightAll = {
20+
/**
21+
* Adds a new filter for the elements of `highlightAll` and `highlightAllUnder` such that only elements for
22+
* which the given function returns `true` will be highlighted.
23+
*
24+
* @param {(value: { element: HTMLElement, language: string }) => boolean} condition
25+
*/
26+
add: function (condition: any): void {
27+
filters.push(function (element: any) {
28+
return condition({
29+
element: element,
30+
language: Prism.util.getLanguage(element),
31+
});
32+
});
33+
},
34+
35+
/**
36+
* Adds a new filter for the elements of `highlightAll` and `highlightAllUnder` such that only elements that
37+
* match the given CSS selection will be highlighted.
38+
*
39+
* @param {string} selector
40+
*/
41+
addSelector: function (selector: any): void {
42+
filters.push(function (element: any) {
43+
return element.matches(selector);
44+
});
45+
},
46+
47+
reject: {
48+
/**
49+
* Adds a new filter for the elements of `highlightAll` and `highlightAllUnder` such that only elements for
50+
* which the given function returns `false` will be highlighted.
51+
*
52+
* @param {(value: { element: HTMLElement, language: string }) => boolean} condition
53+
*/
54+
add: function (condition: any): void {
55+
filters.push(function (element: any) {
56+
return !condition({
57+
element: element,
58+
language: Prism.util.getLanguage(element),
59+
});
60+
});
61+
},
62+
63+
/**
64+
* Adds a new filter for the elements of `highlightAll` and `highlightAllUnder` such that only elements that do
65+
* not match the given CSS selection will be highlighted.
66+
*
67+
* @param {string} selector
68+
*/
69+
addSelector: function (selector: any): void {
70+
filters.push(function (element: any) {
71+
return !element.matches(selector);
72+
});
73+
},
74+
},
75+
76+
/**
77+
* Filters the elements of `highlightAll` and `highlightAllUnder` such that only elements with a known language
78+
* will be highlighted. All elements with an unset or unknown language will be ignored.
79+
*
80+
* __Note:__ This will effectively disable the AutoLoader plugin.
81+
*
82+
* @type {boolean}
83+
*/
84+
filterKnown: !!script && script.hasAttribute('data-filter-known'),
85+
});
86+
87+
config.add(function filterKnown(env: any) {
88+
return !config.filterKnown || typeof Prism.languages[env.language] === 'object';
89+
});
90+
91+
if (script) {
92+
let attr;
93+
attr = script.getAttribute('data-filter-selector');
94+
if (attr) {
95+
config.addSelector(attr);
96+
}
97+
attr = script.getAttribute('data-reject-selector');
98+
if (attr) {
99+
config.reject.addSelector(attr);
100+
}
101+
}
102+
103+
/**
104+
* Applies all filters to the given element and returns true if and only if every filter returned true on the
105+
* given element.
106+
*
107+
* @param {HTMLElement} element
108+
* @returns {boolean}
109+
*/
110+
function combinedFilter(element: any): boolean {
111+
for (let i: number = 0, l = filters.length; i < l; i++) {
112+
if (!filters[i](element)) {
113+
return false;
114+
}
115+
}
116+
return true;
117+
}
118+
119+
Prism.hooks.add('before-all-elements-highlight', function (env: any) {
120+
env.elements = env.elements.filter(combinedFilter);
121+
});
122+
}
File renamed without changes.

src/Cm6_ViewPlugin.ts renamed to src/codemirror/Cm6_ViewPlugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Decoration, type DecorationSet, type EditorView, ViewPlugin, type ViewU
33
import { type Range } from '@codemirror/state';
44
import { type SyntaxNode } from '@lezer/common';
55
import { syntaxTree } from '@codemirror/language';
6-
import { Cm6_Util } from './Cm6_Util';
6+
import { Cm6_Util } from 'src/codemirror/Cm6_Util';
77
import { type ThemedToken } from 'shiki';
88

99
// eslint-disable-next-line @typescript-eslint/no-explicit-any

src/main.ts

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
1-
import { Plugin, TFile } from 'obsidian';
2-
import { type BundledLanguage, bundledLanguages, getHighlighter, type Highlighter, type TokensResult } from 'shiki';
1+
import { loadPrism, Plugin, TFile } from 'obsidian';
2+
import { bundledLanguages, getHighlighter, type Highlighter, type TokensResult } from 'shiki';
33
import { ExpressiveCodeEngine, ExpressiveCodeTheme } from '@expressive-code/core';
44
import { pluginShiki } from '@expressive-code/plugin-shiki';
55
import { pluginTextMarkers } from '@expressive-code/plugin-text-markers';
66
import { pluginCollapsibleSections } from '@expressive-code/plugin-collapsible-sections';
77
import { pluginLineNumbers } from '@expressive-code/plugin-line-numbers';
88
import { pluginFrames } from '@expressive-code/plugin-frames';
9-
import { ThemeMapper } from 'src/ThemeMapper';
10-
import { EC_THEME } from 'src/ECTheme';
9+
import { ThemeMapper } from 'src/themes/ThemeMapper';
10+
import { EC_THEME } from 'src/themes/ECTheme';
1111
import { CodeBlock } from 'src/CodeBlock';
12-
import { OBSIDIAN_THEME } from 'src/ObsidianTheme';
13-
import { createCm6Plugin } from 'src/Cm6_ViewPlugin';
12+
import { OBSIDIAN_THEME } from 'src/themes/ObsidianTheme';
13+
import { createCm6Plugin } from 'src/codemirror/Cm6_ViewPlugin';
14+
import { DEFAULT_SETTINGS, type Settings } from 'src/settings/Settings';
15+
import { ShikiSettingsTab } from 'src/settings/SettingsTab';
16+
import { filterHighlightAllPlugin } from 'src/PrismPlugin';
17+
import { LoadedLanguage } from 'src/LoadedLanguage';
1418

1519
// some languages break obsidian's `registerMarkdownCodeBlockProcessor`, so we blacklist them
16-
const languageNameBlacklist = new Set(['c++', 'c#', 'f#']);
20+
const languageNameBlacklist = new Set(['c++', 'c#', 'f#', 'mermaid']);
1721

1822
export default class ShikiPlugin extends Plugin {
1923
// @ts-expect-error TS2564
@@ -25,23 +29,25 @@ export default class ShikiPlugin extends Plugin {
2529
// @ts-expect-error TS2564
2630
activeCodeBlocks: Map<string, CodeBlock[]>;
2731
// @ts-expect-error TS2564
28-
loadedLanguages: Map<string, string>;
32+
loadedLanguages: Map<string, LoadedLanguage>;
2933
// @ts-expect-error TS2564
3034
shiki: Highlighter;
35+
// @ts-expect-error TS2564
36+
settings: Settings;
3137

3238
async onload(): Promise<void> {
39+
await this.loadSettings();
40+
41+
this.addSettingTab(new ShikiSettingsTab(this));
42+
3343
this.themeMapper = new ThemeMapper();
3444
this.activeCodeBlocks = new Map();
3545
this.loadedLanguages = new Map();
3646

3747
await this.loadLanguages();
3848

39-
this.shiki = await getHighlighter({
40-
themes: [OBSIDIAN_THEME],
41-
langs: Object.keys(bundledLanguages),
42-
});
43-
4449
await this.loadEC();
50+
await this.loadShiki();
4551

4652
this.registerCodeBlockProcessors();
4753

@@ -69,17 +75,45 @@ export default class ShikiPlugin extends Plugin {
6975
for (const [shikiLanguage, registration] of Object.entries(bundledLanguages)) {
7076
// the last element of the array is seemingly the most recent version of the language
7177
const language = (await registration()).default.at(-1);
78+
const shikiLanguageName = shikiLanguage as keyof typeof bundledLanguages;
7279

7380
if (language === undefined) {
7481
continue;
7582
}
7683

7784
for (const alias of [language.name, ...(language.aliases ?? [])]) {
78-
if (!this.loadedLanguages.has(alias) && !languageNameBlacklist.has(alias)) {
79-
this.loadedLanguages.set(alias, shikiLanguage);
85+
if (languageNameBlacklist.has(alias)) {
86+
continue;
87+
}
88+
89+
if (!this.loadedLanguages.has(alias)) {
90+
const newLanguage = new LoadedLanguage(alias);
91+
newLanguage.addLanguage(shikiLanguageName);
92+
93+
this.loadedLanguages.set(alias, newLanguage);
94+
}
95+
96+
this.loadedLanguages.get(alias)!.addLanguage(shikiLanguageName);
97+
}
98+
}
99+
100+
for (const [alias, language] of this.loadedLanguages) {
101+
if (language.languages.length === 1) {
102+
language.setDefaultLanguage(language.languages[0]);
103+
} else {
104+
const defaultLanguage = language.languages.find(lang => lang === alias);
105+
if (defaultLanguage !== undefined) {
106+
language.setDefaultLanguage(defaultLanguage);
107+
} else {
108+
console.warn(`No default language found for ${alias}, using the first language in the list`);
109+
language.setDefaultLanguage(language.languages[0]);
80110
}
81111
}
82112
}
113+
114+
for (const disabledLanguage of this.settings.disabledLanguages) {
115+
this.loadedLanguages.delete(disabledLanguage);
116+
}
83117
}
84118

85119
async loadEC(): Promise<void> {
@@ -113,17 +147,38 @@ export default class ShikiPlugin extends Plugin {
113147
}
114148
}
115149

150+
async loadShiki(): Promise<void> {
151+
this.shiki = await getHighlighter({
152+
themes: [OBSIDIAN_THEME],
153+
langs: Object.keys(bundledLanguages),
154+
});
155+
}
156+
157+
async registerPrismPlugin(): Promise<void> {
158+
/* eslint-disable */
159+
160+
await loadPrism();
161+
162+
const prism = await loadPrism();
163+
filterHighlightAllPlugin(prism);
164+
prism.plugins.filterHighlightAll.reject.addSelector('div.expressive-code pre code');
165+
}
166+
116167
registerCodeBlockProcessors(): void {
117168
for (const [alias, language] of this.loadedLanguages) {
118-
this.registerMarkdownCodeBlockProcessor(
119-
alias,
120-
async (source, el, ctx) => {
121-
const codeBlock = new CodeBlock(this, el, source, language, alias, ctx);
122-
123-
ctx.addChild(codeBlock);
124-
},
125-
-1,
126-
);
169+
try {
170+
this.registerMarkdownCodeBlockProcessor(
171+
alias,
172+
async (source, el, ctx) => {
173+
const codeBlock = new CodeBlock(this, el, source, language.getDefaultLanguage(), alias, ctx);
174+
175+
ctx.addChild(codeBlock);
176+
},
177+
-1,
178+
);
179+
} catch (e) {
180+
console.warn(`Failed to register code block processor for ${alias}`, e);
181+
}
127182
}
128183
}
129184

@@ -167,8 +222,16 @@ export default class ShikiPlugin extends Plugin {
167222
}
168223

169224
return this.shiki.codeToTokens(code, {
170-
lang: shikiLanguage as BundledLanguage,
225+
lang: shikiLanguage.getDefaultLanguage(),
171226
theme: 'obsidian-theme',
172227
});
173228
}
229+
230+
async loadSettings(): Promise<void> {
231+
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()) as Settings;
232+
}
233+
234+
async saveSettings(): Promise<void> {
235+
await this.saveData(this.settings);
236+
}
174237
}

src/settings/Settings.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface Settings {
2+
disabledLanguages: string[];
3+
}
4+
5+
export const DEFAULT_SETTINGS: Settings = {
6+
disabledLanguages: [],
7+
};

0 commit comments

Comments
 (0)