From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Philip Kaludercic Newsgroups: gmane.emacs.devel Subject: Re: [ELPA] New package: dict Date: Sat, 20 May 2023 16:49:41 +0000 Message-ID: <871qjb9cd6.fsf@posteo.net> References: <834joj55pt.fsf@gnu.org> <87ednnvtt6.fsf@posteo.net> <875y8ywwko.fsf@posteo.net> <83zg69br5f.fsf@gnu.org> <83ednj9sw2.fsf@gnu.org> <837ct5x5v6.fsf@gnu.org> <83sfbtvii4.fsf@gnu.org> <83h6s9vdft.fsf@gnu.org> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="7023"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Eli Zaretskii , emacs-devel@gnu.org To: Eshel Yaron Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sat May 20 18:50:34 2023 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1q0Pmr-0001dk-V8 for ged-emacs-devel@m.gmane-mx.org; Sat, 20 May 2023 18:50:34 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q0PmE-0001e9-0I; Sat, 20 May 2023 12:49:55 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1q0Pm8-0001dt-0D for emacs-devel@gnu.org; Sat, 20 May 2023 12:49:48 -0400 Original-Received: from mout02.posteo.de ([185.67.36.66]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1q0Pm5-0007Bs-2M for emacs-devel@gnu.org; Sat, 20 May 2023 12:49:47 -0400 Original-Received: from submission (posteo.de [185.67.36.169]) by mout02.posteo.de (Postfix) with ESMTPS id 5BFE1240101 for ; Sat, 20 May 2023 18:49:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1684601382; bh=KWVQ2IVweBqrGDwl7k1e+YJHkqXOXmOpmlt0VEA+IJE=; h=From:To:Cc:Subject:Autocrypt:Date:Message-ID:MIME-Version:From; b=UDz2RVGRMWeYMlIjq229M4IfItL+8FF/Y/5Yev64ECHIsYm8J7StN2oAVsi0N7e9o WIRV7mkXPKiDKeeeBP1aTAXEtDhLPDNFqVIIBc10SOvuMyTED7NrJXpLG9uQf8B+cv NKuqhmO9v5JqAtpSb1VOTc7+ddtEwBhZRBm/CBDcwJc9MtUmWKCty4mUFcZoiTxETY BpvmBfxVmaceywe2PtonSkMknUnSlBrJGbzbeLo9DyFcmhGlE+rq7gO4yIW0JbCQlI 6enh0XpKc6kH7MNlI0aSoKCHWGHmjsIl5VGISfYMpRt+bR26Q13tfush4cLhD0yLsI x8jRAEak5nPJQ== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4QNqSP69bMz9rxF; Sat, 20 May 2023 18:49:41 +0200 (CEST) In-Reply-To: (Eshel Yaron's message of "Fri, 19 May 2023 11:34:12 +0300") Autocrypt: addr=philipk@posteo.net; keydata= mDMEZBBQQhYJKwYBBAHaRw8BAQdAHJuofBrfqFh12uQu0Yi7mrl525F28eTmwUDflFNmdui0QlBo aWxpcCBLYWx1ZGVyY2ljIChnZW5lcmF0ZWQgYnkgYXV0b2NyeXB0LmVsKSA8cGhpbGlwa0Bwb3N0 ZW8ubmV0PoiWBBMWCAA+FiEEDg7HY17ghYlni8XN8xYDWXahwukFAmQQUEICGwMFCQHhM4AFCwkI BwIGFQoJCAsCBBYCAwECHgECF4AACgkQ8xYDWXahwulikAEA77hloUiSrXgFkUVJhlKBpLCHUjA0 mWZ9j9w5d08+jVwBAK6c4iGP7j+/PhbkxaEKa4V3MzIl7zJkcNNjHCXmvFcEuDgEZBBQQhIKKwYB BAGXVQEFAQEHQI5NLiLRjZy3OfSt1dhCmFyn+fN/QKELUYQetiaoe+MMAwEIB4h+BBgWCAAmFiEE Dg7HY17ghYlni8XN8xYDWXahwukFAmQQUEICGwwFCQHhM4AACgkQ8xYDWXahwukm+wEA8cml4JpK NeAu65rg+auKrPOP6TP/4YWRCTIvuYDm0joBALw98AMz7/qMHvSCeU/hw9PL6u6R2EScxtpKnWof z4oM Received-SPF: pass client-ip=185.67.36.66; envelope-from=philipk@posteo.net; helo=mout02.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:306235 Archived-At: Eshel Yaron writes: > Eli Zaretskii writes: > >>> I've added these options in the patch because I want to have minibuffer >>> completions in `dictionary-search`'s prompts, similarly to >>> `dict-describe-word`. That's also why I mentioned all three >>> customizations in my earlier message: to show how to obtain the behavior >>> of `dict-describe-word` in dictionary.el using the new user options. >>> >>> But from the perspective of other dictionary.el users, it may be useful >>> to customize each of the options but not the necessarily all of them. >>> That's why I implemented them as separate user options. Makes sense? >> >> AFAIR, this started from you proposing a package that would behave as >> if all 3 options were customized, and saying that you like this >> alternative behavior much better, so much so that you felt a new >> package is in order. So I'm asking why not let users who, like you, >> will like that much better, to get that behavior, with all its bells >> and whistles, by setting just one option? Wouldn't you personally >> like such an option and use it all the time? > > Yes, I see what you mean. I'm attaching an updated patch that > implements your suggestion to add another `defcustom` with an > appropriate :set function. It's now enough to do: > > (setopt dictionary-search-interface 'help) > > The updated patch also tries to address Philip's recommendation to > extract the default parts of `dictionary-search` to standalone > functions. > > From 2bbd1767594990357f61d4af467093bf6abb117e Mon Sep 17 00:00:00 2001 > From: Eshel Yaron > Date: Mon, 15 May 2023 21:04:21 +0300 > Subject: [PATCH v2] Add customization options for dictionary-search > > Allow users to customize 'dictionary-search' via several new > customization options. > > * lisp/net/dictionary.el (dictionary-define-word) > (dictionary-match-word) > (dictionary-completing-read-word) > (dictionary-dictionaries) > (dictionary-completing-read-dictionary) > (dictionary-display-definition-in-help-buffer): New functions. > (dictionary-read-word-prompt) > (dictionary-display-definition-function) > (dictionary-read-word-function) > (dictionary-read-dictionary-function) > (dictionary-search-interface): New user options. > (dictionary-search): Use them. > (dictionary-read-dictionary-default) > (dictionary-read-word-default): New functions, extracted from > 'dictionary-search'. Shouldn't these Changelog entries be folded together? * lisp/net/dictionary.el (dictionary-define-word, dictionary-match-word, dictionary-completing-read-word, dictionary-dictionaries, dictionary-completing-read-dictionary, dictionary-display-definition-in-help-buffer): New functions. > * etc/NEWS: Announce. > --- > etc/NEWS | 44 +++++++++ > lisp/net/dictionary.el | 197 +++++++++++++++++++++++++++++++++++++---- > 2 files changed, 226 insertions(+), 15 deletions(-) > > diff --git a/etc/NEWS b/etc/NEWS > index f1fb70c5fc6..b673ac54ef3 100644 > --- a/etc/NEWS > +++ b/etc/NEWS > @@ -333,6 +333,50 @@ instead of: > *** New ':vc' keyword. > This keyword enables the user to install packages using 'package-vc'. > > +** Dictionary > + > +--- > +*** New user option 'dictionary-search-interface'. > +Controls how the 'dictionary-search' command prompts for and displays > +dictionary definitions. Customize this user option to 'help' to have > +'dictionary-search' display definitions in a *Help* buffer and provide > +dictionary-based minibuffer completion for word selection. > + > +--- > +*** New user option 'dictionary-read-word-prompt'. > +This allows the user to customize the prompt that is used by > +'dictionary-search' when asking for a word to search in the > +dictionary. > + > +--- > +*** New user option 'dictionary-display-definition-function'. > +This allows the user to customize the way in which 'dictionary-search' > +displays word definitions. If non-nil, this user option should be set > +to a function that displays a word definition obtained from a > +dictionary server. The new function > +'dictionary-display-definition-in-help-buffer' can be used to display > +the definition in a *Help* buffer, instead of the default *Dictionary* > +buffer. > + > +--- > +*** New user option 'dictionary-read-word-function'. > +This allows the user to customize the way in which 'dictionary-search' > +prompts for a word to search in the dictionary. This user option > +should be set to a function that lets the user select a word and > +returns it as a string. The new function > +'dictionary-completing-read-word' can be used to prompt with > +completion based on dictionary matches. > + > +--- > +*** New user option 'dictionary-read-dictionary-function'. > +This allows the user to customize the way in which 'dictionary-search' > +prompts for a dictionary to search in. This user option should be set > +to a function that lets the user select a dictionary and returns its > +name as a string. The new function > +'dictionary-completing-read-dictionary' can be used to prompt with > +completion based on dictionaries that the server supports. > + > + > > * New Modes and Packages in Emacs 30.1 > > diff --git a/lisp/net/dictionary.el b/lisp/net/dictionary.el > index ba65225692a..fafcd0f2594 100644 > --- a/lisp/net/dictionary.el > +++ b/lisp/net/dictionary.el > @@ -38,6 +38,7 @@ > (require 'custom) > (require 'dictionary-connection) > (require 'button) > +(require 'help-mode) > > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > ;; Stuff for customizing. > @@ -247,6 +248,64 @@ dictionary-coding-systems-for-dictionaries > ))) > :version "28.1") > > +(defcustom dictionary-read-word-prompt "Search word" > + "Prompt string to use when prompting for a word." > + :type 'string > + :version "30.1") > + > +(defcustom dictionary-display-definition-function nil > + "Function to use for displaying dictionary definitions. > +It is called with three string arguments: the word being defined, > +the dictionary name, and the full definition." > + :type '(choice (const :tag "Dictionary buffer" nil) > + (const :tag "Help buffer" > + dictionary-display-definition-in-help-buffer) > + (function :tag "Custom function")) > + :version "30.1") What is the reason for having this option fallback to nil? I couldn't make that out from the patch. If all the other options offer a concrete-default function (that you could also call in your own function), then it seems inconsistent to not provide this here as well. > +(defcustom dictionary-read-word-function #'dictionary-read-word-default > + "Function to use for prompting for a word. > +It is called with one string argument, the name of the dictionary to use, and > +must return a string." > + :type '(choice (const :tag "Default" dictionary-read-word-default) > + (const :tag "Dictionary-based completion" > + dictionary-completing-read-word) > + (function :tag "Custom function")) > + :version "30.1") > + > +(defcustom dictionary-read-dictionary-function > + #'dictionary-read-dictionary-default > + "Function to use for prompting for a dictionary. > +It is called with no arguments and must return a string." > + :type '(choice (const :tag "Default" dictionary-read-dictionary-default) > + (const :tag "Choose among server-provided dictionaries" > + dictionary-completing-read-dictionary) > + (function :tag "Custom function")) > + :version "30.1") > + > +(defcustom dictionary-search-interface nil > + "Controls how `dictionary-search' prompts for words and displays definitions. > + > +When set to `help', `dictionary-search' displays definitions in a *Help* buffer, > +and provides completion for word selection based on dictionary matches. > + > +Otherwise, `dictionary-search' displays definitions in a *Dictionary* buffer." > + :type '(choice (const :tag "Dictionary buffer" nil) > + (const :tag "Help buffer" help)) > + :set (lambda (symbol value) > + (let ((vals (pcase value > + ('help '(dictionary-display-definition-in-help-buffer > + dictionary-completing-read-word > + dictionary-completing-read-dictionary)) > + (_ '(nil > + dictionary-read-word-default > + dictionary-read-dictionary-default))))) > + (setq dictionary-display-definition-function (nth 0 vals) > + dictionary-read-word-function (nth 1 vals) > + dictionary-read-dictionary-function (nth 2 vals))) I think you could also make use of seq-setq here? > + (set-default-toplevel-value symbol value)) > + :version "30.1") > + > (defface dictionary-word-definition-face > '((((supports (:family "DejaVu Serif"))) > (:family "DejaVu Serif")) > @@ -366,6 +425,8 @@ dictionary-word-history > '() > "History list of searched word.") > > +(defvar dictionary--last-match nil) > + > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > ;; Basic function providing startup actions > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > @@ -1139,6 +1200,20 @@ dictionary-search-default > ((car (get-char-property (point) 'data))) > (t (current-word t)))) > > +(defun dictionary-read-dictionary-default () > + "Prompt for a dictionary name." > + (read-string (if dictionary-default-dictionary > + (format "Dictionary (%s): " > + dictionary-default-dictionary) > + "Dictionary: ") > + nil nil dictionary-default-dictionary)) > + > +(defun dictionary-read-word-default (_dictionary) > + "Prompt for a word to search in the dictionary." > + (let ((default (dictionary-search-default))) > + (read-string (format-prompt dictionary-read-word-prompt default) > + nil 'dictionary-word-history default))) > + > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > ;; User callable commands > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > @@ -1149,23 +1224,22 @@ dictionary-search > It presents the selection or word at point as default input and > allows editing it." > (interactive > - (list (let ((default (dictionary-search-default))) > - (read-string (format-prompt "Search word" default) > - nil 'dictionary-word-history default)) > - (if current-prefix-arg > - (read-string (if dictionary-default-dictionary > - (format "Dictionary (%s): " dictionary-default-dictionary) > - "Dictionary: ") > - nil nil dictionary-default-dictionary) > - dictionary-default-dictionary))) > - > - ;; if called by pressing the button > - (unless word > - (setq word (read-string "Search word: " nil 'dictionary-word-history))) > - ;; just in case non-interactively called > + (let ((dict > + (if current-prefix-arg > + (funcall dictionary-read-dictionary-function) > + dictionary-default-dictionary))) > + (list (funcall dictionary-read-word-function dict) dict))) > (unless dictionary > (setq dictionary dictionary-default-dictionary)) > - (dictionary-new-search (cons word dictionary))) > + (if dictionary-display-definition-function > + (if-let ((definition (dictionary-define-word word dictionary))) > + (funcall dictionary-display-definition-function word dictionary definition) > + (user-error "No definition found for \"%s\"" word)) > + ;; if called by pressing the button > + (unless word > + (setq word (read-string "Search word: " nil 'dictionary-word-history))) > + ;; just in case non-interactively called > + (dictionary-new-search (cons word dictionary)))) > > ;;;###autoload > (defun dictionary-lookup-definition () > @@ -1386,5 +1460,98 @@ dictionary-context-menu > 'dictionary-separator)) > menu) > > +(defun dictionary-define-word (word dictionary) > + "Return the definition of WORD in DICTIONARY, or nil if not found." > + (dictionary-send-command > + (format "define %s \"%s\"" dictionary word)) > + (when (and (= (read (dictionary-read-reply)) 150) > + (= (read (dictionary-read-reply)) 151)) I think a memq would be nice here. > + (dictionary-read-answer))) > + > +(defun dictionary-match-word (word) > + "Return dictionary matches for WORD as a list of strings." > + (unless (string-empty-p word) > + (if (string= (car dictionary--last-match) word) > + (cdr dictionary--last-match) > + (dictionary-send-command > + (format "match %s %s \"%s\"" > + dictionary-default-dictionary > + dictionary-default-strategy > + word)) > + (when (and (= (read (dictionary-read-reply)) 152)) > + (with-temp-buffer > + (insert (dictionary-read-answer)) > + (goto-char (point-min)) > + (let ((result nil)) > + (while (not (eobp)) > + (search-forward " " nil t) > + (push (read (current-buffer)) result) > + (search-forward "\n" nil t)) > + (setq result (reverse result)) > + (setq dictionary--last-match (cons word result)) > + result)))))) > + > +(defun dictionary-completing-read-word (dictionary) > + "Prompt for a word with completion based on matches in DICTIONARY." > + (let* ((completion-ignore-case t) > + (dictionary-default-dictionary dictionary) > + (word-at-point (thing-at-point 'word t)) > + (default (dictionary-match-word word-at-point))) > + (completing-read (format-prompt dictionary-read-word-prompt default) > + (completion-table-dynamic #'dictionary-match-word) > + nil t nil 'dictionary-word-history default t))) > + > +(defun dictionary-dictionaries () > + "Return the list of dictionaries the server supports." > + (dictionary-send-command "show db") > + (when (and (= (read (dictionary-read-reply)) 110)) > + (with-temp-buffer > + (insert (dictionary-read-answer)) > + (goto-char (point-min)) > + (let ((result '(("!" . "First matching dictionary") > + ("*" . "All dictionaries")))) > + (while (not (eobp)) > + (push (cons (buffer-substring > + (search-forward "\n" nil t) > + (1- (search-forward " " nil t))) > + (read (current-buffer))) > + result)) > + (reverse result))))) > + > +(defun dictionary-completing-read-dictionary () > + "Prompt for a dictionary the server supports." > + (let* ((dicts (dictionary-dictionaries)) > + (len (apply #'max (mapcar #'length (mapcar #'car dicts)))) > + (completion-extra-properties > + (list :annotation-function > + (lambda (key) > + (concat (make-string (1+ (- len (length key))) ?\s) > + (alist-get key dicts nil nil #'string=)))))) > + (completing-read (format-prompt "Select dictionary" > + dictionary-default-dictionary) > + dicts nil t nil nil dictionary-default-dictionary))) > + > +(define-button-type 'help-word > + :supertype 'help-xref > + 'help-function 'dictionary-search > + 'help-echo (purecopy "mouse-2, RET: describe this word")) Why the purecopy? > +(defun dictionary-display-definition-in-help-buffer (word dictionary definition) > + "Display DEFINITION, the definition of WORD in DICTIONARY." > + (let ((help-buffer-under-preparation t)) > + (help-setup-xref (list #'dictionary-search word dictionary) > + (called-interactively-p 'interactive)) > + (with-help-window (help-buffer) > + (with-current-buffer (help-buffer) > + (insert definition) > + (goto-char (point-min)) > + (while (re-search-forward (rx "{" > + (group-n 1 (* (not (any ?})))) > + "}") > + nil t) Perhaps you could explain what is going on here. Why is this pattern significant? > + (help-xref-button 1 'help-word > + (match-string 1) > + dictionary)))))) > + > (provide 'dictionary) > ;;; dictionary.el ends here > -- > 2.40.1