diff options
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/prog-c.el | 19 | ||||
| -rw-r--r-- | modules/prog-general.el | 37 | ||||
| -rw-r--r-- | modules/prog-go.el | 34 | ||||
| -rw-r--r-- | modules/prog-python.el | 38 | ||||
| -rw-r--r-- | modules/prog-shell.el | 165 |
5 files changed, 276 insertions, 17 deletions
diff --git a/modules/prog-c.el b/modules/prog-c.el index 12c28e54..d9191bf4 100644 --- a/modules/prog-c.el +++ b/modules/prog-c.el @@ -40,6 +40,9 @@ ;; Forward declarations for compile (declare-function recompile "compile") +;; Forward declarations for system utilities +(declare-function cj/disabled "system-defaults") + (defvar clangd-path "clangd" "Path to clangd language server executable.") @@ -93,7 +96,7 @@ :if (executable-find clang-format-path) :bind (:map c-mode-base-map ("<f6>" . clang-format-buffer) - ("C-c f" . clang-format-buffer))) + ("C-; f" . clang-format-buffer))) ;; -------------------------------- Compilation -------------------------------- ;; Smart compilation with project detection @@ -132,10 +135,16 @@ ;; -------------------------------- Keybindings -------------------------------- (defun cj/c-mode-keybindings () - "Set up keybindings for C programming." - (local-set-key (kbd "S-<f2>") #'compile) - (local-set-key (kbd "S-<f3>") #'gdb) - (local-set-key (kbd "<f5>") #'recompile)) + "Set up keybindings for C programming. +Overrides default prog-mode keybindings with C-specific commands." + ;; S-f4: Recompile (override default - C uses this more than projectile-compile) + (local-set-key (kbd "S-<f4>") #'recompile) + + ;; S-f5: Static analysis placeholder (could add clang-tidy, cppcheck, etc.) + (local-set-key (kbd "S-<f5>") #'cj/disabled) + + ;; S-f6: Debug with GDB + (local-set-key (kbd "S-<f6>") #'gdb)) (add-hook 'c-mode-hook 'cj/c-mode-keybindings) (add-hook 'c-ts-mode-hook 'cj/c-mode-keybindings) diff --git a/modules/prog-general.el b/modules/prog-general.el index 00b6fbd3..f6ebfe09 100644 --- a/modules/prog-general.el +++ b/modules/prog-general.el @@ -5,6 +5,31 @@ ;; This module provides general programming functionality not related to a ;; specific programming language, such as code-folding, project management, ;; highlighting symbols, snippets, and whitespace management. +;; +;; Keybinding Scheme: +;; ------------------ +;; Unified keybindings across all programming languages using Projectile +;; for project-aware operations with language-specific overrides. +;; +;; Global Keybindings (all prog-mode buffers): +;; F4 - projectile-compile-project (smart compilation) +;; S-F4 - recompile (repeat last compile) +;; F5 - projectile-test-project (run tests) +;; S-F5 - Language-specific static analysis +;; F6 - projectile-run-project (run/execute) +;; S-F6 - Language-specific debugger +;; C-; f - Language-specific formatter +;; +;; Quick Reference Table: +;; | Key | Global | C | Go | Python | Shell | +;; |-------|----------|---------------|-------------|-------------|-------------| +;; | F4 | compile | compile | compile | compile | compile | +;; | S-F4 | recompile| recompile | (projectile)| (projectile)| (projectile)| +;; | F5 | test | test | test | test | test | +;; | S-F5 | (none) | disabled | staticcheck | mypy | shellcheck | +;; | F6 | run | run | run | run | run | +;; | S-F6 | (none) | gdb | dlv | pdb | disabled | +;; | C-; f | format | clang-format | gofmt | blacken | shfmt | ;;; Code: @@ -29,8 +54,12 @@ (declare-function dired-get-filename "dired") (declare-function global-treesit-auto-mode "treesit-auto") (declare-function treesit-auto-add-to-auto-mode-alist "treesit-auto") +(declare-function treesit-auto-recipe-lang "treesit-auto") (declare-function highlight-indent-guides-mode "highlight-indent-guides") +;; Forward declarations for treesit-auto variables +(defvar treesit-auto-recipe-list) + ;; Forward declarations for functions defined later in this file (declare-function cj/find-project-root-file "prog-general") (declare-function cj/project-switch-actions "prog-general") @@ -48,7 +77,13 @@ (setq-default display-line-numbers-width 3) ;; 3 characters reserved for line numbers (turn-on-visual-line-mode) ;; word-wrapping (auto-fill-mode) ;; auto wrap at the fill column set - (local-set-key (kbd "M-;") 'comment-dwim)) ;; comment/uncomment region as appropriate + (local-set-key (kbd "M-;") 'comment-dwim) ;; comment/uncomment region as appropriate + + ;; Project-wide commands (can be overridden by language-specific modes) + (local-set-key (kbd "<f4>") 'projectile-compile-project) ;; compile project + (local-set-key (kbd "S-<f4>") 'recompile) ;; recompile (repeat last) + (local-set-key (kbd "<f5>") 'projectile-test-project) ;; run tests + (local-set-key (kbd "<f6>") 'projectile-run-project)) ;; run project (add-hook 'prog-mode-hook #'cj/general-prog-settings) (add-hook 'html-mode-hook #'cj/general-prog-settings) diff --git a/modules/prog-go.el b/modules/prog-go.el index 1526698e..465cbf14 100644 --- a/modules/prog-go.el +++ b/modules/prog-go.el @@ -27,6 +27,10 @@ This is where tools like goimports and staticcheck are installed.") "Path to gopls (Go language server). Install with: go install golang.org/x/tools/gopls@latest") +(defvar dlv-path "dlv" + "Path to Delve debugger. +Install with: go install github.com/go-delve/delve/cmd/dlv@latest") + (defvar go-ts-mode-map) (defvar go-mod-ts-mode-map) @@ -38,6 +42,7 @@ Install with: go install golang.org/x/tools/gopls@latest") ;; Forward declarations for external packages (declare-function company-mode "company") +(declare-function cj/disabled "system-defaults") (defvar gofmt-command) ;; ---------------------------------- Go Setup --------------------------------- @@ -68,16 +73,35 @@ Install with: go install golang.org/x/tools/gopls@latest") (message "staticcheck not found at %s. Install with: go install honnef.co/go/tools/cmd/staticcheck@latest" staticcheck-bin)))) +(defun cj/go-debug () + "Start Delve debugger for the current Go package." + (interactive) + (let* ((dlv-bin (expand-file-name dlv-path go-bin-path)) + (default-directory (if buffer-file-name + (file-name-directory buffer-file-name) + default-directory))) + (if (or (executable-find dlv-path) + (file-executable-p dlv-bin)) + (gud-gdb (format "%s debug" (or (executable-find dlv-path) dlv-bin))) + (message "Delve not found. Install with: go install github.com/go-delve/delve/cmd/dlv@latest")))) + +(defun cj/go-mode-keybindings () + "Set up keybindings for Go programming. +Overrides default prog-mode keybindings with Go-specific commands." + ;; S-f5: Run staticcheck (static analysis) + (local-set-key (kbd "S-<f5>") #'cj/go-staticcheck) + + ;; S-f6: Debug with Delve + (local-set-key (kbd "S-<f6>") #'cj/go-debug)) + ;; ---------------------------------- Go Mode ---------------------------------- ;; go-ts-mode configuration (treesit-based Go editing) (use-package go-mode - :hook (go-ts-mode . cj/go-setup) + :hook ((go-ts-mode . cj/go-setup) + (go-ts-mode . cj/go-mode-keybindings)) :bind (:map go-ts-mode-map - ("<f6>" . gofmt) - ("C-c 6" . gofmt) - ("<f4>" . cj/go-staticcheck) - ("C-c 4" . cj/go-staticcheck)) + ("C-; f" . gofmt)) ;; Override global formatter with gofmt/goimports :mode (("\\.go\\'" . go-ts-mode) ;; .go files use go-ts-mode ("go\\.mod\\'" . go-mod-ts-mode)) ;; go.mod uses go-mod-ts-mode :config diff --git a/modules/prog-python.el b/modules/prog-python.el index 2775061d..2eee7c50 100644 --- a/modules/prog-python.el +++ b/modules/prog-python.el @@ -31,6 +31,8 @@ ;; Forward declarations for external packages (declare-function company-mode "company") +(declare-function cj/disabled "system-defaults") +(declare-function pdb "gud") (defvar poetry-tracking-strategy) (defvar pylsp-path "pylsp" @@ -38,6 +40,10 @@ Install with: pip install python-lsp-server[all] Or for pyright: pip install pyright") +(defvar mypy-path "mypy" + "Path to mypy static type checker. +Install with: pip install mypy") + ;; -------------------------------- Python Setup ------------------------------- ;; preferences for Python programming @@ -57,13 +63,38 @@ Or for pyright: pip install pyright") (executable-find pylsp-path)) (lsp-deferred))) +(defun cj/python-mypy () + "Run mypy static type checker on the current Python file or directory." + (interactive) + (if (executable-find mypy-path) + (let ((target (or (buffer-file-name) default-directory))) + (compile (format "%s %s" mypy-path (shell-quote-argument target)))) + (message "mypy not found. Install with: pip install mypy"))) + +(defun cj/python-debug () + "Start Python debugger (pdb) on the current file." + (interactive) + (if buffer-file-name + (pdb (format "python3 -m pdb %s" (shell-quote-argument buffer-file-name))) + (message "No file associated with this buffer"))) + +(defun cj/python-mode-keybindings () + "Set up keybindings for Python programming. +Overrides default prog-mode keybindings with Python-specific commands." + ;; S-f5: Run mypy (static type checking) + (local-set-key (kbd "S-<f5>") #'cj/python-mypy) + + ;; S-f6: Debug with pdb + (local-set-key (kbd "S-<f6>") #'cj/python-debug)) + ;; ----------------------------------- Python ---------------------------------- ;; configuration for python-ts-mode (treesit-based Python editing) (use-package python :ensure nil ;; built-in :hook - (python-ts-mode . cj/python-setup) + ((python-ts-mode . cj/python-setup) + (python-ts-mode . cj/python-mode-keybindings)) :custom (python-shell-interpreter "python3") :config @@ -111,7 +142,10 @@ Or for pyright: pip install pyright") :custom (blacken-allow-py36 t) (blacken-skip-string-normalization t) - :hook (python-ts-mode . blacken-mode)) + :hook (python-ts-mode . blacken-mode) + :bind (:map python-ts-mode-map + ("<f6>" . blacken-buffer) + ("C-; f" . blacken-buffer))) ;; ---------------------------------- Numpydoc --------------------------------- ;; automatically insert NumPy style docstrings in Python function definitions 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 |
