unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Yuan Fu <casouri@gmail.com>
To: "João Távora" <joaotavora@gmail.com>
Cc: Augusto Stoffel <arstoffel@gmail.com>,
	Stephen Leake <stephen_leake@stephe-leake.org>,
	Emacs developers <emacs-devel@gnu.org>
Subject: Re: Explain a bit more on how to configure language server in Eglot's manual
Date: Wed, 8 Mar 2023 15:19:02 -0800	[thread overview]
Message-ID: <6866252E-DA6D-4399-B4CD-06C0E44B3A03@gmail.com> (raw)
In-Reply-To: <CALDnm51+=N1NYYyPKvpuAMe8y9bJ-fViYcvtZcpkx1EVTB_LCw@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 4825 bytes --]



> On Mar 8, 2023, at 11:43 AM, João Távora <joaotavora@gmail.com> wrote:
> 
> On Wed, Mar 8, 2023 at 3:01 PM Augusto Stoffel <arstoffel@gmail.com> wrote:
> 
>>> I don't understand: didn't you state you _like_ dotted notation?
>> 
>> Yes, as a quick fix.  But you and the circumstances showed that it isn't
>> nearly good enough.
> 
> OK.  Then we'll hold that idea back.  AFAICT, Yuan also is also a fan
> of that notation, so maybe he can chime in.
> 
>>> A special plist editing mode for a single Eglot variable falls into
>>> that category, IMNSHO.
>> 
>> By the way, my suggestion is to edit a JSON value, which is what the
>> server receives, you can copy and paste from other places, and doesn't
>> require you to know jsonrpc.el internals such as :json-false and the
>> translation of nil.
> 
> They are not internals.  They are the external, published and documented
> way to create JSON for jsonrpc.el contexts.  The documentation isn't
> great, I know.  This is precisely what this thread is originally about
> and I'm working on it.
> 
>>> and I don't see any other good place to put a single utility function
>>> that facilitates it but in Eglot.
>> 
>> The utility function belongs to map.el.  It is called `assoc-in` in
>> Clojure.
> 
> Yuri presented some pretty good arguments about how that would
> be a bad idea.  Also, not a Clojure expert, but AFAIK that
> function doesn't take dotted notation strings as input.
> 
>> Speaking of bloat, and I know I shouldn't insist, but a basic version of
>> the savable eglot-show-workspace-configuration barely adds 30 LOC.
> 
> Your code has at least two big problems:
> 
> * it takes the current value from one place and
>  potentially saves it in another place.  This is asking
>  for trouble.  I know you favour the 'nil' method exclusively
>  for setting e-w-configuration, but it's not the only supported
>  methods and there are configurations out in the wild that
>  we can't break. Also note the that per-file or per-sub-hierarchy
>  workspace configurations _are_supported by LSP (the scopeUri
>  argument to the workspace/configuration server request).
> 
> * it doesn't take into account dir-local-set-class-variables
> 
> These are exactly the type of edge cases that I don't want
> to handle in Eglot.  In other words, Emacs has many variable
> setting methods and making an Eglot function to oversimplify
> them, even if it happens to work for an estimated majority of
> cases, is a bad idea, simply because it will break a minority.
> 
> That said, your code has great ergonomics and you seem to be
> a very able Elisper.  So I'll restate: if a generic, safe
> version of this hits Emacs libraries, Eglot will be happy to
> use it and I at least will be very grateful for that contribution.
> 
> A better dir-local editing facility would account for (at least)
> those two cases.  Even if that accounting is, at first, just bailing
> out and warning the user if it detects them.  That's perfectly fine
> in my book.  It'll mean the code will work fine for the 90% of users
> and not confuse the other 10% to death.
> 
> That new dir-local editing facility could even allow you to
> use JSON notation to edit a variable's plist, and eglot.el could
> hint on that preference by setting 'serializer' and 'deserializer'
> properties on e-w-configuration (though a more lispy name would
> be 'write' and 'read' perhaps).
> 
> Additionally, in eglot.el, a variable watcher could be added to
> e-w-c to take care of LSP signalling automatically if changes to
> the value are detected.
> 
> So I urge you to generalize your code and propose it here
> in a new :core ELPA package.

Orthogonal to Augusto’s function, but I think something like this would be nice: a function that pops up a buffer and enters recursive edit for arbitrary input and edit, and when you hit C-c C-c, it returns the buffer content, kind of like Magit’s commit buffer. Plus buffer content validator (eg, validate JSON), and arbitrary major modes in the buffer, etc.

Load the file, and try

(json-parse-string
 (read-complex-value (lambda ()
                       (js-json-mode)
                       (read-complex-value-insert-prompt
                        "Type C-c C-c to finish, C-c C-k to abort:\n\n"))
                     (lambda ()
                       (condition-case nil
                           (progn (json-parse-buffer)
                                  t)
                         ((or end-of-file json-parse-error)
                          (message "Invalid JSON")
                          nil))))
 :object-type ‘plist)

It should pop a buffer in js-json-mode, and validates your JSON value. Typing C-c C-c returns the JSON as a plist.

Yuan


[-- Attachment #2: read-complex-value.el --]
[-- Type: application/octet-stream, Size: 5387 bytes --]

;;; read-complex-value.el --- Read complex value with recursive edit  -*- lexical-binding: t; -*-

;; Author: Yuan Fu <casouri@gmail.com>

;;; This file is NOT part of GNU Emacs

;;; Commentary:
;;
;; This package provides a function ‘read-complex-value’ that pops up
;; a buffer and goes into recursive edit for user to enter and edit
;; text. When the user is done and hit C-c C-c, the buffer content is
;; returned.

;;; Code:

(defface read-complex-value-prompt-face
  '((t . (:inherit shadow)))
  "Face used for the prompt text in ‘read-complex-value’ buffer.")

(defvar-local read-complex-value--validator nil
  "The function used to validate buffer’s content.
See documentation of ‘read-complex-value-raw’ for more.")

(defvar-local read-complex-value--mode nil
  "Whether this buffer is a ‘read-complex-value’ buffer.")

(defvar read-complex-value-map
  (let ((map (make-sparse-keymap)))
    (keymap-set map "C-c C-c" #'read-complex-value--done)
    (keymap-set map "C-c C-k" #'read-complex-value--abort)
    map)
  "Keymap used in ‘read-complex-value’ buffer.")

(defun read-complex-value-insert-prompt (&rest texts)
  "Insert each string in TEXTS into buffer as prompt text."
  (dolist (text texts)
    (insert (propertize text
                        'read-complex-value-setup t
                        'rear-nonsticky t
                        'read-only t
                        'font-lock-face 'read-complex-value-prompt-face
                        'face 'read-complex-value-prompt-face))))

(defun read-complex-value
    (&optional setup validator history
               display-action return-buffer signal)
  "Pop up a buffer and enter recursive edit, then return buffer content.

Returns the buffer content or nil when user types C-c C-c or C-c
C-k, respectively.

SETUP can be either a string, which is inserted at the beginning
of the buffer as prompt, or a function that takes no arguments
that’s run in the buffer for arbitrary setup.

Any prompt are removed in the returned text. To insert prompt
text in the SETUP function, use
‘read-complex-value-insert-prompt’.

VALIDATOR should be a function that takes no argument, and
returns either t if the buffer content is valid, or nil if not.
It runs in the buffer and should display messages to help the user
to understand why, when buffer content is invalid.

HISTORY is currently unused.

DISPLAY-ACTION controls how to display the buffer, it is the same
as in the ACTION parameter in ‘display-buffer’.

If RETURN-BUFFER is non-nil, return the buffer rather than a string.

If SIGNAL is non-nil, signal ‘read-complex-value-aborted’ when
user types C-c C-k, otherwise C-c C-k causes this function to
return nil."
  (let ((buf (generate-new-buffer " *read-complex-value*"))
        (window-config (current-window-configuration))
        (aborted nil))
    (select-window (display-buffer buf display-action))
    (cond ((stringp setup)
           (read-complex-value-insert-prompt setup))
          ((functionp setup) (funcall setup))
          (t (signal 'wrong-type-argument
                     `((or stringp functionp) . ,setup))))

    (setq-local read-complex-value--validator validator
                read-complex-value--aborted nil
                read-complex-value--mode t
                minor-mode-map-alist
                (cons (cons 'read-complex-value--mode
                            read-complex-value-map)
                      minor-mode-map-alist))

    (unwind-protect
        (progn
          (condition-case data
              (recursive-edit)
            (quit
             (message "%s" data)
             (if signal
                 (signal 'read-complex-value-aborted data)
               (setq aborted t))))

          (unless aborted
            (read-complex-value--prune-buffer)
            (if return-buffer
                (current-buffer)
              (buffer-string))))

      (kill-buffer)
      (set-window-configuration window-config))))

(defun read-complex-value--prune-buffer ()
  "Remove prompt text from the current buffer."
  (goto-char (point-min))
  (let ((inhibit-read-only t)
        prop)
    (while (setq prop (text-property-search-forward
                       'read-complex-value-setup))
      (delete-region (prop-match-beginning prop)
                     (prop-match-end prop)))))

(defun read-complex-value--done ()
  "Validate buffer content and exit recursive edit."
  (interactive)
  (cond ((null read-complex-value--validator)
         (exit-recursive-edit))

        ((functionp read-complex-value--validator)
         (let ((buf (current-buffer))
               (validator read-complex-value--validator))
           (if (with-temp-buffer
                 (insert-buffer-substring buf)
                 (read-complex-value--prune-buffer)
                 (goto-char (point-min))
                 (funcall validator))
               (exit-recursive-edit)))))

  (when (or (null read-complex-value--validator)
            (funcall read-complex-value--validator))
    (exit-recursive-edit)))

(defun read-complex-value--abort ()
  "Abort the recursive edit."
  (interactive)
  (abort-recursive-edit))

(provide 'read-complex-value)

;;; read-complex-value.el ends here

  parent reply	other threads:[~2023-03-08 23:19 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-05  4:45 Explain a bit more on how to configure language server in Eglot's manual Yuan Fu
2023-03-05 22:36 ` [SPAM UNSURE] " Stephen Leake
2023-03-06  0:16   ` João Távora
2023-03-06 22:28     ` Yuan Fu
2023-03-07 11:59       ` João Távora
2023-03-08 13:27         ` Augusto Stoffel
2023-03-08 13:54           ` João Távora
2023-03-08 15:01             ` Augusto Stoffel
2023-03-08 19:43               ` João Távora
2023-03-08 20:43                 ` Augusto Stoffel
2023-03-09  9:43                   ` João Távora
2023-03-08 23:19                 ` Yuan Fu [this message]
2023-03-09  8:18                   ` Augusto Stoffel
2023-03-09 17:20                     ` Juri Linkov
2023-03-10  6:26                       ` Yuan Fu
2023-03-10  7:59                         ` João Távora
2023-03-09 17:40                     ` João Távora
2023-03-09 18:05                       ` Juri Linkov
2023-03-09 18:32                         ` Augusto Stoffel
2023-03-09  8:28                 ` Explain a bit more on how to configure language server in Eglot's manual' Augusto Stoffel
2023-03-08 15:24             ` Explain a bit more on how to configure language server in Eglot's manual Yuri Khan
2023-03-08 15:27               ` João Távora
2023-03-08 15:52                 ` Yuri Khan
2023-03-08 16:03                   ` João Távora
2023-03-09 11:18         ` [SPAM UNSURE] " João Távora
2023-03-10  6:23           ` Yuan Fu
2023-03-14 18:09             ` Michael Eliachevitch
2023-03-14 18:53               ` João Távora
2023-03-14 22:27                 ` [PATCH] " Michael Eliachevitch
2023-03-15 11:49                   ` Michael Eliachevitch
2023-03-15 12:35                   ` Eli Zaretskii
2023-03-15 12:52                     ` Michael Eliachevitch
2023-03-15 18:54                       ` João Távora
2023-03-15 19:26                         ` Michael Eliachevitch
2023-03-16  0:09                           ` João Távora
2023-03-06 10:34 ` Augusto Stoffel
2023-03-06 10:51   ` João Távora
2023-03-06 11:00     ` Augusto Stoffel
2023-03-06 11:13       ` João Távora
2023-03-06 11:30         ` Pedro Andres Aranda Gutierrez
2023-03-06 11:46           ` João Távora
2023-03-06 13:08             ` Augusto Stoffel
2023-03-06 13:50               ` João Távora
2023-03-06 16:10                 ` Augusto Stoffel
2023-03-06 16:25                   ` João Távora
2023-03-06 18:18                     ` Augusto Stoffel
2023-03-06 18:32                       ` João Távora
2023-03-06 20:16                         ` Pedro Andres Aranda Gutierrez
2023-03-06 21:13                         ` Augusto Stoffel
2023-03-06 21:38                           ` João Távora
2023-03-06 13:01         ` Augusto Stoffel
  -- strict thread matches above, loose matches on Subject: below --
2023-03-12 12:09 Pedro Andres Aranda Gutierrez
2023-03-12 19:52 ` João Távora

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6866252E-DA6D-4399-B4CD-06C0E44B3A03@gmail.com \
    --to=casouri@gmail.com \
    --cc=arstoffel@gmail.com \
    --cc=emacs-devel@gnu.org \
    --cc=joaotavora@gmail.com \
    --cc=stephen_leake@stephe-leake.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).