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 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
+ [![NPM version](https://img.shields.io/npm/v/prosemirror-highlight?color=a1b858&label=)](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
+ };
@@ -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 };
@@ -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
+ };
@@ -0,0 +1,8 @@
1
+ import { Highlighter } from 'shiki';
2
+ import { P as Parser } from './types-wUmJTPF3.js';
3
+ import 'prosemirror-model';
4
+ import 'prosemirror-view';
5
+
6
+ declare function createParser(highlighter: Highlighter): Parser;
7
+
8
+ export { createParser };