diff options
| author | Craig Jennings <c@cjennings.net> | 2025-10-20 16:17:30 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-10-20 16:17:30 -0500 |
| commit | df4469f231e86877b7d055bf989220a5fbc0a763 (patch) | |
| tree | ea3ef00d10a7a7c1366213d493ea4e2546b9d783 /modules/prog-shell.el | |
| parent | ce8477e80e0a837ac462b97c1a4b5d834838d6d3 (diff) | |
feat:programming: Enhance language-specific keybindings and setup
Add language-specific keybindings and configurations for C, Go,
Python, and shell scripting panels. Introduce system utility
function declarations and improve keybinding consistency across
languages. Implement keybindings for debug, format, and static
analysis tools tailored to each programming language, enhancing the
developer experience and workflow efficiency.
Diffstat (limited to 'modules/prog-shell.el')
| -rw-r--r-- | modules/prog-shell.el | 165 |
1 files changed, 161 insertions, 4 deletions
diff --git a/modules/prog-shell.el b/modules/prog-shell.el index e63387cf..e9d12839 100644 --- a/modules/prog-shell.el +++ b/modules/prog-shell.el @@ -2,14 +2,171 @@ ;; author Craig Jennings <c@cjennings.net> ;;; Commentary: -;; Open any *.sh buffer and sh-mode loads with Flycheck attached, so syntax errors appear immediately. -;; Re-save or invoke C-c ! l to refresh diagnostics while you iterate on scripts. +;; Modern shell scripting environment with LSP, tree-sitter, linting, and formatting. +;; +;; Installation: +;; sudo pacman -S shellcheck shfmt # Linter and formatter +;; npm install -g bash-language-server +;; +;; Features: +;; - LSP: Intelligent completion, hover docs, jump to definition +;; - ShellCheck: Industry-standard linting (catches common bugs) +;; - shfmt: Google's shell formatter (consistent style) +;; - Tree-sitter: Better syntax highlighting via bash-ts-mode +;; - Auto-executable: Scripts with shebangs auto-get execute permission +;; +;; Workflow: +;; 1. Open .sh file → LSP auto-starts, ShellCheck runs +;; 2. <f6> → Format with shfmt +;; 3. C-c ! l → Show all ShellCheck diagnostics +;; 4. Save → Auto-set executable bit if script has shebang ;;; Code: +(defvar bash-ts-mode-map) +(defvar sh-mode-map) + +;; Forward declarations for LSP +(declare-function lsp-deferred "lsp-mode") +(defvar lsp-bash-explainshell-endpoint) +(defvar lsp-bash-highlight-parsing-errors) + +;; Forward declarations for sh-script +(defvar sh-learn-basic-offset) + +;; Forward declarations for flycheck +(defvar flycheck-shellcheck-follow-sources) +(defvar flycheck-shellcheck-excluded-warnings) +(defvar flycheck-checkers) + +;; Forward declarations for system utilities +(declare-function cj/disabled "system-defaults") + +(defvar bash-language-server-path "bash-language-server" + "Path to bash-language-server executable. +Install with: npm install -g bash-language-server") + +(defvar shfmt-path "shfmt" + "Path to shfmt executable. +Install with: sudo pacman -S shfmt") + +(defvar shellcheck-path "shellcheck" + "Path to shellcheck executable. +Install with: sudo pacman -S shellcheck") + +;; ------------------------------- Shell Script Setup ------------------------------ +;; preferences for shell scripting + +(defun cj/shell-script-setup () + "Settings for shell script editing (bash, sh, zsh)." + (setq-local indent-tabs-mode nil) ;; use spaces, not tabs + (setq-local sh-basic-offset 2) ;; 2 spaces (common shell convention) + (setq-local tab-width 2) ;; tab displays as 2 spaces + (setq-local fill-column 80) ;; wrap at 80 columns + (electric-pair-mode t) ;; automatic quote/bracket pairing + + ;; Enable LSP if available + (when (and (fboundp 'lsp-deferred) + (executable-find bash-language-server-path)) + (lsp-deferred))) + +(defun cj/shell-run-shellcheck () + "Run shellcheck on the current shell script." + (interactive) + (if (executable-find shellcheck-path) + (if buffer-file-name + (compile (format "%s %s" shellcheck-path (shell-quote-argument buffer-file-name))) + (message "No file associated with this buffer")) + (message "shellcheck not found. Install with: sudo pacman -S shellcheck"))) + +(defun cj/shell-mode-keybindings () + "Set up keybindings for shell script editing. +Overrides default prog-mode keybindings with shell-specific commands." + ;; S-f5: Run shellcheck (static analysis) + (local-set-key (kbd "S-<f5>") #'cj/shell-run-shellcheck) + + ;; S-f6: Disabled (shell scripts don't have interactive debugging like gdb/pdb) + (local-set-key (kbd "S-<f6>") #'cj/disabled)) + +;; Apply to both legacy sh-mode and modern bash-ts-mode +(add-hook 'sh-mode-hook 'cj/shell-script-setup) +(add-hook 'bash-ts-mode-hook 'cj/shell-script-setup) +(add-hook 'sh-mode-hook 'cj/shell-mode-keybindings) +(add-hook 'bash-ts-mode-hook 'cj/shell-mode-keybindings) + +;; -------------------------------- Shell Scripts ---------------------------------- +;; built-in shell script mode configuration + (use-package sh-script - :defer .5 - :hook (sh-mode . flycheck-mode)) + :ensure nil ;; built-in + :mode (("\\.sh\\'" . bash-ts-mode) ;; .sh files use bash-ts-mode + ("\\.bash\\'" . bash-ts-mode) ;; .bash files + ("\\.zsh\\'" . sh-mode) ;; zsh doesn't have ts-mode yet + ("/PKGBUILD\\'" . bash-ts-mode)) ;; Arch Linux PKGBUILDs + :config + ;; Set default shell type + (setq sh-shell-file "/bin/bash") + + ;; Improve shell script detection + (setq sh-learn-basic-offset t)) + +;; -------------------------------- LSP for Shell ---------------------------------- +;; Shell script LSP configuration using bash-language-server +;; Core LSP setup is in prog-general.el + +(use-package lsp-mode + :hook ((sh-mode bash-ts-mode) . lsp-deferred) + :config + ;; Configure bash-language-server + (setq lsp-bash-explainshell-endpoint nil) ;; Disable external API calls + (setq lsp-bash-highlight-parsing-errors t)) + +;; --------------------------------- ShellCheck ------------------------------------ +;; Industry-standard shell script linter + +(use-package flycheck + :if (executable-find shellcheck-path) + :hook ((sh-mode bash-ts-mode) . flycheck-mode) + :config + ;; Prefer ShellCheck over basic sh linter + (setq flycheck-shellcheck-follow-sources t) + (setq flycheck-shellcheck-excluded-warnings '("SC2086")) ;; Customize as needed + + ;; Use ShellCheck for shell scripts + (add-to-list 'flycheck-checkers 'sh-shellcheck)) + +;; -------------------------------- Formatting ------------------------------------- +;; Format shell scripts with shfmt + +(use-package shfmt + :if (executable-find shfmt-path) + :hook ((sh-mode bash-ts-mode) . shfmt-on-save-mode) + :bind ((:map sh-mode-map + ("<f6>" . shfmt-buffer) + ("C-; f" . shfmt-buffer)) + (:map bash-ts-mode-map + ("<f6>" . shfmt-buffer) + ("C-; f" . shfmt-buffer))) + :custom + (shfmt-arguments '("-i" "2" ;; indent with 2 spaces + "-ci" ;; indent switch cases + "-bn"))) ;; binary ops like && and | may start a line + +;; ---------------------------- Auto-Executable Scripts ---------------------------- +;; Automatically set execute permission on shell scripts with shebangs + +(defun cj/make-script-executable () + "Make the current file executable if it has a shebang." + (when (and buffer-file-name + (not (file-executable-p buffer-file-name)) + (save-excursion + (goto-char (point-min)) + (looking-at "^#!"))) + (set-file-modes buffer-file-name + (logior (file-modes buffer-file-name) #o111)) + (message "Made %s executable" (file-name-nondirectory buffer-file-name)))) + +(add-hook 'after-save-hook 'cj/make-script-executable) (provide 'prog-shell) ;;; prog-shell.el ends here |
