From e2502ef52c8fbacd8d338871a6a5f14610a27ae8 Mon Sep 17 00:00:00 2001 From: Dan <39170265+chillenberger@users.noreply.github.com> Date: Wed, 3 Jan 2024 15:17:31 -0700 Subject: [PATCH 1/3] use code mirror for syntax highlighting, fix code block spacing --- pgml-dashboard/package-lock.json | 234 ++++++++++++++- pgml-dashboard/package.json | 8 + pgml-dashboard/src/api/cms.rs | 91 ++++-- .../code_block/code_block_controller.js | 128 ++++++++ .../src/components/code_block/mod.rs | 14 + .../src/components/code_block/template.html | 0 pgml-dashboard/src/components/mod.rs | 4 + pgml-dashboard/src/utils/markdown.rs | 273 +++--------------- .../css/scss/components/_admonitions.scss | 3 + .../static/css/scss/components/_code.scss | 4 +- .../static/css/scss/pages/_docs.scss | 28 ++ pgml-dashboard/static/js/copy.js | 9 + .../static/js/utilities/code_mirror_theme.js | 143 +++++++++ 13 files changed, 675 insertions(+), 264 deletions(-) create mode 100644 pgml-dashboard/src/components/code_block/code_block_controller.js create mode 100644 pgml-dashboard/src/components/code_block/mod.rs create mode 100644 pgml-dashboard/src/components/code_block/template.html create mode 100644 pgml-dashboard/static/js/utilities/code_mirror_theme.js diff --git a/pgml-dashboard/package-lock.json b/pgml-dashboard/package-lock.json index 25740517e..40c5a3e84 100644 --- a/pgml-dashboard/package-lock.json +++ b/pgml-dashboard/package-lock.json @@ -5,31 +5,259 @@ "packages": { "": { "dependencies": { + "@codemirror/lang-javascript": "^6.2.1", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/lang-python": "^6.1.3", + "@codemirror/lang-rust": "^6.0.1", + "@codemirror/lang-sql": "^6.5.4", + "@codemirror/state": "^6.2.1", + "@codemirror/view": "^6.21.0", "autosize": "^6.0.1", + "codemirror": "^6.0.1", "dompurify": "^3.0.6", "marked": "^9.1.0" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.11.1.tgz", + "integrity": "sha512-L5UInv8Ffd6BPw0P3EF7JLYAMeEbclY7+6Q11REt8vhih8RuLreKtPy/xk8wPxs4EQgYqzI7cdgpiYwWlbS/ow==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz", + "integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", + "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", + "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-python": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.1.3.tgz", + "integrity": "sha512-S9w2Jl74hFlD5nqtUMIaXAq9t5WlM0acCkyuQWUUSvZclk1sV+UfnpFiZzuZSG+hfEaOmxKR5UxY/Uxswn7EhQ==", + "dependencies": { + "@codemirror/autocomplete": "^6.3.2", + "@codemirror/language": "^6.8.0", + "@lezer/python": "^1.1.4" + } + }, + "node_modules/@codemirror/lang-rust": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-rust/-/lang-rust-6.0.1.tgz", + "integrity": "sha512-344EMWFBzWArHWdZn/NcgkwMvZIWUR1GEBdwG8FEp++6o6vT6KL9V7vGs2ONsKxxFUPXKI0SPcWhyYyl2zPYxQ==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/rust": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-sql": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.5.5.tgz", + "integrity": "sha512-DvOaP2RXLb2xlxJxxydTFfwyYw5YDqEFea6aAfgh9UH0kUD6J1KFZ0xPgPpw1eo/5s2w3L6uh5PVR7GM23GxkQ==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.0.tgz", + "integrity": "sha512-2vaNn9aPGCRFKWcHPFksctzJ8yS5p7YoaT+jHpc0UGKzNuAIx4qy6R5wiqbP+heEEdyaABA582mNqSHzSoYdmg==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", + "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.5.tgz", + "integrity": "sha512-PIEN3Ke1buPod2EHbJsoQwlbpkz30qGZKcnmH1eihq9+bPQx8gelauUwLYaY4vBOuBAuEhmpDLii4rj/uO0yMA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.0.tgz", + "integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==" + }, + "node_modules/@codemirror/view": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.0.tgz", + "integrity": "sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ==", + "dependencies": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@lezer/common": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.0.tgz", + "integrity": "sha512-Wmvlm4q6tRpwiy20TnB3yyLTZim38Tkc50dPY8biQRwqE+ati/wD84rm3N15hikvdT4uSg9phs9ubjvcLmkpKg==" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.11.tgz", + "integrity": "sha512-B5Y9EJF4BWiMgj4ufxUo2hrORnmMBDrMtR+L7dwIO5pocuSAahG6QBwXR6PbKJOjRywJczU2r2LJPg79ER91TQ==", + "dependencies": { + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz", + "integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", + "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/python": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.10.tgz", + "integrity": "sha512-pvSjn+OWivmA/si/SFeGouHO50xoOZcPIFzf8dql0gRvcfCvLDpVIpnnGFFlB7wa0WDscDLo0NmH+4Tx80nBdQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/rust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz", + "integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/autosize": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/autosize/-/autosize-6.0.1.tgz", "integrity": "sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ==" }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "node_modules/dompurify": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" }, "node_modules/marked": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.0.tgz", - "integrity": "sha512-VZjm0PM5DMv7WodqOUps3g6Q7dmxs9YGiFUZ7a2majzQTTCgX+6S6NAJHPvOhgFBzYz8s4QZKWWMfZKFmsfOgA==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", "bin": { "marked": "bin/marked.js" }, "engines": { "node": ">= 16" } + }, + "node_modules/style-mod": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", + "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" } } } diff --git a/pgml-dashboard/package.json b/pgml-dashboard/package.json index 4347d2563..3dfc7d703 100644 --- a/pgml-dashboard/package.json +++ b/pgml-dashboard/package.json @@ -1,5 +1,13 @@ { "dependencies": { + "@codemirror/lang-javascript": "^6.2.1", + "@codemirror/lang-python": "^6.1.3", + "@codemirror/lang-rust": "^6.0.1", + "@codemirror/lang-sql": "^6.5.4", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/state": "^6.2.1", + "@codemirror/view": "^6.21.0", + "codemirror": "^6.0.1", "autosize": "^6.0.1", "dompurify": "^3.0.6", "marked": "^9.1.0" diff --git a/pgml-dashboard/src/api/cms.rs b/pgml-dashboard/src/api/cms.rs index 91eda8e0b..bdecd4996 100644 --- a/pgml-dashboard/src/api/cms.rs +++ b/pgml-dashboard/src/api/cms.rs @@ -406,32 +406,6 @@ mod test { use rocket::local::asynchronous::Client; use rocket::{Build, Rocket}; - #[test] - fn test_syntax_highlighting() { - let code = r#" -# Hello - -```postgresql -SELECT * FROM test; -``` - "#; - - let arena = Arena::new(); - let root = parse_document(&arena, code, &options()); - - // Style headings like we like them - let mut plugins = ComrakPlugins::default(); - let binding = MarkdownHeadings::new(); - plugins.render.heading_adapter = Some(&binding); - plugins.render.codefence_syntax_highlighter = Some(&SyntaxHighlighter {}); - - let mut html = vec![]; - format_html_with_plugins(root, &options(), &mut html, &plugins).unwrap(); - let html = String::from_utf8(html).unwrap(); - - assert!(html.contains("SELECT")); - } - #[test] fn test_wrapping_tables() { let markdown = r#" @@ -574,4 +548,69 @@ This is the end of the markdown rsp.status() ); } + + // Test backend for line highlights and line numbers added + #[test] + fn gitbook_codeblock_test() { + let contents = r#" +{% code title="Test name for html" lineNumbers="true" %} +```javascript-highlightGreen="1" + import something + let a = 1 +``` +{% endcode %} +"#; + + let expected = r#" +
+
+ Test name for html +
+
+        
+ content_copy + link + edit +
+ +
importsomething
+
leta=1
+
+
+
+
"#; + + // Parse Markdown + let arena = Arena::new(); + let spaced_contents = crate::utils::markdown::gitbook_preprocess(contents); + let root = parse_document(&arena, &spaced_contents, &crate::utils::markdown::options()); + + crate::utils::markdown::wrap_tables(root, &arena).unwrap(); + + // MkDocs, gitbook syntax support, e.g. tabs, notes, alerts, etc. + crate::utils::markdown::mkdocs(root, &arena).unwrap(); + + // Style headings like we like them + let mut plugins = ComrakPlugins::default(); + let headings = crate::utils::markdown::MarkdownHeadings::new(); + plugins.render.heading_adapter = Some(&headings); + plugins.render.codefence_syntax_highlighter = + Some(&crate::utils::markdown::SyntaxHighlighter {}); + + let mut html = vec![]; + format_html_with_plugins( + root, + &crate::utils::markdown::options(), + &mut html, + &plugins, + ) + .unwrap(); + let html = String::from_utf8(html).unwrap(); + + println!("expected: {}", expected); + + println!("response: {}", html); + + assert!(html.chars().filter(|c| !c.is_whitespace()).collect::() == expected.chars().filter(|c| !c.is_whitespace()).collect::()) + } } diff --git a/pgml-dashboard/src/components/code_block/code_block_controller.js b/pgml-dashboard/src/components/code_block/code_block_controller.js new file mode 100644 index 000000000..0ecfda207 --- /dev/null +++ b/pgml-dashboard/src/components/code_block/code_block_controller.js @@ -0,0 +1,128 @@ +import { Controller } from "@hotwired/stimulus"; +import { basicSetup } from "codemirror"; +import { sql } from "@codemirror/lang-sql"; +import { python } from "@codemirror/lang-python"; +import { javascript } from "@codemirror/lang-javascript"; +import { rust } from "@codemirror/lang-rust"; +import { json } from "@codemirror/lang-json"; +import { EditorView, ViewPlugin, Decoration } from "@codemirror/view"; +import { RangeSetBuilder, Facet} from "@codemirror/state"; + +import buildCustomTheme from "../../../static/js/utilities/code_mirror_theme"; + +const buildEditorView = (target, content, languageExtension, classes) => { + let editorView = new EditorView({ + doc: content, + extensions: [ + basicSetup, + languageExtension !== null ? languageExtension() : [], // if no language chosen do not highlight syntax + buildCustomTheme(), + EditorView.contentAttributes.of({ contenteditable: false }), + addClasses.of(classes), + highlight + ], + parent: target, + highlightActiveLine: false + }); + return editorView; +}; + +const highlight = ViewPlugin.fromClass(class { + constructor(view) { + this.decorations = highlightLine(view) + } + + update(update) { + if (update.docChanged || update.viewportChanged) + this.decorations = highlightLine(update.view) + } +}, { + decorations: v => v.decorations +}) + +function highlightLine(view) { + let builder = new RangeSetBuilder() + let classes = view.state.facet(addClasses).shift() + for (let {from, to} of view.visibleRanges) { + for (let pos = from; pos <= to;) { + let lineClasses = classes.shift() + let line = view.state.doc.lineAt(pos) + builder.add(line.from, line.from, Decoration.line({attributes: {class: lineClasses}})) + pos = line.to + 1 + } + } + return builder.finish() +} + +const addClasses = Facet.define({ + combone: values => values +}) + +const language = (element) => { + switch (element.getAttribute("language")) { + case "sql": + return sql; + case "postgresql": + return sql; + case "python": + return python; + case "javascript": + return javascript; + case "rust": + return rust; + case "json": + return json; + default: + return null; + } +} + +const codeBlockCallback = (element) => { + let highlights = element.getElementsByClassName("highlight") + let classes = []; + for(let lineNum = 0; lineNum < highlights.length; lineNum++) { + classes.push(highlights[lineNum].classList) + } + + let content = element.textContent.trim() + element.innerHTML = ""; + + return [element, content, classes] +} + +// Add Codemirror with data controller +export default class extends Controller { + connect() { + let [element, content, classes] = codeBlockCallback(this.element) + let lang = language(this.element) + + buildEditorView(element, content, lang, classes); + } +} + +// Add Codemirror with web component +class CodeBlockA extends HTMLElement { + constructor() { + super(); + + this.language = language(this) + } + + connectedCallback() { + let [element, content, classes] = codeBlockCallback(this) + + buildEditorView(element, content, this.language, classes); + } + + // component attributes + static get observedAttributes() { + return ["type"]; + } + + // attribute change + attributeChangedCallback(property, oldValue, newValue) { + if (oldValue === newValue) return; + this[property] = newValue; + } +} +customElements.define("code-block", CodeBlockA); diff --git a/pgml-dashboard/src/components/code_block/mod.rs b/pgml-dashboard/src/components/code_block/mod.rs new file mode 100644 index 000000000..4a68d0a7b --- /dev/null +++ b/pgml-dashboard/src/components/code_block/mod.rs @@ -0,0 +1,14 @@ +use pgml_components::component; +use sailfish::TemplateOnce; + +#[derive(TemplateOnce, Default)] +#[template(path = "code_block/template.html")] +pub struct CodeBlock {} + +impl CodeBlock { + pub fn new() -> CodeBlock { + CodeBlock {} + } +} + +component!(CodeBlock); diff --git a/pgml-dashboard/src/components/code_block/template.html b/pgml-dashboard/src/components/code_block/template.html new file mode 100644 index 000000000..e69de29bb diff --git a/pgml-dashboard/src/components/mod.rs b/pgml-dashboard/src/components/mod.rs index 373dbe776..d04961b77 100644 --- a/pgml-dashboard/src/components/mod.rs +++ b/pgml-dashboard/src/components/mod.rs @@ -16,6 +16,10 @@ pub use chatbot::Chatbot; // src/components/cms pub mod cms; +// src/components/code_block +pub mod code_block; +pub use code_block::CodeBlock; + // src/components/confirm_modal pub mod confirm_modal; pub use confirm_modal::ConfirmModal; diff --git a/pgml-dashboard/src/utils/markdown.rs b/pgml-dashboard/src/utils/markdown.rs index 999e8222e..53f6cb332 100644 --- a/pgml-dashboard/src/utils/markdown.rs +++ b/pgml-dashboard/src/utils/markdown.rs @@ -183,7 +183,7 @@ impl HighlightLines { struct CodeFence<'a> { lang: &'a str, highlight: HashMap, - enumerate: bool, + line_numbers: bool, } impl<'a> From<&str> for CodeFence<'a> { @@ -194,12 +194,16 @@ impl<'a> From<&str> for CodeFence<'a> { "bash" } else if options.starts_with("python") { "python" + } else if options.starts_with("javascript") { + "javascript" } else if options.starts_with("postgresql") { "postgresql" } else if options.starts_with("postgresql-line-nums") { "postgresql-line-nums" } else if options.starts_with("rust") { - "rust" + "rust" + } else if options.starts_with("json") { + "json" } else { "code" }; @@ -212,7 +216,7 @@ impl<'a> From<&str> for CodeFence<'a> { CodeFence { lang, highlight, - enumerate: options.contains("enumerate"), + line_numbers: options.contains("lineNumbers"), } } } @@ -220,233 +224,19 @@ impl<'a> From<&str> for CodeFence<'a> { pub struct SyntaxHighlighter {} impl SyntaxHighlighterAdapter for SyntaxHighlighter { + fn highlight(&self, options: Option<&str>, code: &str) -> String { let code = if let Some(options) = options { let code = code.to_string(); let options = CodeFence::from(options); - let code = match options.lang { - "postgresql" | "sql" | "postgresql-line-nums" => { - lazy_static! { - static ref SQL_KEYS: [&'static str; 69] = [ - "PARTITION OF", - "PARTITION BY", - "CASCADE", - "INNER ", - "ON ", - "WITH", - "SELECT", - "UPDATE", - "DELETE", - "WHERE", - "AS", - "HAVING", - "ORDER BY", - "ASC", - "DESC", - "LIMIT", - "FROM", - "CREATE", - "REPLACE", - "DROP", - "VIEW", - "EXTENSION", - "SERVER", - "FOREIGN DATA WRAPPER", - "OPTIONS", - "IMPORT FOREIGN SCHEMA", - "CREATE USER MAPPING", - "INTO", - "PUBLICATION", - "FOR", - "ALL", - "TABLES", - "CONNECTION", - "SUBSCRIPTION", - "JOIN", - "INTO", - "INSERT", - "BEGIN", - "ALTER", - "SCHEMA", - "RENAME", - "COMMIT", - "AND ", - "ADD COLUMN", - "ALTER TABLE", - "PRIMARY KEY", - "DO", - "END", - "BETWEEN", - "SET", - "REINDEX", - "INDEX", - "USING", - "GROUP BY", - "CREATE TABLE", - "pgml.embed", - "pgml.sum", - "pgml.norm_l2", - "CONCURRENTLY", - "ON\n", - "VALUES", - "@@", - "=>", - "GENERATED ALWAYS AS", - "STORED", - "IF NOT EXISTS", - "pgml.train", - "pgml.predict", - "pgml.transform", - ]; - static ref SQL_KEYS_REPLACEMENTS: [&'static str; 69] = [ - r#"PARTITION OF"#, - r#"PARTITION BY"#, - "CASCADE", - "INNER ", - "ON ", - "WITH", - "SELECT", - "UPDATE", - "DELETE", - "WHERE", - "AS", - "HAVING", - "ORDER BY", - "ASC", - "DESC", - "LIMIT", - "FROM", - "CREATE", - "REPLACE", - "DROP", - "VIEW", - "EXTENSION", - "SERVER", - "FOREIGN DATA WRAPPER", - "OPTIONS", - "IMPORT FOREIGN SCHEMA", - "CREATE USER MAPPING", - "INTO", - "PUBLICATION", - "FOR", - "ALL", - "TABLES", - "CONNECTION", - "SUBSCRIPTION", - "JOIN", - "INTO", - "INSERT", - "BEGIN", - "ALTER", - "SCHEMA", - "RENAME", - "COMMIT", - "AND ", - "ADD COLUMN", - "ALTER TABLE", - "PRIMARY KEY", - "DO", - "END", - "BETWEEN", - "SET", - "REINDEX", - "INDEX", - "USING", - "GROUP BY", - "CREATE TABLE", - "pgml.embed", - "pgml.sum", - "pgml.norm_l2", - "CONCURRENTLY", - "ON\n", - "VALUES", - "@@", - "=>", - "GENERATED ALWAYS AS", - "STORED", - "IF NOT EXISTS", - "pgml.train", - "pgml.predict", - "pgml.transform", - ]; - static ref AHO_SQL: AhoCorasick = AhoCorasickBuilder::new() - .match_kind(MatchKind::LeftmostLongest) - .build(SQL_KEYS.iter()); - } - - AHO_SQL - .replace_all(&code, &SQL_KEYS_REPLACEMENTS[..]) - .to_string() - } - - "bash" => { - lazy_static! { - static ref RE_BASH: regex::Regex = regex::Regex::new(r"(cd)").unwrap(); - } - - RE_BASH - .replace_all(&code, r#"$1"#) - .to_string() - } - - "python" => { - lazy_static! { - static ref RE_PYTHON: regex::Regex = regex::Regex::new( - r"(import |def |return |if |else|class |async |await )" - ) - .unwrap(); - } - - RE_PYTHON - .replace_all(&code, r#"$1"#) - .to_string() - } - - "rust" => { - lazy_static! { - static ref RE_RUST: regex::Regex = regex::Regex::new( - r"(struct |let |pub |fn |await |impl |const |use |type |move |if |else| |match |for |enum)" - ) - .unwrap(); - } - - RE_RUST - .replace_all(&code, r#"$1"#) - .to_string() - } - - _ => code, - }; - - // Add line numbers - let code = if options.enumerate { - let mut code = code.split('\n') - .enumerate() - .map(|(index, code)| { - format!(r#"{}{}"#, - if index < 9 {format!(" {}", index+1)} else { format!("{}", index+1)}, - code) - }) - .collect::>(); - code.pop(); - code.into_iter().join("\n") - } else { - let mut code = code - .split('\n') - .map(|code| format!("{}", code)) - .collect::>(); - code.pop(); - code.into_iter().join("\n") - }; - // Add line highlighting let code = code .split('\n') .enumerate() .map(|(index, code)| { format!( - r#"
{}
"#, + r#"
{}
"#, match options.highlight.get(&(index + 1).to_string()) { Some(color) => color, _ => "none", @@ -461,10 +251,7 @@ impl SyntaxHighlighterAdapter for SyntaxHighlighter { code.to_string() }; - format!( - "
{}
", - code - ) + code } fn build_pre_tag(&self, _attributes: &HashMap) -> String { @@ -475,8 +262,16 @@ impl SyntaxHighlighterAdapter for SyntaxHighlighter { ") } - fn build_code_tag(&self, _attributes: &HashMap) -> String { - String::from("") + fn build_code_tag(&self, attributes: &HashMap) -> String { + let data = match attributes.get("class") { + Some(lang) => lang.replace("language-", ""), + _ => "".to_string() + }; + + let parsed_data = CodeFence::from(data.as_str()); + + // code-block web component uses codemirror to add syntax highlighting + format!("", if parsed_data.line_numbers { "class='line-numbers'"} else {""}, parsed_data.lang, ) } } @@ -847,10 +642,19 @@ impl From<&str> for Admonition { struct CodeBlock { time: Option, title: Option, + line_numbers: Option, } impl CodeBlock { fn html(&self, html_type: &str) -> Option { + let line_numbers: bool = match &self.line_numbers { + Some(val) => match val.as_str() { + "true" => true, + _ => false + }, + _ => false + }; + match html_type { "time" => self.time.as_ref().map(|time| { format!( @@ -866,18 +670,20 @@ impl CodeBlock { "code" => match &self.title { Some(title) => Some(format!( r#" -
+
{}
"#, + if line_numbers {"line-numbers" } else {""}, title )), - None => Some( + None => Some(format!( r#" -
- "# - .to_string(), +
+ "#, + if line_numbers {"line-numbers" } else {""}, + ) ), }, "results" => match &self.title { @@ -940,7 +746,7 @@ pub fn gitbook_preprocess(item: &str) -> String { pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyhow::Result<()> { let mut tabs = Vec::new(); - // tracks open !!! blocks and holds items to apppend prior to closing + // tracks openning tags and holds items to apppend prior to closing let mut info_block_close_items: Vec> = vec![]; iter_nodes(root, &mut |node| { @@ -1213,7 +1019,8 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho let title = parser(text.as_ref(), r#"title=""#); let time = parser(text.as_ref(), r#"time=""#); - let code_block = CodeBlock { time, title }; + let line_numbers = parser(text.as_ref(), r#"lineNumbers=""#); + let code_block = CodeBlock { time, title, line_numbers }; if let Some(html) = code_block.html("code") { let n = arena.alloc(Node::new(RefCell::new(Ast::new( @@ -1229,7 +1036,7 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho let parent = node.parent().unwrap(); let title = parser(text.as_ref(), r#"title=""#); - let code_block = CodeBlock { time: None, title }; + let code_block = CodeBlock { time: None, title, line_numbers: None }; if let Some(html) = code_block.html("results") { let n = arena.alloc(Node::new(RefCell::new(Ast::new( diff --git a/pgml-dashboard/static/css/scss/components/_admonitions.scss b/pgml-dashboard/static/css/scss/components/_admonitions.scss index e145e7dc8..6e3dde527 100644 --- a/pgml-dashboard/static/css/scss/components/_admonitions.scss +++ b/pgml-dashboard/static/css/scss/components/_admonitions.scss @@ -69,6 +69,9 @@ pre { margin: 0px; } + pre[data-controller="copy"] { + padding-top: 2rem !important; + } div.code-block { border: none !important; diff --git a/pgml-dashboard/static/css/scss/components/_code.scss b/pgml-dashboard/static/css/scss/components/_code.scss index 0545363cd..f7c97f2a0 100644 --- a/pgml-dashboard/static/css/scss/components/_code.scss +++ b/pgml-dashboard/static/css/scss/components/_code.scss @@ -64,7 +64,7 @@ pre { display: inline-block; width: 100%; @if $color { - background-color: $color; + background-color: $color !important; } } @@ -110,7 +110,7 @@ pre { } .code-line-highlight-none { @include code-line-highlight(null); - } + } .code-line-numbers { @extend .noselect; diff --git a/pgml-dashboard/static/css/scss/pages/_docs.scss b/pgml-dashboard/static/css/scss/pages/_docs.scss index ad71f1564..4fca4c7ae 100644 --- a/pgml-dashboard/static/css/scss/pages/_docs.scss +++ b/pgml-dashboard/static/css/scss/pages/_docs.scss @@ -178,5 +178,33 @@ margin-right: auto; } } + + // Codemirror overrideds + .cm-editor { + background: inherit; + + // default no line numbers. + .cm-gutters { + display: none; + } + } + + .cm-gutters { + background: inherit; + } + + .code-highlight { + background: blue; + } + + .cm-activeLine { + background-color: transparent; + } + + .line-numbers { + .cm-gutters { + display: contents !important; + } + } } diff --git a/pgml-dashboard/static/js/copy.js b/pgml-dashboard/static/js/copy.js index a7b45eda5..a5c9ba343 100644 --- a/pgml-dashboard/static/js/copy.js +++ b/pgml-dashboard/static/js/copy.js @@ -9,10 +9,19 @@ import { export default class extends Controller { codeCopy() { + + // mkdocs / original style code let text = [...this.element.querySelectorAll('span.code-content')] .map((copied) => copied.innerText) .join('\n') + // codemirror style code + if (text.length === 0 ) { + text = [...this.element.querySelectorAll('div.cm-line')] + .map((copied) => copied.innerText) + .join('\n') + } + if (text.length === 0) { text = this.element.innerText.replace('content_copy', '') } diff --git a/pgml-dashboard/static/js/utilities/code_mirror_theme.js b/pgml-dashboard/static/js/utilities/code_mirror_theme.js new file mode 100644 index 000000000..8272f415a --- /dev/null +++ b/pgml-dashboard/static/js/utilities/code_mirror_theme.js @@ -0,0 +1,143 @@ +import { EditorView } from "@codemirror/view"; +import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; +import { tags as t } from "@lezer/highlight"; + +// Theme builder is taken from: https://github.com/codemirror/theme-one-dark#readme +const buildCustomTheme = () => { + const chalky = "#FF0"; // Set + const coral = "#F5708B"; // Set + const salmon = "#e9467a"; + const blue = "#00e0ff"; + const cyan = "#56b6c2"; + const invalid = "#ffffff"; + const ivory = "#abb2bf"; + const stone = "#7d8799"; + const malibu = "#61afef"; + const sage = "#0F0"; // Set + const whiskey = "#d19a66"; + const violet = "#F3F"; // Set + const darkBackground = "#17181A"; // Set + const highlightBackground = "#2c313a"; + const background = "#17181A"; // Set + const tooltipBackground = "#353a42"; + const selection = "#3E4451"; + const cursor = "#528bff"; + + const customTheme = EditorView.theme({ + "&": { + color: ivory, + backgroundColor: background, + }, + + ".cm-content": { + caretColor: cursor, + }, + + ".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor }, + "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": + { backgroundColor: selection }, + + ".cm-panels": { backgroundColor: darkBackground, color: ivory }, + ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" }, + ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" }, + + ".cm-searchMatch": { + backgroundColor: "#72a1ff59", + outline: "1px solid #457dff", + }, + ".cm-searchMatch.cm-searchMatch-selected": { + backgroundColor: "#6199ff2f", + }, + + ".cm-activeLine": { backgroundColor: "#6699ff0b" }, + ".cm-selectionMatch": { backgroundColor: "#aafe661a" }, + + "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": { + backgroundColor: "#bad0f847", + }, + + ".cm-gutters": { + backgroundColor: background, + color: stone, + border: "none", + }, + + ".cm-activeLineGutter": { + backgroundColor: highlightBackground, + }, + + ".cm-foldPlaceholder": { + backgroundColor: "transparent", + border: "none", + color: "#ddd", + }, + + ".cm-tooltip": { + border: "none", + backgroundColor: tooltipBackground, + }, + ".cm-tooltip .cm-tooltip-arrow:before": { + borderTopColor: "transparent", + borderBottomColor: "transparent", + }, + ".cm-tooltip .cm-tooltip-arrow:after": { + borderTopColor: tooltipBackground, + borderBottomColor: tooltipBackground, + }, + ".cm-tooltip-autocomplete": { + "& > ul > li[aria-selected]": { + backgroundColor: highlightBackground, + color: ivory, + }, + }, + }); + + /// The highlighting style for code in the One Dark theme. + const customHighlightStyle = HighlightStyle.define([ + { tag: t.keyword, color: violet }, + { + tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], + color: salmon, + }, + { tag: [t.function(t.variableName), t.labelName], color: malibu }, + { tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey }, + { tag: [t.definition(t.name), t.separator], color: ivory }, + { + tag: [ + t.typeName, + t.className, + t.number, + t.changed, + t.annotation, + t.modifier, + t.self, + t.namespace, + ], + color: chalky, + }, + { + tag: [ + t.operator, + t.operatorKeyword, + t.url, + t.escape, + t.regexp, + t.link, + t.special(t.string), + ], + color: blue, + }, + { tag: [t.meta, t.comment], color: stone }, + { tag: t.strong, fontWeight: "bold" }, + { tag: t.emphasis, fontStyle: "italic" }, + { tag: t.strikethrough, textDecoration: "line-through" }, + { tag: t.link, color: stone, textDecoration: "underline" }, + { tag: t.heading, fontWeight: "bold", color: salmon }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey }, + { tag: [t.processingInstruction, t.string, t.inserted], color: sage }, + { tag: t.invalid, color: invalid }, + ]); + return [customTheme, syntaxHighlighting(customHighlightStyle)]; +}; + +export default buildCustomTheme; From 9d058441306a3e947b501dc8ce6aca0759955031 Mon Sep 17 00:00:00 2001 From: Dan <39170265+chillenberger@users.noreply.github.com> Date: Thu, 4 Jan 2024 14:37:27 -0700 Subject: [PATCH 2/3] remove direct import of node module --- pgml-dashboard/package-lock.json | 12 +- .../code_block/code_block_controller.js | 6 +- .../static/js/utilities/code_mirror_theme.js | 252 +++++++++--------- 3 files changed, 134 insertions(+), 136 deletions(-) diff --git a/pgml-dashboard/package-lock.json b/pgml-dashboard/package-lock.json index 40c5a3e84..c7f315dec 100644 --- a/pgml-dashboard/package-lock.json +++ b/pgml-dashboard/package-lock.json @@ -163,9 +163,9 @@ } }, "node_modules/@lezer/javascript": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.11.tgz", - "integrity": "sha512-B5Y9EJF4BWiMgj4ufxUo2hrORnmMBDrMtR+L7dwIO5pocuSAahG6QBwXR6PbKJOjRywJczU2r2LJPg79ER91TQ==", + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.12.tgz", + "integrity": "sha512-kwO5MftUiyfKBcECMEDc4HYnc10JME9kTJNPVoCXqJj/Y+ASWF0rgstORi3BThlQI6SoPSshrK5TjuiLFnr29A==", "dependencies": { "@lezer/highlight": "^1.1.3", "@lezer/lr": "^1.3.0" @@ -234,9 +234,9 @@ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" }, "node_modules/dompurify": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", - "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.7.tgz", + "integrity": "sha512-BViYTZoqP3ak/ULKOc101y+CtHDUvBsVgSxIF1ku0HmK6BRf+C03MC+tArMvOPtVtZp83DDh5puywKDu4sbVjQ==" }, "node_modules/marked": { "version": "9.1.6", diff --git a/pgml-dashboard/src/components/code_block/code_block_controller.js b/pgml-dashboard/src/components/code_block/code_block_controller.js index 0ecfda207..3a4f92483 100644 --- a/pgml-dashboard/src/components/code_block/code_block_controller.js +++ b/pgml-dashboard/src/components/code_block/code_block_controller.js @@ -7,8 +7,9 @@ import { rust } from "@codemirror/lang-rust"; import { json } from "@codemirror/lang-json"; import { EditorView, ViewPlugin, Decoration } from "@codemirror/view"; import { RangeSetBuilder, Facet} from "@codemirror/state"; +import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; -import buildCustomTheme from "../../../static/js/utilities/code_mirror_theme"; +import { highlightStyle, editorTheme } from "../../../static/js/utilities/code_mirror_theme"; const buildEditorView = (target, content, languageExtension, classes) => { let editorView = new EditorView({ @@ -16,7 +17,8 @@ const buildEditorView = (target, content, languageExtension, classes) => { extensions: [ basicSetup, languageExtension !== null ? languageExtension() : [], // if no language chosen do not highlight syntax - buildCustomTheme(), + EditorView.theme(editorTheme), + syntaxHighlighting(HighlightStyle.define(highlightStyle)), EditorView.contentAttributes.of({ contenteditable: false }), addClasses.of(classes), highlight diff --git a/pgml-dashboard/static/js/utilities/code_mirror_theme.js b/pgml-dashboard/static/js/utilities/code_mirror_theme.js index 8272f415a..4ec9ca39e 100644 --- a/pgml-dashboard/static/js/utilities/code_mirror_theme.js +++ b/pgml-dashboard/static/js/utilities/code_mirror_theme.js @@ -1,143 +1,139 @@ -import { EditorView } from "@codemirror/view"; -import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; import { tags as t } from "@lezer/highlight"; // Theme builder is taken from: https://github.com/codemirror/theme-one-dark#readme -const buildCustomTheme = () => { - const chalky = "#FF0"; // Set - const coral = "#F5708B"; // Set - const salmon = "#e9467a"; - const blue = "#00e0ff"; - const cyan = "#56b6c2"; - const invalid = "#ffffff"; - const ivory = "#abb2bf"; - const stone = "#7d8799"; - const malibu = "#61afef"; - const sage = "#0F0"; // Set - const whiskey = "#d19a66"; - const violet = "#F3F"; // Set - const darkBackground = "#17181A"; // Set - const highlightBackground = "#2c313a"; - const background = "#17181A"; // Set - const tooltipBackground = "#353a42"; - const selection = "#3E4451"; - const cursor = "#528bff"; - - const customTheme = EditorView.theme({ - "&": { - color: ivory, - backgroundColor: background, - }, - ".cm-content": { - caretColor: cursor, - }, +const chalky = "#FF0"; // Set +const coral = "#F5708B"; // Set +const salmon = "#e9467a"; +const blue = "#00e0ff"; +const cyan = "#56b6c2"; +const invalid = "#ffffff"; +const ivory = "#abb2bf"; +const stone = "#7d8799"; +const malibu = "#61afef"; +const sage = "#0F0"; // Set +const whiskey = "#d19a66"; +const violet = "#F3F"; // Set +const darkBackground = "#17181A"; // Set +const highlightBackground = "#2c313a"; +const background = "#17181A"; // Set +const tooltipBackground = "#353a42"; +const selection = "#3E4451"; +const cursor = "#528bff"; - ".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor }, - "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": - { backgroundColor: selection }, +const editorTheme = { + "&": { + color: ivory, + backgroundColor: background, + }, - ".cm-panels": { backgroundColor: darkBackground, color: ivory }, - ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" }, - ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" }, + ".cm-content": { + caretColor: cursor, + }, - ".cm-searchMatch": { - backgroundColor: "#72a1ff59", - outline: "1px solid #457dff", - }, - ".cm-searchMatch.cm-searchMatch-selected": { - backgroundColor: "#6199ff2f", - }, + ".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor }, + "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": + { backgroundColor: selection }, - ".cm-activeLine": { backgroundColor: "#6699ff0b" }, - ".cm-selectionMatch": { backgroundColor: "#aafe661a" }, + ".cm-panels": { backgroundColor: darkBackground, color: ivory }, + ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" }, + ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" }, - "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": { - backgroundColor: "#bad0f847", - }, + ".cm-searchMatch": { + backgroundColor: "#72a1ff59", + outline: "1px solid #457dff", + }, + ".cm-searchMatch.cm-searchMatch-selected": { + backgroundColor: "#6199ff2f", + }, - ".cm-gutters": { - backgroundColor: background, - color: stone, - border: "none", - }, + ".cm-activeLine": { backgroundColor: "#6699ff0b" }, + ".cm-selectionMatch": { backgroundColor: "#aafe661a" }, + + "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": { + backgroundColor: "#bad0f847", + }, + + ".cm-gutters": { + backgroundColor: background, + color: stone, + border: "none", + }, + + ".cm-activeLineGutter": { + backgroundColor: highlightBackground, + }, + + ".cm-foldPlaceholder": { + backgroundColor: "transparent", + border: "none", + color: "#ddd", + }, - ".cm-activeLineGutter": { + ".cm-tooltip": { + border: "none", + backgroundColor: tooltipBackground, + }, + ".cm-tooltip .cm-tooltip-arrow:before": { + borderTopColor: "transparent", + borderBottomColor: "transparent", + }, + ".cm-tooltip .cm-tooltip-arrow:after": { + borderTopColor: tooltipBackground, + borderBottomColor: tooltipBackground, + }, + ".cm-tooltip-autocomplete": { + "& > ul > li[aria-selected]": { backgroundColor: highlightBackground, + color: ivory, }, + }, +} - ".cm-foldPlaceholder": { - backgroundColor: "transparent", - border: "none", - color: "#ddd", - }, +const highlightStyle = [ + { tag: t.keyword, color: violet }, + { + tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], + color: salmon, + }, + { tag: [t.function(t.variableName), t.labelName], color: malibu }, + { tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey }, + { tag: [t.definition(t.name), t.separator], color: ivory }, + { + tag: [ + t.typeName, + t.className, + t.number, + t.changed, + t.annotation, + t.modifier, + t.self, + t.namespace, + ], + color: chalky, + }, + { + tag: [ + t.operator, + t.operatorKeyword, + t.url, + t.escape, + t.regexp, + t.link, + t.special(t.string), + ], + color: blue, + }, + { tag: [t.meta, t.comment], color: stone }, + { tag: t.strong, fontWeight: "bold" }, + { tag: t.emphasis, fontStyle: "italic" }, + { tag: t.strikethrough, textDecoration: "line-through" }, + { tag: t.link, color: stone, textDecoration: "underline" }, + { tag: t.heading, fontWeight: "bold", color: salmon }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey }, + { tag: [t.processingInstruction, t.string, t.inserted], color: sage }, + { tag: t.invalid, color: invalid }, +] - ".cm-tooltip": { - border: "none", - backgroundColor: tooltipBackground, - }, - ".cm-tooltip .cm-tooltip-arrow:before": { - borderTopColor: "transparent", - borderBottomColor: "transparent", - }, - ".cm-tooltip .cm-tooltip-arrow:after": { - borderTopColor: tooltipBackground, - borderBottomColor: tooltipBackground, - }, - ".cm-tooltip-autocomplete": { - "& > ul > li[aria-selected]": { - backgroundColor: highlightBackground, - color: ivory, - }, - }, - }); - - /// The highlighting style for code in the One Dark theme. - const customHighlightStyle = HighlightStyle.define([ - { tag: t.keyword, color: violet }, - { - tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], - color: salmon, - }, - { tag: [t.function(t.variableName), t.labelName], color: malibu }, - { tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey }, - { tag: [t.definition(t.name), t.separator], color: ivory }, - { - tag: [ - t.typeName, - t.className, - t.number, - t.changed, - t.annotation, - t.modifier, - t.self, - t.namespace, - ], - color: chalky, - }, - { - tag: [ - t.operator, - t.operatorKeyword, - t.url, - t.escape, - t.regexp, - t.link, - t.special(t.string), - ], - color: blue, - }, - { tag: [t.meta, t.comment], color: stone }, - { tag: t.strong, fontWeight: "bold" }, - { tag: t.emphasis, fontStyle: "italic" }, - { tag: t.strikethrough, textDecoration: "line-through" }, - { tag: t.link, color: stone, textDecoration: "underline" }, - { tag: t.heading, fontWeight: "bold", color: salmon }, - { tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey }, - { tag: [t.processingInstruction, t.string, t.inserted], color: sage }, - { tag: t.invalid, color: invalid }, - ]); - return [customTheme, syntaxHighlighting(customHighlightStyle)]; -}; - -export default buildCustomTheme; + +export {highlightStyle, editorTheme}; From b3f63b38d83831c02fb8cde0030dc65ff548301c Mon Sep 17 00:00:00 2001 From: Dan <39170265+chillenberger@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:04:45 -0700 Subject: [PATCH 3/3] fmt and remove unused imports --- pgml-dashboard/src/api/cms.rs | 10 ++++++- pgml-dashboard/src/utils/markdown.rs | 40 ++++++++++++++++++---------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/pgml-dashboard/src/api/cms.rs b/pgml-dashboard/src/api/cms.rs index bdecd4996..1f606b9f9 100644 --- a/pgml-dashboard/src/api/cms.rs +++ b/pgml-dashboard/src/api/cms.rs @@ -611,6 +611,14 @@ This is the end of the markdown println!("response: {}", html); - assert!(html.chars().filter(|c| !c.is_whitespace()).collect::() == expected.chars().filter(|c| !c.is_whitespace()).collect::()) + assert!( + html.chars() + .filter(|c| !c.is_whitespace()) + .collect::() + == expected + .chars() + .filter(|c| !c.is_whitespace()) + .collect::() + ) } } diff --git a/pgml-dashboard/src/utils/markdown.rs b/pgml-dashboard/src/utils/markdown.rs index 53f6cb332..fb0557aa2 100644 --- a/pgml-dashboard/src/utils/markdown.rs +++ b/pgml-dashboard/src/utils/markdown.rs @@ -8,7 +8,6 @@ use std::sync::{ Arc, }; -use aho_corasick::{AhoCorasick, AhoCorasickBuilder, MatchKind}; use anyhow::Result; use comrak::{ adapters::{HeadingAdapter, HeadingMeta, SyntaxHighlighterAdapter}, @@ -17,7 +16,6 @@ use comrak::{ parse_document, Arena, ComrakExtensionOptions, ComrakOptions, ComrakRenderOptions, }; use itertools::Itertools; -use lazy_static::lazy_static; use regex::Regex; use tantivy::collector::TopDocs; use tantivy::query::{QueryParser, RegexQuery}; @@ -201,7 +199,7 @@ impl<'a> From<&str> for CodeFence<'a> { } else if options.starts_with("postgresql-line-nums") { "postgresql-line-nums" } else if options.starts_with("rust") { - "rust" + "rust" } else if options.starts_with("json") { "json" } else { @@ -224,7 +222,6 @@ impl<'a> From<&str> for CodeFence<'a> { pub struct SyntaxHighlighter {} impl SyntaxHighlighterAdapter for SyntaxHighlighter { - fn highlight(&self, options: Option<&str>, code: &str) -> String { let code = if let Some(options) = options { let code = code.to_string(); @@ -265,13 +262,21 @@ impl SyntaxHighlighterAdapter for SyntaxHighlighter { fn build_code_tag(&self, attributes: &HashMap) -> String { let data = match attributes.get("class") { Some(lang) => lang.replace("language-", ""), - _ => "".to_string() + _ => "".to_string(), }; let parsed_data = CodeFence::from(data.as_str()); // code-block web component uses codemirror to add syntax highlighting - format!("", if parsed_data.line_numbers { "class='line-numbers'"} else {""}, parsed_data.lang, ) + format!( + "", + if parsed_data.line_numbers { + "class='line-numbers'" + } else { + "" + }, + parsed_data.lang, + ) } } @@ -650,9 +655,9 @@ impl CodeBlock { let line_numbers: bool = match &self.line_numbers { Some(val) => match val.as_str() { "true" => true, - _ => false + _ => false, }, - _ => false + _ => false, }; match html_type { @@ -675,16 +680,15 @@ impl CodeBlock { {}
"#, - if line_numbers {"line-numbers" } else {""}, + if line_numbers { "line-numbers" } else { "" }, title )), None => Some(format!( r#"
"#, - if line_numbers {"line-numbers" } else {""}, - ) - ), + if line_numbers { "line-numbers" } else { "" }, + )), }, "results" => match &self.title { Some(title) => Some(format!( @@ -1020,7 +1024,11 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho let title = parser(text.as_ref(), r#"title=""#); let time = parser(text.as_ref(), r#"time=""#); let line_numbers = parser(text.as_ref(), r#"lineNumbers=""#); - let code_block = CodeBlock { time, title, line_numbers }; + let code_block = CodeBlock { + time, + title, + line_numbers, + }; if let Some(html) = code_block.html("code") { let n = arena.alloc(Node::new(RefCell::new(Ast::new( @@ -1036,7 +1044,11 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho let parent = node.parent().unwrap(); let title = parser(text.as_ref(), r#"title=""#); - let code_block = CodeBlock { time: None, title, line_numbers: None }; + let code_block = CodeBlock { + time: None, + title, + line_numbers: None, + }; if let Some(html) = code_block.html("results") { let n = arena.alloc(Node::new(RefCell::new(Ast::new(