Skip to content

Commit

Permalink
Add a new action-filer client parameter (#4194)
Browse files Browse the repository at this point in the history
This lets us fix #4184 by selectively changing `nil` code action
argument values to `:json-false`. Once this is merged, HLS needs to set
the field to `'(:withSig)` (and possibly some other values).
  • Loading branch information
robbert-vdh authored Jun 10, 2024
1 parent 171d5a6 commit a1b4c75
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.org
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Add SQL support
* Add support for Meson build system. (~meson-mode~).
* Add support for go to definition for external files (.dll) in CSharp projects for OmniSharp server.
* Added a new optional ~:action-filter~ argument when defining LSP clients that allows code action requests to be modified before they are sent to the server. This is used by the Haskell language server client to work around an ~lsp-mode~ parsing quirk that incorrectly sends ~null~ values instead of ~false~ in code action requests.

** 9.0.0
* Add language server config for QML (Qt Modeling Language) using qmlls.
Expand Down
51 changes: 50 additions & 1 deletion lsp-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,13 @@ return value of `body' or nil if interrupted."
;; to the server.
(action-handlers (make-hash-table :test 'equal))

;; `action-filter' can be set to a function that modifies any incoming
;; `CodeAction' in place before it is executed. The return value is ignored.
;; This can be used to patch up broken code action requests before they are
;; sent back to the LSP server. See `lsp-fix-code-action-booleans' for an
;; example of a function that can be useful here.
(action-filter nil)

;; major modes supported by the client.
major-modes
;; Function that will be called to decide if this language client
Expand Down Expand Up @@ -6005,7 +6012,49 @@ Request codeAction/resolve for more info if server supports."

(cond
((stringp command?) (lsp--execute-command action))
((lsp-command? command?) (lsp--execute-command command?))))
((lsp-command? command?) (progn
(when-let ((action-filter (->> (lsp-workspaces)
(cl-first)
(or lsp--cur-workspace)
(lsp--workspace-client)
(lsp--client-action-filter))))
(funcall action-filter command?))
(lsp--execute-command command?)))))

(lsp-defun lsp-fix-code-action-booleans ((&Command :arguments?) boolean-action-arguments)
"Patch incorrect boolean argument values in the provided `CodeAction' command
in place, based on the BOOLEAN-ACTION-ARGUMENTS list. The values
in this list can be either symbols or lists of symbols that
represent paths to boolean arguments in code actions:

> (lsp-fix-code-action-booleans command '(:foo :bar (:some :nested :boolean)))

When there are available code actions, the server sends
`lsp-mode' a list of possible command names and arguments as
JSON. `lsp-mode' parses all boolean false values as `nil'. As a
result code action arguments containing falsy values don't
roundtrip correctly because `lsp-mode' will end up sending null
values back to the client. This list makes it possible to
selectively transform `nil' values back into `:json-false'."
(seq-doseq (path boolean-action-arguments)
(seq-doseq (args arguments?)
(lsp--fix-nested-boolean args (if (listp path) path (list path))))))

(defun lsp--fix-nested-boolean (structure path)
"Traverse STRUCTURE using the paths from the PATH list, changing the value to
`:json-false' if it was `nil'. PATH should be a list containing
one or more symbols, and STRUCTURE should be compatible with
`lsp-member?', `lsp-get', and `lsp-put'."
(let ((key (car path))
(rest (cdr path)))
(if (null rest)
;; `lsp-put' returns `nil' both when the key doesn't exist and when the
;; value is `nil', so we need to explicitly check its presence here
(when (and (lsp-member? structure key) (not (lsp-get structure key)))
(lsp-put structure key :json-false))
;; If `key' does not exist, then we'll silently ignore it
(when-let ((child (lsp-get structure key)))
(lsp--fix-nested-boolean child rest)))))

(defvar lsp--formatting-indent-alist
;; Taken from `dtrt-indent-mode'
Expand Down

0 comments on commit a1b4c75

Please sign in to comment.