prosemirror-highlight 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/chunk-YSQDPG26.js +30 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +157 -0
- package/dist/lowlight.d.ts +12 -0
- package/dist/lowlight.js +56 -0
- package/dist/shiki.d.ts +8 -0
- package/dist/shiki.js +2674 -0
- package/dist/types-wUmJTPF3.d.ts +20 -0
- package/package.json +110 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 ocavue
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# prosemirror-highlight
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/prosemirror-highlight)
|
|
4
|
+
|
|
5
|
+
Highlight your code blocks in ProseMirror, with any syntax highlighter you want.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
### With [Shiki]
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { getHighlighter, setCDN } from 'shiki'
|
|
13
|
+
|
|
14
|
+
import { createHighlightPlugin } from 'prosemirror-highlight'
|
|
15
|
+
import { createParser } from 'prosemirror-highlight/shiki'
|
|
16
|
+
|
|
17
|
+
setCDN('https://unpkg.com/shiki@0.14.6/')
|
|
18
|
+
|
|
19
|
+
const highlighter = await getHighlighter({
|
|
20
|
+
theme: 'github-light',
|
|
21
|
+
langs: ['javascript', 'typescript', 'python'],
|
|
22
|
+
})
|
|
23
|
+
const parser = createParser(highlighter)
|
|
24
|
+
export const shikiPlugin = createHighlightPlugin({ parser })
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### With [lowlight] (based on [Highlight.js])
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import 'highlight.js/styles/default.css'
|
|
31
|
+
|
|
32
|
+
import { common, createLowlight } from 'lowlight'
|
|
33
|
+
|
|
34
|
+
import { createHighlightPlugin } from 'prosemirror-highlight'
|
|
35
|
+
import { createParser } from 'prosemirror-highlight/lowlight'
|
|
36
|
+
|
|
37
|
+
const lowlight = createLowlight(common)
|
|
38
|
+
const parser = createParser(lowlight)
|
|
39
|
+
export const lowlightPlugin = createHighlightPlugin({ parser })
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Credits
|
|
43
|
+
|
|
44
|
+
- [prosemirror-highlightjs] - Highlight.js syntax highlighting for ProseMirror
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT
|
|
49
|
+
|
|
50
|
+
[prosemirror-highlightjs]: https://github.com/b-kelly/prosemirror-highlightjs
|
|
51
|
+
[lowlight]: https://github.com/wooorm/lowlight
|
|
52
|
+
[Highlight.js]: https://github.com/highlightjs/highlight.js
|
|
53
|
+
[Shiki]: https://github.com/shikijs/shiki
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
8
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
+
mod
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
__commonJS,
|
|
29
|
+
__toESM
|
|
30
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Transaction, Plugin } from 'prosemirror-state';
|
|
2
|
+
import { Decoration, DecorationSet } from 'prosemirror-view';
|
|
3
|
+
import { Node } from 'prosemirror-model';
|
|
4
|
+
import { P as Parser, L as LanguageExtractor } from './types-wUmJTPF3.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Represents a cache of doc positions to the node and decorations at that position
|
|
8
|
+
*/
|
|
9
|
+
declare class DecorationCache {
|
|
10
|
+
private cache;
|
|
11
|
+
constructor(cache?: Map<number, {
|
|
12
|
+
node: Node;
|
|
13
|
+
decorations: Decoration[];
|
|
14
|
+
}>);
|
|
15
|
+
/**
|
|
16
|
+
* Gets the cache entry at the given doc position, or null if it doesn't exist
|
|
17
|
+
* @param pos The doc position of the node you want the cache for
|
|
18
|
+
*/
|
|
19
|
+
get(pos: number): {
|
|
20
|
+
node: Node;
|
|
21
|
+
decorations: Decoration[];
|
|
22
|
+
} | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Sets the cache entry at the given position with the give node/decoration
|
|
25
|
+
* values
|
|
26
|
+
* @param pos The doc position of the node to set the cache for
|
|
27
|
+
* @param node The node to place in cache
|
|
28
|
+
* @param decorations The decorations to place in cache
|
|
29
|
+
*/
|
|
30
|
+
set(pos: number, node: Node, decorations: Decoration[]): void;
|
|
31
|
+
/**
|
|
32
|
+
* Removes the value at the oldPos (if it exists) and sets the new position to
|
|
33
|
+
* the given values
|
|
34
|
+
* @param oldPos The old node position to overwrite
|
|
35
|
+
* @param newPos The new node position to set the cache for
|
|
36
|
+
* @param node The new node to place in cache
|
|
37
|
+
* @param decorations The new decorations to place in cache
|
|
38
|
+
*/
|
|
39
|
+
private replace;
|
|
40
|
+
/**
|
|
41
|
+
* Removes the cache entry at the given position
|
|
42
|
+
* @param pos The doc position to remove from cache
|
|
43
|
+
*/
|
|
44
|
+
private remove;
|
|
45
|
+
/**
|
|
46
|
+
* Invalidates the cache by removing all decoration entries on nodes that have
|
|
47
|
+
* changed, updating the positions of the nodes that haven't and removing all
|
|
48
|
+
* the entries that have been deleted; NOTE: this does not affect the current
|
|
49
|
+
* cache, but returns an entirely new one
|
|
50
|
+
* @param tr A transaction to map the current cache to
|
|
51
|
+
*/
|
|
52
|
+
invalidate(tr: Transaction): DecorationCache;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Describes the current state of the highlightPlugin
|
|
57
|
+
*/
|
|
58
|
+
interface HighlightPluginState {
|
|
59
|
+
cache: DecorationCache;
|
|
60
|
+
decorations: DecorationSet;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Creates a plugin that highlights the contents of all nodes (via Decorations)
|
|
64
|
+
* with a type passed in blockTypes
|
|
65
|
+
*/
|
|
66
|
+
declare function createHighlightPlugin({ parser, nodeTypes, languageExtractor, }: {
|
|
67
|
+
/**
|
|
68
|
+
* A function that returns an array of decorations for the given node text
|
|
69
|
+
* content, language, and position.
|
|
70
|
+
*/
|
|
71
|
+
parser: Parser;
|
|
72
|
+
/**
|
|
73
|
+
* An array containing all the node type name to target for highlighting.
|
|
74
|
+
*
|
|
75
|
+
* @default ['code_block']
|
|
76
|
+
*/
|
|
77
|
+
nodeTypes?: string[];
|
|
78
|
+
/**
|
|
79
|
+
* A function that returns the language string to use when highlighting that
|
|
80
|
+
* node. By default, it returns `node.attrs.language`.
|
|
81
|
+
*/
|
|
82
|
+
languageExtractor?: LanguageExtractor;
|
|
83
|
+
}): Plugin<HighlightPluginState>;
|
|
84
|
+
|
|
85
|
+
export { DecorationCache, type HighlightPluginState, createHighlightPlugin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import "./chunk-YSQDPG26.js";
|
|
2
|
+
|
|
3
|
+
// src/plugin.ts
|
|
4
|
+
import "prosemirror-model";
|
|
5
|
+
import { Plugin, PluginKey } from "prosemirror-state";
|
|
6
|
+
import { DecorationSet } from "prosemirror-view";
|
|
7
|
+
|
|
8
|
+
// src/cache.ts
|
|
9
|
+
import "prosemirror-model";
|
|
10
|
+
import "prosemirror-state";
|
|
11
|
+
import "prosemirror-view";
|
|
12
|
+
var DecorationCache = class _DecorationCache {
|
|
13
|
+
constructor(cache) {
|
|
14
|
+
this.cache = new Map(cache);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Gets the cache entry at the given doc position, or null if it doesn't exist
|
|
18
|
+
* @param pos The doc position of the node you want the cache for
|
|
19
|
+
*/
|
|
20
|
+
get(pos) {
|
|
21
|
+
return this.cache.get(pos);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sets the cache entry at the given position with the give node/decoration
|
|
25
|
+
* values
|
|
26
|
+
* @param pos The doc position of the node to set the cache for
|
|
27
|
+
* @param node The node to place in cache
|
|
28
|
+
* @param decorations The decorations to place in cache
|
|
29
|
+
*/
|
|
30
|
+
set(pos, node, decorations) {
|
|
31
|
+
if (pos < 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this.cache.set(pos, { node, decorations });
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Removes the value at the oldPos (if it exists) and sets the new position to
|
|
38
|
+
* the given values
|
|
39
|
+
* @param oldPos The old node position to overwrite
|
|
40
|
+
* @param newPos The new node position to set the cache for
|
|
41
|
+
* @param node The new node to place in cache
|
|
42
|
+
* @param decorations The new decorations to place in cache
|
|
43
|
+
*/
|
|
44
|
+
replace(oldPos, newPos, node, decorations) {
|
|
45
|
+
this.remove(oldPos);
|
|
46
|
+
this.set(newPos, node, decorations);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Removes the cache entry at the given position
|
|
50
|
+
* @param pos The doc position to remove from cache
|
|
51
|
+
*/
|
|
52
|
+
remove(pos) {
|
|
53
|
+
this.cache.delete(pos);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Invalidates the cache by removing all decoration entries on nodes that have
|
|
57
|
+
* changed, updating the positions of the nodes that haven't and removing all
|
|
58
|
+
* the entries that have been deleted; NOTE: this does not affect the current
|
|
59
|
+
* cache, but returns an entirely new one
|
|
60
|
+
* @param tr A transaction to map the current cache to
|
|
61
|
+
*/
|
|
62
|
+
invalidate(tr) {
|
|
63
|
+
const returnCache = new _DecorationCache(this.cache);
|
|
64
|
+
const mapping = tr.mapping;
|
|
65
|
+
this.cache.forEach(({ node, decorations }, pos) => {
|
|
66
|
+
if (pos < 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const result = mapping.mapResult(pos);
|
|
70
|
+
const mappedNode = tr.doc.nodeAt(result.pos);
|
|
71
|
+
if (result.deleted || !(mappedNode == null ? void 0 : mappedNode.eq(node))) {
|
|
72
|
+
returnCache.remove(pos);
|
|
73
|
+
} else if (pos !== result.pos) {
|
|
74
|
+
const updatedDecorations = decorations.map((d) => {
|
|
75
|
+
return d.map(mapping, 0, 0);
|
|
76
|
+
}).filter((d) => d != null);
|
|
77
|
+
returnCache.replace(pos, result.pos, mappedNode, updatedDecorations);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return returnCache;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/plugin.ts
|
|
85
|
+
function createHighlightPlugin({
|
|
86
|
+
parser,
|
|
87
|
+
nodeTypes = ["code_block"],
|
|
88
|
+
languageExtractor = (node) => node.attrs.language
|
|
89
|
+
}) {
|
|
90
|
+
const key = new PluginKey();
|
|
91
|
+
return new Plugin({
|
|
92
|
+
key,
|
|
93
|
+
state: {
|
|
94
|
+
init(_, instance) {
|
|
95
|
+
const cache = new DecorationCache();
|
|
96
|
+
const decorations = getDecorationSet(
|
|
97
|
+
instance.doc,
|
|
98
|
+
parser,
|
|
99
|
+
nodeTypes,
|
|
100
|
+
languageExtractor,
|
|
101
|
+
cache
|
|
102
|
+
);
|
|
103
|
+
return { cache, decorations };
|
|
104
|
+
},
|
|
105
|
+
apply(tr, data) {
|
|
106
|
+
const cache = data.cache.invalidate(tr);
|
|
107
|
+
if (!tr.docChanged) {
|
|
108
|
+
const decorations2 = data.decorations.map(tr.mapping, tr.doc);
|
|
109
|
+
return { cache, decorations: decorations2 };
|
|
110
|
+
}
|
|
111
|
+
const decorations = getDecorationSet(
|
|
112
|
+
tr.doc,
|
|
113
|
+
parser,
|
|
114
|
+
nodeTypes,
|
|
115
|
+
languageExtractor,
|
|
116
|
+
cache
|
|
117
|
+
);
|
|
118
|
+
return { cache, decorations };
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
props: {
|
|
122
|
+
decorations(state) {
|
|
123
|
+
var _a;
|
|
124
|
+
return (_a = this.getState(state)) == null ? void 0 : _a.decorations;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function getDecorationSet(doc, parser, nodeTypes, languageExtractor, cache) {
|
|
130
|
+
const result = [];
|
|
131
|
+
doc.descendants((node, pos) => {
|
|
132
|
+
if (!node.type.isTextblock) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
if (nodeTypes.includes(node.type.name)) {
|
|
136
|
+
const language = languageExtractor(node);
|
|
137
|
+
const cached = cache.get(pos);
|
|
138
|
+
if (cached) {
|
|
139
|
+
result.push(...cached.decorations);
|
|
140
|
+
} else {
|
|
141
|
+
const decorations = parser({
|
|
142
|
+
content: node.textContent,
|
|
143
|
+
language: language || void 0,
|
|
144
|
+
pos
|
|
145
|
+
});
|
|
146
|
+
cache.set(pos, node, decorations);
|
|
147
|
+
result.push(...decorations);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
});
|
|
152
|
+
return DecorationSet.create(doc, result);
|
|
153
|
+
}
|
|
154
|
+
export {
|
|
155
|
+
DecorationCache,
|
|
156
|
+
createHighlightPlugin
|
|
157
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Root } from 'hast';
|
|
2
|
+
import { P as Parser } from './types-wUmJTPF3.js';
|
|
3
|
+
import 'prosemirror-model';
|
|
4
|
+
import 'prosemirror-view';
|
|
5
|
+
|
|
6
|
+
type Lowlight = {
|
|
7
|
+
highlight: (language: string, value: string) => Root;
|
|
8
|
+
highlightAuto: (value: string) => Root;
|
|
9
|
+
};
|
|
10
|
+
declare function createParser(lowlight: Lowlight): Parser;
|
|
11
|
+
|
|
12
|
+
export { type Lowlight, createParser };
|
package/dist/lowlight.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import "./chunk-YSQDPG26.js";
|
|
2
|
+
|
|
3
|
+
// src/lowlight.ts
|
|
4
|
+
import { Decoration } from "prosemirror-view";
|
|
5
|
+
function createParser(lowlight) {
|
|
6
|
+
return function highlighter({ content, language, pos }) {
|
|
7
|
+
const root = language ? lowlight.highlight(language, content) : lowlight.highlightAuto(content);
|
|
8
|
+
const decorations = [];
|
|
9
|
+
const from = pos + 1;
|
|
10
|
+
fillFromRoot(decorations, root, from);
|
|
11
|
+
return decorations;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function fillFromRoot(decorations, node, from) {
|
|
15
|
+
for (const child of node.children) {
|
|
16
|
+
from = fillFromRootContent(decorations, child, from);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function fillFromRootContent(decorations, node, from) {
|
|
20
|
+
if (node.type === "element") {
|
|
21
|
+
const to = from + getElementSize(node);
|
|
22
|
+
const { className, ...rest } = node.properties;
|
|
23
|
+
decorations.push(
|
|
24
|
+
Decoration.inline(from, to, {
|
|
25
|
+
class: className ? Array.isArray(className) ? className.join(" ") : String(className) : void 0,
|
|
26
|
+
...rest,
|
|
27
|
+
nodeName: node.tagName
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
return to;
|
|
31
|
+
} else if (node.type === "text") {
|
|
32
|
+
return from + node.value.length;
|
|
33
|
+
} else {
|
|
34
|
+
return from;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function getElementSize(node) {
|
|
38
|
+
let size = 0;
|
|
39
|
+
for (const child of node.children) {
|
|
40
|
+
size += getElementContentSize(child);
|
|
41
|
+
}
|
|
42
|
+
return size;
|
|
43
|
+
}
|
|
44
|
+
function getElementContentSize(node) {
|
|
45
|
+
switch (node.type) {
|
|
46
|
+
case "element":
|
|
47
|
+
return getElementSize(node);
|
|
48
|
+
case "text":
|
|
49
|
+
return node.value.length;
|
|
50
|
+
default:
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
createParser
|
|
56
|
+
};
|
package/dist/shiki.d.ts
ADDED