diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx
index 5e118be3b..188bb020a 100644
--- a/client/components/codeEditor/codeEditor.jsx
+++ b/client/components/codeEditor/codeEditor.jsx
@@ -20,9 +20,9 @@ import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
import * as themes from '@uiw/codemirror-themes-all';
const themeCompartment = new Compartment();
-
const highlightCompartment = new Compartment();
+import { customKeymap } from './customKeyMap.js';
import { homebreweryFold, hbFolding } from './customFolding.js';
import { customHighlightStyle, tokenizeCustomMarkdown } from './customHighlight.js';
import { legacyCustomHighlightStyle, legacyTokenizeCustomMarkdown } from './legacyCustomHighlight.js'; //only makes highlight for
@@ -64,7 +64,6 @@ const createHighlightPlugin = (renderer)=>{
);
};
-
const CodeEditor = forwardRef(
(
{
@@ -92,37 +91,6 @@ const CodeEditor = forwardRef(
}
});
- const boldCommand = (view)=>{
- const { from, to } = view.state.selection.main;
- const selected = view.state.doc.sliceString(from, to);
- const text = `**${selected}**`;
-
- view.dispatch({
- changes : { from, to, insert: text },
- selection : { anchor: from + text.length },
- });
-
- return true;
- };
-
- const italicCommand = (view)=>{
- const { from, to } = view.state.selection.main;
- const selected = view.state.doc.sliceString(from, to);
- const text = `*${selected}*`;
-
- view.dispatch({
- changes : { from, to, insert: text },
- selection : { anchor: from + text.length },
- });
-
- return true;
- };
-
- const customKeymap = keymap.of([
- { key: 'Mod-b', run: boldCommand },
- { key: 'Mod-i', run: italicCommand },
- ]);
-
const highlightExtension = renderer === 'V3'
? syntaxHighlighting(customHighlightStyle)
: syntaxHighlighting(legacyCustomHighlightStyle);
@@ -134,13 +102,10 @@ const CodeEditor = forwardRef(
highlightExtension,
];
-
- const languageExtension =
- language === 'css' ? css() : markdown({ base: markdownLanguage, codeLanguages: languages });
+ const languageExtension = language === 'css' ? css() : markdown({ base: markdownLanguage, codeLanguages: languages });
const themeExtension = Array.isArray(themes[editorTheme]) ? themes[editorTheme] : [];
-
return [
history(),
keymap.of(defaultKeymap),
diff --git a/client/components/codeEditor/customKeyMap.js b/client/components/codeEditor/customKeyMap.js
new file mode 100644
index 000000000..9be1ac9e5
--- /dev/null
+++ b/client/components/codeEditor/customKeyMap.js
@@ -0,0 +1,224 @@
+import { keymap } from '@codemirror/view';
+
+const indentMore = (view) => {
+ const { from, to } = view.state.selection.main;
+ const lines = [];
+ for (let l = view.state.doc.lineAt(from).number; l <= view.state.doc.lineAt(to).number; l++) {
+ const line = view.state.doc.line(l);
+ lines.push({ from: line.from, to: line.from, insert: ' ' }); // 2 spaces for tab
+ }
+ view.dispatch({ changes: lines });
+ return true;
+};
+
+const indentLess = (view) => {
+ const { from, to } = view.state.selection.main;
+ const lines = [];
+ for (let l = view.state.doc.lineAt(from).number; l <= view.state.doc.lineAt(to).number; l++) {
+ const line = view.state.doc.line(l);
+ const match = line.text.match(/^ {1,2}/); // match up to 2 spaces
+ if (match) {
+ lines.push({ from: line.from, to: line.from + match[0].length, insert: '' });
+ }
+ }
+ if (lines.length > 0) view.dispatch({ changes: lines });
+ return true;
+};
+
+const makeBold = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const text = selected.startsWith('**') && selected.endsWith('**')
+ ? selected.slice(2, -2)
+ : `**${selected}**`;
+ view.dispatch({
+ changes : { from, to, insert: text },
+ selection : { anchor: from + text.length },
+ });
+ return true;
+};
+
+const makeItalic = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const text = selected.startsWith('*') && selected.endsWith('*')
+ ? selected.slice(1, -1)
+ : `*${selected}*`;
+ view.dispatch({
+ changes : { from, to, insert: text },
+ selection : { anchor: from + text.length },
+ });
+ return true;
+};
+
+const makeUnderline = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const text = selected.startsWith('') && selected.endsWith('')
+ ? selected.slice(3, -4)
+ : `${selected}`;
+ view.dispatch({
+ changes : { from, to, insert: text },
+ selection : { anchor: from + text.length },
+ });
+ return true;
+};
+
+const makeSuper = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const text = selected.startsWith('^') && selected.endsWith('^')
+ ? selected.slice(1, -1)
+ : `^${selected}^`;
+ view.dispatch({
+ changes : { from, to, insert: text },
+ selection : { anchor: from + text.length },
+ });
+ return true;
+};
+
+const makeSub = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const text = selected.startsWith('^^') && selected.endsWith('^^')
+ ? selected.slice(2, -2)
+ : `^^${selected}^^`;
+ view.dispatch({
+ changes : { from, to, insert: text },
+ selection : { anchor: from + text.length },
+ });
+ return true;
+};
+
+const makeNbsp = (view)=>{
+ const { from, to } = view.state.selection.main;
+ view.dispatch({ changes: { from, to, insert: ' ' } });
+ return true;
+};
+
+const makeSpace = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const match = selected.match(/^{{width:(\d+)% }}$/);
+ let newText = '{{width:10% }}';
+ if(match) {
+ const percent = Math.min(parseInt(match[1], 10) + 10, 100);
+ newText = `{{width:${percent}% }}`;
+ }
+ view.dispatch({ changes: { from, to, insert: newText } });
+ return true;
+};
+
+const removeSpace = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const match = selected.match(/^{{width:(\d+)% }}$/);
+ if(match) {
+ const percent = parseInt(match[1], 10) - 10;
+ const newText = percent > 0 ? `{{width:${percent}% }}` : '';
+ view.dispatch({ changes: { from, to, insert: newText } });
+ }
+ return true;
+};
+
+const makeSpan = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const text = selected.startsWith('{{') && selected.endsWith('}}')
+ ? selected.slice(2, -2)
+ : `{{${selected}}}`;
+ view.dispatch({ changes: { from, to, insert: text } });
+ return true;
+};
+
+const makeDiv = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const text = selected.startsWith('{{') && selected.endsWith('}}')
+ ? selected.slice(2, -2)
+ : `{{\n${selected}\n}}`;
+ view.dispatch({ changes: { from, to, insert: text } });
+ return true;
+};
+
+const makeComment = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const isHtmlComment = selected.startsWith('');
+ const text = isHtmlComment
+ ? selected.slice(4, -3)
+ : ``;
+ view.dispatch({ changes: { from, to, insert: text } });
+ return true;
+};
+
+const makeLink = (view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to).trim();
+ const isLink = /^\[(.*)\]\((.*)\)$/.exec(selected);
+ const text = isLink ? `${isLink[1]} ${isLink[2]}` : `[${selected || 'alt text'}](url)`;
+ view.dispatch({ changes: { from, to, insert: text } });
+ return true;
+};
+
+const makeList = (type)=>(view)=>{
+ const { from, to } = view.state.selection.main;
+ const lines = [];
+ for (let l = from; l <= to; l++) {
+ const lineText = view.state.doc.line(l + 1).text;
+ lines.push(lineText);
+ }
+ const joined = lines.join('\n');
+ let newText;
+ if(type === 'UL') newText = joined.replace(/^/gm, '- ');
+ else newText = joined.replace(/^/gm, (m, i)=>`${i + 1}. `);
+ view.dispatch({ changes: { from, to, insert: newText } });
+ return true;
+};
+
+const makeHeader = (level)=>(view)=>{
+ const { from, to } = view.state.selection.main;
+ const selected = view.state.doc.sliceString(from, to);
+ const text = `${'#'.repeat(level)} ${selected}`;
+ view.dispatch({ changes: { from, to, insert: text } });
+ return true;
+};
+
+const newColumn = (view)=>{
+ const { from, to } = view.state.selection.main;
+ view.dispatch({ changes: { from, to, insert: '\n\\column\n\n' } });
+ return true;
+};
+
+const newPage = (view)=>{
+ const { from, to } = view.state.selection.main;
+ view.dispatch({ changes: { from, to, insert: '\n\\page\n\n' } });
+ return true;
+};
+
+export const customKeymap = keymap.of([
+ { key: 'Tab', run: indentMore },
+ { key: 'Shift-Tab', run: indentLess },
+ { key: 'Mod-b', run: makeBold },
+ { key: 'Mod-i', run: makeItalic },
+ { key: 'Mod-u', run: makeUnderline },
+ { key: 'Shift-Mod-=', run: makeSuper },
+ { key: 'Mod-=', run: makeSub },
+ { key: 'Mod-.', run: makeNbsp },
+ { key: 'Shift-Mod-.', run: makeSpace },
+ { key: 'Shift-Mod-,', run: removeSpace },
+ { key: 'Mod-m', run: makeSpan },
+ { key: 'Shift-Mod-m', run: makeDiv },
+ { key: 'Mod-/', run: makeComment },
+ { key: 'Mod-k', run: makeLink },
+ { key: 'Mod-l', run: makeList('UL') },
+ { key: 'Shift-Mod-l', run: makeList('OL') },
+ { key: 'Shift-Mod-1', run: makeHeader(1) },
+ { key: 'Shift-Mod-2', run: makeHeader(2) },
+ { key: 'Shift-Mod-3', run: makeHeader(3) },
+ { key: 'Shift-Mod-4', run: makeHeader(4) },
+ { key: 'Shift-Mod-5', run: makeHeader(5) },
+ { key: 'Shift-Mod-6', run: makeHeader(6) },
+ { key: 'Shift-Mod-Enter', run: newColumn },
+ { key: 'Mod-Enter', run: newPage },
+]);