1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
;;; prog-webdev.el --- Web Development Packages and Settings -*- lexical-binding: t; coding: utf-8; -*-
;; Author: Craig Jennings <c@cjennings.net>
;;
;;; Commentary:
;;
;; Layer: 3 (Domain Workflow).
;; Category: D/P.
;; Load shape: eager.
;; Eager reason: none necessary; currently eager but should load by web major
;; modes (Phase 6 deferral candidate).
;; Top-level side effects: package configuration via use-package; warns at load
;; if prettier is missing.
;; Runtime requires: system-lib.
;; Direct test load: yes.
;;
;; TypeScript, JavaScript, and HTML development with tree-sitter, LSP, and
;; prettier formatting.
;;
;; Installation:
;; sudo pacman -S typescript-language-server typescript prettier
;; sudo pacman -S vscode-html-languageserver # LSP in web-mode buffers
;;
;; Features:
;; - Tree-sitter: Syntax highlighting for TS, TSX, JS (via treesit-auto)
;; - LSP: Completion, go-to-definition, hover docs, rename, references
;; - Formatting: prettier via C-; f
;; - Web-mode: Mixed HTML/CSS/JS templates with context-aware completion
;;
;; Workflow:
;; 1. Open .ts/.tsx/.js file → tree-sitter mode + LSP auto-starts
;; 2. C-; f → Format with prettier
;; 3. Open .html file → web-mode with LSP support
;;; Code:
(require 'system-lib) ; for cj/executable-find-or-warn
(defvar typescript-ts-mode-map)
(defvar tsx-ts-mode-map)
(defvar js-ts-mode-map)
;; Forward declarations for LSP
(declare-function lsp-deferred "lsp-mode")
;; Forward declarations for external packages
(declare-function company-mode "company")
(defvar ts-language-server-path "typescript-language-server"
"Path to typescript-language-server executable.
Install with: sudo pacman -S typescript-language-server")
(defvar prettier-path "prettier"
"Path to prettier executable.
Install with: sudo pacman -S prettier")
(defvar html-language-server-path "vscode-html-language-server"
"Path to the HTML language server executable used in web-mode buffers.
Install with: sudo pacman -S vscode-html-languageserver")
;; Warn at load time if prettier is missing rather than waiting for the
;; first format-on-save to fail mid-edit.
(cj/executable-find-or-warn prettier-path "prettier formatter" 'prog-webdev)
;; ------------------------------ Web Dev Setup --------------------------------
;; shared setup for TypeScript, JavaScript, and TSX modes
(defun cj/--webdev-prefs ()
"Apply the shared buffer-local preferences for web development buffers."
(company-mode)
(flyspell-prog-mode)
(superword-mode)
(setq-local fill-column 100)
(setq-local tab-width 2)
(setq-local standard-indent 2)
(setq-local indent-tabs-mode nil)
(electric-pair-local-mode t))
(defun cj/webdev-setup ()
"Set up common preferences for TypeScript/JavaScript buffers."
(cj/--webdev-prefs)
;; Enable LSP if available
(when (and (fboundp 'lsp-deferred)
(executable-find ts-language-server-path))
(lsp-deferred)))
(defun cj/web-mode-setup ()
"Set up preferences for web-mode (HTML template) buffers.
Same shared preferences as the TS/JS modes, but the LSP attach is
guarded on the HTML language server rather than the TypeScript one, so
machines without it stay silent instead of prompting."
(cj/--webdev-prefs)
(when (and (fboundp 'lsp-deferred)
(executable-find html-language-server-path))
(lsp-deferred)))
(defun cj/--webdev-format-args (file)
"Return the prettier argv list that formats FILE's contents on stdin.
No shell quoting is needed: the args are passed to prettier directly
via `call-process-region', so FILE can contain spaces or shell
metacharacters without risk."
(list "--stdin-filepath" file))
(defun cj/webdev-format-buffer ()
"Format the current buffer with prettier.
Detects the file type automatically from the filename."
(interactive)
(if (executable-find prettier-path)
(apply #'cj/format-region-with-program prettier-path
(cj/--webdev-format-args (or buffer-file-name "file.ts")))
(user-error "prettier not found; install with: sudo pacman -S prettier")))
(defun cj/webdev-keybindings ()
"Set up keybindings for web development buffers."
(local-set-key (kbd "C-; f") #'cj/webdev-format-buffer))
;; ----------------------------- TypeScript / JS -------------------------------
;; tree-sitter modes (built-in, Emacs 29+)
;; NOTE: No :mode directives — treesit-auto (in prog-general.el) handles
;; the auto-mode-alist mappings and auto-installs grammars on first use.
(use-package typescript-ts-mode
:ensure nil
:defer t
:hook
((typescript-ts-mode . cj/webdev-setup)
(typescript-ts-mode . cj/webdev-keybindings)))
(use-package tsx-ts-mode
:ensure nil
:defer t
:hook
((tsx-ts-mode . cj/webdev-setup)
(tsx-ts-mode . cj/webdev-keybindings)))
(use-package js-ts-mode
:ensure nil
:defer t
:hook
((js-ts-mode . cj/webdev-setup)
(js-ts-mode . cj/webdev-keybindings)))
;; Classic-mode fallback: when the JS grammar is unavailable the buffer
;; lands in js-mode; give it the same setup as the tree-sitter modes.
(add-hook 'js-mode-hook #'cj/webdev-setup)
(add-hook 'js-mode-hook #'cj/webdev-keybindings)
;; ----------------------------------- LSP -------------------------------------
;; TypeScript/JavaScript LSP configuration
;; Core LSP setup is in prog-general.el
(use-package lsp-mode
:hook ((typescript-ts-mode tsx-ts-mode js-ts-mode) . lsp-deferred))
;; --------------------------------- CSS Eldoc ---------------------------------
;; CSS info in the echo area
(use-package css-eldoc
:defer t)
;; ---------------------------------- Web Mode ---------------------------------
;; major mode for editing web templates (HTML with embedded JS/CSS)
(use-package web-mode
:defer t
:custom
(web-mode-enable-current-element-highlight t)
(web-mode-markup-indent-offset 2)
(web-mode-code-indent-offset 2)
(web-mode-engines-alist '(("django" . "\\.html\\'")))
:mode ("\\.html?$" . web-mode)
:hook ((web-mode . cj/web-mode-setup)
(web-mode . cj/webdev-keybindings)))
(provide 'prog-webdev)
;;; prog-webdev.el ends here.
|