1 ;;; edictc.el --- DICT client for Emacs
3 ;; Author: Vaidheeswaran C <vaidheeswaran.chinnaraju@gmail.com>
6 ;; This file is not part of GNU Emacs.
8 ;; This program is free software: you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation, either version 3 of the License, or
11 ;; (at your option) any later version.
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
31 (require 'tabulated-list
)
36 ;;; Regex for parsing command responses
41 (defvar edictc-dqtext-re
42 `(one-or-more (not (in "\"" "\\" control
))))
44 (defvar edictc-quoted-pair-re
47 (defvar edictc-dqstring-re
48 `(and "\"" (zero-or-more (or ,edictc-dqtext-re
,edictc-quoted-pair-re
)) "\""))
50 (defvar edictc-sqtext-re
51 `(one-or-more (not (in "'" "\\" control
))))
53 (defvar edictc-sqstring-re
54 `(and "'" (zero-or-more (or ,edictc-dqtext-re
,edictc-quoted-pair-re
)) "'"))
56 (defvar edictc-atom-re
57 `(one-or-more (not (in " " "\t" "\"" "'" "\\"))))
59 (defvar edictc-string-re
60 `(zero-or-more (or ,edictc-dqstring-re
,edictc-sqstring-re
,edictc-quoted-pair-re
)))
62 (defvar edictc-word-re
63 `(zero-or-more (or ,edictc-atom-re
,edictc-string-re
)))
65 (defvar edictc-description-re
66 `(zero-or-more (or ,edictc-word-re
,edictc-ws-re
)))
68 (defvar edictc-text-re
69 `(zero-or-more (or ,edictc-word-re
,edictc-ws-re
)))
71 (defvar edictc-status-re
72 '(and (group-n 1 (and (zero-or-more "0")
76 (group-n 2 (zero-or-more anything
))))
78 (defvar edictc-msg-atom-re
79 '(one-or-more (not (in " " control
"<" ">" "." "\\"))))
81 (defvar edictc-capabilities-re
82 `(and "<" ,edictc-msg-atom-re
(zero-or-more (and "." ,edictc-msg-atom-re
)) ">"))
84 (defvar edictc-local-part-re
85 `(and ,edictc-msg-atom-re
(zero-or-more (and "." ,edictc-msg-atom-re
))))
87 (defvar edictc-domain-re
88 `(and ,edictc-msg-atom-re
(zero-or-more (and "." ,edictc-msg-atom-re
))))
90 (defvar edictc-spec-re
91 `(and ,edictc-local-part-re
"@" ,edictc-domain-re
))
93 (defvar edictc-msg-id-re
94 `(and "<" ,edictc-spec-re
">"))
96 (defvar edictc-param-text-re
97 `(or ,edictc-atom-re
,edictc-dqstring-re
))
99 (defvar edictc-banner-re
101 " " (group-n 1 ,edictc-text-re
)
102 " " (group-n 2 (zero-or-one ,edictc-capabilities-re
))
103 " " (group-n 3 ,edictc-msg-id-re
)
106 (defvar edictc-database-description-re
107 `(and (group-n 1 (or ,edictc-atom-re
,edictc-dqstring-re
))
109 (group-n 2 (or ,edictc-atom-re
,edictc-dqstring-re
))))
111 (defvar edictc-strategy-description-re
112 `(and (group-n 1 (or ,edictc-atom-re
,edictc-dqstring-re
))
114 (group-n 2 (or ,edictc-atom-re
,edictc-dqstring-re
))))
116 (defvar edictc-word-definition-re
117 `(and (group-n 1 (or ,edictc-atom-re
,edictc-dqstring-re
))
119 (group-n 2 (or ,edictc-atom-re
,edictc-dqstring-re
))
121 (group-n 3 (or ,edictc-atom-re
,edictc-dqstring-re
))))
123 (defvar edictc-match-description-re
124 `(and (group-n 1 (or ,edictc-atom-re
,edictc-dqstring-re
))
126 (group-n 2 (or ,edictc-atom-re
,edictc-dqstring-re
))))
130 (defconst edictc-port
2628)
132 (defvar edictc-process-name
"edictc")
134 (defvar edictc-output-buffer-name
"edictc")
135 (defvar edictc-debug-buffer-name
"edictc debug")
137 ;;; User Customization
141 :link
'(custom-manual "(emacs)Highlight Interactively")
144 (defcustom edictc-servers
145 `(("DICT Dev. Group" :server
"dict.org" :about
"http://www.dict.org/")
146 ("GNU Dico" :server
"dicoweb.gnu.org.ua" :about
"http://dicoweb.gnu.org.ua")
147 ("FreeDict" :server
"dict.freedict.org" :about
"http://freedict.org/dict")
148 ("localhost" :server
"localhost"))
149 "List of DICT servers."
150 :type
'(repeat (cons (string :tag
"Dict server name")
151 (plist :options
((:server string
)
155 (:password string
)))))
158 (defcustom edictc-server
(caar edictc-servers
)
159 "Default DICT Server."
160 :type
'(choice (const :tag
"None" nil
)
161 (string :tag
"DICT Server"))
164 ;; "*" ; retrieve matches from ALL databases that provide a match.
165 ;; "!" ; retrieve matches from database that provides the first match.
167 (defcustom edictc-default-database
"*"
169 :type
'(choice (const :tag
"Retrieve from ALL databases" "*")
170 (const :tag
"Retrieve match from the first databases" "!"))
173 (defcustom edictc-default-strategy
"prefix"
174 "Default Match strategy."
175 :type
'(choice (const :tag
"Exact match" "exact")
176 (const :tag
"Prefix" "prefix")
177 (const :tag
"Server default" "."))
180 ;;; DICT protocol handling
182 (defvar edictc-process nil
)
183 (put 'edictc-process
'permanent-local t
)
185 (defvar edictc-process-list nil
)
187 (defstruct (edictc-database (:type list
) :named
)
191 (defstruct (edictc-strategy (:type list
) :named
)
195 ;; (defstruct (edictc-connection (:type list) :named)
211 ;; server-capabilities
232 (defstruct (edictc-word-definition (:type list
) :named
)
238 (defstruct (edictc-word-match (:type list
) :named
)
244 (defcustom edictc-debug t
246 :type
'(choice (const :tag
"none" nil
)
247 (const :tag
"all" t
))
250 (defun edictc-get-create-debug-buffer (ep)
251 (let ((buffer (process-get ep
:debug-buffer
)))
252 (unless (buffer-live-p buffer
)
253 (setq buffer
(generate-new-buffer edictc-debug-buffer-name
))
254 (process-put ep
:debug-buffer buffer
)
255 (with-current-buffer buffer
256 (setq-local edictc-process ep
)
260 (defun edictc--debug (ep &rest args
)
262 (error "Interrupted!"))
263 (if (eq edictc-debug t
)
264 (with-current-buffer (edictc-get-create-debug-buffer ep
)
265 (goto-char (point-max))
267 (format "%s -> %s\n" (process-name (process-get ep
:process
))
268 (apply 'format args
))))))
270 (defsubst edictc-debug
(ep &rest args
)
272 (let* ((proc (process-get ep
:process
)))
273 ;; The user hit C-g, honor it! Some things can get in an
274 ;; incredibly tight loop (chunked encoding)
277 (set-process-sentinel proc nil
)
278 (set-process-filter proc nil
)))
279 (error "Transfer interrupted!")))
280 (apply 'edictc--debug ep args
))
284 (defun edictc-send-command (ep &optional request
)
285 (when (eq (process-get ep
:state
) 'READY
)
287 (setq request
(caar (process-get ep
:cmdq-head
))))
290 (let* ((string (edictc-request->string request
))
291 (tag (edictc-request->tag request
))
292 (command (concat string
"\r\n")))
293 (process-put ep
:state tag
)
294 (message "[%s] <- %s" (process-get ep
:server-nick
)
296 (edictc-debug ep
"COMMAND SENT: %S" string
)
297 (process-send-string (process-get ep
:process
) command
)))))
299 (defun edictc-queue-command (ep request
&rest request-context
)
302 (when (stringp (car r
))
303 (setcar r
(edictc-quote-parameter (car r
))))
306 (let* ((send-immediate nil
)
307 (elt (cons request request-context
)))
308 (edictc-debug ep
"STATE: %s" (process-get ep
:state
))
309 (edictc-debug ep
"COMMAND QUEUED: %s, %S"
310 (edictc-request->string request
)
312 (if (derived-mode-p 'edictc-debug-mode
)
313 ;; In debug mode, send the command immediately.
314 (edictc-send-command ep request
)
315 ;; Otherwise, queue the command.
317 ((process-get ep
:cmdq-head
)
318 ;; There is a pending command.
319 (edictc-debug ep
"COMMAND HEAD before ENQUEUE: %S" (process-get ep
:cmdq-head
))
320 (edictc-debug ep
"COMMAND TAIL before ENQUEUE: %S" (process-get ep
:cmdq-tail
))
321 (process-put ep
:cmdq-tail
322 (setcdr (process-get ep
:cmdq-tail
) (list elt
))))
324 ;; This is the only outstanding command
325 (process-put ep
:cmdq-head
(list elt
))
326 (process-put ep
:cmdq-tail
327 (process-get ep
:cmdq-head
))
328 ;; Send it immediately.
329 (setq send-immediate t
)))
330 (edictc-debug ep
"COMMAND HEAD after ENQUEUE: %S" (process-get ep
:cmdq-head
))
331 (edictc-debug ep
"COMMAND TAIL after ENQUEUE: %S" (process-get ep
:cmdq-tail
))
332 (edictc-debug ep
"STATE: %s SEND IMMEDIATE: %s"
333 (process-get ep
:state
) send-immediate
)
334 (when (and (eq (process-get ep
:state
) 'READY
) send-immediate
)
335 ;; Initial handshake is done and there is no pending command.
336 (edictc-send-command ep
)))))
338 (defun edictc-response-type (status-code)
339 (or (and (numberp status-code
) (> status-code
99) (< status-code
600)
340 (assoc-default (/ status-code
100)
341 '((1 . positive-preliminary
)
343 (3 . positive-intermediate
)
344 (4 . transient-negative
)
345 (5 . permanent-negative
))))
348 (defun edictc-unqueue-command (ep)
349 (assert (numberp (process-get ep
:state
)))
350 (let* ((gratis-response (= (process-get ep
:state
) 220))
351 (response-type (edictc-response-type (process-get ep
:state
))))
353 (when (memq response-type
'(positive permanent-negative
))
354 ;; Ready for next command.
355 (process-put ep
:state
'READY
))
357 (when (and (null gratis-response
)
358 (eq (process-get ep
:state
) 'READY
))
359 ;; A command is now finished. Unqueue it from the pending list.
360 (assert (process-get ep
:cmdq-head
))
361 (let* ((elt (pop (process-get ep
:cmdq-head
)))
362 (last-request (car elt
))
363 (last-command (edictc-request->string last-request
))
364 (last-command-tag (edictc-request->tag last-request
))
365 (last-request-context (cdr elt
))
366 (success-callback (car last-request-context
))
367 (success-callback-args (cdr last-request-context
)))
369 (message "[%s] -> %s (%s)" (process-get ep
:server-nick
)
372 (edictc-debug ep
"COMMAND DONE: %s STAUS: %s" last-command response-type
)
374 (unless (process-get ep
:cmdq-head
)
375 ;; No more pending commands.
376 (process-put ep
:cmdq-tail nil
))
379 (edictc-debug ep
"COMMAND DONE: %s STATUS: %s" last-command response-type
))
381 (edictc-debug ep
"COMMAND HEAD after UNQUEUE: %S"
382 (process-get ep
:cmdq-head
))
383 (edictc-debug ep
"COMMAND TAIL after UNQUEUE: %S"
384 (process-get ep
:cmdq-tail
))
388 (when success-callback
389 (apply success-callback ep last-request success-callback-args
)))
391 (edictc-display-error ep last-request
))
393 (error "Don't know how to handle %s" response-type
)))
394 ;; Clear the current responses.
395 (process-put ep
:num-responses nil
)
396 (process-put ep
:responses nil
))))
398 ;; Send over the pending command, if any
399 (edictc-send-command ep
))
401 ;;;; Interactive helpers
403 (defun edictc-quote-parameter (parameter)
405 ((string-match (rx-to-string `(and bos
,edictc-atom-re eos
)) parameter
) parameter
)
406 ((string-match (rx-to-string `(and bos
,edictc-dqstring-re eos
)) parameter
) parameter
)
407 ((string-match (rx-to-string '(and bos
(zero-or-one "\"")
408 (group-n 1 (minimal-match
409 (zero-or-more not-newline
)))
410 (zero-or-one "\"") eos
))
412 (let ((retval (concat "\"" (replace-regexp-in-string "\"" "\\\""
413 (match-string 1 parameter
) t t
)
416 (assert (string-match (rx-to-string `(or ,edictc-atom-re
417 ,edictc-dqstring-re
))
420 (defun edictc-select-database (ep &optional include-wild-cards-p
)
421 ;; "*" ; retrieve matches from ALL databases
422 ;; ; that provide a match.
423 ;; "!" ; retrieve matches from database that
424 ;; ; provides the first match.
425 (let ((db (completing-read "Use Database: "
427 (and include-wild-cards-p
431 (edictc-database-handle entity
))
432 (process-get ep
:databases
)))
436 (defun edictc-select-strategy (ep)
438 ;; (or "prefix" "exact") ; well-known strategies supported by all DICT
441 (let ((strategy (completing-read
442 "Use Match Strategy: "
444 (list "prefix" "exact")
447 (edictc-strategy-handle entity
))
448 (process-get ep
:strategies
)))
454 (defun edictc-command-client (&optional ep text
)
456 (interactive (list (edictc)))
457 (let ((text (or text
"edictc")))
458 (edictc-queue-command ep
(list 'CLIENT text
))))
460 ;;;; DEFINE database word
462 (defun edictc-command-define (ep word
&optional database
)
463 "Lookup WORD in DATABASE."
465 (let* ((word (or (and (region-active-p)
466 (buffer-substring (region-beginning) (region-end)))
468 (word (and word
(substring-no-properties word
)))
469 (prompt (format "Define word%s: " (if word
(format " (%s)" word
) "")))
470 (word (read-string prompt nil nil word
))
473 (if current-prefix-arg
474 (edictc-select-database ep
'include-wild-cards
)
475 (or (process-get ep
:database
)
476 edictc-default-database
)))))
477 (edictc-queue-command ep
(list 'DEFINE database word
)
478 'edictc-display-definition
))
482 (defun edictc-command-help (&optional ep
)
483 "Show commands accepted by DICT server."
484 (interactive (list (edictc)))
485 (edictc-queue-command ep
(list 'HELP
)
486 'edictc-display-information
))
488 ;;;; MATCH database strategy word
490 (defun edictc-command-match (ep word
&optional strategy database
)
491 "Match WORD in DATABASE using STRATEGY."
493 (let* ((default (word-at-point))
494 (prompt (format "Match word%s: " (if default
(format " (%s)" default
) "")))
495 (word (read-string prompt nil nil default
))
498 (if current-prefix-arg
499 (edictc-select-strategy ep
)
500 (or (process-get ep
:strategy
)
501 edictc-default-strategy
))
502 (if current-prefix-arg
503 (edictc-select-database ep
'include-wild-cards
)
504 (or (process-get ep
:database
)
505 edictc-default-database
)))))
506 (edictc-queue-command ep
(list 'MATCH database strategy word
)
507 'edictc-display-match
))
511 (defun edictc-command-option (&optional ep
)
512 (interactive (list (edictc)))
513 (edictc-queue-command ep
(list 'OPTION
)))
517 (defun edictc-command-quit (&optional ep
)
518 "Close connection to the DICT server."
519 (interactive (list (edictc)))
520 (edictc-queue-command ep
(list 'QUIT
)))
523 (defun edictc-command-show-databases (&optional ep
)
524 (interactive (list (edictc)))
525 (edictc-queue-command ep
(list 'SHOW
'DATABASES
)
526 'edictc-display-databases
527 (not (called-interactively-p 'any
))))
529 ;;;; SHOW INFO database
531 (defun edictc-command-show-info (ep database
)
534 (list ep
(edictc-select-database ep
))))
535 (edictc-queue-command ep
(list 'SHOW
'INFO database
)
536 'edictc-display-information
))
540 (defun edictc-command-show-server (&optional ep suppress-display
)
541 (interactive (list (edictc) nil
))
542 (edictc-queue-command ep
(list 'SHOW
'SERVER
)
543 'edictc-display-server
548 (defun edictc-command-show-strategies (&optional ep
)
549 (interactive (list (edictc)))
550 (edictc-queue-command ep
(list 'SHOW
'STRATEGIES
)
551 'edictc-display-strategies
552 (not (called-interactively-p 'any
))))
556 (defun edictc-command-status (&optional ep
)
557 "Show timing or debugging information."
558 (interactive (list (edictc)))
559 (edictc-queue-command ep
(list 'STATUS
)
560 'edictc-display-information
))
562 ;;; Developer Commands
564 (defun edictc-command-kill (&optional ep
)
565 "Kill connection to the DICT server."
566 (interactive (list (edictc)))
567 (delete-process (process-get ep
:process
)))
569 (defun edictc--devel-command-unknown-command (&optional ep
)
570 "Trigger 500 Syntax error, command not recognized"
571 (interactive (list (edictc)))
572 (edictc-queue-command ep
(list 'HELLO
'WORLD
)
573 'edictc-display-information
))
575 (defun edictc--devel-command-invalid-database (&optional ep
)
576 "Trigger 500 Syntax error, command not recognized"
577 (interactive (list (edictc)))
578 (edictc-command-define ep
"hello" "junk-database"))
580 (defun edictc--devel-command-invalid-strategy (&optional ep
)
581 "Trigger 500 Syntax error, command not recognized"
582 (interactive (list (edictc)))
583 (edictc-command-match ep
"hello" "junk-strategy" "*"))
585 (defun edictc--devel-command-invalid-parameters (&optional ep
)
586 "Trigger 500 Syntax error, command not recognized"
587 (interactive (list (edictc)))
588 (edictc-command-define ep
"hello" ""))
590 ;;; DICT Response Handling
592 (defun edictc-request->tag
(request)
593 (intern (mapconcat (lambda (s)
595 (symbol-name s
))) request
"")))
597 (defun edictc-request->string
(request)
598 (mapconcat (lambda (s) (format "%s" s
)) request
" "))
600 (defvar edictc-status-codes
602 ;; 110 n databases present - text follows
603 (110 n-dbs-present textual-response
)
604 ;; 111 n strategies available - text follows
605 (111 n-strategies-available textual-response
)
606 ;; 112 database information follows
607 (112 database-info textual-response
)
608 ;; 113 help text follows
609 (113 help-text textual-response
)
610 ;; 114 server information follows
611 (114 server-info textual-response
)
612 ;; 130 challenge follows
614 ;; 150 n definitions retrieved - definitions follow
615 (150 n-definitions-retrieved
)
616 ;; 151 word database name - text follows
617 (151 word-db-name textual-response
)
618 ;; 152 n matches found - text follows
619 (152 n-matches-found textual-response
)
620 ;; 210 (optional timing and statistical information here)
621 (210 timing-and-statistical-information-here
)
624 ;; 221 Closing Connection
625 (221 closing-connection
)
626 ;; 230 Authentication successful
627 (230 authentication-successful
)
628 ;; 250 ok (optional timing information here)
632 ;; 420 Server temporarily unavailable
633 (420 server-temporarily-unavailable
)
634 ;; 421 Server shutting down at operator request
635 (421 server-shutting-down-at-operator-request
)
636 ;; 500 Syntax error, command not recognized
637 (500 syntax-error--command-not-recognized
)
638 ;; 501 Syntax error, illegal parameters
639 (501 syntax-error--illegal-parameters
)
640 ;; 502 Command not implemented
641 (502 command-not-implemented
)
642 ;; 503 Command parameter not implemented
643 (503 command-parameter-not-implemented
)
646 ;; 531 Access denied, use "SHOW INFO" for server information
647 (531 access-denied--use--show-info--for-server-info
)
648 ;; 532 Access denied, unknown mechanism
649 (532 access-denied--unknown-mechanism
)
650 ;; 550 Invalid database, use "SHOW DB" for list of databases
652 ;; 551 Invalid strategy, use "SHOW STRAT" for a list of strategies
653 (551 invalid-strategy
)
656 ;; 554 No databases present
658 ;; 555 No strategies available
659 (555 no-strategies-available
)))
661 (defvar edictc-in-out-states
662 '((START .
(220 420 421))
669 (SHOWDATABASES .
(110))
671 (SHOWSTRATEGIES .
(111))
683 (defun edictc-receive (ep &optional st nd length
)
685 (edictc-debug ep
"DATA RECEIVED"))
686 (edictc-debug ep
"DATA OUTSTANDING:\n%s<-" (buffer-substring (point-min) (point-max)))
688 (goto-char (point-min))
691 '(and (group-n 1 (and (group-n 2 (one-or-more digit
))
693 (group-n 3 (minimal-match (one-or-more anything
)))))
695 ;; Received a status response.
696 (let* ((status-response (match-string 1))
697 (status-code (string-to-number (match-string 2)))
698 (response-type (edictc-response-type status-code
))
699 (status-rest (match-string 3))
700 (end-of-status-response (match-end 0))
702 (textual-response-codes '(110 111 112 113 114 151 152))
704 (edictc-debug ep
"Peeking at status code: %s" status-code
)
706 ;; Check if status code falls in the expected range.
707 (when (eq response-type
'invalid
)
708 (error "Invalid status code: %d" status-code
))
710 (unless (memq response-type
'(permanent-negative))
711 ;; Check if the status code agrees with what we expect.
712 (let ((expected-status-codes
713 (assoc-default (process-get ep
:state
) edictc-in-out-states
)))
714 (unless (memq status-code expected-status-codes
)
717 (format "Unexpected status code %d while in %s state;"
718 status-code
(process-get ep
:state
))
719 (format "\nExpecting one of the following status codes: %S"
720 expected-status-codes
))))))
723 ((member status-code textual-response-codes
)
724 (goto-char end-of-status-response
)
725 (when (re-search-forward "\r\n\\.\r\n" nil t
)
726 (process-put ep
:state status-code
)
727 ;; Recieved full textual response.
728 (setq text
(replace-regexp-in-string
729 "\r\n" "\n" (buffer-substring end-of-status-response
732 ;; Erase the whole response.
733 (delete-region (point-min) (match-end 0))
734 (edictc-debug ep
"STATE: %s" (process-get ep
:state
))))
736 (delete-region (point-min) end-of-status-response
)
737 (process-put ep
:state status-code
)))
739 ;; Take a peek at the code.
741 ;; * 110 n databases present - text follows
744 (when (string-match (rx-to-string '(and bos
(one-or-more digit
))) status-rest
)
745 (process-put ep
:num-responses
746 (string-to-number (match-string 0 status-rest
))))
748 (let ((dbs (split-string text
"\n")))
749 (process-put ep
:responses
751 (lambda (db) ; database description
752 (if (string-match (rx-to-string edictc-database-description-re
) db
)
753 (make-edictc-database
754 :handle
(match-string 1 db
)
755 :description
(match-string 2 db
))
756 (error "Unable to parse database list: %s" db
)))
758 ;; Start processing rest of text.
759 (edictc-receive ep
)))
760 ;; * 111 n strategies available - text follows
763 (when (string-match (rx-to-string '(and bos
(one-or-more digit
))) status-rest
)
764 (process-put ep
:num-responses
765 (string-to-number (match-string 0 status-rest
))))
767 (let ((strategies (split-string text
"\n")))
768 (process-put ep
:responses
770 (lambda (strategy) ; strategy description
771 (if (string-match (rx-to-string edictc-strategy-description-re
) strategy
)
772 (make-edictc-strategy
773 :handle
(match-string 1 strategy
)
774 :description
(match-string 2 strategy
))
775 (error "Unable to parse database list: %s" strategy
)))
777 (edictc-receive ep
)))
778 ;; 112 database information follows
781 (process-put ep
:responses
(cons status-rest text
))
782 (edictc-receive ep
)))
783 ;; 113 help text follows
786 (process-put ep
:responses
(cons status-rest text
))
787 (edictc-receive ep
)))
788 ;; 114 server information follows
791 (process-put ep
:responses
(cons status-rest text
))
792 (edictc-receive ep
)))
793 ;; 130 challenge follows
797 ;; * 150 n definitions retrieved - definitions follow
799 (when (string-match (rx-to-string '(and bos
(one-or-more digit
))) status-rest
)
800 (process-put ep
:num-responses
801 (string-to-number (match-string 0 status-rest
))))
803 ;; * 151 word database name - text follows
806 (let (word database name
)
807 (when (string-match (rx-to-string edictc-word-definition-re
) status-rest
)
808 (setq word
(match-string 1 status-rest
))
809 (setq database
(match-string 2 status-rest
))
810 (setq name
(match-string 3 status-rest
)))
812 (let ((word-definition
813 (make-edictc-word-definition
814 :word word
:db-handle database
:db-description name
816 (push word-definition
(process-get ep
:responses
))))
817 (edictc-receive ep
)))
818 ;; * 152 n matches found - text follows
821 (when (string-match (rx-to-string '(and bos
(one-or-more digit
))) status-rest
)
822 (process-put ep
:num-responses
823 (string-to-number (match-string 0 status-rest
))))
824 (let* ((matches (split-string text
"\n"))
827 (lambda (match) ; database word
828 (if (string-match (rx-to-string edictc-match-description-re
) match
)
829 (make-edictc-word-match
830 :db-handle
(match-string 1 match
)
831 :text
(match-string 2 match
))
832 (error "Unable to parse match list: %s" match
)))
834 (process-put ep
:responses matches-list
))
835 (edictc-receive ep
)))
836 ;; 210 (optional timing and statistical information here)
838 (process-put ep
:responses status-rest
)
839 (edictc-unqueue-command ep
)
840 (process-put ep
:num-responses nil
)
841 (process-put ep
:responses nil
))
844 (when (string-match (rx-to-string edictc-banner-re
) status-response
)
845 (process-put ep
:server-string
846 (match-string 1 status-response
))
847 (process-put ep
:server-capabilities
848 (match-string 2 status-response
))
849 (process-put ep
:msg-id
850 (match-string 3 status-response
)))
851 (edictc-debug ep
"EDICTC ACTIVE")
852 (edictc-unqueue-command ep
))
853 ;; 221 Closing Connection
857 ;; 230 Authentication successful
861 ;; 250 ok (optional timing information here)
863 (edictc-unqueue-command ep
))
868 ;; 420 Server temporarily unavailable
872 ;; 421 Server shutting down at operator request
876 ;; 500 Syntax error, command not recognized
877 ;; 501 Syntax error, illegal parameters
878 ;; 502 Command not implemented
879 ;; 503 Command parameter not implemented
881 ;; 531 Access denied, use "SHOW INFO" for server information
882 ;; 532 Access denied, unknown mechanism
883 ;; 550 Invalid database, use "SHOW DB" for list of databases
884 ;; 551 Invalid strategy, use "SHOW STRAT" for a list of strategies
886 ;; 554 No databases present
887 ;; 555 No strategies available
888 ((500 501 501 501 502 503 530 531 532 550 551 552 554 555)
889 (process-put ep
:responses status-rest
)
890 (edictc-unqueue-command ep
))
892 (error "Received unknown status response: %s" status-response
))))))
894 (defun edictc-process-filter (proc data
)
895 ;; (edictc-debug ep "FILTER RECEIVED: %s" data)
897 (buffer (process-get ep
:process-buffer
)))
900 ;; (display-buffer buffer)
901 (with-current-buffer (process-buffer proc
)
902 (let* ((start (point-max))
903 (inhibit-read-only t
))
904 ;; Append received data in to process buffer.
905 (goto-char (point-max))
907 ;; Hand over the data for protocol processing, when not in
909 (unless (derived-mode-p 'edictc-debug-mode
)
910 (let* ((end (point-max))
911 (length (length data
)))
912 (assert (not (zerop length
)))
913 (funcall (process-get ep
:receivef
) ep start end length
))))))))
915 (defun edictc-cleanup (ep why
)
916 (let* ((process-buffer (process-get ep
:process-buffer
))
917 (output-buffer (process-get ep
:output-buffer
))
918 (debug-buffer (process-get ep
:debug-buffer
))
919 (owner-buffer (process-get ep
:owner-buffer
)))
921 (when (eq edictc-process ep
)
922 (setq edictc-process nil
))
924 (with-current-buffer debug-buffer
925 (setq-local edictc-process nil
))
927 (with-current-buffer output-buffer
928 (setq-local edictc-process nil
))
932 (message "[%s] CONNECTION RESET" (process-get ep
:server-nick
))
934 (with-current-buffer owner-buffer
935 (kill-local-variable 'edictc-process
)))
937 (message "[%s] QUIT, %s" (process-get ep
:server-nick
) why
)
938 (set-default 'edictc-process nil
)))
940 (with-current-buffer debug-buffer
941 (setq edictc-process nil
))
943 ;; FIXME: Remove it from connection list.
946 (kill-buffer process-buffer
))
948 ;; (when output-buffer
949 ;; (kill-buffer output-buffer))
951 (if (get-buffer-window-list output-buffer
)
952 (when (y-or-n-p (format "DICT buffer %s is orphaned. Kill it?" output-buffer
))
953 (kill-buffer output-buffer
))
954 (kill-buffer output-buffer
))
957 (let ((debug-output (with-current-buffer debug-buffer
958 (prog1 (buffer-string)
959 (kill-buffer (current-buffer))))))
960 ;; (with-current-buffer-window (get-buffer-create "*debug*") nil nil
962 ;; (insert debug-output))
965 (defun edictc-process-sentinel (ep why
)
966 (edictc-debug ep
"edictc-process-sentinel in buffer (%s). REASON %s"
967 (process-buffer ep
) why
)
968 (let ((why (format "[%s: %s]" (process-status ep
) why
)))
969 (case (process-status ep
)
970 ;; Kill => closed: deleted.
971 ;; Quit => closed: connection broken by remote peer.
973 (edictc-cleanup ep why
))
975 (error "FIXME: Don't know how to handle process-status %s" why
)))))
978 (if edictc-process edictc-process
979 (edictc-connect edictc-server
)))
981 (defun edictc-connect (&optional server-nick type
)
982 (interactive (list edictc-server
))
984 (assert (memq type
'(nil this-buffer-only orphan
)))
985 (setq server-nick
(or server-nick edictc-server
))
988 (when (and (memq type
'(orphan this-buffer-only
))
989 (local-variable-p 'edictc-process
))
991 "Refusing to start another local connection.\n"
992 "First, shutdown the existing connection.")))
994 (when (and (null type
) (default-value 'edictc-process
))
996 "Refusing to start another default connection.\n"
997 "First shutdown the existing connection or\n"
998 "Start a connection specific to this buffer."))))
1000 (let* ((server-settings (assoc server-nick edictc-servers
))
1001 (server-plist (cdr server-settings
))
1002 (host (plist-get server-plist
:server
))
1003 (port (or (plist-get server-plist
:port
) edictc-port
))
1004 (ep (open-network-stream edictc-process-name nil host port
))
1005 (buffer (generate-new-buffer
1006 (format " *%s %s:%d*" edictc-process-name host port
))))
1008 ;; Failed to open the connection for some reason
1010 (kill-buffer buffer
)
1012 (error "Could not create connection to %s:%d" host port
))
1013 (set-process-plist ep
1015 (list :server-nick server-nick
)
1020 :database
(or (plist-get server-plist
:database
)
1021 edictc-default-database
)
1022 :strategy
(or (plist-get server-plist
:strategy
)
1023 edictc-default-strategy
)
1024 :process-buffer buffer
1025 :receivef
'edictc-receive
)))
1027 (with-current-buffer buffer
1028 (setq mode-line-format
"%b [%s]")
1029 (setq-local edictc-process ep
)
1032 (with-current-buffer (edictc-get-create-output-buffer ep
)
1035 (with-current-buffer (edictc-get-create-debug-buffer ep
)
1040 (process-put ep
:owner-buffer
1041 (process-get ep
:output-buffer
)))
1043 (setq-local edictc-process ep
)
1044 (process-put ep
:owner-buffer
(current-buffer)))
1046 (setq edictc-process ep
)
1047 (process-put ep
:owner-buffer nil
)))
1049 ;; (edictc-debug-mode)
1051 (push ep edictc-process-list
)
1053 (set-process-buffer ep buffer
)
1054 (set-process-filter ep
'edictc-process-filter
)
1055 (set-process-coding-system ep
'utf-8-unix
'utf-8-unix
)
1056 (pcase (process-status ep
)
1058 ;; Asynchronous connection
1059 (set-process-sentinel ep
'edictc-async-sentinel
))
1061 ;; Asynchronous connection failed
1062 (error "Could not create connection to %s:%d" host port
))
1064 (message "Connected to %s:%s" host port
)
1065 (set-process-sentinel ep
1066 'edictc-process-sentinel
)
1067 ;; (process-send-string connection (edictc-create-request))
1069 ;; Queue some commands.
1070 (edictc-command-client ep
)
1071 (edictc-command-show-databases ep
)
1072 (edictc-command-show-strategies ep
)
1073 (edictc-command-show-server ep
))
1076 (defvar edictc-mode-map
1077 (let ((map (copy-keymap special-mode-map
)))
1078 (define-key map
"h" 'edictc-command-show-server
)
1079 (define-key map
"d" 'edictc-command-show-databases
)
1080 (define-key map
"s" 'edictc-command-show-strategies
)
1081 (define-key map
"c" 'edictc-command-help
)
1082 (define-key map
"q" 'edictc-command-quit
)
1083 (define-key map
"i" 'edictc-command-show-info
)
1085 "Local keymap for `tabulated-list-mode' buffers.")
1087 (defvar-local edictc-mode-line nil
)
1089 (defun edictc-describe-process-or-nick (ep-or-nick &optional for
)
1091 ((processp ep-or-nick
)
1094 (format "%s (%s--%s) {%d Dbs/%d Strats}"
1095 (process-get ep-or-nick
:server-nick
)
1096 (process-get ep-or-nick
:database
)
1097 (process-get ep-or-nick
:strategy
)
1098 (length (process-get ep-or-nick
:databases
))
1099 (length (process-get ep-or-nick
:strategies
))))
1101 (format "%s (%s--%s) [Active %s]"
1102 (process-get edictc-process
:server-nick
)
1103 (process-get edictc-process
:database
)
1104 (process-get edictc-process
:strategy
)
1105 (if (local-variable-p 'edictc-process
)
1109 (user-error "This should not happen"))))
1110 ((stringp ep-or-nick
)
1111 (let ((server-plist (assoc-default ep-or-nick edictc-servers
)))
1112 (format "%s (%s--%s)"
1114 (or (plist-get server-plist
:database
)
1115 edictc-default-database
)
1116 (or (plist-get server-plist
:strategy
)
1117 edictc-default-strategy
))))
1120 (define-minor-mode edictc-mode
1122 :lighter
(" " edictc-mode-line
)
1123 (setq edictc-mode-line
1124 (edictc-describe-process-or-nick edictc-process
'mode-line
)))
1126 (defvar edictc-debug-mode-map edictc-mode-map
)
1128 (define-derived-mode edictc-debug-mode edictc-mode
"DICTC Debug"
1129 "Control buffer for interacting with DICT servers.
1130 Use it to interact with DICT server as via telnet."
1136 ;;;; Display Helpers
1138 (defun edictc-get-create-output-buffer (ep)
1140 (generate-new-buffer edictc-output-buffer-name
)
1141 (let ((buffer (process-get ep
:output-buffer
)))
1142 (unless (buffer-live-p buffer
)
1143 (setq buffer
(generate-new-buffer edictc-output-buffer-name
))
1144 (process-put ep
:output-buffer buffer
)
1145 (with-current-buffer buffer
1146 (setq-local edictc-process ep
)
1150 (defmacro edictc-display-text
(ep &rest body
)
1151 (declare (indent 1) (debug t
))
1152 `(with-current-buffer (edictc-get-create-output-buffer ep
)
1157 (goto-char (point-min))
1158 (display-buffer (current-buffer))))
1160 (defun edictc-normalize-text (text &optional indent
)
1161 ;; Delete trailing whitespace
1162 (setq text
(replace-regexp-in-string
1163 (rx-to-string '(and (one-or-more blank
) eol
)) "" text t t
))
1165 ;; Replace 2 or more newlines with just 2 newlines
1166 (setq text
(replace-regexp-in-string
1167 (rx-to-string '(>= 2 "\n")) "\n\n" text t t
))
1169 ;; No leading or trailing newlines
1170 (setq text
(replace-regexp-in-string
1171 (rx-to-string '(or (and bos
(one-or-more "\n"))
1172 (and (one-or-more "\n") eos
))) "" text t t
))
1174 ;; Optionally indent the text.
1176 (let ((leading-space (make-string indent
(string-to-char " "))))
1177 (setq text
(replace-regexp-in-string
1178 (rx-to-string '(and bol
(group-n 1 nonl
)))
1179 (concat leading-space
"\\1") text t
))))
1181 ;; Return normalized text.
1184 ;;;; DEFINE database word
1186 (defun edictc-display-definition (ep request
)
1187 (edictc-display-text ep
1189 (let* ((word-definitions (process-get ep
:responses
))
1190 ;; (list 'DEFINE database word)
1191 (database (nth 1 request
))
1192 (word (nth 2 request
)))
1195 (message "Displaying all %d definitions for %s" (length word-definitions
) word
)
1196 (insert (format "* Definition for %s\n\n" word
))
1198 (lambda (definition)
1199 (format "** Definition for %s from %s\n\n%s"
1200 (edictc-word-definition-word definition
)
1201 (edictc-word-definition-db-description definition
)
1202 (edictc-normalize-text
1203 (edictc-word-definition-text definition
) 1)))
1204 word-definitions
"\n\n")))
1205 (t (edictc-display-information ep request
))))))
1207 ;;;; MATCH database strategy word
1209 (defun edictc-display-match (ep request
)
1210 (edictc-display-text ep
1212 (let* ((word-matches (process-get ep
:responses
))
1213 ;; (list 'MATCH database strategy word)
1214 (database (nth 1 request
))
1215 (strategy (nth 2 request
))
1216 (word (nth 3 request
)))
1219 (message "Displaying all %d matches for %s" (length word-matches
) word
)
1220 (insert (format "* Match for %s\n\n" word
))
1223 (format "** Match for %s from %s\n\n%s"
1225 (edictc-word-match-db-handle match
)
1226 (edictc-normalize-text (edictc-word-match-text match
) 1)))
1227 word-matches
"\n\n")))
1228 (t (insert (format "No matches found for %s" word
)))))))
1230 ;;; Interactive functions
1234 (define-button-type 'edictc-browse-server
1235 :supertype
'help-xref
1236 'help-function
(lambda (server-nick)
1237 (let ((ep (edictc-connect server-nick
'orphan
)))
1238 (lambda (ep) (edictc-command-show-server ep nil
))))
1239 ;; 'help-echo (purecopy "mouse-2, RET: customize variable")
1242 (define-button-type 'edictc-help-server
1243 :supertype
'help-xref
1244 'help-function
(lambda (ep)
1245 (edictc-command-show-server ep nil
))
1246 ;; 'help-echo (purecopy "mouse-2, RET: customize variable")
1249 (define-button-type 'edictc-help-server-commands
1250 :supertype
'help-xref
1251 'help-function
(lambda (ep)
1252 (edictc-command-help ep
))
1253 ;; 'help-echo (purecopy "mouse-2, RET: customize variable")
1256 (define-button-type 'edictc-help-server-status
1257 :supertype
'help-xref
1258 'help-function
(lambda (ep)
1259 (edictc-command-status ep
))
1260 ;; 'help-echo (purecopy "mouse-2, RET: customize variable")
1263 (define-button-type 'edictc-help-databases
1264 :supertype
'help-xref
1265 'help-function
(lambda (ep)
1266 (call-interactively 'edictc-command-show-databases
))
1267 ;; 'help-echo (purecopy "mouse-2, RET: customize variable")
1270 (define-button-type 'edictc-help-strategies
1271 :supertype
'help-xref
1272 'help-function
(lambda (ep)
1273 (call-interactively 'edictc-command-show-strategies
))
1274 ;; 'help-echo (purecopy "mouse-2, RET: customize variable")
1277 (define-button-type 'edictc-help-database
1278 :supertype
'help-xref
1279 'help-function
(lambda (ep database
)
1280 (edictc-command-show-info ep database
))
1281 ;; 'help-echo (purecopy "mouse-2, RET: customize variable")
1286 (defvar edictc-databases-menu-mode-map
1287 (let ((map (make-sparse-keymap))
1288 (menu-map (make-sparse-keymap "Font Family")))
1289 (set-keymap-parent map tabulated-list-mode-map
)
1290 (define-key map
"\C-m" 'edictc-databases-menu-set-default-database
)
1292 "Local keymap for `font-family-menu-mode' buffers.")
1294 (define-derived-mode edictc-databases-menu-mode tabulated-list-mode
"DICT"
1296 (setq tabulated-list-format
1299 (setq tabulated-list-padding
2)
1300 (setq tabulated-list-sort-key
(cons "Database" nil
))
1301 (setq tabulated-list-use-header-line nil
)
1302 (setq header-line-format
1303 (make-text-button "Show Server" nil
'type
'edictc-help-server
1304 'help-args
(list (edictc))))
1305 (add-hook 'tabulated-list-revert-hook
'edictc-databases-menu--refresh nil t
)
1306 (edictc-databases-menu--refresh)
1307 (tabulated-list-init-header)
1308 (tabulated-list-print))
1310 (defun edictc-menu-show-database (&optional button
)
1312 (let* (;; (context (list tabulated-list-sort-key (point) (window-start)))
1313 (ep (button-get button
'ep
))
1314 (database-handle (button-get button
'database-handle
)))
1315 (edictc-command-show-info ep database-handle
)))
1317 (defun edictc-databases-menu--refresh ()
1318 (setq tabulated-list-entries
1319 (let* ((ep (edictc)))
1322 (list (edictc-database-handle entity
)
1324 (list (edictc-database-handle entity
)
1325 'type
'edictc-help-database
1326 'help-args
(list ep
(edictc-database-handle entity
)))
1327 (edictc-database-description entity
))))
1328 (process-get ep
:databases
)))))
1330 (defun edictc-display-databases (ep request
&optional suppress-display
)
1333 (unless (process-get ep
:databases
)
1334 (process-put ep
:databases
1335 (process-get ep
:responses
))
1336 (with-current-buffer (edictc-get-create-output-buffer ep
)
1337 ;; Update `edictc-mode-line'
1340 (unless suppress-display
1341 (with-current-buffer (edictc-get-create-output-buffer ep
)
1342 (edictc-databases-menu-mode)
1343 (pop-to-buffer (current-buffer)))))
1345 ;;;; Selecting a Database
1347 (defun edictc-database-set-default-database (ep database-handle
)
1348 (let ((current-database (or (process-get ep
:database
)
1349 edictc-default-database
)))
1350 (when (y-or-n-p (format "Switch Database from %s to %s?"
1351 current-database database-handle
))
1352 (message "Database set to %s" database-handle
)
1353 (process-put ep
:database database-handle
)
1354 ;; Update mode line.
1357 ;;;; SHOW INFO database & Other commands
1359 (defun edictc-display-information (ep request
)
1360 (edictc-display-text ep
1361 (setq header-line-format
1362 (make-text-button "Databases" nil
'type
1363 'edictc-help-databases
'help-args
(list ep
)))
1364 (let* ((response (process-get ep
:responses
)))
1367 (insert (edictc-normalize-text response
)))
1369 (insert (format "* %s\n\n%s"
1370 (propertize (capitalize (car response
)) 'face
'bold
)
1371 (edictc-normalize-text (cdr response
) 1))))
1372 (t (insert "This should not happen"))))))
1374 (defun edictc-display-error (ep request
)
1375 (edictc-display-text ep
1376 (let* ((response (process-get ep
:responses
)))
1379 (insert (edictc-normalize-text response
)))
1381 (insert (format "* %s\n\n%s"
1382 (propertize (capitalize (car response
)) 'face
'bold
)
1383 (edictc-normalize-text (cdr response
) 1))))
1384 (t (insert "This should not happen"))))))
1388 (defun edictc-display-server (ep request suppress-display
)
1389 (unless suppress-display
1390 (edictc-display-text ep
1391 (let* ((response (process-get ep
:responses
)))
1392 (when (stringp response
)
1393 (setq response
(cons "Server Information" response
)))
1394 (setq header-line-format
1396 (make-text-button "Databases" nil
'type
1397 'edictc-help-databases
'help-args
(list ep
))
1398 (propertize " " 'display
`(space :align-to
20))
1399 (make-text-button "Match Strategies" nil
'type
1400 'edictc-help-strategies
'help-args
(list ep
))
1404 (propertize " " 'display
`(space :align-to
40))
1405 (make-text-button "Server Commands" nil
'type
1406 'edictc-help-server-commands
'help-args
(list ep
))))
1410 (propertize " " 'display
`(space :align-to
60))
1411 (make-text-button "Server Status" nil
'type
1412 'edictc-help-server-status
'help-args
(list ep
))))))
1415 (insert (propertize (capitalize (car response
)) 'face
'bold
) "\n\n")
1416 (insert (cdr response
))))))
1418 ;;;; SHOW STRATEGIES
1420 (defvar edictc-strategies-menu-mode-map
1421 (let ((map (make-sparse-keymap))
1422 (menu-map (make-sparse-keymap "Font Family")))
1423 (set-keymap-parent map tabulated-list-mode-map
)
1424 (define-key map
"\C-m" 'edictc-strategies-menu-set-default-strategy
)
1426 "Local keymap for `font-family-menu-mode' buffers.")
1428 (define-derived-mode edictc-strategies-menu-mode tabulated-list-mode
"DICT Strategies"
1430 (setq tabulated-list-format
1433 (setq tabulated-list-padding
2)
1434 (setq tabulated-list-sort-key
(cons "Strategy" nil
))
1435 (add-hook 'tabulated-list-revert-hook
'edictc-strategies-menu--refresh nil t
)
1436 (setq tabulated-list-use-header-line nil
)
1437 (setq header-line-format
1438 (make-text-button "Show Server" nil
'type
'edictc-help-server
1439 'help-args
(list (edictc))))
1440 (edictc-strategies-menu--refresh)
1441 (tabulated-list-init-header)
1442 (tabulated-list-print))
1444 (defun edictc-strategies-menu--refresh ()
1445 (setq tabulated-list-entries
1446 (let* ((ep (edictc)))
1449 (list (edictc-strategy-handle entity
)
1450 (vector (edictc-strategy-handle entity
)
1451 (edictc-strategy-description entity
))))
1452 (process-get ep
:strategies
)))))
1454 (defun edictc-display-strategies (ep request
&optional suppress-display
)
1457 (unless (process-get ep
:strategies
)
1458 (process-put ep
:strategies
1459 (process-get ep
:responses
))
1460 ;; Update `edictc-mode-line'
1461 (with-current-buffer (edictc-get-create-output-buffer ep
)
1464 (unless suppress-display
1465 (with-current-buffer (edictc-get-create-output-buffer ep
)
1466 (edictc-strategies-menu-mode)
1467 (pop-to-buffer (current-buffer)))))
1469 (defun edictc-databases-menu-set-default-database ()
1471 (when (derived-mode-p 'edictc-databases-menu-mode
)
1472 (let ((database-handle (tabulated-list-get-id)))
1473 (edictc-database-set-default-database (edictc) database-handle
))))
1475 ;;;; Selecting a Strategy
1477 (defun edictc-strategies-menu-set-default-strategy ()
1479 (when (derived-mode-p 'edictc-strategies-menu-mode
)
1480 (let ((strategy-handle (tabulated-list-get-id)))
1481 (when (and strategy-handle
1482 (y-or-n-p (concat "Set match strategy to " strategy-handle
)))
1483 (message "Match strategy set to %s" strategy-handle
)
1484 (process-put (edictc) :strategy strategy-handle
)
1485 ;; Update mode line.
1490 (defvar edictc-servers-menu-mode-map
1491 (let ((map (make-sparse-keymap))
1492 (menu-map (make-sparse-keymap "Font Family")))
1493 (set-keymap-parent map tabulated-list-mode-map
)
1494 (define-key map
"\C-m" 'edictc-servers-menu-set-default-server
)
1496 "Local keymap for `font-family-menu-mode' buffers.")
1498 (define-derived-mode edictc-servers-menu-mode tabulated-list-mode
"DICT Servers"
1499 (setq tabulated-list-format
1503 (setq tabulated-list-padding
2)
1504 (setq tabulated-list-sort-key
(cons "Name" nil
))
1505 (add-hook 'tabulated-list-revert-hook
'edictc-servers-menu--refresh nil t
)
1506 (edictc-servers-menu--refresh)
1507 (tabulated-list-init-header)
1508 (tabulated-list-print))
1510 (defun edictc-servers-menu--refresh ()
1511 (setq tabulated-list-entries
1515 (vector (list (car server
)
1516 'type
'edictc-browse-server
1517 'help-args
(list (car server
)))
1518 (or (plist-get (cdr server
) :server
) "Unknown")
1519 (or (plist-get (cdr server
) :about
) "Unknown"))))
1522 (put 'edictc-display-servers
'disabled t
)
1523 (defun edictc-display-servers (&optional suppress-display
)
1525 (unless suppress-display
1526 (with-current-buffer (edictc-get-create-output-buffer nil
)
1527 (edictc-servers-menu-mode)
1528 (pop-to-buffer (current-buffer)))))
1530 ;;;; Selecting a Server
1532 (defun edictc-servers-menu-set-default-server ()
1534 (when (derived-mode-p 'edictc-servers-menu-mode
)
1535 (let ((server-nick (tabulated-list-get-id)))
1536 (when (and server-nick
1537 (y-or-n-p (concat "Set match server to " server-nick
)))
1538 (customize-save-variable 'edictc-server server-nick
)
1539 (message "Match server set to %s" server-nick
)))))
1543 (easy-menu-define edictc-menu edictc-mode
"Dictionary menu"
1545 ["Define word" edictc-command-define t
]
1546 ["Match word" edictc-command-match t
]
1547 ["--" nil
:visible edictc-process
]
1548 ["About Server" edictc-command-show-server
:visible edictc-process
]
1549 ["Available Databases" edictc-command-show-databases
:visible edictc-process
]
1550 ["Available Strategies" edictc-command-show-strategies
:visible edictc-process
]
1551 ["Server Status" edictc-command-status
:visible
(and edictc-debug edictc-process
)]
1552 ["Server Commands" edictc-command-help
:visible edictc-process
]
1553 ["About a Database" edictc-command-show-info
:visible
(and edictc-debug edictc-process
)]
1554 ["Shutdown" edictc-command-quit
:visible edictc-process
]
1555 ["Save Session Options"
1558 (let* ((default-settings (assoc (process-get edictc-process
:server-nick
)
1560 (unless (string= (process-get edictc-process
:database
)
1561 (plist-get (cdr default-settings
) :database
))
1562 (plist-put (cdr default-settings
) :database
1563 (process-get edictc-process
:database
)))
1564 (unless (string= (process-get edictc-process
:strategy
)
1565 (plist-get (cdr default-settings
) :strategy
))
1566 (plist-put (cdr default-settings
) :strategy
1567 (process-get edictc-process
:strategy
)))
1568 (customize-save-variable 'edictc-servers edictc-servers
)
1569 (edictc-update-menu)))
1570 :visible
(let ((default-settings (assoc (process-get edictc-process
:server-nick
)
1572 (not (and (string= (process-get edictc-process
:database
)
1573 (plist-get (cdr default-settings
) :database
))
1574 (string= (process-get edictc-process
:strategy
)
1575 (plist-get (cdr default-settings
) :strategy
)))))]
1576 ["Kill" edictc-command-kill
:visible
(and edictc-debug edictc-process
)]
1577 ["--" nil
:visible
(or (null edictc-process
)
1578 (null (default-value 'edictc-process
))
1579 (null (local-variable-p 'edictc-process
)))]
1580 ["Start" edictc-connect
:visible
(null (default-value 'edictc-process
))]
1581 ["Start for this buffer" (lambda nil
1583 (edictc-connect nil
'this-buffer-only
))
1584 :visible
(null (local-variable-p 'edictc-process
))]
1586 ("Select DICT Server")
1588 ["--" nil
:visible edictc-debug
]
1589 ("Developer Commands" :visible edictc-debug
1591 ["EDICTC Dashboard" edictc-display-servers t
]
1592 ["Client string" edictc-command-client t
]
1593 ["Option" edictc-command-option t
]
1594 ["Show Debug" (lambda (ep)
1595 (interactive (list edictc-process
))
1597 (pop-to-buffer (process-get ep
:debug-buffer
))
1598 (user-error "No running instance of edictc")))
1600 ["Trigger Unknown Command" edictc--devel-command-unknown-command t
]
1601 ["Trigger Invalid Database" edictc--devel-command-invalid-database t
]
1602 ["Trigger Invalid Strategy" edictc--devel-command-invalid-strategy t
]
1603 ["Trigger Invalid Parameters" edictc--devel-command-invalid-parameters t
]
1606 (easy-menu-add-item menu-bar-tools-menu nil edictc-menu
"Spell checking")
1608 (add-hook 'menu-bar-update-hook
'edictc-update-menu
)
1610 (defun edictc-update-menu ()
1611 (easy-menu-define edictc-servers-menu nil
"DICT Server menu"
1612 (cons "Select DICT Server"
1613 (mapcar (lambda (server)
1614 (vector (edictc-describe-process-or-nick (car server
))
1615 (list (lambda (server-nick)
1616 (customize-save-variable
1617 'edictc-server server-nick
))
1620 :selected
`(string= ,(car server
)
1624 (easy-menu-add-item edictc-menu nil edictc-servers-menu
)
1628 (vector "Active Server" nil
1630 :label
(when edictc-process
1631 (edictc-describe-process-or-nick edictc-process
'menubar
)))
1637 (vector "Default Server" nil
1639 :visible
(or (null edictc-process
)
1640 (null (default-value 'edictc-process
))
1641 (null (local-variable-p 'edictc-process
)))
1642 :label
(edictc-describe-process-or-nick edictc-server
))
1645 (edictc-update-menu)
1649 (define-key esc-map
"#" 'edictc-command-define
)
1650 (define-key esc-map
"*" 'edictc-command-match
)
1654 ;;; edictc.el ends here