forked from kuanyui/moedict.el
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmoedict.el
689 lines (601 loc) · 28 KB
/
moedict.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
;;; moedict.el --- Moe Dict ("萌典", a Chinese dictionary) client for Emacs -*- lexical-binding: t; -*-
;; Author: ono hiroko (kuanyui) <[email protected]>
;; Keywords: tools, dictionary
;; Package-Requires: ((emacs "24.3") (helm "1.9.1") (esqlite "0.3.1"))
;; X-URL: https://github.com/kuanyui/moedict.el
;; Version: {{VERSION}}
;;
;; Copyright (C) 2014, 2016 ono hiroko
;; This program is free software. It comes without any warranty, to
;; the extent permitted by applicable law. You can redistribute it
;; and/or modify it under the terms of the Do What The Fuck You Want
;; To Public License, Version 2, as published by Sam Hocevar. See
;; http://www.wtfpl.net/ for more details.
;;; Commentary:
;; Three commands which suitable for `global-set-key':
;;
;; `moedict' to lookup vocabulary.
;; `moedict/try-region' to try to lookup selected text.
;; `moedict/last-vocabulary' to lookup with the input in the last search.
;;
;; `moedict/help' to read all available key-bindings in moedict-mode.
;;
;; To read more info & screenshot, please visit
;; https://github.com/kuanyui/moedict.el
;;; Code:
;; Due to mystery power from nowhere, the answer of Universe is 42,
;; and the line number of this file *MUST* be 689. If you want to
;; contribute, please keep the line number unchanged.
;;
;; ======================================================
;; Variables
;; ======================================================
(require 'esqlite)
(require 'helm)
(require 'org-table)
(require 'cl)
(require 'url)
(defconst moedict-files-directory (file-name-directory load-file-name))
(defvar moedict-dictionary-file-path (concat moedict-files-directory "dict.sqlite3")
"萌典 sqlite3 字典檔的檔案路令")
(defvar moedict-dictionary-xz-file-path (concat moedict-files-directory "dict.sqlite3.xz")
"萌典 sqlite3.xz 的路徑")
(defvar moedict-dictionary-source-url
"https://raw.githubusercontent.com/kuanyui/moedict.el/master/dict.sqlite3.xz"
"萌典字典檔的 .xz 下載連結")
(defvar moedict-prompt "萌典:")
(defvar moedict-buffer-name "*[萌典] 查詢結果*")
(defvar moedict-help-buffer-name "*[萌典] 幫助視窗*")
(defvar moedict-candidate-buffer-name "*[萌典] 候選字*")
(defvar moedict-history-buffer-name "*[萌典] 查詢歷史*")
(defvar moedict-synonyms-tag (propertize "同" 'face 'moedict-syn/antonyms-tag))
(defvar moedict-antonyms-tag (propertize "反" 'face 'moedict-syn/antonyms-tag))
(defvar moedict-punctuations "[ \n。,!?;:.「」『』()、【】《》〈〉—]")
(defvar moedict--history nil "History list of current moedict buffer. DON'T SETQ ME.")
(defvar moedict--current-vocabulary nil
"History list of current moedict buffer. DON'T SETQ ME.")
(defcustom moedict-candidates-limit 200
"查詢時所列出的候選字最大數量"
:type 'integer :group 'moedict)
(defcustom moedict-try-to-get-vocabulary-max-length 4
"嘗試猜測字彙時,猜測字彙的最大長度"
:type 'integer :group 'moedict)
(defcustom moedict-mode-hook nil
"Normal hook run when entering moedict-mode."
:type 'hook :group 'moedict)
(defvar moedict-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "Q") 'moedict/exit)
(define-key map (kbd "SPC") 'moedict)
(define-key map (kbd "l") 'moedict)
(define-key map (kbd "h") 'moedict/help)
(define-key map (kbd "?") 'moedict/help)
(define-key map (kbd "r") 'moedict/try-region)
(define-key map (kbd "<enter>") 'moedict:enter)
(define-key map (kbd "RET") 'moedict:enter)
(define-key map (kbd "<tab>") 'moedict:tab)
(define-key map (kbd "TAB") 'moedict:tab)
(define-key map (kbd "<backtab>") 'moedict:shift+tab)
(define-key map (kbd "H") 'moedict/history-show-list)
(define-key map (kbd "[") 'moedict/history-previous)
(define-key map (kbd "q") 'moedict/history-previous)
(define-key map (kbd "^") 'moedict/history-previous)
(define-key map (kbd "C-c C-b") 'moedict/history-previous)
(define-key map (kbd "]") 'moedict/history-next)
(define-key map (kbd "C-c C-f") 'moedict/history-next)
(define-key map (kbd "o") 'moedict/open-website)
map)
"Keymap for Moedict major mode.")
(define-derived-mode moedict-mode nil "萌典"
"Moedict (萌典) Client for Emacs"
(set (make-local-variable 'buffer-read-only) t))
(defgroup moedict nil
"Moedict (萌典) Client for Emacs"
:prefix "moedict" :link '(url-link "http://github.com/kuanyui/moedict.el"))
(defgroup moedict-faces nil
"Faces used in `moedict-mode'"
:group 'moedict :group 'faces)
(defface moedict-title
'((((class color) (background light)) (:foreground "#ff8700" :bold t :height 1.2))
(((class color) (background dark)) (:foreground "#ffa722" :bold t :height 1.2)))
"Face for title. ex:"
:group 'moedict-faces)
(defface moedict-stroke-count
'((((class color) (background light)) (:foreground "#787878"))
(((class color) (background dark)) (:foreground "#c1c1c1")))
"Face for stroke-count."
:group 'moedict-faces)
(defface moedict-radical
'((((class color) (background light)) (:foreground "#ffffff" :background "#a40000"))
(((class color) (background dark)) (:foreground "#ffffff" :background "#a40000")))
"Face for character's radical."
:group 'moedict-faces)
(defface moedict-non-radical-stroke-count
'((((class color) (background light)) (:inherit moedict-stroke-count))
(((class color) (background dark)) (:inherit moedict-stroke-count)))
"Face for non-radical stroke-count."
:group 'moedict-faces)
(defface moedict-bopomofo
'((((class color) (background light)) (:foreground "#008700" :background "#d7ff87"))
(((class color) (background dark)) (:foreground "#a1db00" :background "#5a5a5a")))
"Face for bopomofo (注音符號)."
:group 'moedict-faces)
(defface moedict-bopomofo2
'((((class color) (background light)) (:inherit moedict-bopomofo))
(((class color) (background dark)) (:inherit moedict-bopomofo)))
"Face for bopomofo2 (注音二式)."
:group 'moedict-faces)
(defface moedict-pinyin
'((((class color) (background light)) (:inherit moedict-bopomofo))
(((class color) (background dark)) (:inherit moedict-bopomofo)))
"Face for pinyin (拼音)."
:group 'moedict-faces)
(defface moedict-type
'((((class color) (background light)) (:foreground "#ffffd7" :background "#525252"))
(((class color) (background dark)) (:foreground "#525252" :background "#c1c1c1")))
"Face for type. ex: [動]、[名]"
:group 'moedict-faces)
(defface moedict-quote
'((((class color) (background light)) (:foreground "#ff4ea3" :slant italic))
(((class color) (background dark)) (:foreground "#ff6fa5" :slant italic)))
"Face for quote."
:group 'moedict-faces)
(defface moedict-def
'((((class color) (background light)) (:foreground "#1f5bff"))
(((class color) (background dark)) (:foreground "#6faaff")))
"Face for definitions."
:group 'moedict-faces)
(defface moedict-example
'((((class color) (background light)) (:foreground "#525252"))
(((class color) (background dark)) (:foreground "#cdcdcd")))
"Face for example. ex: Example"
:group 'moedict-faces)
(defface moedict-link
'((((class color) (background light)) (:foreground "#00a775"))
(((class color) (background dark)) (:foreground "#00d7af")))
"Face for link. ex:「見...等條」"
:group 'moedict-faces)
(defface moedict-synonyms
'((((class color) (background light)) (:foreground "#9a08ff"))
(((class color) (background dark)) (:foreground "#aa71ff")))
"Face for synonyms."
:group 'moedict-faces)
(defface moedict-antonyms
'((((class color) (background light)) (:foreground "#9a08ff"))
(((class color) (background dark)) (:foreground "#aa71ff")))
"Face for antonyms."
:group 'moedict-faces)
(defface moedict-syn/antonyms-tag
'((((class color) (background light)) (:foreground "#ffffff" :background "#9a08ff"))
(((class color) (background dark)) (:foreground "#7008a0" :background "#eeaeff")))
"Face for syn/antonyms-tag. ex: [同]"
:group 'moedict-faces)
;; ======================================================
;; Download Dictionary File
;; ======================================================
(defmacro moedict-ensure-dictionary-file-exist (&rest body)
"If dictionary file is not existed, download & uncompress it with xz."
`(if (file-exists-p moedict-dictionary-file-path)
(progn ,@body)
(moedict-message "字典檔似乎尚未下載、或者尚未解壓縮,請執行 M-x moedict-install-dictionary (可能會花上一段時間)
The dictionary file seems haven't been downloaded or extracted. Please M-x moedict-install-dictionary (It would take some time)")))
(defun moedict-install-dictionary ()
(interactive)
(if (not (executable-find "xz"))
(moedict-message "您的系統上沒有發現xz這個指令,請安裝後再重試一次。
Command 'xz' not found on your system. Please install it then try again")
(if (file-exists-p moedict-dictionary-xz-file-path)
(progn (shell-command (format "xz -kdfvv %s.xz" moedict-dictionary-file-path))
(moedict-message "字典檔設定完成!您現在已經可以使用 M-x moedict"))
(moedict-download-dictionary-file-then-uncompress))))
(defun moedict-download-dictionary-file-then-uncompress ()
(url-copy-file moedict-dictionary-source-url moedict-dictionary-xz-file-path)
(moedict-install-dictionary))
;; ======================================================
;; Query
;; ======================================================
(defmacro moedict-query (sql-query-string)
`(progn
(esqlite-read moedict-dictionary-file-path ,sql-query-string)))
(defmacro moedict-query-with-limit (sql-query-string)
`(progn
(esqlite-read moedict-dictionary-file-path
(format "%s LIMIT %s" ,sql-query-string ,moedict-candidates-limit))))
(defun moedict-get-candidates-list (string)
(cl-remove-if
(lambda (x) (string-prefix-p "{" x))
(cl-mapcon #'car (moedict-query-with-limit
(format "SELECT title FROM entries WHERE title LIKE %s"
(esqlite-format-text (concat string "%")))))))
(defun moedict-query-vocabulary (vocabulary)
"title, radical, stroke_count, non_radical_stroke_count,
bopomofo, bopomofo2, pinyin,
type, def, example, quote, source,
synonyms, antonyms, link
"
(moedict-query
(format "
SELECT entries.title, entries.radical, entries.stroke_count, entries.non_radical_stroke_count,
heteronyms.bopomofo, heteronyms.bopomofo2, heteronyms.pinyin,
definitions.type, definitions.def, definitions.example, definitions.quote, definitions.source,
definitions.synonyms, definitions.antonyms, definitions.link
FROM entries, heteronyms, definitions
WHERE entries.title = %s
AND heteronyms.entry_id = entries.id
AND definitions.heteronym_id = heteronyms.id;
" (esqlite-format-text vocabulary))))
(defun moedict-if-a-vocabulary-exactly-exist (vocabulary)
(moedict-query
(format "SELECT 1 FROM entries WHERE title = %s;" (esqlite-format-text vocabulary))))
;; ======================================================
;; Low-level & Internal Tools
;; ======================================================
(defun moedict--get-column (row attr)
"Return column's content from a row directly.
Don't borthered by the serial numbers."
(nth
(cdr (assq attr '((title . 0)
(radical . 1)
(stroke_count . 2)
(non_radical_stroke_count . 3)
(bopomofo . 4)
(bopomofo2 . 5)
(pinyin . 6)
(type . 7)
(def . 8)
(example . 9)
(quote . 10)
(source . 11)
(synonyms . 12)
(antonyms . 13)
(link . 14))))
row))
(defun moedict-mapconcat-with-newline (list)
"Ignore nil, seperator is \\n."
(mapconcat #'identity
(cl-remove-if #'null list)
"\n"))
(defun moedict-mapconcat-with-2-newlines (list)
"Ignore nil, seperator is \\n\\n."
(mapconcat #'identity
(cl-remove-if #'null list)
"\n\n"))
(defun moedict--replace-null-with-nil (list)
"replace all :null in (two-level) list with nil"
(mapcar (lambda (x)
(mapcar (lambda (y) (if (eq y :null) nil y))
x))
list))
(defun moedict-match-positions (regexp string &optional subexp-depth)
"Get all matched REGEXP position in a STRING.
SUBEXP-DEPTH is 0 by default."
(if (null subexp-depth)
(setq subexp-depth 0))
(let ((pos 0) result)
(while (and (string-match regexp string pos)
(< pos (length string)))
(let ((m (match-end subexp-depth)))
(push (cons (match-beginning subexp-depth) (match-end subexp-depth)) result)
(setq pos m)))
(nreverse result)))
;; ======================================================
;; Render
;; ======================================================
(defmacro moedict--render-type ()
`(let ((aaa (moedict--get-column row 'type)))
(if aaa
(concat " " (propertize (format "[%s]" aaa) 'face 'moedict-type))
"")))
(defmacro moedict--render-def ()
`(let* ((def (moedict--get-column row 'def))
(example (moedict--get-column row 'example))
(quote (moedict--get-column row 'quote))
(quote (if (stringp quote) (replace-regexp-in-string "," "\n " quote)))
(link (moedict--get-column row 'link))
(synonyms (moedict--get-column row 'synonyms))
(antonyms (moedict--get-column row 'antonyms)))
(when link
(add-text-properties 0 (length link) '(face moedict-link) link)
(mapc (lambda (begin-end)
(add-face-text-property (car begin-end) (cdr begin-end) '(underline t) t link))
(moedict-match-positions "「\\(.+?\\)」" link 1)))
(moedict-mapconcat-with-newline
(list
(if def (format " %s" (propertize def 'face 'moedict-def)))
(if example (format " %s" (propertize example 'face 'moedict-example)))
(if quote (format " %s" (propertize quote 'face 'moedict-quote)))
(if link (format " %s" link))
(if synonyms (format " %s %s" moedict-synonyms-tag (propertize synonyms 'face 'moedict-synonyms)))
(if antonyms (format " %s %s" moedict-antonyms-tag (propertize antonyms 'face 'moedict-antonyms)))))))
(defun moedict--render-rows (rows)
"ROWS is the query result retrieved from `moedict-query-vocabulary',
Return value is rendered string."
(moedict-mapconcat-with-2-newlines
(cons
;; car
(if (eq :null (moedict--get-column (car rows) 'radical))
nil
(format "%s + %s = %s"
(propertize (moedict--get-column (car rows) 'radical) 'face 'moedict-radical)
(moedict--get-column (car rows) 'non_radical_stroke_count)
(moedict--get-column (car rows) 'stroke_count)))
;; cdr
(let ( bopomofo type )
(mapcar
(lambda (row)
(cond ((not (equal (moedict--get-column row 'bopomofo) bopomofo))
(setq bopomofo (moedict--get-column row 'bopomofo))
(setq type (moedict--get-column row 'type))
(format "%s %s %s %s%s%s\n\n%s"
(propertize (moedict--get-column row 'title) 'face 'moedict-title)
(propertize (moedict--get-column row 'bopomofo) 'face 'moedict-bopomofo)
(propertize (moedict--get-column row 'pinyin) 'face 'moedict-pinyin)
(propertize (moedict--get-column row 'bopomofo2) 'face 'moedict-bopomofo2)
(if type "\n\n" "") ; Because some vocabulary have no type
(moedict--render-type)
(moedict--render-def)))
((not (equal (moedict--get-column row 'type) type))
(setq type (moedict--get-column row 'type))
(concat (moedict--render-type)
(if type "\n\n" "") ; Because some vocabulary have no type
(moedict--render-def)))
(t (moedict--render-def))))
(moedict--replace-null-with-nil rows))
))))
(defun moedict-render (vocabulary)
"Return rendered string"
(moedict--render-rows (moedict-query-vocabulary vocabulary)))
;; ======================================================
;; UI
;; ======================================================
(defun moedict-message (string)
(message (format "[萌典] %s" string)))
(defun moedict-lookup-and-show-in-buffer (vocabulary &optional no-push-to-history)
"Hello"
(when (stringp vocabulary)
(moedict-message "查詢中...")
(if no-push-to-history
'pass
(moedict-history-push vocabulary))
(let ((rendered-result (moedict-render vocabulary)))
(with-temp-buffer-window moedict-buffer-name nil nil)
(with-selected-window (get-buffer-window moedict-buffer-name)
(moedict-mode)
(let (buffer-read-only)
(insert rendered-result))
(goto-char (point-min))))
(moedict-message "完成~")))
;;;###autoload
(defun moedict (&optional init-input)
"開啟萌典查詢界面。"
(interactive)
(moedict-ensure-dictionary-file-exist
(if (null
(helm :sources (helm-make-source "[萌典] 請輸入您欲查詢的單字:" 'helm-source-sync
`(:candidates (lambda () ,(moedict-get-candidates-list helm-pattern))
:volatile t
:candidate-number-limit ,moedict-candidates-limit
:action moedict-lookup-and-show-in-buffer
:requires-pattern t))
:input (or init-input "")
:buffer moedict-candidate-buffer-name
:prompt moedict-prompt))
(moedict-message "找不到結果,取消~"))))
(defun moedict/try-region ()
"功能同 `moedict' ,但如果目前有文字被選取,就查詢該該文字。"
(interactive)
(moedict-ensure-dictionary-file-exist
(if (region-active-p)
(moedict (buffer-substring-no-properties (region-beginning) (region-end)))
(moedict))))
(defun moedict/last-vocabulary ()
"開啟萌典查詢界面,並以目前條目為預設輸入"
(interactive)
(moedict-ensure-dictionary-file-exist
(moedict moedict--current-vocabulary)))
;; ======================================================
;; Tools for Interactive Commands
;; ======================================================
(defun moedict-point-at-underline-p (&optional point)
(if (null point) (setq point (point)))
(let ((face (get-text-property point 'face)))
(and face (listp face) (or (member '(underline t) face)
(equal '(underline t) face)))))
(defun moedict-try-to-get-vocabulary-at-point ()
(let ((pos (point))
begin end)
(if (moedict-point-at-underline-p)
;; [1] if at an underlined vocabulary.
(progn (if (null (moedict-point-at-underline-p (1- pos)))
(setq pos (1+ pos)))
(buffer-substring-no-properties (previous-property-change pos)
(next-property-change pos)))
;; If cursor is not at an underlined vocabulary,
;; guess vocabulary according `moedict-punctuations' &
;; `moedict-try-to-get-vocabulary-max-length'
(save-excursion
(search-backward-regexp moedict-punctuations nil t 1)
(if (not (bolp)) (right-char 1))
(setq begin (point))
(search-forward-regexp moedict-punctuations nil t 1)
(setq end (1- (point)))
(if (<= (- end begin) moedict-try-to-get-vocabulary-max-length)
;; [2] got guessed vocabulary according punctuation
(format "%s" (buffer-substring-no-properties begin end))
;; [3] Try to get single character at point
(moedict-try-to-get-single-char-at-point pos))))))
(defun moedict-try-to-get-single-char-at-point (&optional pos)
(if (null pos) (setq pos (point)))
(if (moedict-if-a-valid-chinese-character (char-after pos))
(char-to-string (char-after pos))
""))
(defun moedict-if-a-valid-chinese-character (char)
"Not include punctuation."
(if (characterp char)
(setq char (char-to-string char)))
(and (string-match (rx (category chinese)) char)
(not (string-match moedict-punctuations char))))
;; ======================================================
;; Commands for Keys
;; ======================================================
(defun moedict:enter ()
(interactive)
(if (region-active-p)
(moedict (buffer-substring-no-properties (region-beginning) (region-end)))
(let ((vocabulary (moedict-try-to-get-vocabulary-at-point)))
(if (and (moedict-point-at-underline-p)
(moedict-if-a-vocabulary-exactly-exist vocabulary))
(moedict-lookup-and-show-in-buffer vocabulary)
(moedict vocabulary)))))
(defun moedict:tab ()
(interactive)
(let ((pos (point)))
;; If already on underline
(if (moedict-point-at-underline-p pos)
(setq pos (next-property-change pos)))
(while (and (not (moedict-point-at-underline-p pos)) ;if current point have no underline
(next-property-change pos))
(setq pos (next-property-change pos)))
(goto-char pos)))
(defun moedict:shift+tab ()
(interactive)
(let ((pos (point)))
;; If already on underline
(if (moedict-point-at-underline-p pos)
(setq pos (previous-property-change pos)))
(while (and (not (moedict-point-at-underline-p pos)) ;if current point have no underline
(previous-property-change pos))
(setq pos (previous-property-change pos)))
(goto-char pos)))
;; ======================================================
;; Misc Commands :: 有的沒的命令
;; ======================================================
(defun moedict/exit ()
"Kill all moedict buffers."
(interactive)
(mapc (lambda (x)
(when (get-buffer x)
(bury-buffer x)
(kill-buffer x)))
(list moedict-buffer-name
moedict-history-buffer-name
moedict-help-buffer-name
moedict-candidate-buffer-name)))
(defun moedict/open-website ()
(interactive)
(if moedict--current-vocabulary
(browse-url-default-browser (format "https://www.moedict.tw/%s" moedict--current-vocabulary))
(browse-url-default-browser "https://www.moedict.tw/"))
(moedict-message "開啟網頁版中..."))
(defun moedict/help ()
(interactive)
(with-temp-buffer-window moedict-help-buffer-name nil nil)
(with-selected-window (get-buffer-window moedict-help-buffer-name)
(help-mode)
(let (buffer-read-only)
(delete-region (point-min) (point-max))
(insert (with-temp-buffer
(insert (moedict-get-help-string))
(org-table-align)
(buffer-string))
"\n")))
(switch-to-buffer-other-window moedict-help-buffer-name))
(defun moedict-get-help-string ()
(concat
(propertize "* 萌え萌えキュン的萌典說明書\n" 'face 'bold)
"以下三個命令適合設定到全域快速鍵:\n
M-x moedict 開啟查詢界面
M-x moedict/try-region 嘗試查詢被選取的文字
M-x moedict/last-vocabulary 開啟查詢界面,並以最後一次查詢為預設輸入
以下按鍵可以在萌典的 buffer 中使用:\n
| 函數名稱 | 按鍵 | 描述 |
|----------|------|------|\n"
(mapconcat
(lambda (x)
(format "| %s | %s | %s |"
(propertize (symbol-name (car x)) 'face 'font-lock-keyword-face) ;command name
(mapconcat ;key-binding
(lambda (a)
(propertize (key-description a) 'face 'font-lock-constant-face))
(where-is-internal (car x) moedict-mode-map) ", ") ; (key-binding list)
(cdr x))) ;description
'((moedict/help . "開啟目前這個說明書")
(moedict . "開啟萌典查詢界面")
(moedict/try-region . "嘗試搜尋選取的範圍")
(moedict/last-vocabulary . "開啟萌典查詢界面,並以目前條目為預設輸入")
(moedict/exit . "關掉所有萌典相關視窗跟buffer")
(moedict/history-show-list . "開啟查詢歷史清單")
(moedict/history-next . "跳到下一查詢歷史")
(moedict/history-previous . "跳到上一查詢歷史")
(moedict/history-clean . "清除查詢歷史")
(moedict:enter . "不太智慧的智慧查詢(自動猜測您想查詢的東西)")
(moedict:tab . "往下跳到連結")
(moedict:shift+tab . "往上跳到連結")
(moedict/open-website . "開啟目前條目的網頁版界面"))
"\n")))
;; ======================================================
;; History :: 查詢歷史
;; ======================================================
(defun moedict/history-show-list ()
(interactive)
(if (= (length moedict--history) 0)
(moedict-message "目前歷史紀錄是空的喔")
(if (null (helm :sources (helm-make-source "請選擇單字:" 'helm-source-sync
'(:candidates moedict--history
:volatile t
:action (lambda (x) (moedict-lookup-and-show-in-buffer x)
(kill-buffer moedict-history-buffer-name))))
:buffer moedict-history-buffer-name
:prompt moedict-prompt))
(moedict-message "取消動作!"))))
(defun moedict/history-previous ()
(interactive)
(if (= (length moedict--history) 0)
(moedict-message "目前歷史紀錄是空的喔")
(let ((previous (moedict-history-get-previous-vocabulary)))
(if previous
(progn (moedict-lookup-and-show-in-buffer previous :no-push-history)
(moedict-set-current-vocabulary-to previous))
(moedict-message "沒有更舊的歷史了!")))))
(defun moedict/history-next ()
(interactive)
(if (= (length moedict--history) 0)
(moedict-message "目前歷史紀錄是空的喔")
(let ((next (moedict-history-get-next-vocabulary)))
(if next
(progn (moedict-lookup-and-show-in-buffer next :no-push-history)
(moedict-set-current-vocabulary-to next))
(moedict-message "已經是最新的項目!")))))
(defun moedict/history-clean ()
(interactive)
(if (y-or-n-p "確定要清除歷史紀錄嗎?")
(progn (setq moedict--history (list (or moedict--current-vocabulary "")))
(moedict-message "清除啦~"))
(moedict-message "不清除~")))
(defun moedict-history-push (vocabulary)
"1. Move `moedict--current-vocabulary' to the first item in `moedict--history',
2. Remove the existed same vocabulary in `moedict--history',
3. Push the VOCABULARY into `moedict--history' as the first item"
(setq moedict--history (delete moedict--current-vocabulary moedict--history))
(push moedict--current-vocabulary moedict--history)
(setq moedict--current-vocabulary vocabulary)
(setq moedict--history (delete vocabulary moedict--history))
(push vocabulary moedict--history))
(defun moedict-history-get-previous-vocabulary ()
"Get previous vocabulary.
Return value is string or nil"
(cadr (member moedict--current-vocabulary moedict--history)))
(defun moedict-history-get-next-vocabulary ()
"Get next vocabulary in history.
Return value is string or nil"
(let* ((n (1- (- (length moedict--history)
(length (member moedict--current-vocabulary moedict--history))))))
(if (>= n 0)
(nth n moedict--history)
nil)))
(defun moedict-set-current-vocabulary-to (value)
(setq moedict--current-vocabulary value))
(provide 'moedict)
;; The ultimate answer of life, Universe, and everything is `42',
;; the ultimate answer of Taiwan is `689'.
;;; moedict.el ends here at line `689'.