From 13fe6c5f3dd2e79b9bcbdf62d955123a847e137d Mon Sep 17 00:00:00 2001 From: fccview Date: Wed, 19 Nov 2025 15:54:42 +0000 Subject: [PATCH] fix bash language highlight and enhance editor with tabbing functionality --- .../FeatureComponents/Scripts/BashEditor.tsx | 86 +++++++++++++++++-- package.json | 1 + yarn.lock | 7 ++ 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/app/_components/FeatureComponents/Scripts/BashEditor.tsx b/app/_components/FeatureComponents/Scripts/BashEditor.tsx index 78b92c8..5d8bd6a 100644 --- a/app/_components/FeatureComponents/Scripts/BashEditor.tsx +++ b/app/_components/FeatureComponents/Scripts/BashEditor.tsx @@ -1,9 +1,10 @@ "use client"; import { useEffect, useRef, useState } from "react"; -import { EditorView } from "@codemirror/view"; -import { EditorState } from "@codemirror/state"; -import { javascript } from "@codemirror/lang-javascript"; +import { EditorView, keymap } from "@codemirror/view"; +import { EditorState, Transaction } from "@codemirror/state"; +import { shell } from "@codemirror/legacy-modes/mode/shell"; +import { StreamLanguage } from "@codemirror/language"; import { oneDark } from "@codemirror/theme-one-dark"; import { Button } from "@/app/_components/GlobalComponents/UIElements/Button"; import { Terminal, Copy, Check } from "lucide-react"; @@ -27,19 +28,88 @@ export const BashEditor = ({ const editorRef = useRef(null); const editorViewRef = useRef(null); + const insertFourSpaces = ({ + state, + dispatch, + }: { + state: EditorState; + dispatch: (tr: Transaction) => void; + }) => { + if (state.selection.ranges.some((range) => !range.empty)) { + const changes = state.selection.ranges + .map((range) => { + const fromLine = state.doc.lineAt(range.from).number; + const toLine = state.doc.lineAt(range.to).number; + const changes = []; + for (let line = fromLine; line <= toLine; line++) { + const lineObj = state.doc.line(line); + changes.push({ from: lineObj.from, insert: " " }); + } + return changes; + }) + .flat(); + dispatch(state.update({ changes })); + } else { + dispatch(state.update(state.replaceSelection(" "))); + } + return true; + }; + + const removeFourSpaces = ({ + state, + dispatch, + }: { + state: EditorState; + dispatch: (tr: Transaction) => void; + }) => { + if (state.selection.ranges.some((range) => !range.empty)) { + const changes = state.selection.ranges + .map((range) => { + const fromLine = state.doc.lineAt(range.from).number; + const toLine = state.doc.lineAt(range.to).number; + const changes = []; + for (let line = fromLine; line <= toLine; line++) { + const lineObj = state.doc.line(line); + const indent = lineObj.text.match(/^ /); + if (indent) { + changes.push({ from: lineObj.from, to: lineObj.from + 4 }); + } + } + return changes; + }) + .flat(); + dispatch(state.update({ changes })); + } else { + const cursor = state.selection.main.head; + const line = state.doc.lineAt(cursor); + const beforeCursor = line.text.slice(0, cursor - line.from); + const spacesToRemove = beforeCursor.match(/ {1,4}$/); + if (spacesToRemove) { + const removeCount = spacesToRemove[0].length; + dispatch( + state.update({ + changes: { from: cursor - removeCount, to: cursor }, + }) + ); + } + } + return true; + }; + useEffect(() => { if (!editorRef.current) return; - const bashLanguage = javascript({ - typescript: false, - jsx: false, - }); + const bashLanguage = StreamLanguage.define(shell); const state = EditorState.create({ doc: value || placeholder, extensions: [ bashLanguage, oneDark, + keymap.of([ + { key: "Tab", run: insertFourSpaces }, + { key: "Shift-Tab", run: removeFourSpaces }, + ]), EditorView.updateListener.of((update: any) => { if (update.docChanged) { onChange(update.state.doc.toString()); @@ -134,4 +204,4 @@ export const BashEditor = ({ ); -} +}; diff --git a/package.json b/package.json index 02e5f1a..b89622c 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@codemirror/commands": "^6.8.1", "@codemirror/lang-javascript": "^6.2.4", "@codemirror/language": "^6.11.3", + "@codemirror/legacy-modes": "^6.5.2", "@codemirror/lint": "^6.8.5", "@codemirror/search": "^6.5.11", "@codemirror/state": "^6.5.2", diff --git a/yarn.lock b/yarn.lock index 19f6c1d..5d7e303 100644 --- a/yarn.lock +++ b/yarn.lock @@ -856,6 +856,13 @@ "@lezer/lr" "^1.0.0" style-mod "^4.0.0" +"@codemirror/legacy-modes@^6.5.2": + version "6.5.2" + resolved "https://registry.yarnpkg.com/@codemirror/legacy-modes/-/legacy-modes-6.5.2.tgz#7e2976c79007cd3fa9ed8a1d690892184a7f5ecf" + integrity sha512-/jJbwSTazlQEDOQw2FJ8LEEKVS72pU0lx6oM54kGpL8t/NJ2Jda3CZ4pcltiKTdqYSRk3ug1B3pil1gsjA6+8Q== + dependencies: + "@codemirror/language" "^6.0.0" + "@codemirror/lint@^6.0.0", "@codemirror/lint@^6.8.5": version "6.8.5" resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-6.8.5.tgz#9edaa808e764e28e07665b015951934c8ec3a418"