[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/eglot d6af4df 4/8: New command M-x eglot-code-actions
From: |
João Távora |
Subject: |
[elpa] externals/eglot d6af4df 4/8: New command M-x eglot-code-actions |
Date: |
Fri, 1 Jun 2018 12:58:25 -0400 (EDT) |
branch: externals/eglot
commit d6af4df11ad962e38d0f8ab9306e7c664ff89ac5
Author: João Távora <address@hidden>
Commit: João Távora <address@hidden>
New command M-x eglot-code-actions
Also available when left-clicking diagnostics.
* README.md: Mention eglot-code-actions. Slightly rewrite
differences to lsp-mode.
* eglot.el (eglot-code-actions): New command.
(eglot-handle-notification :textDocument/publishDiagnostics): Use
eglot--make-diag and eglot--overlay-diag-props.
(eglot--mode-line-props): Use eglot--mouse-call.
(eglot--mouse-call): Renamed from eglot--mode-line-call.
(eglot-client-capabilities): List :executeCommand and :codeAction
as capabilities.
(eglot--diag, advice-add flymake--highlight-line): Horrible hack.
(eglot--overlay-diag-props): Horrible hack.
---
README.md | 21 +++++++-----
eglot.el | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 110 insertions(+), 19 deletions(-)
diff --git a/README.md b/README.md
index 07463bb..e530159 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,11 @@ Here's a summary of available commands:
- `M-x eglot-rename` asks the server to rename the symbol at point;
+- `M-x eglot-code-actions` asks the server for any code actions at
+ point. These may tipically be simple fixes, like deleting an unused
+ variable, or fixing an import. Left click on diagnostics to check if
+ there are any there;
+
- `M-x eglot-help-at-point` asks the server for help for symbol at
point. Currently this is what `eldoc-mode` displays in the echo
area;
@@ -115,7 +120,7 @@ eglot-shutdown`.
- [ ] workspace/configuration (3.6.0)
- [x] workspace/didChangeWatchedFiles
- [x] workspace/symbol
-- [ ] workspace/executeCommand
+- [x] workspace/executeCommand
- [x] workspace/applyEdit
## Text Synchronization
@@ -140,7 +145,7 @@ eglot-shutdown`.
- [x] textDocument/references
- [x] textDocument/documentHighlight
- [x] textDocument/documentSymbol
-- [ ] textDocument/codeAction
+- [x] textDocument/codeAction
- [ ] textDocument/codeLens
- [ ] codeLens/resolve
- [ ] textDocument/documentLink
@@ -183,17 +188,17 @@ Under the hood:
- Message parser is much simpler.
- Defers signature requests like `textDocument/hover` until server is
- ready. Also sends `textDocument/didChange` for groups of edits, not
+ ready.
+- Sends `textDocument/didChange` for groups of edits, not
one per each tiny change.
- Easier to read and maintain elisp. Yeah I know, *very subjective*,
so judge for yourself.
-- About 1k LOC lighter.
-- Development doesn't require Cask, just Emacs.
-- Project support doesn't need `projectile.el`, uses Emacs's `project.el`
-- Requires the upcoming Emacs 26
+- Doesn't *require* anything other than Emacs 26, but will
+ automatically upgrade to work with stuff outside Emacs, like
+ `company`, `markdown-mode`, if you happen to have these installed.
- Contained in one file
- Has automated tests that check against actual LSP servers
-
+
[lsp]: https://microsoft.github.io/language-server-protocol/
[rls]: https://github.com/rust-lang-nursery/rls
diff --git a/eglot.el b/eglot.el
index a991cb1..8437d8c 100644
--- a/eglot.el
+++ b/eglot.el
@@ -155,6 +155,8 @@ deferred to the future.")
(list
:workspace (list
:applyEdit t
+ :executeCommand `(:dynamicRegistration :json-false)
+ :codeAction `(:dynamicRegistration :json-false)
:workspaceEdit `(:documentChanges :json-false)
:didChangeWatchesFiles `(:dynamicRegistration t)
:symbol `(:dynamicRegistration :json-false))
@@ -908,12 +910,15 @@ that case, also signal textDocument/didOpen."
(put 'eglot--mode-line-format 'risky-local-variable t)
-(defun eglot--mode-line-call (what)
+(defun eglot--mouse-call (what)
"Make an interactive lambda for calling WHAT from mode-line."
(lambda (event)
(interactive "e")
- (with-selected-window (posn-window (event-start event))
- (call-interactively what))))
+ (let ((start (event-start event))) (with-selected-window (posn-window
start)
+ (save-excursion
+ (goto-char (or (posn-point start)
+ (point)))
+ (call-interactively what))))))
(defun eglot--mode-line-props (thing face defs &optional prepend)
"Helper for function `eglot--mode-line-format'.
@@ -921,7 +926,7 @@ Uses THING, FACE, DEFS and PREPEND."
(cl-loop with map = (make-sparse-keymap)
for (elem . rest) on defs
for (key def help) = elem
- do (define-key map `[mode-line ,key] (eglot--mode-line-call def))
+ do (define-key map `[mode-line ,key] (eglot--mouse-call def))
concat (format "%s: %s" key help) into blurb
when rest concat "\n" into blurb
finally (return `(:propertize ,thing
@@ -969,6 +974,39 @@ Uses THING, FACE, DEFS and PREPEND."
`(eglot--managed-mode (" [" eglot--mode-line-format "] ")))
+;; FIXME: A horrible hack of Flymake's insufficient API that must go
+;; into Emacs master, or better, 26.2
+(cl-defstruct (eglot--diag (:include flymake--diag)
+ (:constructor eglot--make-diag
+ (buffer beg end type text props)))
+ props)
+
+(advice-add 'flymake--highlight-line :after
+ (lambda (diag)
+ (when (cl-typep diag 'eglot--diag)
+ (let ((ov (cl-find diag
+ (overlays-at (flymake-diagnostic-beg diag))
+ :key (lambda (ov)
+ (overlay-get ov
'flymake-diagnostic)))))
+ (cl-loop for (key . value) in (eglot--diag-props diag)
+ do (overlay-put ov key value)))))
+ '((name . eglot-hacking-in-some-per-diag-overlay-properties)))
+
+
+(defun eglot--overlay-diag-props ()
+ `((mouse-face . highlight)
+ (help-echo . (lambda (window _ov pos)
+ (with-selected-window window
+ (mapconcat
+ #'flymake-diagnostic-text
+ (flymake-diagnostics pos)
+ "\n"))))
+ (keymap . ,(let ((map (make-sparse-keymap)))
+ (define-key map [mouse-1]
+ (eglot--mouse-call 'eglot-code-actions))
+ map))))
+
+
;;; Protocol implementation (Requests, notifications, etc)
;;;
(defun eglot-shutdown (server &optional _interactive)
@@ -1037,16 +1075,18 @@ function with the server still running."
(with-current-buffer buffer
(cl-loop
for diag-spec across diagnostics
- collect (cl-destructuring-bind (&key range severity _group
+ collect (cl-destructuring-bind (&key range ((:severity sev)) _group
_code source message)
diag-spec
+ (setq message (concat source ": " message))
(pcase-let ((`(,beg . ,end) (eglot--range-region range)))
- (flymake-make-diagnostic (current-buffer)
- beg end
- (cond ((<= severity 1) :error)
- ((= severity 2) :warning)
- (t :note))
- (concat source ": " message))))
+ (eglot--make-diag (current-buffer) beg end
+ (cond ((<= sev 1) ':error)
+ ((= sev 2) ':warning)
+ (t ':note))
+ message (cons
+ `(eglot-lsp-diag . ,diag-spec)
+ (eglot--overlay-diag-props)))))
into diags
finally (cond (eglot--current-flymake-report-fn
(funcall eglot--current-flymake-report-fn diags)
@@ -1528,6 +1568,52 @@ If SKIP-SIGNATURE, don't try to send
textDocument/signatureHelp."
:newName ,newname))
current-prefix-arg))
+
+(defun eglot-code-actions (&optional beg end)
+ "Get and offer to execute code actions between BEG and END."
+ (interactive
+ (let (diags)
+ (cond ((region-active-p) (list (region-beginning) (region-end)))
+ ((setq diags (flymake-diagnostics (point)))
+ (list (cl-reduce #'min (mapcar #'flymake-diagnostic-beg diags))
+ (cl-reduce #'max (mapcar #'flymake-diagnostic-end diags))))
+ (t (list (point-min) (point-max))))))
+ (unless (eglot--server-capable :codeActionProvider)
+ (eglot--error "Server can't execute code actions!"))
+ (let* ((server (eglot--current-server-or-lose))
+ (actions (eglot--request
+ server
+ :textDocument/codeAction
+ (list :textDocument (eglot--TextDocumentIdentifier)
+ :range (list :start (eglot--pos-to-lsp-position beg)
+ :end (eglot--pos-to-lsp-position end))
+ :context
+ `(:diagnostics
+ [,@(mapcar (lambda (diag)
+ (cdr (assoc 'eglot-lsp-diag
+ (eglot--diag-props diag))))
+ (cl-remove-if-not
+ (lambda (diag) (cl-typep diag
'eglot--diag))
+ (flymake-diagnostics beg end)))]))))
+ (menu-items (mapcar (eglot--lambda (&key title command arguments)
+ `(,title . (:command ,command :arguments
,arguments)))
+ actions))
+ (menu (and menu-items `("Eglot code actions:" ("dummy"
,@menu-items))))
+ (command-and-args
+ (and menu
+ (if (listp last-nonmenu-event)
+ (x-popup-menu last-nonmenu-event menu)
+ (let ((never-mind (gensym)) retval)
+ (setcdr (cadr menu)
+ (cons `("never mind..." . ,never-mind) (cdadr
menu)))
+ (if (eq (setq retval (tmm-prompt menu)) never-mind)
+ (keyboard-quit)
+ retval))))))
+ (if command-and-args
+ (eglot--request server :workspace/executeCommand command-and-args)
+ (eglot--message "No code actions here"))))
+
+
;;; Dynamic registration
;;;
- [elpa] externals/eglot updated (4211e0c -> e103d5f), João Távora, 2018/06/01
- [elpa] externals/eglot 5f723ba 1/8: Prevent possible cquery choke on :initializationOptions, João Távora, 2018/06/01
- [elpa] externals/eglot 8f17393 5/8: Explicitly trigger eldoc after workspace edits, João Távora, 2018/06/01
- [elpa] externals/eglot 51f5e87 6/8: Fix completionItem/resolve, João Távora, 2018/06/01
- [elpa] externals/eglot e0c642e 2/8: Add MELPA badge, João Távora, 2018/06/01
- [elpa] externals/eglot 29c3d76 3/8: Revert an unfinished feature that made it to the last commit, João Távora, 2018/06/01
- [elpa] externals/eglot d6af4df 4/8: New command M-x eglot-code-actions,
João Távora <=
- [elpa] externals/eglot e103d5f 8/8: * eglot.el (Version): Bump to 0.8, João Távora, 2018/06/01
- [elpa] externals/eglot 3dbcece 7/8: Add animated gifs to README.md, João Távora, 2018/06/01