Extract sdcv to own repo, add integrations
This commit is contained in:
parent
dd1fe16066
commit
a58192edd1
38
config.org
38
config.org
|
@ -718,10 +718,10 @@ advantages, namely:
|
|||
+ integration of multiple dictionaries
|
||||
[[http://goldendict.org/][GoldenDict]] seems like the best option currently avalible, but lacks a CLI.
|
||||
Hence, we'll fall back to [[https://dushistov.github.io/sdcv/][sdcv]] (a CLI version of StarDict) for now.
|
||||
To interface with this, we'll use a local copy of [[https://github.com/gucong/emacs-sdcv][emacs-sdcv]] (to enable easy
|
||||
modification).
|
||||
To interface with this, we'll use a my =sdcv= package.
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(package! sdcv-mode :recipe (:local-repo "lisp" :no-byte-compile t))
|
||||
(package! sdcv-mode :recipe (:host github :repo "tecosaur/sdcv")
|
||||
:pin "7fabf83c74afbd0d0c9f2f0cd16967c9b8bb37d5")
|
||||
#+END_SRC
|
||||
|
||||
Given that a request for a CLI is the most upvoted issue on GitHub for
|
||||
|
@ -2398,11 +2398,37 @@ add package src directories to projectile. This isn't desirable in my opinion.
|
|||
(or (mapcar (lambda (p) (s-starts-with-p p filepath)) projectile-ignored-projects)))
|
||||
#+END_SRC
|
||||
** StarDict
|
||||
We start off my loading =sdcv-mode=, then we'll integrate it into pre-existing
|
||||
We start off my loading =sdcv=, then we'll integrate it into pre-existing
|
||||
definition functionality (like ~+lookup/dictionary-definition~).
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(use-package! sdcv-mode
|
||||
:commands sdcv-search sdcv-list-dictionary)
|
||||
(use-package! sdcv
|
||||
:commands sdcv-search sdcv-list-dictionary
|
||||
:config
|
||||
(map! :map sdcv-mode-map
|
||||
:n "q" #'sdcv-return-from-sdcv
|
||||
:nv "RET" #'sdcv-search-word-at-point
|
||||
:n "a" #'outline-show-all
|
||||
:n "h" (cmd! (outline-hide-sublevels 3))
|
||||
:n "o" #'sdcv-toggle-entry
|
||||
:n "n" #'sdcv-next-entry
|
||||
:n "N" (cmd! (sdcv-next-entry t))
|
||||
:n "p" #'sdcv-previous-entry
|
||||
:n "P" (cmd! (sdcv-previous-entry t))
|
||||
:n "b" #'sdcv-search-history-backwards
|
||||
:n "f" #'sdcv-search-history-forwards
|
||||
:n "/" (cmd! (call-interactively #'sdcv-search))))
|
||||
#+END_SRC
|
||||
|
||||
Now let's use this instead of wordnet.
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defadvice! +lookup/dictionary-definition-cdcv (identifier &optional arg)
|
||||
"Look up the definition of the word at point (or selection) using `sdcv-search'."
|
||||
:override #'+lookup/dictionary-definition
|
||||
(interactive
|
||||
(list (or (doom-thing-at-point-or-region 'word)
|
||||
(read-string "Look up in dictionary: "))
|
||||
current-prefix-arg))
|
||||
(sdcv-search identifier nil nil t))
|
||||
#+END_SRC
|
||||
** Smart Parentheses
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
|
|
@ -1,413 +0,0 @@
|
|||
;;; sdcv-mode.el --- major mode to do dictionary query through sdcv
|
||||
|
||||
;; Copyright 2006~2008 pluskid,
|
||||
;; 2011 gucong
|
||||
;;
|
||||
;; Author: pluskid <pluskid@gmail.com>,
|
||||
;; gucong <gucong43216@gmail.com>
|
||||
;;
|
||||
;; This program is free software; you can redistribute it and/or
|
||||
;; modify it under the terms of the GNU General Public License as
|
||||
;; published by the Free Software Foundation; either version 2, or (at
|
||||
;; your option) any later version.
|
||||
;;
|
||||
;; This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This is a major mode to view output of dictionary search of sdcv.
|
||||
|
||||
;; Put this file into your load-path and the following into your
|
||||
;; ~/.emacs:
|
||||
;; (require 'sdcv-mode)
|
||||
;; (global-set-key (kbd "C-c d") 'sdcv-search)
|
||||
|
||||
;;; Changelog:
|
||||
|
||||
;; 2012/01/02
|
||||
;; * New variable: `sdcv-word-processor'
|
||||
;; * Breaking change:
|
||||
;; for `sdcv-dictionary-list' and `sdcv-dictionary-alist',
|
||||
;; non-list (non-nil) value now means full dictionary list
|
||||
;; * Rewrite `sdcv-search' for both interactive and non-interactive use
|
||||
;; * `sdcv-dictionary-list' is left for customization use only
|
||||
;; * Better highlighting.
|
||||
;;
|
||||
;; 2011/06/30
|
||||
;; * New feature: parse output for failed lookup
|
||||
;; * Keymap modification
|
||||
;;
|
||||
;; 2008/06/11
|
||||
;; * sdcv-mode v 0.1 init (with background process)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'outline)
|
||||
(provide 'sdcv-mode)
|
||||
(eval-when-compile
|
||||
(require 'cl))
|
||||
|
||||
;;; ==================================================================
|
||||
;;; Frontend, search word and display sdcv buffer
|
||||
(defun sdcv-search (word &optional dict-list-name dict-list interactive-p)
|
||||
"Search WORD through the command-line tool sdcv.
|
||||
The result will be displayed in buffer named with
|
||||
`sdcv-buffer-name' with `sdcv-mode' if called interactively.
|
||||
|
||||
When provided with DICT-LIST-NAME, query `sdcv-dictionary-alist'
|
||||
to get the new dictionary list before search.
|
||||
Alternatively, dictionary list can be specified directly
|
||||
by DICT-LIST. Any non-list value of it means using all dictionaries.
|
||||
|
||||
When called interactively, prompt for the word.
|
||||
Prefix argument have the following meaning:
|
||||
If `sdcv-dictionary-alist' is defined,
|
||||
use prefix argument to select a new DICT-LIST-NAME.
|
||||
Otherwise, prefix argument means using all dictionaries.
|
||||
|
||||
Word may contain some special characters:
|
||||
* match zero or more characters
|
||||
? match zero or one character
|
||||
/ used at the beginning, for fuzzy search
|
||||
| used at the beginning, for data search
|
||||
\ escape the character right after"
|
||||
(interactive
|
||||
(let* ((dict-list-name
|
||||
(and current-prefix-arg sdcv-dictionary-alist
|
||||
(completing-read "Select dictionary list: "
|
||||
sdcv-dictionary-alist nil t)))
|
||||
(dict-list
|
||||
(and current-prefix-arg (not sdcv-dictionary-alist)))
|
||||
(guess (or (and transient-mark-mode mark-active
|
||||
(buffer-substring-no-properties
|
||||
(region-beginning) (region-end)))
|
||||
(current-word nil t)))
|
||||
(word (read-string (format "Search dict (default: %s): " guess)
|
||||
nil nil guess)))
|
||||
(list word dict-list-name dict-list t)))
|
||||
;; init current dictionary list
|
||||
(when (null sdcv-current-dictionary-list)
|
||||
(setq sdcv-current-dictionary-list sdcv-dictionary-list))
|
||||
;; dict-list-name to dict-list
|
||||
(when (and (not dict-list) dict-list-name)
|
||||
(if (not sdcv-dictionary-alist)
|
||||
(error "`sdcv-dictionary-alist' not defined"))
|
||||
(setq dict-list
|
||||
(cdr (assoc dict-list-name sdcv-dictionary-alist))))
|
||||
;; prepare new dictionary list
|
||||
(when (and dict-list (not (equal sdcv-current-dictionary-list dict-list)))
|
||||
(setq sdcv-current-dictionary-list dict-list)
|
||||
;; kill sdcv process
|
||||
(and (get-process sdcv-process-name)
|
||||
(kill-process (get-process sdcv-process-name)))
|
||||
(while (get-process sdcv-process-name)
|
||||
(sleep-for 0.01)))
|
||||
(let ((result
|
||||
(concat ">>>"
|
||||
(mapconcat
|
||||
(lambda (w) (sdcv-do-lookup w))
|
||||
(if sdcv-word-processor
|
||||
(let ((processed (funcall sdcv-word-processor word)))
|
||||
(if (listp processed) processed (list processed)))
|
||||
(list word))
|
||||
">>>"))))
|
||||
(if (not interactive-p)
|
||||
result
|
||||
(with-current-buffer (get-buffer-create sdcv-buffer-name)
|
||||
(setq buffer-read-only nil)
|
||||
(erase-buffer)
|
||||
(insert result))
|
||||
(sdcv-goto-sdcv)
|
||||
(sdcv-mode)
|
||||
(sdcv-mode-reinit))))
|
||||
|
||||
(defun sdcv-list-dictionary ()
|
||||
"Show available dictionaries."
|
||||
(interactive)
|
||||
(let (resize-mini-windows)
|
||||
(shell-command "sdcv -l" sdcv-buffer-name)))
|
||||
|
||||
(defvar sdcv-current-dictionary-list nil)
|
||||
|
||||
(defun sdcv-generate-dictionary-argument ()
|
||||
"Generate dictionary argument for sdcv from `sdcv-current-dictionary-list'
|
||||
and `sdcv-dictionary-path'."
|
||||
(append
|
||||
(and sdcv-dictionary-path (list "--data-dir" sdcv-dictionary-path))
|
||||
(and (listp sdcv-current-dictionary-list)
|
||||
(mapcan (lambda (dict)
|
||||
(list "-u" dict))
|
||||
sdcv-current-dictionary-list))))
|
||||
|
||||
;;; ==================================================================
|
||||
;;; utilities to switch from and to sdcv buffer
|
||||
(defvar sdcv-previous-window-conf nil
|
||||
"Window configuration before switching to sdcv buffer.")
|
||||
(defun sdcv-goto-sdcv ()
|
||||
"Switch to sdcv buffer in other window."
|
||||
(interactive)
|
||||
(unless (eq (current-buffer)
|
||||
(sdcv-get-buffer))
|
||||
(setq sdcv-previous-window-conf (current-window-configuration)))
|
||||
(let* ((buffer (sdcv-get-buffer))
|
||||
(window (get-buffer-window buffer)))
|
||||
(if (null window)
|
||||
(switch-to-buffer-other-window buffer)
|
||||
(select-window window))))
|
||||
(defun sdcv-return-from-sdcv ()
|
||||
"Bury sdcv buffer and restore the previous window configuration."
|
||||
(interactive)
|
||||
(if (window-configuration-p sdcv-previous-window-conf)
|
||||
(progn
|
||||
(set-window-configuration sdcv-previous-window-conf)
|
||||
(setq sdcv-previous-window-conf nil)
|
||||
(bury-buffer (sdcv-get-buffer)))
|
||||
(bury-buffer)))
|
||||
|
||||
(defun sdcv-get-buffer ()
|
||||
"Get the sdcv buffer. Create one if there's none."
|
||||
(let ((buffer (get-buffer-create sdcv-buffer-name)))
|
||||
(with-current-buffer buffer
|
||||
(unless (eq major-mode 'sdcv-mode)
|
||||
(sdcv-mode)))
|
||||
buffer))
|
||||
|
||||
;;; ==================================================================
|
||||
;;; The very major mode
|
||||
(defvar sdcv-mode-font-lock-keywords
|
||||
'(
|
||||
;; dictionary name
|
||||
("^-->\\(.*\\)$" . (1 sdcv-hit-face))
|
||||
("^==>\\(.*\\)$" . (1 sdcv-failed-face))
|
||||
("^\\(>>>.*\\)$" . (1 sdcv-heading-face))
|
||||
)
|
||||
"Expressions to hilight in `sdcv-mode'")
|
||||
|
||||
(defvar sdcv-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map "q" 'sdcv-return-from-sdcv)
|
||||
(define-key map (kbd "RET") 'sdcv-search)
|
||||
(define-key map "a" 'show-all)
|
||||
(define-key map "h" 'hide-body)
|
||||
(define-key map "o" 'sdcv-toggle-entry)
|
||||
(define-key map "n" 'sdcv-next-entry)
|
||||
(define-key map "p" 'sdcv-previous-entry)
|
||||
map)
|
||||
"Keymap for `sdcv-mode'.")
|
||||
|
||||
(define-derived-mode sdcv-mode nil "sdcv"
|
||||
"Major mode to look up word through sdcv.
|
||||
\\{sdcv-mode-map}
|
||||
Turning on Text mode runs the normal hook `sdcv-mode-hook'."
|
||||
(setq font-lock-defaults '(sdcv-mode-font-lock-keywords))
|
||||
(setq buffer-read-only t)
|
||||
(set (make-local-variable 'outline-regexp) "-->.*\n-->\\|==>\\|>>>")
|
||||
(set (make-local-variable font-lock-string-face) nil)
|
||||
)
|
||||
|
||||
(defun sdcv-mode-reinit ()
|
||||
"Re-initialize buffer.
|
||||
Hide all entrys but the first one and goto
|
||||
the beginning of the buffer."
|
||||
(ignore-errors
|
||||
(setq buffer-read-only nil)
|
||||
(sdcv-parse-failed)
|
||||
(setq buffer-read-only t)
|
||||
(hide-body)
|
||||
(goto-char (point-min))
|
||||
(forward-line 1)
|
||||
(show-entry)))
|
||||
|
||||
(defun sdcv-parse-failed ()
|
||||
(goto-char (point-min))
|
||||
(let (save-word)
|
||||
(while (re-search-forward "^[0-9]+).*-->\\(.*\\)$" nil t)
|
||||
(let ((cur-word (match-string-no-properties 1)))
|
||||
(unless (string= save-word cur-word)
|
||||
(setq save-word cur-word)
|
||||
(re-search-backward "^\\(.\\)" nil t)
|
||||
(match-string 1)
|
||||
(insert (format "\n==>%s\n" save-word)))))))
|
||||
|
||||
(defun sdcv-next-entry ()
|
||||
(interactive)
|
||||
(outline-next-heading)
|
||||
(show-entry)
|
||||
(recenter-top-bottom 0))
|
||||
(defun sdcv-previous-entry ()
|
||||
(interactive)
|
||||
(outline-previous-heading)
|
||||
(show-entry)
|
||||
(recenter-top-bottom 0))
|
||||
|
||||
(defun sdcv-toggle-entry ()
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(outline-back-to-heading)
|
||||
(if (not (outline-invisible-p (line-end-position)))
|
||||
(hide-entry)
|
||||
(show-entry))))
|
||||
|
||||
;;; ==================================================================
|
||||
;;; Support for sdcv process in background
|
||||
(defun sdcv-do-lookup (word)
|
||||
"Send the word to the sdcv process and return the result."
|
||||
(let ((process (sdcv-get-process)))
|
||||
(process-send-string process (concat word "\n"))
|
||||
(with-current-buffer (process-buffer process)
|
||||
(let ((i 0) rlt done)
|
||||
(while (and (not done)
|
||||
(< i sdcv-wait-timeout))
|
||||
(when (sdcv-match-tail sdcv-word-prompts)
|
||||
(setq rlt (buffer-substring-no-properties (point-min)
|
||||
(point-max)))
|
||||
(setq done t))
|
||||
(when (sdcv-match-tail sdcv-choice-prompts)
|
||||
(process-send-string process "-1\n"))
|
||||
(unless done
|
||||
(sleep-for sdcv-wait-interval)
|
||||
(setq i (+ i sdcv-wait-interval))))
|
||||
(unless (< i sdcv-wait-timeout)
|
||||
;; timeout
|
||||
(kill-process process)
|
||||
(error "ERROR: timeout waiting for sdcv"))
|
||||
(erase-buffer)
|
||||
rlt))))
|
||||
|
||||
(defvar sdcv-wait-timeout 2
|
||||
"The max time (in seconds) to wait for the sdcv process to
|
||||
produce some output.")
|
||||
(defvar sdcv-wait-interval 0.01
|
||||
"The interval (in seconds) to sleep each time to wait for
|
||||
sdcv's output.")
|
||||
|
||||
(defconst sdcv-process-name "%sdcv-mode-process%")
|
||||
(defconst sdcv-process-buffer-name "*sdcv-mode-process*")
|
||||
|
||||
(defvar sdcv-word-prompts '("Enter word or phrase: "
|
||||
"请输入单词或短语:"
|
||||
"請輸入單字或片語:")
|
||||
"A list of prompts that sdcv use to prompt for word.")
|
||||
|
||||
(defvar sdcv-choice-prompts '("Your choice[-1 to abort]: "
|
||||
"您的选择为:"
|
||||
"您的選擇為:")
|
||||
"A list of prompts that sdcv use to prompt for a choice
|
||||
of multiple candicates.")
|
||||
|
||||
(defvar sdcv-result-patterns '("^Found [0-9]+ items, similar to [*?/|]*\\(.+?\\)[*?]*\\."
|
||||
"^发现 [0-9]+ 条记录和 [*?/|]*\\(.+?\\)[*?]* 相似。"
|
||||
)
|
||||
"A list of patterns to extract result word of sdcv. Special
|
||||
characters are stripped.")
|
||||
|
||||
(defun sdcv-get-process ()
|
||||
"Get or create the sdcv process."
|
||||
(let ((process (get-process sdcv-process-name)))
|
||||
(when (null process)
|
||||
(with-current-buffer (get-buffer-create
|
||||
sdcv-process-buffer-name)
|
||||
(erase-buffer)
|
||||
(setq process (apply 'start-process
|
||||
sdcv-process-name
|
||||
sdcv-process-buffer-name
|
||||
sdcv-program-path
|
||||
(sdcv-generate-dictionary-argument)))
|
||||
;; kill the initial prompt
|
||||
(let ((i 0))
|
||||
(message "starting sdcv...")
|
||||
(while (and (not (sdcv-match-tail sdcv-word-prompts))
|
||||
(< i sdcv-wait-timeout))
|
||||
(sleep-for sdcv-wait-interval)
|
||||
(setq i (+ i sdcv-wait-interval)))
|
||||
(unless (< i sdcv-wait-timeout)
|
||||
;; timeout
|
||||
(kill-process process)
|
||||
(error "ERROR: timeout waiting for sdcv"))
|
||||
(erase-buffer))))
|
||||
process))
|
||||
|
||||
(defun sdcv-buffer-tail (length)
|
||||
"Get a substring of length LENGTH at the end of
|
||||
current buffer."
|
||||
(let ((beg (- (point-max) length))
|
||||
(end (point-max)))
|
||||
(if (< beg (point-min))
|
||||
(setq beg (point-min)))
|
||||
(buffer-substring-no-properties beg end)))
|
||||
|
||||
(defun sdcv-match-tail (prompts)
|
||||
(let ((done nil)
|
||||
(prompt nil))
|
||||
(while (and (not done)
|
||||
prompts)
|
||||
(setq prompt (car prompts))
|
||||
(setq prompts (cdr prompts))
|
||||
(when (string-equal prompt
|
||||
(sdcv-buffer-tail (length prompt)))
|
||||
(delete-region (- (point-max) (length prompt))
|
||||
(point-max))
|
||||
(setq done t)))
|
||||
done))
|
||||
|
||||
|
||||
;;;;##################################################################
|
||||
;;;; User Options, Variables
|
||||
;;;;##################################################################
|
||||
|
||||
(defvar sdcv-buffer-name "*sdcv*"
|
||||
"The name of the buffer of sdcv.")
|
||||
(defvar sdcv-dictionary-list t
|
||||
"A list of dictionaries to use.
|
||||
Each entry is a string denoting the name of a dictionary, which
|
||||
is then passed to sdcv through the '-u' command line option.
|
||||
Any non-list value means using all the dictionaries.")
|
||||
(defvar sdcv-dictionary-alist nil
|
||||
"An alist of dictionaries, used to interactively form
|
||||
dictionary list. It has the form:
|
||||
((\"full\" . t)
|
||||
(\"group1\" \"dict1\" \"dict2\" ...)
|
||||
(\"group2\" \"dict2\" \"dict3\"))
|
||||
Any cons cell here means using all dictionaries.
|
||||
")
|
||||
|
||||
(defvar sdcv-program-path "sdcv"
|
||||
"The path of sdcv program.")
|
||||
|
||||
(defvar sdcv-dictionary-path nil
|
||||
"The path of dictionaries.")
|
||||
|
||||
(defvar sdcv-word-processor nil
|
||||
"This is the function that take a word (stirng)
|
||||
and return a word or a list of words for lookup by `sdcv-search'.
|
||||
All lookup result(s) will finally be concatenated together.
|
||||
|
||||
`nil' value means do nothing with the original word.
|
||||
|
||||
The following is an example. This function takes the original word and
|
||||
compare whether simplified and traditional form of the word are the same.
|
||||
If not, look up both of the words.
|
||||
|
||||
(lambda (word)
|
||||
(let ((sim (chinese-conv word \"simplified\"))
|
||||
(tra (chinese-conv word \"traditional\")))
|
||||
(if (not (string= sim tra))
|
||||
(list sim tra)
|
||||
word)))
|
||||
")
|
||||
|
||||
(defvar sdcv-hit-face 'font-lock-type-face
|
||||
"Face for search hits")
|
||||
(defvar sdcv-failed-face 'font-lock-keyword-face
|
||||
"Face for suggestions for a failed lookup.")
|
||||
(defvar sdcv-heading-face 'highlight
|
||||
"Face for heading of lookup")
|
||||
|
||||
;;; sdcv-mode.el ends here
|
Loading…
Reference in New Issue