Skip to content

Commit 485378a

Browse files
committed
syntax highlighting
1 parent d921708 commit 485378a

23 files changed

+573
-87
lines changed

bun.lockb

54 Bytes
Binary file not shown.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
text: asd
3+
locked: false
4+
---
5+
6+
7+
Locked: `INPUT[toggle:locked]`
8+
```js-engine
9+
const mb = engine.getPlugin('obsidian-meta-bind-plugin').api;
10+
11+
const signal = mb.createSignal(undefined)
12+
component.register(mb.listenToMetadata(signal, context.file.path, ['locked']))
13+
14+
function render() {
15+
container.empty();
16+
if (signal.get()) {
17+
mb.createViewFieldFromString("VIEW[{text}][text]", "INLINE", context.file.path, container, component);
18+
} else {
19+
mb.createInputFieldFromString("INPUT[text:text]", "INLINE", context.file.path, container, component);
20+
}
21+
22+
}
23+
24+
const reactive = engine.reactive(render)
25+
signal.registerListener({
26+
callback: () => reactive.refresh(),
27+
})
28+
29+
return reactive;
30+
```
31+

exampleVault/Button Example.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,4 @@ actions:
150150
- type: command
151151
command: obsidian-meta-bind-plugin:open-help
152152
153-
```
153+
```

exampleVault/Examples.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
slider1: 7
2+
slider1: 3
33
suggest: test
44
toggle1: false
55
Domestic_tasks:
@@ -32,9 +32,9 @@ Input and view fields work in the top level and in
3232
3333
Input fields work with nested front-matter.
3434
35-
```meta-bind
36-
INPUT[text(showcase):nested["object"]]
37-
```
35+
`INPUT[text(showcase):nested["object"]]`
36+
37+
`INPUT[inlineSelect(option(0, 'don\'t do this'), option(1, 'do this \\'), showcase):inlineSelect]`
3838
3939
## String Escaping
4040
@@ -90,15 +90,15 @@ option(option d)
9090

9191
Inline input fields should not cause line breaks.
9292

93-
Lorem ipsum dolor sit amet, `INPUT[date():other note#date]` consectetur adipiscing elit. Pellentesque sit amet porttitor arcu. Quisque scelerisque dolor augue, et posuere nulla bibendum nec. `INPUT[date():other note#date]` Curabitur sed rhoncus nisl. Maecenas nisi justo, viverra vel tempus vel, hendrerit at metus. `INPUT[datePicker():other note#date]` asdasd asdasdasd
93+
Lorem ipsum dolor sit amet, `INPUT[date():other note#date]` consectetur adipiscing elit. Pellentesque sit amet porttitor arcu. Quisque scelerisque dolor augue, et posuere nulla bibendum nec. `INPUT[date():other note#date]` Curabitur sed rhoncus nisl. Maecenas nisi justo, viverra vel tempus vel, hendrerit at metus. `INPUT[datePicker():other note#date]` asdasd asdasdasd
9494

9595
## Templates
9696

9797
For input fields, templates can be set in the plugin settings
9898

9999
- `INPUT[][toggle:toggle1]` empty template
100100
- `INPUT[nonExistantTemplate][toggle:toggle1]` unknown template
101-
- `INPUT[toggleTemplate][]`
101+
- `INPUT[toggleTemplate][]`
102102

103103
## Error Messages
104104

@@ -110,7 +110,7 @@ There are a lot of different error messages and they are clickable
110110
- `INPUT[inlineSelect(option(option a),option(option b),option(option c),option(option d):select]`
111111
- `INPUT[inlineSelect(option(option a),option(option b),option(option c),option(option d)):select#]`
112112
- `INPUT[inlineSelect(option(option a),option(option b),option(option c),option(option d)):select#:]`
113-
- `INPUT[toggle:Bible Reading]`
113+
- `INPUT[toggle:Bible Reading]`
114114

115115
Lorem ipsum dolor sit amet, `INPUT[text():meta bind/nonExistantFile#title]` consectetur adipiscing elit. Pellentesque sit amet porttitor arcu. Quisque scelerisque dolor augue, et posuere nulla bibendum nec. `INPUT[slider(nonExistantArgument)]` Curabitur sed rhoncus nisl. Maecenas nisi justo, viverra vel tempus vel, hendrerit at metus. `INPUT[select(option(option a),option(option b),option(option c),option(option d)):select]` asdasd asdasdasd
116116

exampleVault/View Fields/View Field.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ Self Loop Error: `VIEW[**{computed}**][text():computed]`
5454
```meta-bind
5555
INPUT[imageSuggester(optionQuery("Other/Images")):image]
5656
```
57+
58+
5759
`VIEW[!\[\[{image}\]\]][text(renderMarkdown)]`
5860

5961
## Arrays and Objects

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
"devDependencies": {
2727
"@codemirror/lang-javascript": "^6.2.1",
2828
"@codemirror/language": "^6.9.2",
29-
"@codemirror/state": "^6.3.1",
30-
"@codemirror/view": "^6.22.0",
29+
"@codemirror/state": "6.3.2",
30+
"@codemirror/view": "6.22.1",
3131
"@happy-dom/global-registrator": "^12.10.3",
3232
"@tsconfig/svelte": "^5.0.0",
3333
"@types/web": "^0.0.119",
@@ -53,6 +53,7 @@
5353
"typescript": "^5.2.2"
5454
},
5555
"dependencies": {
56+
"@codemirror/legacy-modes": "^6.3.3",
5657
"@lemons_dev/parsinom": "^0.0.12",
5758
"itertools-ts": "^1.27.0",
5859
"mathjs": "^12.0.0",

src/api/API.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { type BindTargetDeclaration, BindTargetStorageType } from '../parsers/Bi
3232
import { ObsidianButtonActionRunner } from '../fields/button/ObsidianButtonActionRunner';
3333
import { ButtonMDRC } from '../renderChildren/ButtonMDRC';
3434
import { InlineButtonMDRC } from '../renderChildren/InlineButtonMDRC';
35+
import { SyntaxHighlightingAPI } from './SyntaxHighlightingAPI';
3536

3637
export class API implements IAPI {
3738
public plugin: MetaBindPlugin;
@@ -47,6 +48,8 @@ export class API implements IAPI {
4748
public readonly buttonActionRunner: ButtonActionRunner;
4849
public readonly buttonManager: ButtonManager;
4950

51+
public readonly syntaxHighlighting: SyntaxHighlightingAPI;
52+
5053
constructor(plugin: MetaBindPlugin) {
5154
this.plugin = plugin;
5255
this.inputField = new InputFieldAPI(this);
@@ -61,6 +64,8 @@ export class API implements IAPI {
6164

6265
this.buttonActionRunner = new ObsidianButtonActionRunner(this.plugin);
6366
this.buttonManager = new ButtonManager();
67+
68+
this.syntaxHighlighting = new SyntaxHighlightingAPI(this.plugin);
6469
}
6570

6671
/**

src/api/SyntaxHighlightingAPI.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { type ParsingRange } from '@lemons_dev/parsinom/lib/HelperTypes';
2+
import { type API } from './API';
3+
import type MetaBindPlugin from '../main';
4+
import { ParsingError, runParser } from '../parsers/ParsingError';
5+
import {
6+
INLINE_BUTTON_DECLARATION_HLP,
7+
INPUT_FIELD_DECLARATION_HLP,
8+
VIEW_FIELD_DECLARATION_HLP,
9+
} from '../parsers/HLPUtils';
10+
import { InlineMDRCType } from '../utils/InlineMDRCUtils';
11+
12+
export class Highlight {
13+
range: ParsingRange;
14+
tokenClass: string;
15+
16+
constructor(range: ParsingRange, tokenClass: string) {
17+
this.range = range;
18+
this.tokenClass = tokenClass;
19+
}
20+
}
21+
22+
export class SyntaxHighlighting {
23+
highlights: Highlight[];
24+
parsingError?: ParsingError;
25+
26+
constructor(highlights: Highlight[], parsingError?: ParsingError) {
27+
this.highlights = highlights;
28+
this.parsingError = parsingError;
29+
}
30+
31+
getHighlights(): Highlight[] {
32+
if (this.parsingError === undefined) {
33+
return this.highlights.filter(x => x.range.from.index !== x.range.to.index);
34+
}
35+
36+
return [
37+
new Highlight(
38+
{
39+
from: this.parsingError.parseFailure.furthest,
40+
to: {
41+
line: this.parsingError.parseFailure.furthest.line,
42+
column: this.parsingError.parseFailure.furthest.column + 1,
43+
index: this.parsingError.parseFailure.furthest.index + 1,
44+
},
45+
},
46+
'error',
47+
),
48+
];
49+
}
50+
}
51+
52+
export class SyntaxHighlightingAPI {
53+
public readonly api: API;
54+
public readonly plugin: MetaBindPlugin;
55+
56+
constructor(plugin: MetaBindPlugin) {
57+
this.plugin = plugin;
58+
this.api = plugin.api;
59+
}
60+
61+
highlightInputFieldDeclaration(str: string): SyntaxHighlighting {
62+
try {
63+
return new SyntaxHighlighting(runParser(INPUT_FIELD_DECLARATION_HLP, str));
64+
} catch (e) {
65+
if (e instanceof ParsingError) {
66+
return new SyntaxHighlighting([], e);
67+
} else {
68+
console.error(e);
69+
return new SyntaxHighlighting([]);
70+
}
71+
}
72+
}
73+
74+
highlightViewFieldDeclaration(str: string): SyntaxHighlighting {
75+
try {
76+
return new SyntaxHighlighting(runParser(VIEW_FIELD_DECLARATION_HLP, str));
77+
} catch (e) {
78+
if (e instanceof ParsingError) {
79+
return new SyntaxHighlighting([], e);
80+
} else {
81+
console.error(e);
82+
return new SyntaxHighlighting([]);
83+
}
84+
}
85+
}
86+
87+
highlightInlineButtonDeclaration(str: string): SyntaxHighlighting {
88+
try {
89+
return new SyntaxHighlighting(runParser(INLINE_BUTTON_DECLARATION_HLP, str));
90+
} catch (e) {
91+
if (e instanceof ParsingError) {
92+
return new SyntaxHighlighting([], e);
93+
} else {
94+
console.error(e);
95+
return new SyntaxHighlighting([]);
96+
}
97+
}
98+
}
99+
100+
highlight(str: string, mdrcType: InlineMDRCType): SyntaxHighlighting {
101+
if (mdrcType === InlineMDRCType.INPUT_FIELD) {
102+
return this.highlightInputFieldDeclaration(str);
103+
} else if (mdrcType === InlineMDRCType.VIEW_FIELD) {
104+
return this.highlightViewFieldDeclaration(str);
105+
} else if (mdrcType === InlineMDRCType.BUTTON) {
106+
return this.highlightInlineButtonDeclaration(str);
107+
}
108+
109+
throw new Error(`Unknown MDRCType ${mdrcType}`);
110+
}
111+
}

src/cm6/Cm6_Util.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import { type EditorSelection, type EditorState } from '@codemirror/state';
22
import { editorInfoField, type TFile } from 'obsidian';
33
import { type DecorationSet, type EditorView } from '@codemirror/view';
44

5+
export enum MB_WidgetType {
6+
FIELD = 'field',
7+
HIGHLIGHT = 'highlight',
8+
}
9+
510
export class Cm6_Util {
611
/**
712
* Checks if a selection overlaps with a given range.
@@ -24,6 +29,18 @@ export class Cm6_Util {
2429
return false;
2530
}
2631

32+
/**
33+
* Checks if two ranges overlap.
34+
*
35+
* @param fromA
36+
* @param toA
37+
* @param fromB
38+
* @param toB
39+
*/
40+
static checkRangeOverlap(fromA: number, toA: number, fromB: number, toB: number): boolean {
41+
return fromA <= toB && fromB <= toA;
42+
}
43+
2744
/**
2845
* Gets the editor content of a given range.
2946
*
@@ -59,4 +76,20 @@ export class Cm6_Util {
5976
});
6077
return exists;
6178
}
79+
80+
static existsDecorationOfTypeBetween(
81+
decorations: DecorationSet,
82+
widgetType: MB_WidgetType,
83+
from: number,
84+
to: number,
85+
): boolean {
86+
let exists = false;
87+
decorations.between(from, to, (_from, _to, decoration) => {
88+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
89+
if (decoration.spec.mb_widgetType === widgetType) {
90+
exists = true;
91+
}
92+
});
93+
return exists;
94+
}
6295
}

0 commit comments

Comments
 (0)