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( pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy