Browse Source

add editor shortcuts

WNomunomu 1 year ago
parent
commit
5af44a6bbd
1 changed files with 70 additions and 1 deletions
  1. 70 1
      packages/editor/src/client/stores/use-editor-shortcuts.ts

+ 70 - 1
packages/editor/src/client/stores/use-editor-shortcuts.ts

@@ -1,6 +1,6 @@
 import { useEffect, useCallback } from 'react';
 import { useEffect, useCallback } from 'react';
 
 
-import type { SelectionRange } from '@codemirror/state';
+import type { SelectionRange, StateCommand } from '@codemirror/state';
 import { EditorSelection } from '@codemirror/state';
 import { EditorSelection } from '@codemirror/state';
 import { keymap } from '@codemirror/view';
 import { keymap } from '@codemirror/view';
 
 
@@ -8,11 +8,80 @@ import type { UseCodeMirrorEditor } from '../services';
 
 
 import type { EditorView } from 'src/interfaces';
 import type { EditorView } from 'src/interfaces';
 
 
+const addSymbol = (text: string, symbol: string, shouldWrap: boolean): string => {
+  return shouldWrap ? `${symbol}${text}${symbol}` : `${symbol}${text}`;
+};
+
+const removeSymbol = (text: string, symbol: string): string => {
+  const replaceRegex = new RegExp(`^${symbol}|${symbol}$`, 'g');
+  return text.replace(replaceRegex, '');
+};
+
+const processLine = (line: string, symbol: string, shouldWrap: boolean, safeSymbol: string): string => {
+  const fullWrapRegex = new RegExp(`^${safeSymbol}.*${safeSymbol}$`);
+  const partialWrapRegex = new RegExp(`^${safeSymbol}|${safeSymbol}$`);
+
+  if (shouldWrap ? fullWrapRegex.test(line) : partialWrapRegex.test(line)) {
+    return removeSymbol(line, safeSymbol);
+  }
+  return addSymbol(line, symbol, shouldWrap);
+};
+
+const generateAddMarkdownSymbolCommand = (symbol: string, shouldWrap = false): StateCommand => {
+  const addMarkdownSymbolCommand: StateCommand = ({ state, dispatch }) => {
+    const escapeSymbol = (symbol: string): string => {
+      const specialCharactersRegex = /[.*+?^${}()|[\]\\]/g;
+      return symbol.replace(specialCharactersRegex, '\\$&');
+    };
+
+    const safeSymbol = escapeSymbol(symbol);
+
+    if (state.selection.ranges.length === 0) return false;
+
+    dispatch(state.update({
+      changes: state.selection.ranges.map((range) => {
+        const selectedText = state.sliceDoc(range.from, range.to);
+
+        const changedText = selectedText
+          .split('\n')
+          .map(line => processLine(line, symbol, shouldWrap, safeSymbol))
+          .join('\n');
+
+        return {
+          from: range.from,
+          to: range.to,
+          insert: changedText,
+        };
+      }),
+    }));
+
+    return true;
+  };
+
+  return addMarkdownSymbolCommand;
+};
 
 
 export const useEditorShortcuts = (
 export const useEditorShortcuts = (
     codeMirrorEditor?: UseCodeMirrorEditor,
     codeMirrorEditor?: UseCodeMirrorEditor,
 ): void => {
 ): void => {
 
 
+  useEffect(() => {
+
+    const extension = keymap.of([
+      { key: 'mod-i', run: generateAddMarkdownSymbolCommand('*', true) },
+      { key: 'mod-b', run: generateAddMarkdownSymbolCommand('**', true) },
+      { key: 'mod-shift-x', run: generateAddMarkdownSymbolCommand('~~', true) },
+      { key: 'mod-shift-c', run: generateAddMarkdownSymbolCommand('`', true) },
+      { key: 'mod-shift-7', run: generateAddMarkdownSymbolCommand('1. ') },
+      { key: 'mod-shift-8', run: generateAddMarkdownSymbolCommand('- ') },
+      { key: 'mod-shift-9', run: generateAddMarkdownSymbolCommand('> ') },
+    ]);
+
+    const cleanupFunction = codeMirrorEditor?.appendExtensions?.(extension);
+    return cleanupFunction;
+
+  }, [codeMirrorEditor]);
+
   const addMultiCursor = useCallback((view: EditorView, direction: 'up' | 'down') => {
   const addMultiCursor = useCallback((view: EditorView, direction: 'up' | 'down') => {
 
 
     const selection = view.state.selection;
     const selection = view.state.selection;