summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-10-20 16:17:30 -0500
committerCraig Jennings <c@cjennings.net>2025-10-20 16:17:30 -0500
commitdf4469f231e86877b7d055bf989220a5fbc0a763 (patch)
treeea3ef00d10a7a7c1366213d493ea4e2546b9d783
parentce8477e80e0a837ac462b97c1a4b5d834838d6d3 (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.
-rw-r--r--modules/prog-c.el19
-rw-r--r--modules/prog-general.el37
-rw-r--r--modules/prog-go.el34
-rw-r--r--modules/prog-python.el38
-rw-r--r--modules/prog-shell.el165
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