summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-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