--- /dev/null
+#+title: Dict: Display Word Definitions from RFC2229 Dictionary Servers
+#+author: Eshel Yaron
+#+email: me@eshelyaron.com
+#+language: en
+#+options: ':t toc:nil author:t email:t num:nil ^:{}
+#+startup: content indent
+#+export_file_name: dict.texi
+#+texinfo_filename: dict.info
+#+texinfo_dir_category: Emacs
+#+texinfo_dir_title: Dict: (dict)
+#+texinfo_dir_desc: Display Word Definitions from RFC2229 Dictionary Servers
+#+texinfo_header: @set MAINTAINERSITE @uref{https://eshelyaron.com,maintainer webpage}
+#+texinfo_header: @set MAINTAINER Eshel Yaron
+#+texinfo_header: @set MAINTAINEREMAIL @email{me@eshelyaron.com}
+#+texinfo_header: @set MAINTAINERCONTACT @uref{mailto:me@eshelyaron.com,contact the maintainer}
+
+This manual describes the Emacs package Dict (or =dict.el=), which provides an
+interface for querying RFC2229 dictionary servers and displaying word
+definitions.
+
+#+toc: headlines 8 insert TOC here, with eight headline levels
+
+* TODO Installation
+
+* Displaying Word Definitions
+:PROPERTIES:
+:CUSTOM_ID: word-definitions
+:DESCRIPTION: Obtaining and displaying the word definitions
+:ALT_TITLE: Word Definitions
+:END:
+
+Dict defines a single autoloaded command for displaying the word definitions:
+
+- Command: dict-describe-word :: Prompt for a word and display its definition.
+
+This command prompts for a word in the minibuffer, obtains its definition from a
+dictionary server, and displays it in a =*Help*= buffer. (See [[info:emacs#Help][Help]] in the Emacs
+manual.)
+
+~dict-describe-word~ uses the word at point, if any, as the minibuffer's default
+argument (see [[info:emacs#Basic Minibuffer][Basic Minibuffer]]). This means that you can display the definition
+of the word at point by typing ~M-x dict-describe-word RET RET~.
+
+Dictionary servers usually support several dictionaries that you can query.
+When Dict asks the dictionary server for a word definition, it needs to specify
+in which dictionary the server should look. The dictionary Dict uses is
+determined by the user option ~dict-dictionary~. By default this is set to
+~nil~, which says to prompt for a supported dictionary the first time you invoke
+~dict-describe-word~.
+
+Similarly, the user option ~dict-strategy~ specifies a dictionary matching
+strategy for finding completion candidates. This is set to ~nil~ by default,
+like ~dict-dictionary~, which means that ~dict-describe-word~ prompts also for a
+supported matching strategy the first time you use it.
+
+You can customize ~dict-dictionary~ and ~dict-strategy~ to appropriate values
+before calling ~dict-describe-word~ to inhibit these prompts on first use.
+(Also see [[#customization][Customizing Dict]] for more customization options.)
+
+* Customizing Dict
+:PROPERTIES:
+:CUSTOM_ID: customization
+:DESCRIPTION: Customization options for Dict
+:ALT_TITLE: Customization
+:END:
+
+The following user options affect the behavior of ~dict-describe-word~:
+
+- User Option: dict-server-host :: Host name or IP address of the
+ dictionary server to use. Defaults to ~nil~, which means to first
+ try "localhost", and if no local server is available then fallback
+ to the host specified by ~dict-default-server-host~.
+- User Option: dict-default-server-host :: Remote dictionary server to
+ use when there is no local server and ~dict-server-host~ is ~nil~.
+ Defaults to "dict.org".
+- User Option: dict-server-port :: Port number to use for connecting to the
+ dictionary server. Defaults to 2628.
+- User Option: dict-dictionary :: Name of the dictionary to query, which must be
+ supported by the dictionary server. Default to ~nil~, which means to prompt
+ for a supported dictionary on the first invocation of ~dict-describe-word~.
+- User Option: dict-dictionary-prompt :: Prompt string to use when prompting for
+ a dictionary. Defaults to "Set dictionary".
+- User Option: dict-strategy :: Name of the dictionary matching strategy to use,
+ which must be supported by the dictionary server. Default to ~nil~, which
+ means to prompt for a supported strategy on the first invocation of
+ ~dict-describe-word~.
+- User Option: dict-strategy-prompt :: Prompt string to use when prompting for a
+ matching strategy. Defaults to "Set matching strategy".
+- User Option: dict-process-name :: Name to use for the dictionary server
+ connection process. Defaults to "dict".
+- User Option: dict-process-buffer-name :: Name of the buffer to use for the
+ dictionary server connection process output. Defaults to "*​dict output*".
+ #+FINDEX: dict-display-definition-in-help-buffer
+- User Option: dict-display-definition-function :: Function to use for
+ displaying word definitions. The function must take two string arguments, the
+ word and its definition, and display the definition to the user. Defaults to
+ the function ~dict-display-definition-in-help-buffer~, which displays the
+ definition in a =*Help*= buffer.
+
+* Extending Dict
+:PROPERTIES:
+:CUSTOM_ID: extending-dict
+:DESCRIPTION: Defining bespoke extensions for Dict
+:ALT_TITLE: Extending Dict
+:END:
+
+The best way to extend Dict with bespoke commands for your workflow, is to
+define wrappers around ~dict-describe-word~ that supply specific words or
+locally bind some user options to specific values. For example, the following
+command looks up the definition of the word at point in the WordNet dictionary
+without prompting:
+
+#+begin_src emacs-lisp
+ (defun my/wordnet-definition-for-word-at-point (word)
+ (interactive (list (word-at-point)))
+ (let ((dict-dictionary "wn"))
+ (dict-describe-word word)))
+#+end_src
+
+To create a command that displays definitions in the echo area (see [[info:emacs#Echo Area][Echo Area]]),
+locally bind ~dict-display-definition-function~ to an appropriate function:
+
+#+begin_src emacs-lisp
+ (defun my/echo-word-definition (word)
+ (interactive (list (dict-read-word)))
+ (let ((dict-display-definition-function
+ (lambda (_word definition)
+ (message definition))))
+ (dict-describe-word word)))
+#+end_src
+
+#+FINDEX: dict-read-word
+Note the use of the function ~dict-read-word~ in the above definition.
+This helper function prompts for a word defined in dictionary, with
+completion, using the word at point as the minibuffer's default
+argument. This is also what ~dict-describe-word~ uses to prompt for a
+word when you call it interactively.
+
+* TODO How Dict Compares to Other RFC2229 Client Packages
+:PROPERTIES:
+:CUSTOM_ID: alternatives
+:DESCRIPTION: Comparison of Dict with other RFC2229 client packages
+:ALT_TITLE: Alternatives
+:END:
+
+** dictionary
+
+** define-word
+
+** DictEm
+
+
+#+html: <!--
+
+* Indices
+:PROPERTIES:
+:CUSTOM_ID: indices
+:DESCRIPTION:
+:ALT_TITLE: Indices
+:END:
+
+** Function index
+:PROPERTIES:
+:INDEX: fn
+:CUSTOM_ID: findex
+:DESCRIPTION:
+:ALT_TITLE: Function Index
+:END:
+
+** Variable index
+:PROPERTIES:
+:INDEX: vr
+:CUSTOM_ID: vindex
+:DESCRIPTION:
+:ALT_TITLE: Variable Index
+:END:
+
+** Keystroke index
+:PROPERTIES:
+:INDEX: ky
+:CUSTOM_ID: kindex
+:DESCRIPTION:
+:ALT_TITLE: Keystroke Index
+:END:
+
+** Concept index
+:PROPERTIES:
+:INDEX: cp
+:CUSTOM_ID: cindex
+:DESCRIPTION:
+:ALT_TITLE: Concept Index
+:END:
+
+#+html: -->
;; Author: Eshel Yaron <me@eshelyaron.com>
;; Keywords: help, comm
;; URL: https://git.eshelyaron.com/gitweb/dict.git
-;; Package-Version: 0.1.0
-;; Package-Requires: ((emacs "29.1"))
+;; Package-Version: 0.1.1
+;; Package-Requires: ((emacs "28.1"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; Dict defines a single command that displays the definition of a
;; given word, as obtained from an RFC2229 dictionary server. To use
-;; it, invoke \\[dict-describe-word].
+;; it, do M-x dict-describe-word (or bind it to a key).
;;; Code:
"Access to RFC2229 dictionary servers."
:group 'help)
+(defcustom dict-strategy-prompt "Set matching strategy"
+ "Prompt to use for dictionary matching strategies selection."
+ :type 'string)
+
+(defcustom dict-dictionary-prompt "Set dictionary"
+ "Prompt to use for dictionary selection."
+ :type 'string)
+
(defcustom dict-process-buffer-name "*dict output*"
"Buffer name for dictionary server output."
:type 'string)
"Process name for dictionary server connection."
:type 'string)
-(defcustom dict-server-host "dict.org"
+(defcustom dict-server-host nil
"Host or IP address of dictionary server."
- :type 'string)
+ :type '(choice (const :tag "Try local server, fallback to `dict-default-server-host'" nil)
+ (string :tag "Select host")))
(defcustom dict-server-port 2628
"Port of dictionary server."
"Function to use for displaying dictionary definitions."
:type 'function)
+(defcustom dict-default-server-host "dict.org"
+ "Remote server to use when there is no local server.
+This is only considered when the user option `dict-server-host'
+is nil."
+ :type 'string)
+
(defvar dict-process nil)
(defvar dict-read-word-history nil)
(defvar dict-match-cache nil)
+(defun dict-server-host ()
+ (or dict-server-host
+ (when-let ((proc (condition-case _
+ (make-network-process
+ :name dict-process-name
+ :host "localhost"
+ :service dict-server-port)
+ (file-error nil))))
+ (delete-process proc)
+ (setq dict-server-host "localhost"))
+ (setq dict-server-host dict-default-server-host)))
+
(defun dict-command (command parse)
"Invoke a dictionary server command.
COMMAND is an RFC2229 command in string format, without a newline.
(setq dict-process
(make-network-process
:name dict-process-name
- :host dict-server-host
+ :host (dict-server-host)
:service dict-server-port
:buffer buffer)))))
(set-process-filter proc filter)
result))
result)))))
-(defun dict-read-strategy ()
- "Prompt for a matching strategy supported by the dictionary server."
+(defun dict-read-strategy (prompt)
+ "Prompt with PROMPT for a matching strategy supported by the server."
(let* ((strats (dict-get-strategies))
(len (apply #'max (mapcar #'length (mapcar #'car strats))))
(completion-extra-properties
(lambda (key)
(concat (make-string (1+ (- len (length key))) ?\s)
(alist-get key strats nil nil #'string=))))))
- (completing-read "Strategy: "
- strats nil t)))
+ (completing-read (format-prompt prompt dict-strategy)
+ strats nil t nil nil dict-strategy)))
+
+(defun dict-set-strategy ()
+ "Prompt for a value and set the user option `dict-strategy'."
+ (setq dict-strategy (dict-read-strategy dict-strategy-prompt)))
(defun dict-strategy ()
"Return the value of the user option `dict-strategy'.
If its nil, prompt for a supported strategy and set the user
option to it first."
(or dict-strategy
- (setq dict-strategy (dict-read-strategy))))
+ (dict-set-strategy)))
(defun dict-get-dictionaries ()
"Return the dictionary server's supported dictionaries."
result))
result)))))
-(defun dict-read-dictionary ()
- "Prompt for dictionary server's supported by the dictionary server."
+(defun dict-read-dictionary (prompt)
+ "Prompt with PROMPT for a dictionary supported by the server."
(let* ((dicts (dict-get-dictionaries))
(len (apply #'max (mapcar #'length (mapcar #'car dicts))))
(completion-extra-properties
(lambda (key)
(concat (make-string (1+ (- len (length key))) ?\s)
(alist-get key dicts nil nil #'string=))))))
- (completing-read "Dictionary: "
- dicts nil t)))
+ (completing-read (format-prompt prompt dict-dictionary)
+ dicts nil t nil nil dict-dictionary)))
+
+(defun dict-set-dictionary ()
+ "Prompt for a value and set the user option `dict-dictionary'."
+ (setq dict-dictionary (dict-read-dictionary dict-dictionary-prompt)))
(defun dict-dictionary ()
"Return the value of the user option `dict-dictionary'.
If its nil, prompt for a supported dictionary and set the user
option to it first."
(or dict-dictionary
- (setq dict-dictionary (dict-read-dictionary))))
+ (dict-set-dictionary)))
(defun dict-read-word ()
- "Prompt for a word known to the dictionary server."
+ "Prompt for a word defined in the dictionary server."
(let* ((completion-ignore-case t)
(word-at-point (thing-at-point 'word t))
(default (car (dict-match-word word-at-point))))