diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6325663fc..d9e69e8f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ unreleased
* 外部サービス認証情報の配信
* 管理画面のモデレーションのUIを強化
* 管理画面からリモートユーザーの情報を更新できるように
+* シンタックスハイライトの強化
* 引用投稿を削除したとき単なるRenoteとしてタイムラインに残る問題を修正
* イタリック構文の判定の改善
* タイトル構文の判定の改善
diff --git a/package.json b/package.json
index d5afc934b..b561b4de4 100644
--- a/package.json
+++ b/package.json
@@ -97,8 +97,8 @@
"bootstrap-vue": "2.0.0-rc.11",
"cafy": "12.0.0",
"chai": "4.2.0",
- "chalk": "2.4.2",
"chai-http": "4.2.1",
+ "chalk": "2.4.2",
"commander": "2.19.0",
"crc-32": "1.2.0",
"css-loader": "1.0.1",
@@ -178,6 +178,7 @@
"parsimmon": "1.12.0",
"portscanner": "2.2.0",
"postcss-loader": "3.0.0",
+ "prismjs": "1.15.0",
"progress-bar-webpack-plugin": "1.12.0",
"promise-any": "0.2.0",
"promise-limit": "2.7.0",
@@ -230,6 +231,7 @@
"vue-js-modal": "1.3.28",
"vue-loader": "15.5.1",
"vue-marquee-text-component": "1.1.1",
+ "vue-prism-component": "1.1.1",
"vue-router": "3.0.2",
"vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2",
diff --git a/src/client/app/common/views/components/code-core.vue b/src/client/app/common/views/components/code-core.vue
new file mode 100644
index 000000000..a50d94394
--- /dev/null
+++ b/src/client/app/common/views/components/code-core.vue
@@ -0,0 +1,30 @@
+
+{{ code }}
+
+
+
diff --git a/src/client/app/common/views/components/code.vue b/src/client/app/common/views/components/code.vue
new file mode 100644
index 000000000..d52c9f7bc
--- /dev/null
+++ b/src/client/app/common/views/components/code.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/src/client/app/common/views/components/mfm.ts b/src/client/app/common/views/components/mfm.ts
index ad3d8204c..8ffa56666 100644
--- a/src/client/app/common/views/components/mfm.ts
+++ b/src/client/app/common/views/components/mfm.ts
@@ -6,8 +6,8 @@ import MkUrl from './url.vue';
import MkMention from './mention.vue';
import { concat, sum } from '../../../../../prelude/array';
import MkFormula from './formula.vue';
+import MkCode from './code.vue';
import MkGoogle from './google.vue';
-import syntaxHighlight from '../../../../../mfm/syntax-highlight';
import { host } from '../../../config';
import { preorderF, countNodesF } from '../../../../../prelude/tree';
@@ -170,21 +170,22 @@ export default Vue.component('misskey-flavored-markdown', {
}
case 'blockCode': {
- return [createElement('pre', {
- class: 'code'
- }, [
- createElement('code', {
- domProps: {
- innerHTML: syntaxHighlight(token.node.props.code)
- }
- })
- ])];
+ return [createElement(MkCode, {
+ key: Math.random(),
+ props: {
+ code: token.node.props.code,
+ lang: token.node.props.lang,
+ }
+ })];
}
case 'inlineCode': {
- return [createElement('code', {
- domProps: {
- innerHTML: syntaxHighlight(token.node.props.code)
+ return [createElement(MkCode, {
+ key: Math.random(),
+ props: {
+ code: token.node.props.code,
+ lang: token.node.props.lang,
+ inline: true
}
})];
}
diff --git a/src/client/app/common/views/components/misskey-flavored-markdown.vue b/src/client/app/common/views/components/misskey-flavored-markdown.vue
index 448df9aa8..5cb102240 100644
--- a/src/client/app/common/views/components/misskey-flavored-markdown.vue
+++ b/src/client/app/common/views/components/misskey-flavored-markdown.vue
@@ -24,25 +24,10 @@ export default Vue.extend({
background var(--mfmTitleBg)
border-radius 4px
- >>> .code
- margin 8px 0
-
>>> .quote
margin 8px
padding 6px 0 6px 12px
color var(--mfmQuote)
border-left solid 3px var(--mfmQuoteLine)
- >>> code
- padding 4px 8px
- margin 0 0.5em
- font-size 90%
- color #525252
- background var(--bg)
- border-radius 2px
-
- >>> pre > code
- padding 16px
- margin 0
-
diff --git a/src/mfm/syntax-highlight.ts b/src/mfm/syntax-highlight.ts
deleted file mode 100644
index 109923fb7..000000000
--- a/src/mfm/syntax-highlight.ts
+++ /dev/null
@@ -1,343 +0,0 @@
-import { capitalize, toUpperCase } from '../prelude/string';
-
-function escape(text: string) {
- return text
- .replace(/>/g, '>')
- .replace(/ b.length - a.length);
-
-const symbols = [
- '=',
- '+',
- '-',
- '*',
- '/',
- '%',
- '~',
- '^',
- '&',
- '|',
- '>',
- '<',
- '!',
- '?'
-];
-
-type Token = {
- html: string
- next: number
-};
-
-type Element = (code: string, i: number, source: string) => (Token | null);
-
-const elements: Element[] = [
- // comment
- code => {
- if (code.substr(0, 2) != '//') return null;
- const match = code.match(/^\/\/(.+?)(\n|$)/);
- if (!match) return null;
- const comment = match[0];
- return {
- html: ``,
- next: comment.length
- };
- },
-
- // block comment
- code => {
- const match = code.match(/^\/\*([\s\S]+?)\*\//);
- if (!match) return null;
- return {
- html: ``,
- next: match[0].length
- };
- },
-
- // string
- code => {
- if (!/^['"`]/.test(code)) return null;
- const begin = code[0];
- let str = begin;
- let thisIsNotAString = false;
- for (let i = 1; i < code.length; i++) {
- const char = code[i];
- if (char == '\\') {
- str += char;
- str += code[i + 1] || '';
- i++;
- continue;
- } else if (char == begin) {
- str += char;
- break;
- } else if (char == '\n' || i == (code.length - 1)) {
- thisIsNotAString = true;
- break;
- } else {
- str += char;
- }
- }
- if (thisIsNotAString) {
- return null;
- } else {
- return {
- html: `${escape(str)}`,
- next: str.length
- };
- }
- },
-
- // regexp
- code => {
- if (code[0] != '/') return null;
- let regexp = '';
- let thisIsNotARegexp = false;
- for (let i = 1; i < code.length; i++) {
- const char = code[i];
- if (char == '\\') {
- regexp += char;
- regexp += code[i + 1] || '';
- i++;
- continue;
- } else if (char == '/') {
- break;
- } else if (char == '\n' || i == (code.length - 1)) {
- thisIsNotARegexp = true;
- break;
- } else {
- regexp += char;
- }
- }
-
- if (thisIsNotARegexp) return null;
- if (regexp == '') return null;
- if (regexp.startsWith(' ') && regexp.endsWith(' ')) return null;
-
- return {
- html: `/${escape(regexp)}/`,
- next: regexp.length + 2
- };
- },
-
- // label
- code => {
- if (code[0] != '@') return null;
- const match = code.match(/^@([a-zA-Z_-]+?)\n/);
- if (!match) return null;
- const label = match[0];
- return {
- html: `${label}`,
- next: label.length
- };
- },
-
- // number
- (code, i, source) => {
- const prev = source[i - 1];
- if (prev && /[a-zA-Z]/.test(prev)) return null;
- if (!/^[\-\+]?[0-9\.]+/.test(code)) return null;
- const match = code.match(/^[\-\+]?[0-9\.]+/)[0];
- if (match) {
- return {
- html: `${match}`,
- next: match.length
- };
- } else {
- return null;
- }
- },
-
- // nan
- (code, i, source) => {
- const prev = source[i - 1];
- if (prev && /[a-zA-Z]/.test(prev)) return null;
- if (code.substr(0, 3) == 'NaN') {
- return {
- html: `NaN`,
- next: 3
- };
- } else {
- return null;
- }
- },
-
- // method
- code => {
- const match = code.match(/^([a-zA-Z_-]+?)\(/);
- if (!match) return null;
-
- if (match[1] == '-') return null;
-
- return {
- html: `${match[1]}`,
- next: match[1].length
- };
- },
-
- // property
- (code, i, source) => {
- const prev = source[i - 1];
- if (prev != '.') return null;
-
- const match = code.match(/^[a-zA-Z0-9_-]+/);
- if (!match) return null;
-
- return {
- html: `${match[0]}`,
- next: match[0].length
- };
- },
-
- // keyword
- (code, i, source) => {
- const prev = source[i - 1];
- if (prev && /[a-zA-Z]/.test(prev)) return null;
-
- const match = keywords.filter(k => code.substr(0, k.length) == k)[0];
- if (match) {
- if (/^[a-zA-Z]/.test(code.substr(match.length))) return null;
- return {
- html: `${match}`,
- next: match.length
- };
- } else {
- return null;
- }
- },
-
- // symbol
- code => {
- const match = symbols.filter(s => code[0] == s)[0];
- if (match) {
- return {
- html: `${match}`,
- next: 1
- };
- } else {
- return null;
- }
- }
-];
-
-// TODO: specify lang
-export default (source: string, lang?: string): string => {
- let code = source;
- let html = '';
-
- let i = 0;
-
- function push(token: Token) {
- html += token.html;
- code = code.substr(token.next);
- i += token.next;
- }
-
- while (code != '') {
- const parsed = elements.some(el => {
- const e = el(code, i, source);
- if (e) {
- push(e);
- return true;
- } else {
- return false;
- }
- });
-
- if (!parsed) {
- push({
- html: escape(code[0]),
- next: 1
- });
- }
- }
-
- return html;
-};