Skip to content

Commit 9b3fb47

Browse files
committed
breaking: Re-implement using highlight.js.
1 parent 2c94b6c commit 9b3fb47

File tree

7 files changed

+122
-61
lines changed

7 files changed

+122
-61
lines changed

bundle.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
window.__patternslib_import_styles = true;
12
import registry from "@patternslib/patternslib/src/core/registry";
23
import "./src/code-editor";
34

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"main": "./src/code-editor.js",
77
"dependencies": {
88
"codejar": "^3.6.0",
9-
"prismjs": "^1.28.0"
9+
"highlight.js": "<11"
1010
},
1111
"devDependencies": {
1212
"@patternslib/dev": "^3.1.6",

src/_code-editor.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.code-editor {
2+
background: #fff;
3+
border-radius: 0.3em;
4+
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12),
5+
0 3px 1px -2px rgba(0, 0, 0, 0.2);
6+
font-family: "Source Code Pro", monospace;
7+
font-size: 1em;
8+
font-weight: 400;
9+
min-height: 4em;
10+
letter-spacing: normal;
11+
line-height: 1.3em;
12+
padding: 1em;
13+
tab-size: 4;
14+
}

src/code-editor.js

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ import { BasePattern } from "@patternslib/patternslib/src/core/basepattern";
22
import Parser from "@patternslib/patternslib/src/core/parser";
33
import events from "@patternslib/patternslib/src/core/events";
44
import registry from "@patternslib/patternslib/src/core/registry";
5-
import utils from "@patternslib/patternslib/src/core/utils";
65

76
export const parser = new Parser("code-editor");
87
parser.addArgument("language", null); // programming language to use.
98
parser.addArgument("linenumbers", false);
10-
parser.addArgument("theme", null); // theme to use.
9+
parser.addArgument("theme", "dark"); // dark or light or any other theme variant.
1110

1211
// CodeJar options
1312
parser.addArgument("tab", " "); // The tab-string. "\t" for real tabs.
@@ -18,58 +17,97 @@ parser.addArgument("preserve-ident", true);
1817
parser.addArgument("add-closing", true);
1918
parser.addArgument("history", true);
2019

20+
const language_mapping = {
21+
atom: "xml",
22+
html: "xml",
23+
plist: "xml",
24+
rss: "xml",
25+
svg: "xml",
26+
wsf: "xml",
27+
xhtml: "xml",
28+
xjb: "xml",
29+
xsd: "xml",
30+
xsl: "xml",
31+
};
32+
2133
class Pattern extends BasePattern {
2234
static name = "code-editor";
2335
static trigger = ".pat-code-editor";
2436
static parser = parser;
2537

2638
async init() {
2739
const CodeJar = (await import("codejar")).CodeJar;
28-
const Prism = (await import("prismjs")).default;
40+
const hljs = (await import("highlight.js/lib/core")).default;
41+
let el = this.el;
42+
43+
// Set and load the theme.
44+
let theme;
45+
if (this.options.theme === "light") {
46+
theme = "stackoverflow-light";
47+
} else if (this.options.theme === "dark") {
48+
theme = "stackoverflow-dark";
49+
} else {
50+
theme = this.options.theme;
51+
}
52+
import(`highlight.js/styles/${theme}.css`);
53+
if (window.__patternslib_import_styles) {
54+
import("./_code-editor.scss");
55+
}
56+
57+
// Custom hightlighting function, as of:
58+
// https://github.com/antonmedv/codejar/blob/master/index.html
59+
const highlight = (editor) => {
60+
// highlight.js does not trim old tags,
61+
// let's do it by this hack.
62+
editor.textContent = editor.textContent;
63+
hljs.highlightElement(editor);
64+
};
65+
let highlight_wrapper = highlight;
2966

30-
let prism_wrapper = Prism.highlightElement;
67+
// Optional line numbers.
3168
if (this.options.linenumbers) {
3269
const linenumbers = (await import("codejar/linenumbers")).withLineNumbers;
33-
prism_wrapper = linenumbers(Prism.highlightElement);
70+
highlight_wrapper = linenumbers(highlight, {
71+
class: `codejar-linenumbers ${theme}`,
72+
});
3473
}
3574

36-
let el = this.el;
75+
// Import and register the language.
76+
let language = [...el.classList].filter((it) => it.startsWith("language-"))[0];
77+
language = language ? language.replace("language-", "") : this.options.language;
78+
language = language || "plaintext";
79+
const hljs_language = (
80+
await import(
81+
`highlight.js/lib/languages/${language_mapping[language] || language}`
82+
)
83+
).default;
84+
hljs.registerLanguage(language, hljs_language);
85+
86+
// Create a editor element in case of an input element.
3787
if (["textarea", "input"].includes(this.el.nodeName.toLowerCase())) {
38-
const unescaped_html = utils.unescape_html(this.el.value);
39-
const language_class = this.options.language
40-
? `language-${this.options.language}`
41-
: "";
4288
const pre_el = document.createElement("pre");
43-
el = document.createElement("code");
44-
el.setAttribute("class", language_class);
45-
el.setAttribute("contenteditable", "");
46-
el.style.display = "block";
47-
el.textContent = unescaped_html;
48-
pre_el.append(el);
89+
pre_el.innerHTML = `<code contenteditable></code>`;
90+
el = pre_el.querySelector("code");
91+
el.textContent = this.el.value; // value is already unescaped.
4992
this.el.parentNode.insertBefore(pre_el, this.el);
5093
this.el.setAttribute("hidden", "");
5194
}
5295

53-
if (this.options.language) {
54-
el.classList.add(`language-${this.options.language}`);
55-
}
56-
57-
el.classList.add(`theme-${this.options.theme || "default"}`);
58-
59-
import(
60-
`prismjs/themes/prism${
61-
this.options.theme ? "-" + this.options.theme : ""
62-
}.css`
96+
// Add some classes to the editor element.
97+
el.classList.add(
98+
"code-editor",
99+
`theme-${this.options.theme}`,
100+
`language-${language}`
63101
);
64-
//import("./code-editor.scss");
65102

66-
// Allow spaces withing quotes to be added as tab argument.
103+
// Allow spaces within quotes to be added as tab argument.
67104
// Replace escaped tab character with real tab.
68105
const tab = this.options.tab
69106
.replace(/'/g, "") // Allow spaces within quotes
70107
.replace(/"/g, "")
71108
.replace(/\\t/g, "\t"); // Replace double escaped tab character
72109

110+
// Create the editor.
73111
const config = {
74112
tab: tab,
75113
indentOn: new RegExp(this.options.indentOn),
@@ -79,14 +117,14 @@ class Pattern extends BasePattern {
79117
addClosing: this.options.addClosing,
80118
history: this.options.history,
81119
};
120+
this.codeeditor = CodeJar(el, highlight_wrapper, config);
82121

83-
this.codeeditor = CodeJar(el, prism_wrapper, config);
84-
122+
// Update the input element.
85123
if (el !== this.el) {
86124
// Update <textarea> or <input>, if one of these were the
87125
// initializing elements.
88126
this.codeeditor.onUpdate((code) => {
89-
this.el.value = code;
127+
this.el.value = code.trim();
90128
this.el.dispatchEvent(events.input_event());
91129
});
92130
}

src/code-editor.scss

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/code-editor.test.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,19 @@ describe("pat-code-editor", () => {
77
document.body.innerHTML = "";
88
});
99

10-
it("is initialized correctly", async () => {
10+
it("1 - is initialized correctly", async () => {
1111
document.body.innerHTML = `
1212
<textarea
13-
class="pat-code-editor"
14-
data-pat-code-editor="language: javascript"/>`;
13+
class="pat-code-editor language-javascript"></textarea>`;
1514

1615
const instance = new Pattern(document.querySelector(".pat-code-editor"));
17-
events.await_pattern_init(instance);
16+
await events.await_pattern_init(instance);
1817

1918
expect(document.querySelectorAll("pre code").length).toBe(1);
2019
expect(document.querySelector("textarea").getAttribute("hidden")).toBe("");
2120
});
2221

23-
it("handles escaped content correctly", async () => {
22+
it("2 - handles escaped content correctly", async () => {
2423
const unescaped = `<hello attribute="value">this & that</hello>`;
2524
const escaped = `&lt;hello attribute=&quot;value&quot;&gt;this &amp; that&lt;/hello&gt;`;
2625

@@ -33,24 +32,29 @@ describe("pat-code-editor", () => {
3332
`;
3433

3534
const instance = new Pattern(document.querySelector(".pat-code-editor"));
36-
events.await_pattern_init(instance);
35+
await events.await_pattern_init(instance);
3736

37+
const el = document.querySelector("textarea");
3838
const editor_el = document.querySelector("pre code");
39+
3940
expect(editor_el.textContent.trim()).toBe(unescaped);
4041

41-
expect(editor_el.querySelectorAll(".token").length).toBeGreaterThan(0);
42+
editor_el.dispatchEvent(new Event("keyup")); // codejar listens on keyup.
43+
expect(el.value).toBe(unescaped);
44+
45+
expect(editor_el.querySelectorAll(".hljs-string").length).toBeGreaterThan(0);
4246
expect(editor_el.innerHTML.includes("&lt;")).toBe(true);
4347
});
4448

45-
it("Emits input events on update.", async () => {
49+
it("3 - Emits input events on update.", async () => {
4650
document.body.innerHTML = `
4751
<textarea class="pat-code-editor"></textarea>
4852
`;
4953

5054
const el = document.querySelector(".pat-code-editor");
5155

5256
const instance = new Pattern(el);
53-
events.await_pattern_init(instance);
57+
await events.await_pattern_init(instance);
5458

5559
const editor_el = document.querySelector("pre code");
5660

src/index.html

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,26 @@
66
<script src="/bundle.min.js"></script>
77
</head>
88
<body>
9+
<textarea name="" id="aha" autocomplete="off">
10+
11+
&lt;textarea>
12+
&lt;html>
13+
&lt;head>
14+
&lt;title>My page&lt;/title>
15+
&lt;/head>
16+
&lt;body>
17+
&lt;h1>My page&lt;/h1>
18+
&lt;p>My page is awesome!&lt;/p>
19+
&lt;/body>
20+
&lt;/html>
21+
&lt;/textarea>
22+
23+
</textarea>
24+
<div id="ahb" contenteditable></div>
925

1026
<h2>HTML example with textarea</h2>
1127
<textarea class="pat-code-editor language-html"
1228
autocomplete="off"
13-
data-pat-code-editor="theme: dark"
1429
>
1530
&lt;textarea>
1631
&lt;html>
@@ -28,7 +43,7 @@ <h2>HTML example with textarea</h2>
2843
<h2>JavaScript example with textarea</h2>
2944
<textarea class="pat-code-editor"
3045
autocomplete="off"
31-
data-pat-code-editor="language: javascript; theme: okaidia"
46+
data-pat-code-editor="language: javascript"
3247
>
3348
import pattern from "./src/code-editor";
3449

@@ -40,22 +55,20 @@ <h2>JavaScript example with textarea</h2>
4055
</textarea>
4156

4257
<h2>Python example with content editable</h2>
43-
<pre><code class="pat-code-editor language-python"
58+
<code class="pat-code-editor language-python"
4459
autocomplete="off"
4560
contenteditable
46-
>
47-
import this;
61+
>import this;
4862

4963
def fun(val):
5064
print(val*3)
5165

52-
fun("helo, ")
53-
</code></pre>
66+
fun("helo, ")</code>
5467

5568
<h2>CSS example with textarea</h2>
56-
<textarea class="pat-code-editor"
69+
<textarea class="pat-code-editor language-css"
5770
autocomplete="off"
58-
data-pat-code-editor="language: css; tab: ' '; theme: okaidia"
71+
data-pat-code-editor="tab: ' '"
5972
>
6073
body {
6174
background-color: red;
@@ -64,9 +77,9 @@ <h2>CSS example with textarea</h2>
6477
</textarea>
6578

6679
<h2>XML example with textarea</h2>
67-
<textarea class="pat-code-editor"
80+
<textarea class="pat-code-editor language-xml"
6881
autocomplete="off"
69-
data-pat-code-editor="language: xml; tab: ' '; theme: okaidia"
82+
data-pat-code-editor="tab: ' '"
7083
>
7184
&lt;some level="1"&gt;
7285
&lt;xml level="2"&gt;

0 commit comments

Comments
 (0)