From d79f2166877481b942b8f5e274c90e49df38166f Mon Sep 17 00:00:00 2001 From: Gordon Date: Sat, 5 Jun 2021 22:09:13 +0100 Subject: [PATCH] Syntax highlighting in code blocks --- CHANGELOG.md | 3 + docs/styles.md | 4 ++ package-lock.json | 104 +++++++++++++++++++++++++++ package.json | 2 + src/TaskEditor.tsx | 34 ++++++--- src/index.css | 174 +++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 308 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61e8d51..a763ff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +* Added KaTeX support to task markdown (description and comments) +* Added syntax highlighting to code blocks in task markdown (description and comments) + # 0.10.0 * Added task archive, tasks can now be sent to the archive folder and restored from the archive folder diff --git a/docs/styles.md b/docs/styles.md index a3dc981..7220613 100644 --- a/docs/styles.md +++ b/docs/styles.md @@ -127,3 +127,7 @@ Various Codicon icons have been used in this extension. Check [here](https://cod - `kanbn-burndown-tooltip-workload` - `kanbn-burndown-tooltip-count` - `kanbn-burndown-tooltip-task` + +### Syntax highlighting + +See [index.css](https://github.com/basementuniverse/vscode-kanbn/blob/main/src/index.css) for built-in syntax highlighting styles and token class-names. diff --git a/package-lock.json b/package-lock.json index b076dfd..2f017b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1717,6 +1717,15 @@ "redux": "^4.0.0" } }, + "@types/react-syntax-highlighter": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-13.5.0.tgz", + "integrity": "sha512-U7DrUaQRv3b+fsbPXMf7vC21K7DOkdNCQtp14Wm0Z5YLI9fPhndN4YTZ9eVXwmAivIg6lZ3YBVtGYucAS3H76A==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/recharts": { "version": "1.8.19", "resolved": "https://registry.npmjs.org/@types/recharts/-/recharts-1.8.19.tgz", @@ -4246,6 +4255,17 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" }, + "clipboard": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz", + "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==", + "optional": true, + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -5961,6 +5981,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "optional": true + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -7792,6 +7818,14 @@ "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", "dev": true }, + "fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "requires": { + "format": "^0.2.0" + } + }, "faye-websocket": { "version": "0.11.3", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", @@ -8416,6 +8450,11 @@ "mime-types": "^2.1.12" } }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=" + }, "formik": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.6.tgz", @@ -9310,6 +9349,15 @@ } } }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "optional": true, + "requires": { + "delegate": "^3.1.2" + } + }, "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", @@ -9566,6 +9614,11 @@ "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", "dev": true }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -12989,6 +13042,15 @@ } } }, + "lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "requires": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + } + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -20016,6 +20078,14 @@ } } }, + "prismjs": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", + "integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", + "requires": { + "clipboard": "^2.0.0" + } + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -20834,6 +20904,18 @@ "react-transition-group": "2.9.0" } }, + "react-syntax-highlighter": { + "version": "15.4.3", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.3.tgz", + "integrity": "sha512-TnhGgZKXr5o8a63uYdRTzeb8ijJOgRGe0qjrE0eK/gajtdyqnSO6LqB3vW16hHB0cFierYSoy/AOJw8z1Dui8g==", + "requires": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.22.0", + "refractor": "^3.2.0" + } + }, "react-textarea-autosize": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.2.tgz", @@ -21327,6 +21409,16 @@ "@babel/runtime": "^7.9.2" } }, + "refractor": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.3.1.tgz", + "integrity": "sha512-vaN6R56kLMuBszHSWlwTpcZ8KTMG6aUCok4GrxYDT20UIOXxOc5o6oDc8tNTzSlH3m2sI+Eu9Jo2kVdDcUTWYw==", + "requires": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.23.0" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -22626,6 +22718,12 @@ "ajv-keywords": "^3.1.0" } }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", + "optional": true + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -23997,6 +24095,12 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "optional": true + }, "tiny-invariant": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", diff --git a/package.json b/package.json index ff54c7d..3d28eb4 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "react-beautiful-dnd": "12.2.0", "react-dom": "^16.3.2", "react-markdown": "^6.0.1", + "react-syntax-highlighter": "^15.4.3", "react-textarea-autosize": "^8.3.2", "recharts": "^2.0.9", "rehype-katex": "^5.0.0", @@ -130,6 +131,7 @@ "@types/jest": "^23.3.14", "@types/lodash": "^4.14.168", "@types/node": "^10.17.56", + "@types/react-syntax-highlighter": "^13.5.0", "@types/recharts": "^1.8.19", "@types/uuid": "^8.3.0", "react-scripts": "^2.1.8", diff --git a/src/TaskEditor.tsx b/src/TaskEditor.tsx index ecd9672..675a998 100644 --- a/src/TaskEditor.tsx +++ b/src/TaskEditor.tsx @@ -8,6 +8,7 @@ import ReactMarkdown from 'react-markdown'; import TextareaAutosize from 'react-textarea-autosize'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import 'katex/dist/katex.min.css'; interface KanbnTaskValidationOutput { @@ -27,11 +28,30 @@ interface KanbnTaskValidationInput extends KanbnTaskValidationOutput { id: string } +const components = { + code({ node, inline, className, children, ...props }) { + const match = /language-(\w+)/.exec(className || ''); + return !inline && match ? ( + + ) : ( + + ); + } +}; + const Markdown = props => (); + rehypePlugins: [rehypeKatex], + components, + ...props, +}} />); const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUuid, vscode }: { task: KanbnTask | null, @@ -269,9 +289,7 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui as={TextareaAutosize} name="description" /> - : - {values.description} - + : } - : - {comment.text} - + : } diff --git a/src/index.css b/src/index.css index 4541e8c..312dd4f 100644 --- a/src/index.css +++ b/src/index.css @@ -9,6 +9,10 @@ body.vscode-high-contrast { color: var(--vscode-foreground); } +/*----------------------------------------------------------------------------- +Board styles +-----------------------------------------------------------------------------*/ + .kanbn-header-name { font-size: 1.5em; margin-top: 0; @@ -85,6 +89,10 @@ body.vscode-high-contrast { background-color: var(--vscode-button-hoverBackground); } +.kanbn-header-button-inactive { + opacity: 0.6; +} + .kanbn-board { display: flex; align-items: stretch; @@ -180,6 +188,10 @@ body.vscode-high-contrast { border-color: #e83; } +/*----------------------------------------------------------------------------- +Task styles +-----------------------------------------------------------------------------*/ + .kanbn-task { position: relative; overflow: hidden; @@ -287,6 +299,10 @@ body.vscode-dark .kanbn-task { transition: width .5s ease-in-out; } +/*----------------------------------------------------------------------------- +Task editor styles +-----------------------------------------------------------------------------*/ + .kanbn-task-editor-title { font-size: 1.5em; margin-top: 0; @@ -539,6 +555,10 @@ body.vscode-dark .kanbn-task-editor-field-input[type="date"]::-webkit-calendar-p float: right; } +/*----------------------------------------------------------------------------- +Burndown chart styles +-----------------------------------------------------------------------------*/ + .kanbn-burndown-settings { position: relative; flex: 1; @@ -587,10 +607,6 @@ body.vscode-dark .kanbn-burndown-settings-input[type="date"]::-webkit-calendar-p filter: invert(1); } -.kanbn-header-button-inactive { - opacity: 0.6; -} - .kanbn-burndown { height: 85vh; width: 95vw; @@ -626,3 +642,153 @@ body.vscode-dark .kanbn-burndown-settings-input[type="date"]::-webkit-calendar-p .kanbn-burndown-tooltip-task { font-style: italic; } + +/*----------------------------------------------------------------------------- +Code block syntax highlighting styles +-----------------------------------------------------------------------------*/ + +body.vscode-dark code[class*="language-"] span.token.comment, +body.vscode-dark code[class*="language-"] span.token.prolog, +body.vscode-dark code[class*="language-"] span.token.cdata { + color: #808080; +} + +body.vscode-dark code[class*="language-"] span.token.delimiter, +body.vscode-dark code[class*="language-"] span.token.boolean, +body.vscode-dark code[class*="language-"] span.token.keyword, +body.vscode-dark code[class*="language-"] span.token.selector, +body.vscode-dark code[class*="language-"] span.token.important, +body.vscode-dark code[class*="language-"] span.token.atrule { + color: #cc7832; +} + +body.vscode-dark code[class*="language-"] span.token.operator, +body.vscode-dark code[class*="language-"] span.token.punctuation, +body.vscode-dark code[class*="language-"] span.token.attr-name { + color: #a9b7c6; +} + +body.vscode-dark code[class*="language-"] span.token.tag, +body.vscode-dark code[class*="language-"] span.token.tag.punctuation, +body.vscode-dark code[class*="language-"] span.token.doctype, +body.vscode-dark code[class*="language-"] span.token.builtin { + color: #e8bf6a; +} + +body.vscode-dark code[class*="language-"] span.token.entity, +body.vscode-dark code[class*="language-"] span.token.number, +body.vscode-dark code[class*="language-"] span.token.symbol { + color: #6897bb; +} + +body.vscode-dark code[class*="language-"] span.token.property, +body.vscode-dark code[class*="language-"] span.token.constant, +body.vscode-dark code[class*="language-"] span.token.variable { + color: #9876aa; +} + +body.vscode-dark code[class*="language-"] span.token.string, +body.vscode-dark code[class*="language-"] span.token.char { + color: #6a8759; +} + +body.vscode-dark code[class*="language-"] span.token.attr-value, +body.vscode-dark code[class*="language-"] span.token.attr-value.punctuation { + color: #a5c261; +} + +body.vscode-dark code[class*="language-"] span.token.attr-value.punctuation:first-child { + color: #a9b7c6; +} + +body.vscode-dark code[class*="language-"] span.token.url { + color: #287bde; +} + +body.vscode-dark code[class*="language-"] span.token.function { + color: #ffc66d; +} + +body.vscode-dark code[class*="language-"] span.token.regex { + color: #364135; +} + +body.vscode-dark code[class*="language-"] span.token.inserted { + color: #294436; +} + +body.vscode-dark code[class*="language-"] span.token.deleted { + color: #484a4a; +} + +body.vscode-dark code.language-css span.token.property, +body.vscode-dark code.language-css span.token.property + .token.punctuation { + color: #a9b7c6; +} + +body.vscode-dark code.language-css span.token.id, +body.vscode-dark code.language-css .token.selector > .token.class, +body.vscode-dark code.language-css .token.selector > .token.attribute, +body.vscode-dark code.language-css .token.selector > .token.pseudo-class, +body.vscode-dark code.language-css .token.selector > .token.pseudo-element { + color: #ffc66d; +} + +body.vscode-light code[class*="language-"] span.token.comment, +body.vscode-light code[class*="language-"] span.token.prolog, +body.vscode-light code[class*="language-"] span.token.doctype, +body.vscode-light code[class*="language-"] span.token.cdata { + color: #708090; +} + +body.vscode-light code[class*="language-"] span.token.punctuation { + color: #999999; +} + +body.vscode-light code[class*="language-"] span.token.namespace { + opacity: 0.7; +} + +body.vscode-light code[class*="language-"] span.token.property, +body.vscode-light code[class*="language-"] span.token.tag, +body.vscode-light code[class*="language-"] span.token.boolean, +body.vscode-light code[class*="language-"] span.token.number, +body.vscode-light code[class*="language-"] span.token.constant, +body.vscode-light code[class*="language-"] span.token.symbol, +body.vscode-light code[class*="language-"] span.token.deleted { + color: #990055; +} + +body.vscode-light code[class*="language-"] span.token.selector, +body.vscode-light code[class*="language-"] span.token.attr-name, +body.vscode-light code[class*="language-"] span.token.string, +body.vscode-light code[class*="language-"] span.token.char, +body.vscode-light code[class*="language-"] span.token.builtin, +body.vscode-light code[class*="language-"] span.token.inserted { + color: #669900; +} + +body.vscode-light code[class*="language-"] span.token.operator, +body.vscode-light code[class*="language-"] span.token.entity, +body.vscode-light code[class*="language-"] span.token.url, +body.vscode-light code.language-css span.token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); +} + +body.vscode-light code[class*="language-"] span.token.atrule, +body.vscode-light code[class*="language-"] span.token.attr-value, +body.vscode-light code[class*="language-"] span.token.keyword { + color: #0077aa; +} + +body.vscode-light code[class*="language-"] span.token.function, +body.vscode-light code[class*="language-"] span.token.class-name { + color: #dd4a68; +} + +body.vscode-light code[class*="language-"] span.token.regex, +body.vscode-light code[class*="language-"] span.token.important, +body.vscode-light code[class*="language-"] span.token.variable { + color: #ee9900; +}