;;; dictem.el --- DICT protocol client (rfc-2229) for [X]Emacs ;; This code was initially based on ;; dictionary.el written by Torsten Hilbrich ;; but now probably doesn't contain original code. ;; Most of the code has been written ;; from scratch by Aleksey Cheusov , 2004-2008. ;; ;; DictEm is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2 of the License, or ;; (at your option) any later version. ;; ;; DictEm is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ;; 02111-1307, USA ;;; Commentary: ;; DICT protocol client (rfc-2229) for [X]Emacs ;; NOTE! Documentation is in README file. ;; ;; Latest information about dictem project and sources ;; are available at ;; ;; http://freshmeat.net/projects/dictem ;; http://sourceforge.net/projects/dictem ;; http://mova.org/~cheusov/pub/dictem ;;; Code: (require 'cl) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; Custom Things ;;;;; (defgroup dictem nil "Client for accessing the DICT server." :tag "DictEm" :group 'help :group 'hypermedia) (defgroup dictem-faces nil "Face options for dictem DICT client." :tag "DictEm faces" :group 'dictem :group 'faces) (defcustom dictem-server nil "The DICT server" :group 'dictem :type '(restricted-sexp :match-alternatives (stringp 'nil))) (defcustom dictem-port 2628 "The port of the DICT server" :group 'dictem :type 'number) (defcustom dictem-client-prog "dict" "The command line DICT client. dictem accesses DICT server through this executable. dict-1.9.14 or later (or compatible) is strongly recomented." :group 'dictem :type 'string) (defcustom dictem-client-prog-args-list nil "A list of additional arguments (strings) passed to dict client. For example '(\"--some-option\")." :group 'dictem :type 'list) (defcustom dictem-option-mime nil "If `t' the OPTION MIME command (see RFC-2229 for details) will be sent to the DICT server. i.e. \"dict\" program will be run with \"-M\" option. As a result server's response will be prepanded with MIME header followed by a blank line. Because of bugs in dict -M (version < 1.10.3) utility, dict-1.10.3 or later is strongly recommended " :group 'dictem :type 'boolean) (defcustom dictem-default-strategy nil "The default search strategy." :group 'dictem :type 'string) (defcustom dictem-default-database nil "The default database name." :group 'dictem :type 'string) (defcustom dictem-user-databases-alist nil "ALIST of user's \"virtual\"databases. Valid value looks like this: '((\"en-ru\" . (\"mueller7\" \"korolew_en-ru\")) ((\"en-en\" . (\"foldoc\" \"gcide\" \"wn\"))) ((\"gazetteer\" . \"gaz\"))) " :group 'dictem :type '(alist :key-type string)) (defcustom dictem-exclude-databases nil "ALIST of regexps for databases that will not appear in autocompletion list. " :group 'dictem :type '(alist :key-type string)) (defcustom dictem-use-user-databases-only nil "If `t', only user's dictionaries from dictem-user-databases-alist will be used by dictem-select-database" :group 'dictem :type 'boolean) (defcustom dictem-mode-hook nil "Hook run in dictem mode buffers." :group 'dictem :type 'hook) (defcustom dictem-use-existing-buffer nil "If `t' the `dictem-run' function will not create new *dictem* buffer. Instead, existing buffer will be erased and used to show results. " :group 'dictem :type 'boolean) (defcustom dictem-empty-initial-input nil "If `t' the `dictem-read-query' leave initial input empty" :group 'dictem :type 'boolean) (defcustom dictem-use-content-history t "If not nil and dictem-use-existing-buffer is also not nil, buffer content and (point) is saved in dictem-content-history variable when DEFINE hyperlinks are accessed. It is restored by dictem-last function. On slow machines it may better to set this variable to nil" :group 'dictem) ;;;;; Faces ;;;;; (defface dictem-reference-definition-face '((((background light)) (:foreground "blue")) (((background dark)) (:foreground "cyan"))) "The face that is used for displaying a reference to a phrase in a DEFINE search." :group 'dictem-faces) (defface dictem-reference-m1-face '((((background light)) (:foreground "darkgreen")) (((background dark)) (:foreground "lightblue"))) "The face that is used for displaying a reference to a phrase in a MATCH search." :group 'dictem-faces) (defface dictem-reference-m2-face '((((background light)) (:foreground "blue")) (((background dark)) (:bold true :foreground "gray"))) "The face that is used for displaying a reference to a single word in a MATCH search." :group 'dictem-faces) (defface dictem-reference-dbname-face '((((background light)) (:foreground "darkgreen")) (((background dark)) (:bold t :foreground "white"))) "The face that is used for displaying a reference to database" :group 'dictem-faces) (defface dictem-database-description-face '((((background light)) (:bold t :foreground "darkblue")) (((background dark)) (:bold t :foreground "white"))) "The face that is used for displaying a database description" :group 'dictem-faces) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; Variables ;;;;; (defconst dictem-version "1.0.4" "DictEm version information.") (defvar dictem-strategy-alist nil "ALIST of search strategies") (defvar dictem-database-alist nil "ALIST of databases") (defvar dictem-strategy-history nil "List of strategies entered from minibuffer") (defvar dictem-database-history nil "List of database names entered from minibuffer") (defvar dictem-query-history nil "List of queries entered from minibuffer") (defvar dictem-last-database "*" "Last used database name") (defvar dictem-last-strategy "." "Last used strategy name") (defvar dictem-mode-map nil "Keymap for dictem mode") (defvar dictem-temp-buffer-name "*dict-temp*" "Temporary dictem buffer name") (defvar dictem-current-dbname nil "This variable keeps a database name of the definition currently processed by functions run from dictem-postprocess-each-definition-hook.") (defvar dictem-error-messages nil "A list of error messages collected by dictem-run") (defvar dictem-hyperlinks-alist nil "ALIST of hyperlinks collected from dictem buffer by the function dictem-postprocess-collect-hyperlinks (add this function to the hook dictem-postprocess-definition-hook). This variable is local to buffer") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun dictem-prepand-special-strats (l) (cons '(".") l)) (defun dictem-prepand-special-dbs (l) (cons '("*") (cons '("!") l))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; Functions ;;;;;; (defmacro save-dictem (&rest funs) `(let ((dictem-port 2628) (dictem-server nil) (dictem-database-alist nil) (dictem-strategy-alist nil) (dictem-use-user-databases-only nil) (dictem-user-databases-alist nil) ) (progn ,@funs) )) (defun dictem-client-text () "Returns a portion of text sent to the server for identifying a client" (concat "dictem " dictem-version ", DICT client for emacs")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Functions related to userdb ;; (defun dictem-make-userdb (name short-name match define) "Make user database object" (list name 'dictem-userdb short-name match define)) (defun dictem-userdb-p (obj) "Returns t if obj is the dictem error object" (and obj (listp obj) (cdr obj) (listp (cdr obj)) (eq (cadr obj) 'dictem-userdb))) (defun dictem-userdb-member (obj name) "Extract member from userdb object by its name" (cond ((dictem-userdb-p obj) (nth (cdr (assoc name '(("name" . 0) ("short-name" . 2) ("match" . 3) ("define" . 4)))) obj)) (t (error "Invalid type of argument")))) (defun dictem-userdb-DEFINE (buffer db query host port) (let* ((fun (dictem-userdb-member db "define")) (name (dictem-userdb-member db "name")) (sname (dictem-userdb-member db "short-name")) (ret (save-excursion (funcall fun query))) (buf (dictem-get-buffer buffer))) (save-excursion (set-buffer buf) (cond ((dictem-error-p ret) ; (insert "From " sname " [" name "]:\n\n" ; (dictem-error-message ret) "\n\n") ; (insert (dictem-error-message ret) "\n") (insert (dictem-error-message ret) "\n") (dictem-error-status ret)) ((null ret) (insert "No matches found" "\n") 20) ((listp ret) (dolist (definition ret) (insert "From " sname " [" name "]:\n\n" (dictem-indent-string definition) "\n\n")) 0) ((stringp ret) (insert "From " sname " [" name "]:\n\n" (dictem-indent-string ret) "\n\n") 0) (t (error "Invalid type of returned value1")))))) (defun dictem-userdb-MATCH (buffer db query strat host port) (let* ((fun (dictem-userdb-member db "match")) (name (dictem-userdb-member db "name")) (ret (save-excursion (funcall fun query strat))) (buf (dictem-get-buffer buffer))) (save-excursion (set-buffer buf) (cond ((dictem-error-p ret) (insert (dictem-error-message ret) "\n") (dictem-error-status ret)) ((listp ret) (insert (concat name ":\n")) (dolist (match ret); (insert (car db) ":\n" )) (progn (insert " " match "\n")) ) 0) (t (error "Invalid type of returned value2")))))) (defun dictem-userdb-SEARCH (buffer db query strat host port) (let* ((funm (dictem-userdb-member db "match")) (name (dictem-userdb-member db "name")) (sname (dictem-userdb-member db "short-name")) (sname nil) (ret (funcall funm query strat)) (buf (dictem-get-buffer buffer))) (save-excursion (set-buffer buf) (cond ((dictem-error-p ret) (insert (dictem-error-message ret) "\n") (dictem-error-status ret)) ((listp ret) (dolist (match ret) (dictem-userdb-DEFINE buffer db match host port)) 0) (t (error "Something strange happened")) )))) (defun dictem-userdb-SHOW-INFO (buffer db host port) (let ((sname (dictem-userdb-member db "short-name")) (buf (dictem-get-buffer buffer))) (save-excursion (set-buffer buf) (cond ((dictem-error-p sname) (insert (dictem-error-message sname) "\n") (dictem-error-status sname)) ((stringp sname) (insert sname) 0) (t (error "Something strange happened")) )))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Functions related to error object ;; (defun dictem-make-error (error_status &optional buffer-or-string) "Creates dictem error object" (cond ((stringp buffer-or-string) (list 'dictem-error error_status buffer-or-string)) ((bufferp buffer-or-string) (dictem-make-error error_status (save-excursion (set-buffer buffer-or-string) (goto-char (point-min)) (dictem-get-line) ))) ((eq nil buffer-or-string) (list 'dictem-error error_status buffer-or-string)) (t (error "Invalid type of argument")) )) (defun dictem-error-p (OBJECT) "Returns t if OBJECT is the dictem error object" (and (not (null OBJECT)) (listp OBJECT) (eq (car OBJECT) 'dictem-error) )) (defun dictem-error-message (err) "Extract error message from dictem error object" (cond ((dictem-error-p err) (nth 2 err)) (t (error "Invalid type of argument")) )) (defun dictem-error-status (err) "Extract error status from dictem error object" (cond ((dictem-error-p err) (nth 1 err)) (t (error "Invalid type of argument")) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun dictem-collect-matches () ; nreverse, setcar and nconc are used to reduce a number of cons (goto-char (point-min)) (let ((dictem-temp nil)) (loop (let ((line (dictem-get-line))) (if (string-match "^[^ ]+:" line) (progn (if (consp dictem-temp) (setcar (cdar dictem-temp) (nreverse (cadar dictem-temp)))) (setq dictem-temp (cons (list (substring line (match-beginning 0) (- (match-end 0) 1)) (nreverse (dictem-tokenize (substring line (match-end 0))))) dictem-temp))) (if (consp dictem-temp) (setcar (cdar dictem-temp) (nconc (nreverse (dictem-tokenize line)) (cadar dictem-temp)) )) )) (if (or (> (forward-line 1) 0) (> (current-column) 0)) (return (nreverse dictem-temp))) ))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun dictem-get-buffer (buf) (cond ((bufferp buf) buf) (buf (current-buffer)) (t (get-buffer-create dictem-temp-buffer-name)) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; call-process functions (defun dictem-local-dict-basic-option (host port option-mime) (let ((server-host (if host host (dictem-get-server)))) (append (list "-P" "-") (if server-host (list "-h" server-host "-p" (dictem-get-port port))) (if option-mime '("-M")) dictem-client-prog-args-list ))) (defun dictem-call-process (buffer host port args) (let (coding-system coding-system-for-read coding-system-for-write) (if (and (functionp 'coding-system-list) (member 'utf-8 (coding-system-list))) (setq coding-system 'utf-8)) (setq coding-system-for-read coding-system) (setq coding-system-for-write coding-system) (apply 'call-process `(,dictem-client-prog nil ,(dictem-get-buffer buffer) nil ,@(dictem-local-dict-basic-option host port nil) ,@args )))) (defun dictem-call-process-SHOW-SERVER (buffer host port) (dictem-call-process buffer host port '("-I"))) (defun dictem-call-process-SHOW-INFO (buffer db host port) (dictem-call-process buffer host port (list "-i" db))) (defun dictem-call-process-SHOW-STRAT (buffer host port) (dictem-call-process buffer host port '("-S"))) (defun dictem-call-process-SHOW-DB (buffer host port) (dictem-call-process buffer host port '("-D"))) (defun dictem-call-process-MATCH (buffer db query strat host port) (dictem-call-process buffer host port (list "-m" "-d" (if db db "*") "-s" (if strat strat ".") query))) (defun dictem-call-process-DEFINE (buffer db query host port) (dictem-call-process buffer host port (list "-d" (if db db "*") query))) (defun dictem-call-process-SEARCH (buffer db query strat host port) (dictem-call-process buffer host port (list "-d" (if db db "*") "-s" (if strat strat ".") query))) ;;;;; GET Functions ;;;;; (defun dictem-get-matches (query &optional database strategy server port) "Returns ALIST of matches" (let ((exit_status (dictem-call-process-MATCH nil database query strategy server port) )) (cond ((= exit_status 20) ;20 means "no matches found", See dict(1) (kill-buffer dictem-temp-buffer-name) nil) ((= exit_status 0) (progn (save-excursion (set-buffer dictem-temp-buffer-name) (let ((matches (dictem-collect-matches))) (kill-buffer dictem-temp-buffer-name) matches)))) (t (let ((err (dictem-make-error exit_status (get-buffer dictem-temp-buffer-name)))) (kill-buffer dictem-temp-buffer-name) err)) ))) (defun dictem-get-strategies (&optional server port) "Obtains strategy ALIST from a DICT server and returns alist containing strategies and their descriptions" (let ((exit_status (dictem-call-process-SHOW-STRAT nil server port) )) (cond ((= exit_status 0) (save-excursion (set-buffer dictem-temp-buffer-name) (goto-char (point-min)) (let ((regexp "^ \\([^ ]+\\) +\\(.*\\)$") (l nil)) (while (search-forward-regexp regexp nil t) (setq l (cons (list (buffer-substring-no-properties (match-beginning 1) (match-end 1)) (buffer-substring-no-properties (match-beginning 2) (match-end 2))) l))) (kill-buffer dictem-temp-buffer-name) l))) (t (let ((err (dictem-make-error exit_status (get-buffer dictem-temp-buffer-name)))) (kill-buffer dictem-temp-buffer-name) err)) ))) (defun dictem-get-databases (&optional server port) "Obtains database ALIST from a DICT server and returns alist containing database names and descriptions" (let ((exit_status (dictem-call-process-SHOW-DB nil server port) )) (cond ((= exit_status 0) (save-excursion (set-buffer dictem-temp-buffer-name) (goto-char (point-min)) (let ((regexp "^ \\([^ ]+\\) +\\(.*\\)$") (l nil)) (while (search-forward-regexp regexp nil t) (let ((dbname (buffer-substring-no-properties (match-beginning 1) (match-end 1))) (dbdescr (buffer-substring-no-properties (match-beginning 2) (match-end 2)))) (if (not (string= "--exit--" dbname)) (setq l (cons (list dbname dbdescr) l))))) (kill-buffer dictem-temp-buffer-name) l))) (t (let ((err (dictem-make-error exit_status (get-buffer dictem-temp-buffer-name)))) (kill-buffer dictem-temp-buffer-name) err)) ))) (defun dictem-get-default-strategy (&optional def-strat) "Gets the default search strategy" (if def-strat def-strat (if dictem-default-strategy dictem-default-strategy (if dictem-last-strategy dictem-last-strategy ".")))) (defun dictem-extract-dbname (database) (cond ((consp database) (dictem-extract-dbname (car database))) ((stringp database) database) (t (error "The database should be either stringp or consp")) )) (defun dictem-get-default-database (&optional def-db) "Returns the default database" (if def-db (dictem-extract-dbname def-db) (if dictem-default-database (dictem-extract-dbname dictem-default-database) (if dictem-last-database (dictem-extract-dbname dictem-last-database) "*")))) ;;;;; Low Level Functions ;;;;; (defun dictem-db-should-be-excluded (dbname) "Returns t if a dbname should is not interesting for user. See dictem-exclude-databases variable" (let ((ret nil)) (dolist (re dictem-exclude-databases) (if (string-match re dbname) (setq ret t))) ret)) (defun dictem-delete-alist-predicate (l pred) "makes a copy of l with no items for which (pred item) is true" (let ((ret nil)) (dolist (item l) (if (not (funcall pred (car item))) (setq ret (cons item ret)))) ret)) (defun dictem-get-line () "Replacement for (thing-at-point 'line)" (save-excursion (buffer-substring-no-properties (progn (beginning-of-line) (point)) (progn (end-of-line) (point))))) (defun dictem-list2alist (l) (cond ((null l) nil) (t (cons (list (car l) nil) (dictem-list2alist (cdr l)))))) (defun dictem-indent-string (str) (let ((start 0)) (while (string-match "\n" str start) (progn (setq start ( + 2 (match-end 0))) (setq str (replace-match "\n " t t str))))) (concat " " str)) (defun dictem-replace-spaces (str) (while (string-match "[ \n][ \n]+" str) (setq str (replace-match " " t t str))) (if (string-match "^ +" str) (setq str (replace-match "" t t str))) (if (string-match " +$" str) (setq str (replace-match "" t t str))) str) (defun dictem-remove-value-from-alist (l) (let ((ret nil)) (dolist (i l) (setq ret (cons (list (car i)) ret))) (reverse ret) )) ;(defun dictem-remove-value-from-alist (l) ; (cond ; ((symbolp l) l) ; (t (cons (list (caar l)) ; (dictem-remove-value-from-alist (cdr l)))))) (defun dictem-select (prompt alist default history) (let* ((completion-ignore-case t) (str (completing-read (concat prompt " [" default "]: ") alist nil t nil history default)) (str-cons (assoc str alist))) (cond ((and str-cons (consp str-cons) (cdr str-cons)) str-cons) ((and str-cons (consp str-cons)) (car str-cons)) (t nil)))) (defun dictem-tokenize (s) (if (string-match "\"[^\"]+\"\\|[^ \"]+" s ) ; (substring s (match-beginning 0) (match-end 0)) (cons (substring s (match-beginning 0) (match-end 0)) (dictem-tokenize (substring s (match-end 0)))) nil)) ;(defun dictem-search-forward-regexp-cs (REGEXP &optional BOUND NOERROR COUNT) ; "Case-sensitive variant for search-forward-regexp" ; (let ((case-replace nil) ; (case-fold-search nil)) ; (search-forward-regexp REGEXP BOUND NOERROR COUNT))) ;(defun dictem-replace-match-cs (NEWTEXT &optional FIXEDCASE LITERAL STRING SUBEXP) ; "Case-sensitive variant for replace-match" ; (let ((case-replace nil) ; (case-fold-search nil)) ; (replace-match NEWTEXT FIXEDCASE LITERAL STRING SUBEXP))) (defun dictem-get-port (&optional port) (let ((p (if port port dictem-port))) (cond ((and (stringp p) (string= "" p)) 2628) ((null p) 2628) ((stringp p) p) ((numberp p) (number-to-string p)) (t (error "The value of dictem-port variable should be \ either a string or a number")) ))) (defun dictem-get-server () (cond ((and (stringp dictem-server) (string= "" dictem-server)) nil) ((stringp dictem-server) dictem-server) ((null dictem-server) nil) (t (error "The value of dictem-server variable should be \ either a string or a nil")) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; Main Functions ;;;;; ;;;;;; Functions for Initializing ;;;;;; (defun dictem-initialize-strategies-alist (&optional server port) "Obtain strategy ALIST from a DICT server and sets dictem-strategy-alist variable." (interactive) (setq dictem-strategy-alist (dictem-get-strategies server (dictem-get-port port)))) (defun dictem-initialize-databases-alist (&optional server port) "Obtain database ALIST from a DICT server and sets dictem-database-alist variable." (interactive) (setq dictem-database-alist (dictem-get-databases server (dictem-get-port port))) (if (dictem-error-p dictem-database-alist) dictem-database-alist (setq dictem-database-alist (dictem-delete-alist-predicate dictem-database-alist 'dictem-db-should-be-excluded)))) (defun dictem-initialize () "Initializes dictem, i.e. obtains a list of available databases and strategiss from DICT server and makes other tasks." (interactive) (let ((dbs (dictem-initialize-databases-alist)) (strats (dictem-initialize-strategies-alist))) (if (dictem-error-p dbs) dbs strats))) (defun dictem-reinitialize-err () "Initializes dictem if it is not initialized yet and run (error ...) if an initialization fails" (interactive) (if (or (dictem-error-p dictem-database-alist) (null dictem-database-alist)) (if (dictem-error-p (dictem-initialize)) (error (dictem-error-message dictem-database-alist))))) ;;; Functions related to Minibuffer ;;;; (defun dictem-select-strategy (&optional default-strat) "Switches to minibuffer and asks the user to enter a search strategy." (dictem-reinitialize-err) (dictem-select "strategy" (dictem-prepand-special-strats (dictem-remove-value-from-alist dictem-strategy-alist)) (dictem-get-default-strategy default-strat) 'dictem-strategy-history)) (defun dictem-select-database (spec-dbs user-dbs &optional default-db) "Switches to minibuffer and asks user to enter a database name." (dictem-reinitialize-err) (let* ((dbs (dictem-remove-value-from-alist dictem-database-alist)) (dbs2 (if user-dbs (if dictem-use-user-databases-only dictem-user-databases-alist (append dictem-user-databases-alist dbs) ) dbs))) (dictem-select "db" (if spec-dbs (dictem-prepand-special-dbs dbs2) dbs2) (dictem-get-default-database default-db) 'dictem-database-history))) (defun dictem-read-query (&optional default-query) "Switches to minibuffer and asks user to enter a query." (if (featurep 'xemacs) (read-string (concat "query [" default-query "]: ") nil 'dictem-query-history default-query) (read-string (concat "query [" default-query "]: ") (if dictem-empty-initial-input nil default-query) 'dictem-query-history default-query t))) ;;;;;;;; Hooks ;;;;;;;; (defcustom dictem-postprocess-definition-hook nil "Hook run in dictem mode buffers containing DEFINE result." :group 'dictem :type 'hook :options '(dictem-postprocess-definition-separator dictem-postprocess-definition-hyperlinks dictem-postprocess-each-definition dictem-postprocess-definition-remove-header dictem-postprocess-collect-hyperlinks)) (defcustom dictem-postprocess-match-hook nil "Hook run in dictem mode buffers containing MATCH result." :group 'dictem :type 'hook :options '(dictem-postprocess-match)) (defcustom dictem-postprocess-show-info-hook nil "Hook run in dictem mode buffers containing SHOW INFO result." :group 'dictem :type 'hook :options '(dictem-postprocess-definition-hyperlinks dictem-postprocess-collect-hyperlinks)) (defcustom dictem-postprocess-show-server-hook nil "Hook run in dictem mode buffers containing SHOW SERVER result." :group 'dictem :type 'hook) ;;;;;;;; Search Functions ;;;;;;; (defun dictem-call-dict-internal (fun databases) (let ((exit-status -1)) (cond ((null databases) 0) ((stringp databases) (dictem-call-dict-internal fun (list databases))) ((listp databases) (dolist (db databases) (let ((ex_st (funcall fun db))) (cond ((= ex_st 0) (setq exit-status 0)) (t (if (/= 0 exit-status) (setq exit-status ex_st))) ))) (if (= exit-status -1) 0 exit-status) ) (t (error "wrong type of argument")) ) )) ;(defun dictem-call-dict-internal (fun databases) ; (dolist (db databases) ; (funcall fun db))) ; (funcall fun databases)) (defun dictem-make-url (host port database cmd_sign query &optional strategy) "Returns dict:// URL" (concat "dict://" host ":" (dictem-get-port (if port port "2628")) "/" cmd_sign ":" query ":" database (if strategy (concat ":" (if strategy strategy "."))) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun dictem-base-do-selector (cmd hook &optional database &rest args) (let* ((splitted-url nil) (databases nil) (user-db (assoc database dictem-user-databases-alist)) ) (goto-char (point-max)) (cond ((dictem-userdb-p database) (apply 'dictem-base-do-default-server (append (list cmd hook database) args))) ((and database (listp database)) (dictem-call-dict-internal `(lambda (db) (apply 'dictem-base-do-selector (append (list ,cmd hook db) args))) (cdr database)) (setq dictem-last-database (car database))) ((and database (stringp database) (setq splitted-url (dictem-parse-url database))) (apply 'dictem-base-do-foreign-server (append (list cmd hook (nth 1 splitted-url) (dictem-get-port (nth 2 splitted-url)) (nth 3 splitted-url)) args))) (user-db (let ((exit_status (apply 'dictem-base-do-selector (append (list cmd hook user-db) args)))) (progn (setq dictem-last-database database) exit_status) )) (t (apply 'dictem-base-do-default-server (append (list cmd hook database) args))) ))) (defun dictem-base-do-foreign-server (cmd hook server port database &rest args) (let ((dictem-last-database nil) (dictem-last-strategy nil)) (save-dictem (setq dictem-server server) (setq dictem-port port) (setq database database) (dictem-initialize) (apply 'dictem-base-do-default-server (append (list cmd hook database) args)) ))) (defun dictem-base-do-default-server (cmd hook &optional database query strategy) (let* ((beg (point)) (fun (if (dictem-userdb-p database) (dictem-cmd2userdb cmd) (dictem-cmd2function cmd))) (exit_status (save-excursion (apply fun (append (list t) (if database (list database)) (if query (list query)) (if strategy (list strategy)) (list nil) (list nil)))) )) (cond ((= 0 exit_status) (save-excursion (narrow-to-region beg (point-max)) (run-hooks hook) (widen))) ((= 21 exit_status) (save-excursion (narrow-to-region beg (point-max)) (run-hooks 'dictem-postprocess-match-hook) (widen))) (t (if (/= beg (point)) (setq dictem-error-messages (append (list (dictem-make-url (dictem-get-server) (dictem-get-port) database "?" query) (buffer-substring-no-properties beg (point))) dictem-error-messages))) (kill-region beg (point)))) (if database (setq dictem-last-database database)) (if strategy (setq dictem-last-strategy strategy)) exit_status )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun dictem-base-search (databases query strategy) "MATCH + DEFINE commands" (dictem-base-do-selector "search" 'dictem-postprocess-definition-hook databases query strategy)) (defun dictem-base-define (databases query c) "DEFINE command" (dictem-base-do-selector "define" 'dictem-postprocess-definition-hook databases query)) (defun dictem-base-match (databases query strategy) "MATCH command" (dictem-base-do-selector "match" 'dictem-postprocess-match-hook databases query strategy)) (defun dictem-base-show-databases (a b c) "SHOW DB command" (dictem-base-do-selector "show-db" nil)) (defun dictem-base-show-strategies (a b c) "SHOW STRAT command" (dictem-base-do-selector "show-strat" nil)) (defun dictem-base-show-info (databases b c) "SHOW INFO command" (dictem-base-do-selector "show-info" 'dictem-postprocess-show-info-hook databases)) (defun dictem-base-show-server (a b c) "SHOW SERVER command" (dictem-base-do-selector "show-server" 'dictem-postprocess-show-server-hook)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun dictem-get-error-message (exit_status) (cond ((= exit_status 0) "All is fine") ((= exit_status 20) "No matches found") ((= exit_status 21) "Approximate matches found") ((= exit_status 22) "No databases available") ((= exit_status 23) "No strategies available") ((= exit_status 30) "Unexpected response code from server") ((= exit_status 31) "Server is temporarily unavailable") ((= exit_status 32) "Server is shutting down") ((= exit_status 33) "Syntax error, command not recognized") ((= exit_status 34) "Syntax error, illegal parameters") ((= exit_status 35) "Command not implemented") ((= exit_status 36) "Command parameter not implemented") ((= exit_status 37) "Access denied") ((= exit_status 38) "Authentication failed") ((= exit_status 39) "Invalid database name") ((= exit_status 40) "Invalid strategy name") ((= exit_status 41) "Connection to server failed") (t (concat "Ooops!" (number-to-string exit_status))) )) (defun dictem-local-internal (err-msgs exit_status) (if err-msgs (concat (car err-msgs) "\n" (cadr err-msgs) "\n" (dictem-local-internal (cddr err-msgs) nil) ) (if exit_status (dictem-get-error-message exit_status) nil))) (defun dictem-generate-full-error-message (exit_status) (concat "Error messages:\n\n" (dictem-local-internal dictem-error-messages exit_status))) (defun dictem-run (search-fun &optional database query strategy) "Creates new *dictem* buffer and run search-fun" (let ((ex_status -1)) (defun dictem-local-run-functions (funs database query strategy) (cond ((functionp funs) (let ((ex_st (funcall funs database query strategy))) (if (/= ex_status 0) (setq ex_status ex_st)))) ((and (consp funs) (functionp (car funs))) (dictem-local-run-functions (car funs) database query strategy) (dictem-local-run-functions (cdr funs) database query strategy)) ((null funs) nil) (t (error "wrong argument type")) ) ex_status) (let ((selected-window (frame-selected-window)) ; here we remember values of variables local to buffer (server dictem-server) (port dictem-port) (dbs dictem-database-alist) (strats dictem-strategy-alist) (user-dbs dictem-user-databases-alist) (user-only dictem-use-user-databases-only) (use-existing-buf dictem-use-existing-buffer) ; (option-mime dictem-option-mime) (dict-buf nil) ) (cond ((eq dictem-use-existing-buffer 'always) (dictem-ensure-buffer)) ((eq dictem-use-existing-buffer t) (dictem-ensure-buffer)) (t (dictem)) 0) (setq dict-buf (buffer-name)) ; (set-buffer-file-coding-system coding-system) (make-local-variable 'dictem-default-strategy) (make-local-variable 'dictem-default-database) (make-local-variable 'case-replace) (make-local-variable 'case-fold-search) ; the following lines are to inherit values local to buffer (set (make-local-variable 'dictem-server) server) (set (make-local-variable 'dictem-port) port) (set (make-local-variable 'dictem-database-alist) dbs) (set (make-local-variable 'dictem-strategy-alist) strats) (set (make-local-variable 'dictem-user-databases-alist) user-dbs) (set (make-local-variable 'dictem-use-user-databases-only) user-only) (set (make-local-variable 'dictem-use-existing-buffer) use-existing-buf) ; (set (make-local-variable 'dictem-option-mime) option-mime) (set (make-local-variable 'dictem-hyperlinks-alist) nil) ;;;;;;;;;;;;;; (setq case-replace nil) (setq case-fold-search nil) (setq dictem-error-messages nil) (dictem-local-run-functions search-fun database query strategy) (switch-to-buffer dict-buf) (if (and (not (equal ex_status 0)) (= (point-min) (point-max))) (insert (dictem-generate-full-error-message ex_status))) (goto-char (point-min)) (setq buffer-read-only t) ex_status ))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun dictem-next-section () "Move point to the next definition" (interactive) (forward-char) (if (search-forward-regexp "^From " nil t) (beginning-of-line) (goto-char (point-max)))) (defun dictem-previous-section () "Move point to the previous definition" (interactive) (backward-char) (if (search-backward-regexp "^From " nil t) (beginning-of-line) (goto-char (point-min)))) (defun dictem-hyperlinks-menu () "Hyperlinks menu with autocompletion" (interactive) (let ((link (completing-read "Go to:" dictem-hyperlinks-alist))) (if (and link (setq link (assoc link dictem-hyperlinks-alist))) (dictem-run-define (cadr link) dictem-last-database)) )) (defun dictem-next-link () "Move point to the next hyperlink" (interactive) (let ((pt nil) (limit (point-max))) (if (and (setq pt (next-single-property-change (point) 'link nil limit)) (/= limit pt)) (if (get-char-property pt 'link) (goto-char pt) (goto-char (next-single-property-change pt 'link nil limit)))) )) (defun dictem-previous-link () "Move point to the previous hyperlink" (interactive) (let ((pt nil) (limit (point-min))) (if (and (setq pt (previous-single-property-change (point) 'link nil limit)) (/= limit pt)) (if (get-char-property pt 'link) (goto-char pt) (goto-char (previous-single-property-change pt 'link nil limit)))) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun dictem-help () "Display a dictem help" (interactive) (describe-function 'dictem-mode)) (defun dictem-mode () "This is a mode for dict client implementing the protocol defined in RFC 2229. The following basic commands are available in the buffer. \\[dictem-help] display the help information \\[dictem-kill] kill the dictem buffer \\[dictem-kill-all-buffers] kill all dictem buffers \\[dictem-quit] bury the dictem buffer \\[dictem-last] restore content of the previously visited dictem buffer \\[dictem-run-search] make a new SEARCH, i.e. ask for a database, strategy and query and show definitions \\[dictem-run-match] make a new MATCH, i.e. ask for database, strategy and query and show matches \\[dictem-run-define] make a new DEFINE, i.e. ask for a database and query and show definitions \\[dictem-run-show-server] show information about DICT server \\[dictem-run-show-info] ask for a database and show information about it \\[dictem-run-show-databases] show databases DICT server provides \\[dictem-run-show-strategies] show search strategies DICT server provides \\[dictem-next-section] move point to the next definition \\[dictem-previous-section] move point to the previous definition \\[dictem-next-link] move point to the next hyper link \\[dictem-previous-link] move point to the previous hyper link \\[dictem-hyperlinks-menu] display the menu with hyperlinks \\[scroll-up] scroll dictem buffer up \\[scroll-down] scroll dictem buffer down \\[dictem-define-on-click] or \\[dictem-define-on-press] visit a link (DEFINE using all dictionaries) Also some advanced commands are available. \\[dictem-initialize] Initializes dictem, i.e. obtains a list of available databases and strategiss from DICT server and makes other tasks \\[dictem-initialize-strategies-alist] Obtain strategy ALIST from a DICT server and sets dictem-strategy-alist variable \\[dictem-initialize-databases-alist] Obtain database ALIST from a DICT server and sets dictem-database-alist variable The following key bindings are currently in effect in the buffer: \\{dictem-mode-map} " (interactive) (kill-all-local-variables) (buffer-disable-undo) (use-local-map dictem-mode-map) (setq major-mode 'dictem-mode) (setq mode-name "dictem") (add-hook 'kill-buffer-hook 'dictem-kill t t) (run-hooks 'dictem-mode-hook) ) (defvar dictem-window-configuration nil "The window configuration to be restored upon closing the buffer") (defvar dictem-selected-window nil "The currently selected window") (defvar dictem-content-history nil "A list of lists (buffer_content point)") (defconst dictem-buffer-name "*dictem buffer*") (defconst dictem-url-regexp "^\\(dict\\)://\\([^/:]*\\)\\(:\\([0-9]+\\)\\)?/\\(.*\\)$") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defconst dictem-cmd2function-alist '(("show-server" dictem-call-process-SHOW-SERVER) ("show-info" dictem-call-process-SHOW-INFO) ("show-strat" dictem-call-process-SHOW-STRAT) ("show-db" dictem-call-process-SHOW-DB) ("match" dictem-call-process-MATCH) ("define" dictem-call-process-DEFINE) ("search" dictem-call-process-SEARCH) )) (defconst dictem-cmd2userdb-alist '(("match" dictem-userdb-MATCH) ("define" dictem-userdb-DEFINE) ("search" dictem-userdb-SEARCH) ("show-info" dictem-userdb-SHOW-INFO) )) (defun dictem-cmd2xxx (cmd alist) (let ((fun (assoc cmd alist))) (if fun (symbol-function (cadr fun)) (error "Unknown command \"%s\"" cmd) ) )) (defun dictem-cmd2function (cmd) (dictem-cmd2xxx cmd dictem-cmd2function-alist)) (defun dictem-cmd2userdb (cmd) (dictem-cmd2xxx cmd dictem-cmd2userdb-alist)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun dictem-parse-url (url) "Parses string like dict://dict.org:2628/foldoc and returns a list containing protocol, server, port and path on nil if fails" (if (string-match dictem-url-regexp url) (list (match-string 1 url) ; protocol (match-string 2 url) ; host (match-string 4 url) ; port (match-string 5 url) ; path (database name for dict://) ) nil)) (defun dictem () "Create a new dictem buffer and install dictem-mode" (interactive) (let ( (buffer (generate-new-buffer dictem-buffer-name)) (window-configuration (current-window-configuration)) (selected-window (frame-selected-window))) (switch-to-buffer-other-window buffer) (dictem-mode) (make-local-variable 'dictem-window-configuration) (make-local-variable 'dictem-selected-window) (make-local-variable 'dictem-content-history) (setq dictem-window-configuration window-configuration) (setq dictem-selected-window selected-window) )) ;(unless dictem-mode-map (setq dictem-mode-map (make-sparse-keymap)) (suppress-keymap dictem-mode-map) ; Kill the buffer (define-key dictem-mode-map "k" 'dictem-kill) ; Kill all dictem buffers (define-key dictem-mode-map "x" 'dictem-kill-all-buffers) ; Bury the buffer (define-key dictem-mode-map "q" 'dictem-quit) ; LAST, works like in Info-mode (define-key dictem-mode-map "l" 'dictem-last) ; Show help message (define-key dictem-mode-map "h" 'dictem-help) ; SEARCH = MATCH + DEFINE (define-key dictem-mode-map "s" 'dictem-run-search) ; MATCH (define-key dictem-mode-map "m" 'dictem-run-match) ; DEFINE (define-key dictem-mode-map "d" 'dictem-run-define) ; SHOW SERVER (define-key dictem-mode-map "r" 'dictem-run-show-server) ; SHOW INFO (define-key dictem-mode-map "i" 'dictem-run-show-info) ; Move point to the next DEFINITION (define-key dictem-mode-map "n" 'dictem-next-section) ; Move point to the previous DEFINITION (define-key dictem-mode-map "p" 'dictem-previous-section) ; Move point to the next HYPER LINK (define-key dictem-mode-map "\M-n" 'dictem-next-link) ; Move point to the previous HYPER LINK (define-key dictem-mode-map "\M-p" 'dictem-previous-link) ; Hyperlinks menu (define-key dictem-mode-map "e" 'dictem-hyperlinks-menu) ; Scroll up dictem buffer (define-key dictem-mode-map " " 'scroll-up) ; Scroll down dictem buffer (define-key dictem-mode-map "\177" 'scroll-down) ; Define on click (if (featurep 'xemacs) (define-key dictem-mode-map [button2] 'dictem-define-on-click) (define-key dictem-mode-map [mouse-2] 'dictem-define-on-click)) (define-key dictem-mode-map "\C-m" 'dictem-define-on-press) (defun dictem-mode-p () "Return non-nil if current buffer has dictem-mode" (eq major-mode 'dictem-mode)) (defun dictem-ensure-buffer () "If current buffer is not a dictem buffer, create a new one." (if (dictem-mode-p) (progn (if dictem-use-content-history (setq dictem-content-history (cons (list (buffer-substring (point-min) (point-max)) (point)) dictem-content-history))) (setq buffer-read-only nil) (erase-buffer)) (dictem))) (defun dictem-quit () "Bury the current dictem buffer." (interactive) (if (featurep 'xemacs) (bury-buffer) (quit-window))) (defun dictem-kill () "Kill the current dictem buffer." (interactive) (if (eq major-mode 'dictem-mode) (progn (setq major-mode nil) (let ((configuration dictem-window-configuration) (selected-window dictem-selected-window)) (kill-buffer (current-buffer)) (if (window-live-p selected-window) (progn (select-window selected-window) (set-window-configuration configuration))))))) (defun dictem-last () "Go back to the last buffer visited visited." (interactive) (if (eq major-mode 'dictem-mode) (if dictem-content-history (progn (setq buffer-read-only nil) (delete-region (point-min) (point-max)) (insert (car (car dictem-content-history))) (goto-char (cadr (car dictem-content-history))) (setq dictem-content-history (cdr dictem-content-history)) ) ) )) (defun dictem-kill-all-buffers () "Kill all dictem buffers." (interactive) (dolist (buffer (buffer-list)) (let ((buf-name (buffer-name buffer))) (if (and (<= (length dictem-buffer-name) (length buf-name)) (string= dictem-buffer-name (substring buf-name 0 (length dictem-buffer-name)))) (kill-buffer buf-name)) ))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; Top-level Functions ;;;;;; (defun dictem-run-match (query database strat) "Asks a user about database name, search strategy and query, creates new *dictem* buffer and shows matches in it." (interactive (list (dictem-read-query (thing-at-point 'word)) (dictem-select-database t t (dictem-get-default-database)) (dictem-select-strategy (dictem-get-default-strategy)))) (dictem-run 'dictem-base-match database query strat)) (defun dictem-run-define (query database) "Asks a user about database name and query, creates new *dictem* buffer and shows definitions in it." (interactive (list (dictem-read-query (thing-at-point 'word)) (dictem-select-database t t (dictem-get-default-database)))) (dictem-run 'dictem-base-define database query nil)) (defun dictem-run-search (query database strat) "Asks a user about database name, search strategy and query, creates new *dictem* buffer and shows definitions in it." (interactive (list (dictem-read-query (thing-at-point 'word)) (dictem-select-database t t (dictem-get-default-database)) (dictem-select-strategy (dictem-get-default-strategy)))) (dictem-run 'dictem-base-search database query strat)) (defun dictem-run-show-info (database) "Asks a user about database name creates new *dictem* buffer and shows information about it." (interactive (list (dictem-select-database nil nil (dictem-get-default-database)))) (dictem-run 'dictem-base-show-info database)) (defun dictem-run-show-server () "Creates new *dictem* buffer and shows information about DICT server in it." (interactive) (dictem-run 'dictem-base-show-server)) (defun dictem-run-show-databases () "Creates new *dictem* buffer and shows a list of databases provided by DICT." (interactive) (dictem-run 'dictem-base-show-databases)) (defun dictem-run-show-strategies () "Creates new *dictem* buffer and shows a list of search stratgeies provided by DICT." (interactive) (dictem-run 'dictem-base-show-strategies)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (easy-menu-define dictem-menu dictem-mode-map "DictEm Menu" `("DictEm" ["DictEm..." dictem-help t] "--" ["Next Section" dictem-next-section t] ["Previous Section" dictem-previous-section t] "--" ["Match" dictem-run-match t] ["Definition" dictem-run-define t] ["Search" dictem-run-search t] "--" ["Information about server" dictem-run-show-server t] ["Information about database" dictem-run-show-info t] ["A list of available databases" dictem-run-show-databases t] "--" ["Bury Dictem Buffer" dictem-quit t] ["Kill Dictem Buffer" dictem-kill t] )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; Optional Features ;;;;; (defun dictem-create-link (start end face function &optional data add-props) "Create a link in the current buffer starting from `start' going to `end'. The `face' is used for displaying, the `data' are stored together with the link. Upon clicking the `function' is called with `data' as argument." (let ((properties (append (list 'face face 'mouse-face 'highlight 'link-data data 'link-function function 'dictem-server dictem-server 'dictem-port dictem-port) add-props))) (remove-text-properties start end properties) (add-text-properties start end properties))) ;;;;;;; Postprocessing Functions ;;;;;;; (defun dictem-postprocess-definition-separator () (save-excursion (goto-char (point-min)) (let ((regexp "^\\(From\\)\\( [^\n]+\\)\\(\\[[^\n]+\\]\\)")) (while (search-forward-regexp regexp nil t) (let ((beg (match-beginning 1)) (end (match-end 1)) (beg-dbdescr (match-beginning 2)) (end-dbdescr (match-end 2)) (beg-dbname (match-beginning 3)) (end-dbname (match-end 3)) ) (put-text-property beg end 'face 'dictem-database-description-face) (put-text-property beg-dbdescr end-dbdescr 'face 'dictem-database-description-face) (setq dictem-current-dbname (dictem-replace-spaces (buffer-substring-no-properties (+ beg-dbname 1) (- end-dbname 1)))) (dictem-create-link beg-dbname end-dbname 'dictem-reference-dbname-face 'dictem-base-show-info (list (cons 'dbname dictem-current-dbname)))) )))) (defvar dictem-hyperlink-beginning "{" "String that begins hyperlink. This variable is used by the function 'dictem-postprocess-definition-hyperlinks'") (defvar dictem-hyperlink-end "}" "String that ends hyperlink. This variable is used by the function 'dictem-postprocess-definition-hyperlinks'") (defvar dictem-hyperlink-define-func 'dictem-base-define "Function called when user clicks on hyperlinks inside the definition. This variable is used by the function 'dictem-postprocess-definition-hyperlinks'") (defun dictem-postprocess-collect-hyperlinks () (save-excursion (goto-char (point-min)) (let ((regexp (concat "\\(" dictem-hyperlink-beginning "\\([^{}|]+\\)" dictem-hyperlink-end "\\|\\(" dictem-hyperlink-beginning "\\([^{}|\n]+\\)|\\([^{}|\n]+\\)" dictem-hyperlink-end "\\)\\)"))) (while (search-forward-regexp regexp nil t) (cond ((match-beginning 2) (let* ((word (dictem-replace-spaces (buffer-substring-no-properties (match-beginning 2) (match-end 2))))) (setq dictem-hyperlinks-alist (cons (list word word) dictem-hyperlinks-alist)) )) ((match-beginning 3) (let* ((word-beg (match-beginning 4)) (word-end (match-end 4)) (link-beg (match-beginning 5)) (link-end (match-end 5)) (word (dictem-replace-spaces (buffer-substring-no-properties word-beg word-end))) (link (dictem-replace-spaces (buffer-substring-no-properties link-beg link-end))) ) (setq dictem-hyperlinks-alist (cons (list word link) dictem-hyperlinks-alist)) ))))) )) (defun dictem-find-brackets (re-beg re-end) (let ((beg-beg (make-marker)) (beg-end (make-marker)) (end-beg (make-marker)) (end-end (make-marker))) (if (search-forward-regexp re-beg nil t) (progn (set-marker beg-beg (match-beginning 0)) (set-marker beg-end (match-end 0)) (if (search-forward-regexp re-end nil t) (progn (set-marker end-beg (match-beginning 0)) (set-marker end-end (match-end 0)) (list beg-beg beg-end end-beg end-end)) nil)) nil))) (defun dictem-postprocess-definition-hyperlinks-cyrlybr1 () (save-excursion (goto-char (point-min)) (let ((regexp) (pos) (beg1) (beg2) (beg3) (end) (word)) (while (setq pos (dictem-find-brackets dictem-hyperlink-beginning dictem-hyperlink-end)) (delete-region (nth 0 pos) (nth 1 pos)) (delete-region (nth 2 pos) (nth 3 pos)) (setq word (buffer-substring-no-properties (nth 1 pos) (nth 2 pos))) (dictem-create-link (nth 1 pos) (nth 2 pos) 'dictem-reference-definition-face dictem-hyperlink-define-func (list (cons 'word (dictem-replace-spaces word)) (cons 'dbname dictem-current-dbname)) '(link t)))))) (defun dictem-postprocess-definition-hyperlinks-curlybr2 () (save-excursion (goto-char (point-min)) (let ((regexp (concat dictem-hyperlink-beginning "\\([^{}|\n]+\\)|\\([^{}|\n]+\\)" dictem-hyperlink-end))) (while (search-forward-regexp regexp nil t) (let* ((beg (match-beginning 5)) (end (match-end 5)) (match-beg (match-beginning 3)) (repl-beg (match-beginning 4)) (repl-end (match-end 4)) (repl (buffer-substring-no-properties repl-beg repl-end)) (word (buffer-substring-no-properties beg end))) (replace-match repl t t) (dictem-create-link match-beg (+ match-beg (length repl)) 'dictem-reference-definition-face dictem-hyperlink-define-func (list (cons 'word (dictem-replace-spaces word)) (cons 'dbname dictem-current-dbname)) '(link t))))))) (defun dictem-postprocess-definition-hyperlinks () (dictem-postprocess-definition-hyperlinks-cyrlybr1) (dictem-postprocess-definition-hyperlinks-curlybr2) ; (dictem-postprocess-definition-hyperlinks-curlybr2) ) (defun dictem-postprocess-match () (save-excursion (goto-char (point-min)) (let ((last-database dictem-last-database) (regexp "\\(\"[^\"\n]+\"\\)\\|\\([^ \"\n]+\\)")) (while (search-forward-regexp regexp nil t) (let* ((beg (match-beginning 0)) (end (match-end 0)) (first-char (buffer-substring-no-properties beg beg))) (cond ((save-excursion (goto-char beg) (= 0 (current-column))) (setq last-database (dictem-replace-spaces (buffer-substring-no-properties beg (- end 1)))) (dictem-create-link beg (- end 1) 'dictem-reference-dbname-face 'dictem-base-show-info (list (cons 'dbname last-database)))) ((match-beginning 1) (dictem-create-link beg end 'dictem-reference-m1-face 'dictem-base-define (list (cons 'word (dictem-replace-spaces (buffer-substring-no-properties (+ beg 1) (- end 1)))) (cons 'dbname last-database)))) (t (dictem-create-link beg end 'dictem-reference-m2-face 'dictem-base-define (list (cons 'word (dictem-replace-spaces (buffer-substring-no-properties beg end ))) (cons 'dbname last-database)))) )))))) (defun dictem-postprocess-definition-remove-header () (save-excursion (goto-char (point-min)) (end-of-line) (let (eol (point)) (goto-char (point-min)) (if (search-forward-regexp "[0-9] definitions? found" eol t) (progn (goto-char (point-min)) (let ((kill-whole-line t)) (kill-line 1)) ))))) (defun dictem-add-text-face-properties (start end face-add-props &optional object) (let (face-props) (while (<= start end) (progn (setq face-props (get-text-property start 'face object)) (if (facep face-props) (progn (setq face-props nil) (add-text-properties start (+ 1 start) (list 'face nil) object))) (add-text-properties start (+ 1 start) (list 'face (append face-props face-add-props)) object) (setq start (+ start 1)))))) (defun dictem-add-begendre-face-propertires (re-beg re-end face-properties) (let ((bold-beg-beg (make-marker)) (bold-beg-end (make-marker)) (bold-end-beg (make-marker)) (bold-end-end (make-marker))) (while (search-forward-regexp re-beg nil t) (progn (set-marker bold-beg-beg (match-beginning 0)) (set-marker bold-beg-end (match-end 0)) (if (search-forward-regexp re-end nil t) (progn (set-marker bold-end-beg (match-beginning 0)) (set-marker bold-end-end (match-end 0)) (dictem-add-text-face-properties bold-beg-end (- bold-end-beg 1) face-properties) (delete-region bold-beg-beg bold-beg-end) (delete-region bold-end-beg bold-end-end) )))))) (defun dictem-postprocess-stardict-definition () (interactive) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '(:weight bold)) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '(:height 1.2 :foreground "white" :weight bold)) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '(:weight bold :foreground "green")) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '()) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '(:foreground "green")) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '(:foreground "brown")) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '(:foreground "green")) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '(:foreground "BurlyWood")) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '(:slant "oblique")) (goto-char (point-min)) (dictem-add-begendre-face-propertires "" "" '(:foreground "lightblue")) ; replaceing with [ (goto-char (point-min)) (while (search-forward-regexp "" nil t) (replace-match "[" t t)) ; replaceing with ] (goto-char (point-min)) (while (search-forward-regexp "" nil t) (replace-match "]" t t)) ; replaceing with ( (goto-char (point-min)) (while (search-forward-regexp "" nil t) (replace-match "" t t)) ; replaceing with ( (goto-char (point-min)) (while (search-forward-regexp "" nil t) (replace-match "" t t)) (let ((dictem-hyperlink-beginning "") (dictem-hyperlink-end "")) (dictem-postprocess-definition-hyperlinks-cyrlybr1)) ) ;;;;; On-Click Functions ;;;;; (defun dictem-define-on-press () "Is called upon pressing Enter." (interactive) (let* ( (properties (text-properties-at (point))) (data (plist-get properties 'link-data)) (fun (plist-get properties 'link-function)) (dictem-server (plist-get properties 'dictem-server)) (dictem-port (plist-get properties 'dictem-port)) (word (assq 'word data)) (dbname (assq 'dbname data)) ) (if (or word dbname) (dictem-run fun (if dbname (cdr dbname) dictem-last-database) (if word (cdr word) nil) nil)))) (defun dictem-define-on-click (event) "Is called upon clicking the link." (interactive "@e") (mouse-set-point event) (dictem-define-on-press)) ;(defun dictem-define-with-db-on-click (event) ; "Is called upon clicking the link." ; (interactive "@e") ; ; (mouse-set-point event) ; (let* ( ; (properties (text-properties-at (point))) ; (word (plist-get properties 'link-data))) ; (if word ; (dictem-run 'dictem-base-define (dictem-select-database) word nil)))) ;(define-key dictem-mode-map [C-down-mouse-2] ; 'dictem-define-with-db-on-click) ;;; Function for "narrowing" definitions ;;;;; (defcustom dictem-postprocess-each-definition-hook nil "Hook run in dictem mode buffers containing SHOW SERVER result." :group 'dictem :type 'hook :options '(dictem-postprocess-definition-separator dictem-postprocess-definition-hyperlinks)) (defun dictem-postprocess-each-definition () (save-excursion (goto-char (point-min)) (let ((regexp-from-dbname "^From [^\n]+\\[\\([^\n]+\\)\\]") (beg nil) (end (make-marker)) (dbname nil)) (if (search-forward-regexp regexp-from-dbname nil t) (let ((dictem-current-dbname (buffer-substring-no-properties (match-beginning 1) (match-end 1)))) (setq beg (match-beginning 0)) (while (search-forward-regexp regexp-from-dbname nil t) (set-marker end (match-beginning 0)) ; (set-marker marker (match-end 0)) (setq dbname (buffer-substring-no-properties (match-beginning 1) (match-end 1))) (save-excursion (narrow-to-region beg (marker-position end)) (run-hooks 'dictem-postprocess-each-definition-hook) (widen)) (setq dictem-current-dbname dbname) (goto-char end) (forward-char) (setq beg (marker-position end)) ) (save-excursion (narrow-to-region beg (point-max)) (run-hooks 'dictem-postprocess-each-definition-hook) (widen)) ))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (provide 'dictem)