-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjohnson.texi
More file actions
1822 lines (1486 loc) · 67.1 KB
/
johnson.texi
File metadata and controls
1822 lines (1486 loc) · 67.1 KB
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
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\input texinfo @c -*- texinfo -*-
@c %**start of header
@setfilename johnson.info
@settitle @samp{johnson}: A multi-format dictionary UI for Emacs
@documentencoding UTF-8
@documentlanguage en
@c %**end of header
@dircategory Emacs misc features
@direntry
* Johnson: (johnson). Multi-format dictionary UI for Emacs.
@end direntry
@finalout
@titlepage
@author Pablo Stafforini (@email{pablo@@stafforini.com})
@end titlepage
@contents
@ifnottex
@node Top
@top @samp{johnson}: A multi-format dictionary UI for Emacs
@end ifnottex
@menu
* Overview::
* User options::
* Commands::
* Functions::
* Faces::
* Roadmap::
* Indices::
@detailmenu
--- The Detailed Node Listing ---
User options
* Dictionary discovery::
* Search scope and groups::
* Cross-reference scope::
* Display ordering::
* Lookup history::
* Cache directory::
* Audio playback::
* DICT protocol::
* Images::
* Wildcard search::
* Full-text search::
* Eldoc integration::
* Scan-popup mode::
* Bookmarks::
* History buffer::
Commands
* Looking up words::
* Indexing dictionaries::
* Managing groups and scope::
* Navigating the results buffer::
* Following cross-references::
* Navigation history::
* Copying entries::
* Browsing dictionary directory::
* Dictionary list::
* Cache management::
* Toggling images::
* Searching definitions::
* Eldoc integration commands::
* Scan-popup mode commands::
* Managing bookmarks::
* Browsing history::
Functions
* Format registration::
* Completion at point::
* Database lifecycle::
* Metadata storage::
* Headword normalization::
* Entry insertion::
* Querying entries::
* Staleness detection and reset::
* DSL format detection::
* DSL metadata parsing::
* DSL index building::
* DSL entry retrieval::
* DSL entry rendering::
Faces
* Text styling faces::
* Content type faces::
* Link faces::
* Section header face::
* Color faces::
Roadmap
* Version 0.1 --- DSL: Version 01 --- DSL.
* Version 0.2 --- StarDict and compressed DSL: Version 02 --- StarDict and compressed DSL.
* Version 0.3 --- MDict and DSL abbreviations: Version 03 --- MDict and DSL abbreviations.
* Version 0.4 (current) --- BGL, DICT, and more: Version 04 (current).
* Future features::
Indices
* Function index::
* Variable index::
@end detailmenu
@end menu
@node Overview
@chapter Overview
@samp{johnson} is a multi-format dictionary UI for Emacs. It @*
provides the functionality of programs such as GoldenDict and StarDict, @*
allowing you to look up words across multiple dictionaries simultaneously @*
and view formatted definitions in a dedicated Emacs buffer. @*
The package is implemented entirely in Emacs Lisp with no external @*
dependencies, relying on Emacs 30.1's built-in sqlite support for @*
efficient headword indexing. Dictionary files are parsed natively, @*
without calling external tools. @*
In version 0.4, the package supports the DSL (ABBYY Lingvo), @*
StarDict, MDict, and BGL (Babylon) formats, including @*
dictzip-compressed files (@samp{.dsl.dz} and @samp{.dict.dz}) and @*
encrypted MDict files. It also provides a DICT protocol client @*
(RFC 2229) for querying remote dictionary servers. @*
The architecture is modular. The core module (@samp{johnson.el}) provides @*
the lookup engine, results buffer, navigation, history, and a @*
plist-based format registry (@ref{Format registration}). @*
The sqlite index management is delegated to @samp{johnson-db.el}, which @*
maintains one database per dictionary file in a configurable cache @*
directory (@ref{Cache directory}). Shared infrastructure @*
for dictzip decompression lives in @samp{johnson-dictzip.el}. Each @*
dictionary format is handled by a separate backend @*
module---@samp{johnson-dsl.el}, @samp{johnson-stardict.el}, @*
@samp{johnson-mdict.el}, @samp{johnson-bgl.el}, and @*
@samp{johnson-dict.el}---that registers itself with the core via the @*
format registry. @*
A typical workflow consists of three steps. First, configure @*
@code{johnson-dictionary-directories} to point at your dictionary files @*
(@ref{Dictionary discovery}). Second, run @samp{M-x johnson-index} to scan @*
and index all discovered dictionaries (@ref{Indexing dictionaries}). @*
Third, use @samp{M-x johnson-lookup} to look up words with dynamic @*
completion (@ref{Looking up words}). @*
Here is a sample configuration using @samp{elpaca} and @samp{use-package}: @*
@lisp
(use-package johnson
:ensure (:host github :repo "benthamite/johnson")
:custom
(johnson-dictionary-directories '("~/dictionaries/"))
(johnson-default-search-scope 'all)
(johnson-dictionary-priorities '(("Oxford English-Spanish" . -10)
("Collins English-Spanish" . -5)))
(johnson-history-max 200)
(johnson-history-persist t)
(johnson-display-images t)
:bind
("C-c d" . johnson-lookup))
@end lisp
Results are displayed in a @samp{*johnson*} buffer using @code{johnson-mode}, a @*
read-only major mode derived from @code{special-mode}. Each dictionary that @*
contains a match for the queried word gets its own collapsible section, @*
displayed in priority order (@ref{Display ordering}). The buffer @*
supports Info-like navigation: press @samp{n} and @samp{p} to move between @*
sections, @samp{TAB} to collapse or expand a section, @samp{RET} to follow @*
cross-references, @samp{l} and @samp{r} to navigate back and forward through @*
lookup history, @samp{I} to toggle inline images, @samp{S} to search within @*
definitions, @samp{b} to bookmark an entry, @samp{M} to remove a bookmark, and @*
@samp{H} to browse the full lookup history. @*
Dictionaries are organized into groups by language pair @*
(e.g., ``English → Spanish''). Groups are auto-detected from dictionary @*
metadata but can be customized via @code{johnson-dictionary-groups} @*
(@ref{Search scope and groups}). @*
The package also provides a dictionary list buffer via @samp{M-x
johnson-list-dictionaries}, showing all discovered dictionaries with @*
their indexing status and entry counts @*
(@ref{Dictionary list}). @*
@node User options
@chapter User options
@menu
* Dictionary discovery::
* Search scope and groups::
* Cross-reference scope::
* Display ordering::
* Lookup history::
* Cache directory::
* Audio playback::
* DICT protocol::
* Images::
* Wildcard search::
* Full-text search::
* Eldoc integration::
* Scan-popup mode::
* Bookmarks::
* History buffer::
@end menu
@node Dictionary discovery
@section Dictionary discovery
@vindex johnson-dictionary-directories
The user option @code{johnson-dictionary-directories} specifies the @*
directories to scan recursively for dictionary files. When the package @*
discovers dictionaries, it walks each directory in this list and @*
identifies files using the registered format backends' detection @*
functions. Files matching the @samp{_abrv} naming convention (abbreviation @*
tables) are automatically skipped. @*
The default value is @samp{("~/.local/share/dictionaries/")}. You should @*
change this to point at wherever your dictionary files are stored. The @*
value is a list of directory paths (type: @code{(repeat directory)}), so you @*
can specify multiple directories if your dictionaries are spread across @*
different locations: @*
@lisp
(setopt johnson-dictionary-directories
'("~/dictionaries/"
"/mnt/data/dictionaries/"))
@end lisp
@node Search scope and groups
@section Search scope and groups
@vindex johnson-default-search-scope
@vindex johnson-dictionary-groups
The user option @code{johnson-default-search-scope} controls the default @*
search scope for lookups. When set to @code{all} (the default), lookups @*
search across all discovered dictionaries. When set to @code{group}, lookups @*
search only within the currently active dictionary group. @*
@lisp
(setopt johnson-default-search-scope 'group)
@end lisp
You can switch between these modes interactively via @*
@code{johnson-select-group} (@ref{Managing groups and scope}), which @*
also lets you choose the active group. @*
The user option @code{johnson-dictionary-groups} lets you define custom @*
dictionary groups. When this option is @code{nil} (the default), groups are @*
auto-detected from dictionary metadata: each dictionary is assigned to a @*
group based on its source and target language pair, such as ``English → @*
Spanish''. @*
Set this option when you want to create custom groupings that differ from @*
the language-pair default. For instance, you could group specialized @*
medical dictionaries across different language pairs into a single @*
``Medical'' group: @*
@lisp
(setopt johnson-dictionary-groups
'(("Medical" . ("Dorland's Medical"
"Stedman's Medical"
"Medical Abbreviations"))
("Legal" . ("Black's Law Dictionary"
"Legal Terms EN-ES"))))
@end lisp
When @code{johnson-dictionary-groups} is non-nil, @emph{only} the groups listed @*
here are available; the auto-detected language-pair groups are not used. @*
@node Cross-reference scope
@section Cross-reference scope
@vindex johnson-ref-scope
The user option @code{johnson-ref-scope} controls the scope when following @*
cross-reference links (via @code{johnson-follow-ref}). When set to @code{all} @*
(the default), following a cross-reference performs a lookup across all @*
dictionaries, just like @code{johnson-lookup}. When set to @code{same}, the @*
lookup is restricted to the dictionary containing the link. @*
@lisp
(setopt johnson-ref-scope 'same)
@end lisp
If the target word is not found in the originating dictionary, the @*
command falls back to a full lookup across all dictionaries. @*
@node Display ordering
@section Display ordering
@vindex johnson-dictionary-priorities
The user option @code{johnson-dictionary-priorities} controls the display @*
order of dictionary sections in the results buffer. It is an alist @*
mapping dictionary names to integer priorities. Lower numbers display @*
first. The default priority is 0, so dictionaries without an explicit @*
priority entry are all treated equally and appear in discovery order. @*
For example, to make the ``Oxford English-Spanish'' dictionary always @*
appear before ``Collins English-Spanish'', assign it a lower priority: @*
@lisp
(setopt johnson-dictionary-priorities
'(("Oxford English-Spanish" . -10)
("Collins English-Spanish" . -5)))
@end lisp
@subheading Importing order from GoldenDict
@findex johnson-import-goldendict-order
If you have a GoldenDict (or GoldenDict-ng) installation with an @*
established dictionary order, the command @*
@code{johnson-import-goldendict-order} can import it. The command @*
parses the @code{<dictionaryOrder>} section of the GoldenDict @*
configuration file (usually @file{~/.goldendict/config}) and matches @*
each dictionary name against the dictionaries discovered by Johnson. @*
Matching is done first by exact name, then by a normalized comparison @*
that strips language tags (e.g., @samp{[en-en]}) and ignores case. The @*
result is presented in the @samp{*johnson-reorder*} buffer, where you @*
can review and adjust the order before saving. Dictionaries that could @*
not be matched to any GoldenDict entry are appended at the end and @*
highlighted with the @code{johnson-reorder-unmatched-face} face. @*
@node Lookup history
@section Lookup history
@vindex johnson-history-max
@vindex johnson-history
The user option @code{johnson-history-max} sets the maximum number of entries @*
retained in the lookup history. The default value is @code{100}. @*
@lisp
(setopt johnson-history-max 200)
@end lisp
The variable @code{johnson-history} stores the list of previously looked-up @*
words and serves as the history list for @code{completing-read} in @*
@code{johnson-lookup} (@ref{Looking up words}). You can access @*
previous lookups via @samp{M-p} and @samp{M-n} in the completion prompt. If @*
@code{savehist-mode} is active, this history persists across Emacs sessions. @*
When the history exceeds @code{johnson-history-max} entries, the oldest @*
entries are discarded. @*
@node Cache directory
@section Cache directory
@vindex johnson-cache-directory
The user option @code{johnson-cache-directory} specifies the directory where @*
sqlite index files are stored. Each dictionary gets its own sqlite @*
database file in this directory, named by the MD5 hash of the dictionary @*
file's absolute path. This hashing scheme prevents collisions when @*
multiple dictionaries share similar names or reside in different @*
directories. @*
The default value is @samp{~/.cache/johnson/}. You may want to change this @*
if you prefer to store caches on a faster drive, in a location that is @*
excluded from backups, or alongside other application caches: @*
@lisp
(setopt johnson-cache-directory "~/.local/cache/johnson/")
@end lisp
The directory is created automatically if it does not exist when a @*
database is first opened via @code{johnson-db-open} @*
(@ref{Database lifecycle}). @*
@node Audio playback
@section Audio playback
@vindex johnson-audio-player
@vindex johnson-audio-external-players
@findex johnson-play-sound
@findex johnson-play-audio-at-point
@findex johnson-insert-audio-button
Some dictionary entries contain embedded audio files, such as @*
pronunciation recordings. When they do, the results buffer shows a @*
play button (@samp{▶}) that plays the audio on click. The command @*
@code{johnson-play-audio-at-point} does the same for the button at @*
point and can be called interactively. @*
The user option @code{johnson-audio-player} controls which program is @*
used for playback. The default is @code{auto}, which tries Emacs's @*
built-in @code{play-sound-file} first; if that fails (e.g., because @*
the Emacs binary lacks sound support), it falls back to the first @*
available external player listed in @*
@code{johnson-audio-external-players}. On macOS, @samp{afplay} is @*
always available, so audio playback should work out of the box with no @*
configuration. @*
If you want to use a specific external player, set @*
@code{johnson-audio-player} to a string naming the command: @*
@lisp
(setopt johnson-audio-player "mpv")
@end lisp
The user option @code{johnson-audio-external-players} is the list of @*
external commands that @code{auto} mode tries, in order. The default @*
list is @samp{("afplay" "mpv" "paplay" "aplay")}. You can customize @*
this list if your preferred player is not included or if you want to @*
change the priority order. @*
@subheading Companion zip archives
@findex johnson-clear-resource-cache
Many DSL dictionaries bundle audio in a companion @*
@file{.dsl.files.zip} archive rather than storing individual files on @*
disk (e.g., @file{MyDict.dsl} @expansion{} @file{MyDict.dsl.files.zip}). @*
When a play button is clicked and the referenced audio file does not @*
exist on disk, @code{johnson} automatically checks for a companion zip @*
archive and extracts the file on demand. Extracted files are cached @*
under the @file{resources/} subdirectory of @code{johnson-cache-directory} @*
so subsequent plays are instant. @*
This feature requires the @command{unzip} command-line utility, which @*
is available by default on macOS and most Linux distributions. @*
The command @code{johnson-clear-resource-cache} deletes all extracted @*
resource files. This is useful if you want to reclaim disk space or @*
force re-extraction. @*
@node DICT protocol
@section DICT protocol
@vindex johnson-dict-enabled
@vindex johnson-dict-servers
The user option @code{johnson-dict-enabled} is a master switch that @*
controls whether the DICT protocol client is active. When @code{nil} @*
(the default), no DICT servers are contacted and no remote @*
dictionaries appear in the dictionary list. Set it to @code{t} to @*
enable the DICT protocol backend: @*
@lisp
(setopt johnson-dict-enabled t)
@end lisp
The user option @code{johnson-dict-servers} specifies the list of DICT @*
servers to query. Each element is a @samp{(HOST . PORT)} pair. The @*
default value is @samp{(("dict.org" . 2628))}, which points to the @*
public dict.org server on the standard DICT port. @*
You can add additional servers: @*
@lisp
(setopt johnson-dict-servers
'(("dict.org" . 2628)
("my-dict-server.local" . 2628)))
@end lisp
Dictionaries discovered from DICT servers appear under the ``DICT @*
Servers'' group in the group selector. The DICT backend communicates @*
using the RFC 2229 protocol over a TCP connection. @*
@node Images
@section Images
@vindex johnson-display-images
The user option @code{johnson-display-images} controls whether inline @*
images are rendered in dictionary entries. When @code{t} (the default), @*
images embedded in entries are displayed inline. When @code{nil}, images @*
are suppressed. @*
Image support works across all format backends that include images: @*
DSL @samp{[s]} tags, and HTML @samp{<img>} tags in MDict, StarDict, and @*
BGL entries. @*
@lisp
(setopt johnson-display-images nil) ; suppress images
@end lisp
You can toggle image display interactively via @*
@code{johnson-toggle-images} (@ref{Toggling images}). @*
@node Wildcard search
@section Wildcard search
@vindex johnson-wildcard-max-results
The user option @code{johnson-wildcard-max-results} limits the number @*
of headwords returned by a wildcard search. The default value is @*
@code{200}. Wildcard searches can potentially match a large number of @*
headwords; this limit prevents excessive memory use and keeps the @*
completion interface responsive. @*
@lisp
(setopt johnson-wildcard-max-results 500)
@end lisp
Wildcard patterns use @samp{?} to match a single character and @samp{*} @*
to match zero or more characters. Wildcards are auto-detected in @*
@code{johnson-lookup}: if the query contains @samp{?} or @samp{*} @*
characters, matching headwords are collected and presented for @*
selection rather than performing a direct lookup. @*
@node Full-text search
@section Full-text search
@vindex johnson-fts-enabled
The user option @code{johnson-fts-enabled} controls whether full-text @*
search data is built during indexing. The default value is @code{nil}. @*
When set to @code{t} before indexing, the indexer stores definition text @*
in a way that enables searching within dictionary entries, not just @*
headwords. @*
@lisp
(setopt johnson-fts-enabled t)
@end lisp
You must re-index your dictionaries after enabling this option for @*
the full-text data to be built. Once enabled, the @*
@code{johnson-search} command (@ref{Searching definitions}) @*
becomes available for searching within definitions. @*
@node Eldoc integration
@section Eldoc integration
@vindex johnson-eldoc-max-length
The user option @code{johnson-eldoc-max-length} sets the maximum @*
number of characters shown in the eldoc display for a dictionary @*
definition. The default value is @code{80}. Longer definitions are @*
truncated with an ellipsis. @*
@lisp
(setopt johnson-eldoc-max-length 120)
@end lisp
To enable eldoc integration, activate @code{johnson-eldoc-mode} in @*
the desired buffer (@ref{Eldoc integration commands}). @*
@node Scan-popup mode
@section Scan-popup mode
@vindex johnson-scan-trigger
@vindex johnson-scan-idle-delay
@vindex johnson-scan-popup-duration
The user option @code{johnson-scan-trigger} controls what action @*
triggers popup dictionary definitions. The possible values are: @*
@itemize
@item
@code{selection} (the default) --- show a popup when text is selected. @*
@item
@code{idle} --- show a popup for the word at point after an idle @*
delay. @*
@item
@code{both} --- show a popup on both selection and idle. @*
@end itemize
@lisp
(setopt johnson-scan-trigger 'both)
@end lisp
The user option @code{johnson-scan-idle-delay} sets the number of @*
seconds of idle time before a popup is shown when the trigger is @*
@code{idle} or @code{both}. The default value is @code{1.0}. @*
@lisp
(setopt johnson-scan-idle-delay 0.5)
@end lisp
The user option @code{johnson-scan-popup-duration} sets the number @*
of seconds a popup definition remains visible. The default value @*
is @code{5.0}. @*
@lisp
(setopt johnson-scan-popup-duration 10.0)
@end lisp
The popup display uses posframe if available, otherwise falls back @*
to tooltip, and finally to the echo area. @*
To enable scan-popup mode, activate @code{johnson-scan-mode} @*
(@ref{Scan-popup mode commands}). @*
@node Bookmarks
@section Bookmarks
@vindex johnson-bookmarks-file
The user option @code{johnson-bookmarks-file} specifies the file where @*
bookmarked dictionary entries are persisted. The default value is @*
@samp{~/.cache/johnson/bookmarks.el}. @*
@lisp
(setopt johnson-bookmarks-file "~/.local/share/johnson/bookmarks.el")
@end lisp
Bookmarks allow you to save dictionary entries for later reference. @*
Each bookmark records the headword, dictionary name, and date. @*
@xref{Managing bookmarks}, for the commands that add, remove, and @*
list bookmarks. @*
@node History buffer
@section History buffer
@vindex johnson-history-file
@vindex johnson-history-persist
The user option @code{johnson-history-file} specifies the file where @*
lookup history is persisted across Emacs sessions. The default @*
value is @samp{~/.cache/johnson/history.el}. @*
@lisp
(setopt johnson-history-file "~/.local/share/johnson/history.el")
@end lisp
The user option @code{johnson-history-persist} controls whether lookup @*
history is saved to disk. When @code{t} (the default), history entries @*
are written to @code{johnson-history-file} and restored on startup. @*
When @code{nil}, history exists only for the current Emacs session. @*
@lisp
(setopt johnson-history-persist nil) ; session-only history
@end lisp
@xref{Browsing history}, for commands that display and manage the @*
history buffer. @*
@node Commands
@chapter Commands
@menu
* Transient menu::
* Looking up words::
* Indexing dictionaries::
* Managing groups and scope::
* Navigating the results buffer::
* Following cross-references::
* Navigation history::
* Copying entries::
* Browsing dictionary directory::
* Dictionary list::
* Cache management::
* Toggling images::
* Searching definitions::
* Eldoc integration commands::
* Scan-popup mode commands::
* Managing bookmarks::
* Browsing history::
@end menu
@node Transient menu
@section Transient menu
@findex johnson-menu
The command @code{johnson-menu} opens a transient dispatch menu that @*
provides quick access to all major johnson commands and toggles. It @*
is bound to @samp{m} in @code{johnson-mode}. @*
The menu is organized into the following groups: @*
@subheading Lookup
@table @samp
@item l
Look up a word (@code{johnson-lookup}). @*
@item s
Full-text search (@code{johnson-search}). @*
@item G
Select group (@code{johnson-select-group}). @*
@end table
@subheading Navigate
@table @samp
@item n
Next section (@code{johnson-next-section}). @*
@item p
Previous section (@code{johnson-prev-section}). @*
@item j
Jump to section (@code{johnson-jump-to-section}). @*
@item o
Ace link (@code{johnson-ace-link}). @*
@item RET
Follow reference (@code{johnson-follow-ref}). @*
@end table
@subheading History
@table @samp
@item <
Back (@code{johnson-history-back}). @*
@item >
Forward (@code{johnson-history-forward}). @*
@item H
History list (@code{johnson-history-list}). @*
@item C
Clear history (@code{johnson-history-clear}). @*
@end table
@subheading Bookmarks
@table @samp
@item b
Add bookmark (@code{johnson-bookmark-add}). @*
@item M
Remove bookmark (@code{johnson-bookmark-remove}). @*
@item B
Bookmark list (@code{johnson-bookmark-list}). @*
@end table
@subheading Actions
@table @samp
@item g
Refresh (@code{johnson-refresh}). @*
@item w
Copy entry (@code{johnson-copy-entry}). @*
@item W
Copy dictionary name (@code{johnson-copy-dictionary-name}). @*
@item a
Play audio (@code{johnson-play-audio-at-point}). @*
@end table
@subheading Sections
@table @samp
@item TAB
Toggle section (@code{johnson-toggle-section}). @*
@item <backtab>
Toggle all sections (@code{johnson-toggle-all-sections}). @*
@item e
Expand all (@code{johnson-expand-all}). @*
@item c
Collapse all (@code{johnson-collapse-all}). @*
@end table
@subheading Manage
@table @samp
@item d
List dictionaries (@code{johnson-list-dictionaries}). @*
@item r
Reorder dictionaries (@code{johnson-reorder-dictionaries}). @*
@item I
Import GoldenDict order (@code{johnson-import-goldendict-order}). @*
@item i
Index/re-index (@code{johnson-index}). @*
@item k
Stop indexing (@code{johnson-stop-indexing}). @*
@item X
Clear index (@code{johnson-clear-index}). @*
@item R
Clear resource cache (@code{johnson-clear-resource-cache}). @*
@item Q
Close caches (@code{johnson-close-caches}). @*
@end table
@subheading Options
Option keys use the @samp{-} prefix. Boolean options toggle between @*
on and off; cycle options rotate through a list of choices. @*
@table @samp
@item -i
Toggle images (@code{johnson-display-images}). When toggled, the @*
results buffer is refreshed immediately to reflect the change. @*
@item -f
Toggle full-text search (@code{johnson-fts-enabled}). @*
@item -e
Toggle eldoc mode (@code{johnson-eldoc-mode}). @*
@item -s
Toggle scan mode (@code{johnson-scan-mode}). @*
@item -p
Toggle persist history (@code{johnson-history-persist}). @*
@item -c
Cycle search scope (@code{johnson-default-search-scope}). Rotates @*
between @code{all} and @code{group}. @*
@item -r
Cycle ref scope (@code{johnson-ref-scope}). Rotates between @*
@code{all} and @code{same}. @*
@end table
The transient menu requires the @code{transient} package, which is @*
bundled with Emacs 29 and later. The menu is defined in @*
@file{johnson-transient.el}, which is loaded automatically.
@node Looking up words
@section Looking up words
@findex johnson-lookup
@findex johnson-new-search
@findex johnson-refresh
The command @code{johnson-lookup} is the primary entry point for dictionary @*
lookups. When called interactively, it prompts for a word with @*
@code{completing-read}, defaulting to the word at point. The completion @*
table is backed by sqlite prefix queries across all in-scope @*
dictionaries, providing dynamic suggestions as you type. Each candidate @*
is annotated with the number of dictionaries containing it when there @*
are multiple matches (e.g., @samp{(3 dicts)}). @*
If the dictionaries have not been indexed yet, @code{johnson-lookup} prompts @*
you to index them first. When indexing is already in progress, the @*
lookup is deferred and will resume automatically once indexing completes. @*
If the query contains wildcard characters (@samp{?} or @samp{*}), @*
@code{johnson-lookup} performs a wildcard search instead of a direct @*
lookup. Matching headwords are collected across all in-scope @*
dictionaries (up to @code{johnson-wildcard-max-results}; see @*
@ref{Wildcard search}) and presented for selection via @*
@code{completing-read}. The selected headword is then looked up @*
normally. @*
After a word is selected, the command queries all dictionaries for exact @*
matches (case-insensitive, accent-insensitive via @*
@code{johnson-db-normalize}; see @ref{Headword normalization}) @*
and displays the results in the @samp{*johnson*} buffer. The word is pushed @*
onto the lookup history (@ref{Lookup history}) and the @*
navigation history (@ref{Navigation history}). @*
The optional WORD argument allows calling @code{johnson-lookup} from Lisp @*
code without prompting. @*
The command @code{johnson-new-search} re-invokes @code{johnson-lookup} without @*
arguments, prompting for a new word. It is bound to @samp{s} in @*
@code{johnson-mode} for quick access when you want to search for a different @*
word while viewing results. @*
The command @code{johnson-refresh} re-displays the current word by @*
re-querying all dictionaries and rebuilding the results buffer. This is @*
useful if you have re-indexed a dictionary and want to see updated @*
results. It is bound to @samp{g} in @code{johnson-mode}. Unlike a new search, @*
refreshing does not push a new entry onto the navigation history. @*
@node Indexing dictionaries
@section Indexing dictionaries
@findex johnson-index
@findex johnson-stop-indexing
The command @code{johnson-index} scans all configured dictionary directories, @*
discovers dictionary files, and indexes (or re-indexes) those whose @*
index is stale or missing. Staleness is determined by comparing the @*
file modification time stored in the database against the actual file's @*
modification time (@ref{Staleness detection and reset}). @*
Progress is displayed in a dedicated @samp{*johnson-indexing*} buffer, @*
showing one line per dictionary with its name, progress counter, and @*
result (entry count or error message). @*
When called interactively, indexing runs asynchronously using @*
timer-based cooperative multitasking, so Emacs remains responsive during @*
the process. In batch or noninteractive mode, indexing runs @*
synchronously. If indexing is already in progress, the command signals @*
an error rather than starting a second concurrent run. @*
The optional CALLBACK argument accepts a function to be called with no @*
arguments once indexing completes. This is used internally to chain @*
lookup after indexing, but you can also use it in your own scripts. @*
Dictionaries that are already up to date are skipped with an ``up to @*
date'' message rather than being re-indexed. @*
The command @code{johnson-stop-indexing} cancels the current asynchronous @*
indexing run. It cancels the timer, clears the indexing queue, and @*
reports how many dictionaries were processed before stopping. This is @*
useful if indexing is taking too long or if you want to abort and fix a @*
problem before continuing. @*
@node Managing groups and scope
@section Managing groups and scope
@findex johnson-select-group
The command @code{johnson-select-group} lets you choose the active dictionary @*
group by source and target language. It prompts you in two steps: @*
first for the source language, then for the target language. Each @*
step offers an @samp{<all>} option, enabling flexible filtering. For @*
example, @samp{<all> @arrow{} French} selects all dictionaries with @*
French as the target language regardless of source, while @*
@samp{Spanish @arrow{} <all>} selects all dictionaries with Spanish as @*
the source language. It is bound to @samp{G} in @code{johnson-mode}. @*
Selecting @samp{<all>} for both source and target sets the search scope @*
to @code{all}, meaning lookups search across every discovered dictionary. @*
Any other combination sets the scope to @code{group}, restricting lookups @*
to matching dictionaries. If called from the results buffer, the @*
current lookup is automatically refreshed to reflect the new scope. @*
@node Navigating the results buffer
@section Navigating the results buffer
@findex johnson-next-section
@findex johnson-prev-section
@findex johnson-prev-section-header
@findex johnson-toggle-section
The command @code{johnson-next-section} moves point to the next dictionary @*
section header in the results buffer. If point is already on a header, @*
it moves past it to find the next one. When there are no more sections @*
below, it displays ``No more sections''. It is bound to @samp{n} in @*
@code{johnson-mode}. @*
The command @code{johnson-prev-section} moves point to the previous @*
dictionary section header. When there is no previous section, it @*
displays ``No previous section''. It is bound to @samp{p} in @code{johnson-mode}. @*
The command @code{johnson-prev-section-header} is an alias for @*
@code{johnson-prev-section}, bound to @samp{S-TAB} (@samp{<backtab>}) in @*
@code{johnson-mode}. @*
@findex johnson-jump-to-section
The command @code{johnson-jump-to-section} prompts for a dictionary @*
section using completion and jumps to it. This is useful when the @*
results buffer contains many dictionaries and the desired section is @*
not visible. It is bound to @samp{j} in @code{johnson-mode}. @*
The command @code{johnson-toggle-section} collapses or expands the dictionary @*
section at point. Collapsed sections hide their content using an @*
overlay with the @code{invisible} property and display a @samp{[+]} indicator. @*
Expanding the section removes the overlay, revealing the full entry @*
content. The command works whether point is on the section header or @*
within the section body. It is bound to @samp{TAB} in @code{johnson-mode}. @*
@node Following cross-references
@section Following cross-references
@findex johnson-follow-ref
The command @code{johnson-follow-ref} activates the cross-reference button at @*
point. In DSL dictionaries, cross-references are created by @samp{[ref]} tags @*
or @samp{<<...>>} markers and rendered as clickable text buttons. By @*
default, activating a cross-reference triggers a new @code{johnson-lookup} @*
for the referenced word across all loaded dictionaries. When @*
@code{johnson-ref-scope} is set to @code{same}, the lookup is restricted to @*
the dictionary containing the link; if no match is found there, the @*
command falls back to a full lookup @*
(@ref{Cross-reference scope}). @*
If there is no button at point, the command displays ``No cross-reference @*
at point''. It is bound to @samp{RET} in @code{johnson-mode}. @*
@node Navigation history
@section Navigation history
@findex johnson-history-back
@findex johnson-history-forward
The results buffer maintains a buffer-local navigation history, similar @*
to the history in Info mode or eww. Each lookup pushes the word onto @*
the history list. Following a cross-reference also creates a history @*
entry. @*
The command @code{johnson-history-back} navigates to the previous word in the @*
history, re-executing the lookup for that word. When at the beginning @*
of the history, it displays ``Beginning of history''. It is bound to @samp{l} @*
in @code{johnson-mode}. @*
The command @code{johnson-history-forward} navigates to the next word in the @*
history (after going back). When at the end of the history, it displays @*
``End of history''. It is bound to @samp{r} in @code{johnson-mode}. @*
Navigation history is separate from the lookup history stored in @*
@code{johnson-history} (@ref{Lookup history}). The lookup history @*
is a global list used for @code{completing-read} recall (@samp{M-p=/=M-n}), while @*
the navigation history is buffer-local to the @samp{*johnson*} buffer and @*
tracks the sequence of words viewed in that buffer. @*
When navigating back or forward, the lookup is performed with history @*
push suppressed (via the internal @code{johnson--navigating-history} flag), @*
preventing duplicate entries from cluttering the history. @*
@node Copying entries
@section Copying entries
@findex johnson-copy-entry
The command @code{johnson-copy-entry} copies the current dictionary section's @*
entry as plain text to the kill ring. All text properties, faces, and @*
markup are stripped; only the raw text content is copied. @*
The ``current section'' is determined by point position: whichever @*
dictionary section the cursor is inside. Section boundaries are @*
identified by the section content overlays. If point is not within any @*
section (e.g., between sections or on a header line), the command @*
displays ``No section at point''. @*
It is bound to @samp{w} in @code{johnson-mode}. @*
@findex johnson-copy-dictionary-name
The command @code{johnson-copy-dictionary-name} copies the dictionary name of @*
the section at point to the kill ring. This is useful for obtaining the exact @*
string needed by @code{johnson-dictionary-priorities}. @*
The command first checks for a @code{johnson-section-header} text property @*
(present on header lines), then falls back to the @code{johnson-section} @*
overlay property (present throughout the section body). If neither is @*
found, it displays ``No dictionary section at point''. @*
It is bound to @samp{W} in @code{johnson-mode}. @*
@node Browsing dictionary directory
@section Browsing dictionary directory
@findex johnson-browse-dictionary
The command @code{johnson-browse-dictionary} opens Dired on the directory @*
containing the dictionary file for the section at point. This is useful @*
for inspecting dictionary resources, ancillary files, or the directory @*
structure on disk. @*
It is bound to @samp{D} in the transient menu (@code{johnson-menu}). @*