summaryrefslogtreecommitdiff
path: root/slang-lsp.el
diff options
context:
space:
mode:
Diffstat (limited to 'slang-lsp.el')
-rw-r--r--slang-lsp.el277
1 files changed, 277 insertions, 0 deletions
diff --git a/slang-lsp.el b/slang-lsp.el
new file mode 100644
index 0000000..b5edf80
--- /dev/null
+++ b/slang-lsp.el
@@ -0,0 +1,277 @@
1;;; -*- lexical- binding: t; -*-
2;;; slang-lsp.el --- LSP mode for Slang shader files
3
4;;; Code:
5(require 'eglot)
6(require 'slang-mode)
7
8
9(defgroup slang-lsp nil
10 "LSP integration for Slang mode."
11 :group 'slang
12 :prefix "slang-lsp-")
13
14(defcustom slang-lsp-server-executable "slangd"
15 "Path to the slangd language server executable.
16Can be either a full path or just the executable name if it's in PATH."
17 :type 'string
18 :group 'slang-lsp)
19
20(defcustom slang-lsp-server-args '()
21 "Additional arguments to pass to the slangd server."
22 :type '(repeat string)
23 :group 'slang-lsp)
24
25(defcustom slang-lsp-auto-enable t
26 "Whether to automatically enable LSP when opening Slang files."
27 :type 'boolean
28 :group 'slang-lsp)
29
30(defcustom slang-lsp-server-search-paths
31 '("/usr/local/bin"
32 "/usr/bin"
33 "~/.local/bin"
34 "~/bin"
35 "/opt/slang/bin")
36 "Paths to search for slangd executable."
37 :type '(repeat directory)
38 :group 'slang-lsp)
39
40(defun slang-lsp--find-server-executable ()
41 "Find the slangd executable in the system.
42Returns the full path to slangd if found, nil otherwise."
43 (or
44 ;; Check if custom executable path is valid
45 (when (and slang-lsp-server-executable
46 (not (string= slang-lsp-server-executable "slangd"))
47 (file-executable-p slang-lsp-server-executable))
48 slang-lsp-server-executable)
49
50 ;; Check if slangd is in PATH
51 (executable-find "slangd")
52
53 ;; Search in common installation paths
54 (cl-some (lambda (path)
55 (let ((full-path (expand-file-name "slangd" path)))
56 (when (file-executable-p full-path)
57 full-path)))
58 slang-lsp-server-search-paths)))
59
60(defun slang-lsp-server-available-p ()
61 "Check if slangd server is available on the system."
62 (not (null (slang-lsp--find-server-executable))))
63
64(defun slang-lsp-setup-server ()
65 "Configure eglot to use slangd for Slang files."
66 (let ((server-path (slang-lsp--find-server-executable)))
67 (if server-path
68 (progn
69 (add-to-list 'eglot-server-programs
70 `((slang-mode) . (,server-path ,@slang-lsp-server-args)))
71 (message "Slang LSP: configured slangd server at %s" server-path))
72 (message "Slang LSP: slangd server not found. Please install Slang compiler or set slang-lsp-server-executable."))))
73
74(defun slang-lsp-start ()
75 "Start LSP server for current Slang buffer."
76 (interactive)
77 (if (slang-lsp-server-available-p)
78 (eglot-ensure)
79 (user-error "slangd server not available. Please install Slang compiler")))
80
81(defun slang-lsp-restart ()
82 "Restart LSP server for current Slang buffer."
83 (interactive)
84 (when (eglot-current-server)
85 (eglot-shutdown (eglot-current-server)))
86 (slang-lsp-start))
87
88(defun slang-lsp-stop ()
89 "Stop LSP server for current buffer."
90 (interactive)
91 (when (eglot-current-server)
92 (eglot-shutdown (eglot-current-server))))
93
94(defun slang-lsp-show-server-info ()
95 "Display information about the current LSP server."
96 (interactive)
97 (if-let ((server (eglot-current-server)))
98 (message "Slang LSP: Server running at %s (PID: %s)"
99 (eglot--server-capable-or-lose server :serverInfo :name "Unknown")
100 (process-id (eglot--process server)))
101 (message "Slang LSP: No server running for this buffer")))
102
103(defun slang-lsp-update-configuration ()
104 "Send updated workspace configuration to the language server."
105 (interactive)
106 (if-let ((server (eglot-current-server)))
107 (progn
108 (slang-lsp--setup-workspace-configuration)
109 (eglot-signal-didChangeConfiguration server)
110 (message "Slang LSP: Configuration updated"))
111 (message "Slang LSP: No server running for this buffer")))
112
113;; Enhanced mode configuration
114(defun slang-lsp-configure-buffer ()
115 "Configure current buffer for optimal LSP experience."
116 (when (derived-mode-p 'slang-mode)
117 ;; Enable common LSP features
118 (setq-local eldoc-documentation-strategy 'eldoc-documentation-compose-eagerly)
119
120 ;; Configure completion (use what's available)
121 (if (fboundp 'cape-dabbrev)
122 (setq-local completion-at-point-functions
123 (list #'eglot-completion-at-point
124 #'cape-dabbrev
125 #'cape-file))
126 (setq-local completion-at-point-functions
127 (list #'eglot-completion-at-point
128 #'dabbrev-completion)))
129
130 ;; Set up keybindings - only use capabilities that slangd actually supports
131 (local-set-key (kbd "C-c l d") #'xref-find-definitions) ; Uses definitionProvider
132 ;; Note: slangd doesn't support referencesProvider, implementationProvider, or typeDefinitionProvider
133 ;; so we don't bind those keys to avoid errors
134 (local-set-key (kbd "C-c l n") #'eglot-rename)
135 (local-set-key (kbd "C-c l f") #'eglot-format)
136 (local-set-key (kbd "C-c l a") #'eglot-code-actions)
137 (local-set-key (kbd "C-c l h") #'eldoc)
138 (local-set-key (kbd "C-c l s") #'slang-lsp-show-server-info)
139 (local-set-key (kbd "C-c l R") #'slang-lsp-restart)
140 (local-set-key (kbd "C-c l c") #'slang-lsp-update-configuration)))
141
142;; Auto-enable hook
143(defun slang-lsp-auto-enable ()
144 "Automatically enable LSP for Slang files if configured."
145 (when (and slang-lsp-auto-enable
146 (slang-lsp-server-available-p))
147 (slang-lsp-start)
148 (slang-lsp-configure-buffer)))
149
150;; Initialization function
151(defun slang-lsp-initialize ()
152 "Initialize Slang LSP integration."
153 (slang-lsp-setup-server)
154
155 ;; Add hook for auto-enabling LSP
156 (add-hook 'slang-mode-hook #'slang-lsp-auto-enable)
157
158 ;; Configure eglot for better Slang support
159 (with-eval-after-load 'eglot
160 ;; Slang-specific server configuration
161 (add-to-list 'eglot-stay-out-of 'flymake) ; Use eglot's diagnostics
162
163 ;; Enhanced diagnostics display
164 (setq eglot-events-buffer-size 0) ; Disable events buffer for performance
165 (setq eglot-autoshutdown t) ; Auto-shutdown when last buffer is closed
166
167 ;; Optional: Log when LSP connects successfully
168 (add-hook 'eglot-managed-mode-hook
169 (lambda ()
170 (when (derived-mode-p 'slang-mode)
171 (message "Slang LSP: Connected successfully"))))
172
173 ;; Define workspace configuration for Slang LSP
174 (defun slang-lsp--workspace-configuration (server)
175 "Return workspace configuration for Slang Language Server."
176 (list :slang (list :completion (list :enableSnippets t
177 :enableAutoImport t)
178 :diagnostics (list :enableExperimental t)
179 :formatting (list :enable t)
180 :hover (list :enable t)
181 :semanticTokens (list :enable t)
182 :inlayHints (list :enable t))))
183
184
185 ;; Set up workspace configuration for slang-mode buffers
186 (defun slang-lsp--setup-workspace-configuration ()
187 "Set up workspace configuration for current slang buffer."
188 (setq-local eglot-workspace-configuration
189 (slang-lsp--workspace-configuration nil)))
190
191 ;; Hook to set up workspace configuration when LSP starts
192 (add-hook 'eglot-managed-mode-hook
193 (lambda ()
194 (when (derived-mode-p 'slang-mode)
195 (slang-lsp--setup-workspace-configuration))))
196 ))
197
198;; Interactive commands for menu
199(defun slang-lsp-install-guide ()
200 "Show installation guide for slangd."
201 (interactive)
202 (message "To install slangd LSP server: 1) Download Slang from https://github.com/shader-slang/slang/releases 2) Extract slangd executable from bin/ directory 3) Add to PATH or set slang-lsp-server-executable 4) Restart Emacs. NOTE: The slangd from Vulkan SDK is NOT the LSP server!"))
203
204(defun slang-lsp-check-server-type ()
205 "Check if the current slangd is the correct LSP server."
206 (interactive)
207 (let ((server-path (slang-lsp--find-server-executable)))
208 (if server-path
209 (message "✓ Found slangd at: %s (with LSP symbols based on your analysis)" server-path)
210 (message "No slangd executable found. Download from https://github.com/shader-slang/slang/releases"))))
211
212(defun slang-lsp-test-server-manual ()
213 "Manually test the slangd server connection."
214 (interactive)
215 (let ((server-path (slang-lsp--find-server-executable)))
216 (if server-path
217 (let* ((buffer-name "*slangd-test*")
218 (process (start-process "slangd-test" buffer-name server-path)))
219 (set-process-query-on-exit-flag process nil)
220 (message "Started slangd test process. Check buffer: %s" buffer-name)
221 ;; Send a simple LSP initialize request
222 (run-with-timer 1 nil
223 (lambda ()
224 (when (process-live-p process)
225 (process-send-string process
226 "Content-Length: 122\r\n\r\n{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"capabilities\":{},\"processId\":null}}\n")
227 (message "Sent initialize request to slangd"))))
228 ;; Kill after 5 seconds
229 (run-with-timer 5 nil
230 (lambda ()
231 (when (process-live-p process)
232 (kill-process process)
233 (message "Killed slangd test process")))))
234 (message "No slangd executable found"))))
235
236(defun slang-lsp-debug-capabilities ()
237 "Debug function to show what capabilities the LSP server supports."
238 (interactive)
239 (if-let ((server (eglot-current-server)))
240 (let ((caps (eglot--capabilities server)))
241 (with-current-buffer (get-buffer-create "*Slang LSP Debug*")
242 (erase-buffer)
243 (insert "=== Slang LSP Server Capabilities ===\n\n")
244 (insert (format "Full capabilities object:\n%S\n\n" caps))
245 (insert "Specific capability checks:\n")
246 (insert (format "definitionProvider: %S\n" (eglot-server-capable :definitionProvider)))
247 (insert (format "referencesProvider: %S\n" (eglot-server-capable :referencesProvider)))
248 (insert (format "implementationProvider: %S\n" (eglot-server-capable :implementationProvider)))
249 (insert (format "typeDefinitionProvider: %S\n" (eglot-server-capable :typeDefinitionProvider)))
250 (insert (format "declarationProvider: %S\n" (eglot-server-capable :declarationProvider)))
251 (pop-to-buffer (current-buffer))))
252 (message "No LSP server running")))
253
254;; Add menu items to slang-mode
255(with-eval-after-load 'slang-mode
256 (easy-menu-add-item slang-mode-menu nil
257 '("LSP"
258 ["Start LSP" slang-lsp-start :enable (not (eglot-current-server))]
259 ["Stop LSP" slang-lsp-stop :enable (eglot-current-server)]
260 ["Restart LSP" slang-lsp-restart :enable (eglot-current-server)]
261 "---"
262 ["Find Definition" xref-find-definitions :enable (eglot-current-server)]
263 ;; Note: References, Implementation, Type Definition not supported by this slangd version
264 ["Rename Symbol" eglot-rename :enable (eglot-current-server)]
265 ["Format Buffer" eglot-format :enable (eglot-current-server)]
266 ["Code Actions" eglot-code-actions :enable (eglot-current-server)]
267 "---"
268 ["Update Configuration" slang-lsp-update-configuration :enable (eglot-current-server)]
269 ["Server Info" slang-lsp-show-server-info :enable (eglot-current-server)]
270 ["Debug Capabilities" slang-lsp-debug-capabilities :enable (eglot-current-server)]
271 ["Check Server Type" slang-lsp-check-server-type t]
272 ["Test Server Manual" slang-lsp-test-server-manual t]
273 ["Installation Guide" slang-lsp-install-guide t])))
274
275(provide 'slang-lsp)
276
277;;; slang-lsp.el ends here