johnson manual
Table of contents
User options
Dictionary discovery
The user option 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 _abrv naming convention (abbreviation
tables) are automatically skipped.
The default value is ("~/.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: (repeat directory)), so you
can specify multiple directories if your dictionaries are spread across
different locations:
(setopt johnson-dictionary-directories
'("~/dictionaries/"
"/mnt/data/dictionaries/"))
Search scope and groups
The user option johnson-default-search-scope controls the default
search scope for lookups. When set to all (the default), lookups
search across all discovered dictionaries. When set to group, lookups
search only within the currently active dictionary group.
(setopt johnson-default-search-scope 'group)
You can switch between these modes interactively via
johnson-select-group (Managing groups and scope), or
by setting the source and target language from the transient menu
(Transient menu).
The user option johnson-dictionary-groups lets you define custom
dictionary groups. When this option is 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:
(setopt johnson-dictionary-groups
'(("Medical" . ("Dorland's Medical"
"Stedman's Medical"
"Medical Abbreviations"))
("Legal" . ("Black's Law Dictionary"
"Legal Terms EN-ES"))))
When johnson-dictionary-groups is non-nil, only the groups listed
here are available; the auto-detected language-pair groups are not used.
Cross-reference scope
The user option johnson-ref-scope controls the scope when following
cross-reference links (via johnson-follow-ref). When set to all
(the default), following a cross-reference performs a lookup across all
dictionaries, just like johnson-lookup. When set to same, the
lookup is restricted to the dictionary containing the link.
(setopt johnson-ref-scope 'same)
If the target word is not found in the originating dictionary, the command falls back to a full lookup across all dictionaries.
Display ordering
The user option 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:
(setopt johnson-dictionary-priorities
'(("Oxford English-Spanish" . -10)
("Collins English-Spanish" . -5)))
Lookup history
The user option johnson-history-max sets the maximum number of entries
retained in the lookup history. The default value is 100.
(setopt johnson-history-max 200)
The variable johnson-history stores the list of previously looked-up
words and serves as the history list for completing-read in
johnson-lookup (Looking up words). You can access
previous lookups via M-p and M-n in the completion prompt. If
savehist-mode is active, this history persists across Emacs sessions.
When the history exceeds johnson-history-max entries, the oldest
entries are discarded.
Cache directory
The user option 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 ~/.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:
(setopt johnson-cache-directory "~/.local/cache/johnson/")
The directory is created automatically if it does not exist when a
database is first opened via johnson-db-open
(Database lifecycle).
Audio playback
Some dictionary entries contain embedded audio files, such as
pronunciation recordings. When they do, the results buffer shows a
play button (▶) that plays the audio on click. The command
johnson-play-audio-at-point does the same for the button at
point and can be called interactively.
The user option johnson-audio-player controls which program is
used for playback. The default is auto, which tries Emacs’s
built-in 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
johnson-audio-external-players. On macOS, 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
johnson-audio-player to a string naming the command:
(setopt johnson-audio-player "mpv")
The user option johnson-audio-external-players is the list of
external commands that auto mode tries, in order. The default
list is ("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.
Companion zip archives
Many DSL dictionaries bundle audio in a companion
.dsl.files.zip archive rather than storing individual files on
disk (e.g., MyDict.dsl -> MyDict.dsl.files.zip).
When a play button is clicked and the referenced audio file does not
exist on disk, johnson automatically checks for a companion zip
archive and extracts the file on demand. Extracted files are cached
under the resources/ subdirectory of johnson-cache-directory
so subsequent plays are instant.
This feature requires the unzip command-line utility, which
is available by default on macOS and most Linux distributions.
The command johnson-clear-resource-cache deletes all extracted
resource files. This is useful if you want to reclaim disk space or
force re-extraction.
DICT protocol
The user option johnson-dict-enabled is a master switch that
controls whether the DICT protocol client is active. When nil
(the default), no DICT servers are contacted and no remote
dictionaries appear in the dictionary list. Set it to t to
enable the DICT protocol backend:
(setopt johnson-dict-enabled t)
The user option johnson-dict-servers specifies the list of DICT
servers to query. Each element is a (HOST . PORT) pair. The
default value is (("dict.org" . 2628)), which points to the
public dict.org server on the standard DICT port.
You can add additional servers:
(setopt johnson-dict-servers
'(("dict.org" . 2628)
("my-dict-server.local" . 2628)))
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.
EPWING
The johnson-epwing backend handles EPWING (JIS X 4081) and EB
format electronic dictionaries. These are common Japanese dictionary
formats originally distributed on CD-ROM, characterized by a
directory-based structure with a CATALOGS (EPWING) or CATALOG
(EB) file at the top level and HONMON data files in subbook
directories.
EPWING dictionaries are discovered automatically: during discovery,
johnson scans dictionary directories for CATALOGS=/=CATALOG files,
parses them to enumerate subbooks, and creates one dictionary entry
per subbook. No special configuration is required beyond adding the
EPWING book directory to johnson-dictionary-directories.
Each EPWING book may contain multiple subbooks (dictionaries). All subbooks are discovered and indexed individually. Dictionaries appear under the “EPWING” group in the group selector.
The backend supports:
- Both EPWING (
CATALOGS) and EB (CATALOG) catalog formats. - JIS X 0208 (EUC-JP) and ISO 8859-1 character encodings.
- B-tree search index traversal for headword extraction.
- Text formatting: bold, italic, emphasis, references, sub/superscript, indentation, and newlines.
- EBZIP decompression for compressed EPWING books (
.ebzfiles). - Fullwidth-to-ASCII normalization for readable output.
- Cross-reference buttons that navigate via
johnson-lookup.
Gaiji (custom character bitmaps) are not yet rendered; they may appear as gaps or placeholder characters in the text.
Images
The user option johnson-display-images controls whether inline
images are rendered in dictionary entries. When t (the default),
images embedded in entries are displayed inline. When nil, images
are suppressed.
Image support works across all format backends that include images:
DSL [s] tags, and HTML <img> tags in MDict, StarDict, and
BGL entries.
(setopt johnson-display-images nil) ; suppress images
You can toggle image display interactively via
johnson-toggle-images (Toggling images).
Wildcard search
The user option johnson-wildcard-max-results limits the number
of headwords returned by a wildcard search. The default value is
200. Wildcard searches can potentially match a large number of
headwords; this limit prevents excessive memory use and keeps the
completion interface responsive.
(setopt johnson-wildcard-max-results 500)
Wildcard patterns use ? to match a single character and *
to match zero or more characters. Wildcards are auto-detected in
johnson-lookup: if the query contains ? or *
characters, matching headwords are collected and presented for
selection rather than performing a direct lookup.
Full-text search
The user option johnson-fts-enabled controls whether full-text
search data is built during indexing. The default value is nil.
When set to t before indexing, the indexer stores definition text
in a way that enables searching within dictionary entries, not just
headwords.
(setopt johnson-fts-enabled t)
You must re-index your dictionaries after enabling this option for
the full-text data to be built. Once enabled, the
johnson-search command (Searching definitions)
becomes available for searching within definitions.
Eldoc integration
The user option johnson-eldoc-max-length sets the maximum
number of characters shown in the eldoc display for a dictionary
definition. The default value is 80. Longer definitions are
truncated with an ellipsis.
(setopt johnson-eldoc-max-length 120)
To enable eldoc integration, activate johnson-eldoc-mode in
the desired buffer (Eldoc integration commands).
Scan-popup mode
The user option johnson-scan-trigger controls what action
triggers popup dictionary definitions. The possible values are:
selection(the default) — show a popup when text is selected.idle— show a popup for the word at point after an idle delay.both— show a popup on both selection and idle.
(setopt johnson-scan-trigger 'both)
The user option johnson-scan-idle-delay sets the number of
seconds of idle time before a popup is shown when the trigger is
idle or both. The default value is 1.0.
(setopt johnson-scan-idle-delay 0.5)
The user option johnson-scan-popup-duration sets the number
of seconds a popup definition remains visible. The default value
is 5.0.
(setopt johnson-scan-popup-duration 10.0)
The popup display uses posframe if available, otherwise falls back to tooltip, and finally to the echo area.
To enable scan-popup mode, activate johnson-scan-mode
(Scan-popup mode commands).
Bookmarks
The user option johnson-bookmarks-file specifies the file where
bookmarked dictionary entries are persisted. The default value is
~/.cache/johnson/bookmarks.el.
(setopt johnson-bookmarks-file "~/.local/share/johnson/bookmarks.el")
Bookmarks allow you to save dictionary entries for later reference. Each bookmark records the headword, dictionary name, and date. See Managing bookmarks for the commands that add, remove, and list bookmarks.
History buffer
The user option johnson-history-file specifies the file where
lookup history is persisted across Emacs sessions. The default
value is ~/.cache/johnson/history.el.
(setopt johnson-history-file "~/.local/share/johnson/history.el")
The user option johnson-history-persist controls whether lookup
history is saved to disk. When t (the default), history entries
are written to johnson-history-file and restored on startup.
When nil, history exists only for the current Emacs session.
(setopt johnson-history-persist nil) ; session-only history
See Browsing history for commands that display and manage the history buffer.
Completion behavior
The user option johnson-completion-min-chars sets the minimum number
of characters you must type before completion candidates are fetched
from the headword index. Shorter inputs return no candidates, avoiding
expensive prefix queries against the full index.
The default value is 3 (type: natnum). You may want to lower this
for small dictionary collections where prefix queries are fast, or
raise it if completion feels sluggish with very large indexes:
(setopt johnson-completion-min-chars 2)
Rendering performance
The first dictionary result is rendered synchronously when the
*johnson* buffer is displayed, so the buffer appears immediately
even when a lookup matches many dictionaries. Remaining results are
rendered in the background via an idle timer, which yields to user
input between batches.
The user option johnson-render-batch-size (default 1) controls
how many results are rendered per background batch. A value of 1
keeps Emacs fully responsive between entries, at the cost of slightly
longer wall-clock time until every result is visible. Increase it
to render more results per batch:
(setopt johnson-render-batch-size 5)
The user option johnson-render-idle-delay (default 0.05) sets
the idle time that must elapse before the next batch fires. Raise
this value to prioritize input responsiveness further.
Commands
Looking up words
The command johnson-lookup is the primary entry point for dictionary
lookups. When called interactively, it prompts for a word with
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., (3 dicts)).
If the dictionaries have not been indexed yet, 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 (? or *),
johnson-lookup performs a wildcard search instead of a direct
lookup. Matching headwords are collected across all in-scope
dictionaries (up to johnson-wildcard-max-results; see
Wildcard search) and presented for selection via
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
johnson-db-normalize; see Headword normalization)
and displays the results in the *johnson* buffer. The word is pushed
onto the lookup history (Lookup history) and the
navigation history (Navigation history).
The optional WORD argument allows calling johnson-lookup from Lisp
code without prompting.
The command johnson-new-search re-invokes johnson-lookup without
arguments, prompting for a new word. It is bound to s in
johnson-mode for quick access when you want to search for a different
word while viewing results.
The command 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 g in johnson-mode. Unlike a new search,
refreshing does not push a new entry onto the navigation history.
Indexing dictionaries
The command 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 (Staleness detection and reset).
Progress is displayed in a dedicated *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 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.
Managing groups and scope
The command 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 <all> option, enabling flexible filtering. For example,
<all> → French selects all dictionaries with French as the target
language regardless of source, while Spanish → <all> selects all
dictionaries with Spanish as the source language. It is bound to G in
johnson-mode.
Selecting <all> for both source and target sets the search scope to
all, meaning lookups search across every discovered dictionary. Any
other combination sets the scope to group, restricting lookups to
matching dictionaries. If called from the results buffer, the current
lookup is automatically refreshed to reflect the new scope.
Navigating the results buffer
The command 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 n in
johnson-mode.
The command 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 p in johnson-mode.
The command johnson-prev-section-header is an alias for
johnson-prev-section, bound to P for symmetry with navigation
keybindings.
The command johnson-jump-to-section prompts with completion for a
dictionary section header and jumps directly to it. This is useful
when the results buffer contains many sections and you want to reach a
specific dictionary without scrolling.
The command johnson-toggle-section collapses or expands the dictionary
section at point. Collapsed sections hide their content using an
overlay with the invisible property and display a [+] 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 TAB in johnson-mode.
The command johnson-toggle-all-sections toggles all sections at once:
if any section is currently expanded, it collapses all of them;
otherwise, it expands all of them. This mirrors Org mode’s global
cycling behavior. It is bound to S-TAB (<backtab>) in
johnson-mode.
The command johnson-collapse-all collapses every section in the
results buffer. The command johnson-expand-all expands every section.
These commands are available from the transient menu
(Transient menu).
The command johnson-ace-link uses avy to jump to a visible link in
the results buffer. All cross-reference buttons and URL buttons are
collected as candidates and displayed with avy overlays for rapid
selection. It is bound to o in johnson-mode.
Following cross-references
The command johnson-follow-ref activates the cross-reference button at
point. In DSL dictionaries, cross-references are created by [ref] tags
or <<...>> markers and rendered as clickable text buttons. By
default, activating a cross-reference triggers a new johnson-lookup
for the referenced word across all loaded dictionaries. When
johnson-ref-scope is set to 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
(Cross-reference scope).
If there is no button at point, the command displays “No cross-reference
at point”. It is bound to RET in johnson-mode.
Navigation history
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 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 l
in johnson-mode.
The command 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 r in johnson-mode.
Navigation history is separate from the lookup history stored in
johnson-history (Lookup history). The lookup history
is a global list used for completing-read recall (M-p=/=M-n), while
the navigation history is buffer-local to the *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 johnson--navigating-history flag),
preventing duplicate entries from cluttering the history.
Copying entries
The command 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 w in johnson-mode.
The command 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 johnson-dictionary-priorities.
The command first checks for a johnson-section-header text property
(present on header lines), then falls back to the johnson-section
overlay property (present throughout the section body). If neither is
found, it displays “No dictionary section at point”.
It is bound to W in johnson-mode.
Dictionary list
The command johnson-list-dictionaries opens a *johnson-dictionaries*
buffer displaying all discovered dictionaries in a tabulated list. The
buffer uses johnson-dict-list-mode, a major mode derived from
tabulated-list-mode.
The table shows six columns: Name (the dictionary’s display name), Format (e.g., “DSL”), Languages (the source-to-target language pair), Entries (the number of indexed headwords), Status (“Indexed”, “Needs reindex”, or “Not indexed”), and Path (the abbreviated file path). The list is sorted by name by default and can be re-sorted by clicking on column headers.
The command johnson-dict-list-reindex re-indexes the dictionary at
point in the tabulated list. It deletes the existing index file, forces
a fresh index build, and refreshes the list to show the updated entry
count and status. It is bound to i in johnson-dict-list-mode.
The command johnson-dict-list-show-details opens a
*johnson-dict-details* buffer showing detailed information about the
dictionary at point: its name, format, source and target languages,
group, priority, file path, index path, entry count, and whether the
index is stale. It is bound to RET in johnson-dict-list-mode.
Cache management
The command johnson-close-caches kills all dictionary file cache
buffers and closes all open sqlite database connections. It also resets
the internal dictionary list and indexed state, so the next lookup will
trigger a fresh discovery and staleness check.
This is useful if you have added or removed dictionary files and want to force a clean rediscovery, or if you want to reclaim the memory used by cached dictionary buffers.
The command reports how many cache buffers were closed and confirms that all database connections were closed.
The command johnson-clear-resource-cache deletes the
resources/ subdirectory of johnson-cache-directory,
removing all audio files extracted from companion zip archives
(Audio playback).
Toggling images
The command johnson-toggle-images toggles the display of
inline images in the results buffer. When images are currently
shown, the command hides them; when hidden, it re-renders the
buffer with images visible. The toggle affects the value of
johnson-display-images (Images).
It is bound to I in johnson-mode.
Searching definitions
The command johnson-search performs a full-text search across
all indexed dictionary definitions. It prompts for a search query
and displays results in a tabulated list showing the headword,
dictionary name, and a snippet of the matching definition with
highlighted matches.
Full-text search must be enabled via johnson-fts-enabled
(Full-text search) before indexing for this command to
produce results. If full-text data has not been indexed, the
command signals an error.
It is bound to S in johnson-mode.
Eldoc integration commands
The command johnson-eldoc-mode is a buffer-local minor mode
that adds dictionary definitions to eldoc. When enabled, moving
point over a word displays a brief dictionary definition in the
eldoc area (typically the echo area or an eldoc buffer). The
lighter is " JDict".
The maximum length of the displayed definition is controlled by
johnson-eldoc-max-length (Eldoc integration).
;; Enable in text-mode buffers
(add-hook 'text-mode-hook #'johnson-eldoc-mode)
Internally, the mode registers johnson-eldoc-function as an eldoc
documentation function. This function looks up the word at point and
passes a brief definition string to the eldoc callback.
Scan-popup mode commands
The command johnson-scan-mode is a global minor mode that
shows popup dictionary definitions when you select text or after an
idle delay. The lighter is " JScan".
The trigger behavior, idle delay, and popup duration are controlled
by the user options johnson-scan-trigger,
johnson-scan-idle-delay, and
johnson-scan-popup-duration (Scan-popup mode).
The popup display uses posframe if the posframe package is
available, otherwise falls back to tooltip, and finally to the
echo area.
(johnson-scan-mode 1) ; enable globally
Managing bookmarks
The command johnson-bookmark-add bookmarks the dictionary
entry at point for later reference. The bookmark records the
headword, dictionary name, and current date. Bookmarks are
persisted to johnson-bookmarks-file (Bookmarks).
It is bound to b in johnson-mode.
The command johnson-bookmark-remove removes the bookmark for
the dictionary entry at point. If the entry is not bookmarked, the
command displays a message indicating so.
It is bound to M in johnson-mode.
The command johnson-bookmark-list opens a
*johnson-bookmarks* buffer displaying all bookmarked entries
in a tabulated list. The table has three columns: Headword,
Dictionary, and Date.
In the bookmarks buffer, the command johnson-bookmark-list-goto
re-looks up the bookmarked headword. It is bound to RET. The
command johnson-bookmark-list-delete deletes the bookmark at point
and is bound to d.
Browsing history
The command johnson-history-list opens a
*johnson-history* buffer displaying the full lookup history
in a tabulated list with three columns: Headword, Time (the
timestamp of the lookup), and Results (the number of dictionary
matches).
It is bound to H in johnson-mode.
In the history buffer, the command johnson-history-list-goto re-looks
up the headword at point. It is bound to RET.
The command johnson-history-clear clears all lookup history
after prompting for confirmation. This removes both the in-memory
history and, if persistence is enabled, the history file on disk.
History persistence is controlled by johnson-history-persist
and johnson-history-file (History buffer).
Browsing dictionary files
The command johnson-browse-dictionary opens Dired on the directory
containing the dictionary file for the section at point. This is
useful when you want to inspect companion files (abbreviation tables,
audio archives, resource files) that reside alongside the dictionary.
This command is available from the transient menu (Transient menu).
Clearing the index
The command johnson-clear-index deletes all sqlite index files in the
cache directory and resets internal state. Dictionaries will be
re-indexed on the next lookup. This is a more thorough reset than
johnson-close-caches (Cache management), which closes
connections without deleting files.
Use this command when you suspect index corruption or when you want to force a complete rebuild of every dictionary index. This command is available from the transient menu (Transient menu).
Reordering dictionaries
The command johnson-reorder-dictionaries opens a
*johnson-reorder* buffer listing all discovered dictionaries. You
can rearrange them to define a custom display order that is then saved
as johnson-dictionary-priorities (Display ordering).
The buffer uses johnson-reorder-mode, which provides:
M-<up>(johnson-reorder-move-up) — move the dictionary at point up one position.M-<down>(johnson-reorder-move-down) — move the dictionary at point down one position.C-c C-c(johnson-reorder-save) — save the current ordering. When saving, you are prompted to choose between saving viacustomize-save-variable(persists across sessions), setting for the current session only, or copying the value to the kill ring so you can paste it into your init file manually.
This command is available from the transient menu (Transient menu).
Importing GoldenDict order
The command johnson-import-goldendict-order imports the dictionary
display order from a GoldenDict configuration file. It prompts for the
path to the configuration file, parses the dictionary order, and opens
the reorder buffer (Reordering dictionaries) with
dictionaries sorted according to GoldenDict’s order.
Dictionaries that could not be matched to any discovered johnson
dictionary are appended at the end and highlighted with
johnson-reorder-unmatched-face (UI faces).
This lets you identify dictionaries that need manual attention.
This command is available from the transient menu (Transient menu).
Transient menu
The command johnson-menu opens a transient popup menu providing quick
access to all johnson commands and toggleable options. It requires the
transient package (bundled with Emacs 29+).
The menu uses a three-column layout with aligned columns. Toggleable options are placed within the group they relate to:
- Column 1: Search (with source/target language, FTS, and ref scope options), Navigate sections, Display sections.
- Column 2: History (with persist option), Bookmarks, Copy, Media (with images option).
- Column 3: Dictionaries (with directories option), Index, Modes (eldoc, scan).
The transient menu is defined in johnson-transient.el.
Functions
Format registration
The function johnson-register-format registers a dictionary format
backend with the core. It accepts a plist with the following keys:
:name— a unique string identifying the format (e.g.,"dsl").:extensions— a list of file extension strings used to filter files during directory scanning (e.g.,("dsl")).:detect— a function(PATH) → BOOLEANthat determines whether a file at PATH is a dictionary in this format.:parse-metadata— a function(PATH) → PLISTthat extracts metadata from the dictionary file, returning at minimum:name,:source-lang, and:target-lang.:build-index— a function(PATH CALLBACK) → NILthat parses the dictionary and calls(funcall CALLBACK HEADWORD OFFSET LENGTH)for each entry.:retrieve-entry— a function(PATH OFFSET LENGTH) → STRINGthat retrieves the raw entry text at the given location.:render-entry— a function(RAW-TEXT) → NILthat inserts rendered content into the current buffer at point.
If a format with the same :name is already registered, it is replaced.
This allows format backends to re-register themselves (e.g., after
reloading the file during development).
Backend modules typically call this function inside a
with-eval-after-load form for johnson, ensuring the core is loaded
before registration occurs. For an example, see how johnson-dsl.el
registers the DSL format at the end of the file.
Completion at point
The function johnson-completion-at-point-function provides a
completion-at-point (CAPF) backend backed by the johnson dictionary
index. It completes the word at point using the same dynamic sqlite
prefix queries as johnson-lookup (Looking up words).
To use this function, add it to completion-at-point-functions in the
major modes where you want dictionary completion:
(add-hook 'text-mode-hook
(lambda ()
(add-to-list 'completion-at-point-functions
#'johnson-completion-at-point-function)))
The completion respects the current search scope: if the scope is set to
group, only dictionaries in the active group are queried.
Database lifecycle
The function johnson-db-open opens or creates the sqlite database for
a given dictionary file path. It ensures the cache directory
(Cache directory) exists, creates the metadata and
entries tables if they do not already exist, creates the index on
headword_normalized if absent, and returns the database connection
object. This is the primary entry point for obtaining a database handle.
The function johnson-db-close closes a sqlite database connection.
Pass the connection object previously returned by johnson-db-open.
In practice, the core module caches database connections in a hash table
and closes them all at once via johnson-close-caches
(Cache management).
Metadata storage
The metadata table stores key-value pairs describing each dictionary: its name, format identifier, source and target languages, source file path, file modification time, and entry count. These metadata entries are written during indexing and read during dictionary discovery and staleness detection (Staleness detection and reset).
The function johnson-db-set-metadata writes a key-value pair to the
metadata table using INSERT OR REPLACE semantics, so existing keys are
updated rather than duplicated. Both KEY and VALUE are strings.
The function johnson-db-get-metadata retrieves a single metadata value
by key, returning nil if the key is not present in the database.
The function johnson-db-get-all-metadata returns all metadata as an
alist of (KEY . VALUE) pairs. This is useful for displaying
comprehensive dictionary details, such as in the dictionary detail
buffer opened by johnson-dict-list-show-details
(Dictionary list).
Headword normalization
The function johnson-db-normalize transforms a string for
case-insensitive and accent-insensitive search. It performs three steps
in sequence: NFKD decomposition via ucs-normalize-NFKD-string,
removal of combining diacritical marks (Unicode general category Mn),
and downcasing via downcase.
This normalization means that searching for cafe will match headwords
such as café, CAFÉ, and Café. Both user input and headwords are
normalized with this function before storage and querying, ensuring
consistent matching behavior.
If the input is nil or an empty string, the function returns an empty
string.
Entry insertion
The function johnson-db-insert-entry inserts a single headword entry
into the database. It takes the original HEADWORD text, a BYTE-OFFSET
(or character offset, depending on the format backend), and an
ENTRY-LENGTH specifying the entry’s extent in the dictionary file. The
headword is automatically normalized via johnson-db-normalize
(Headword normalization) before insertion.
For bulk insertion during indexing, the function
johnson-db-insert-entries-batch inserts a list of entries in a single
database transaction, which is dramatically faster than individual
inserts for large dictionaries. Each element in the ENTRIES list is a
(HEADWORD BYTE-OFFSET BYTE-LENGTH) triple. If an error occurs during
the transaction, it is rolled back and the error is re-signaled, leaving
the database in a consistent state.
Querying entries
The function johnson-db-query-exact queries the database for entries
whose normalized headword exactly matches the normalized form of WORD.
It returns a list of (HEADWORD BYTE-OFFSET BYTE-LENGTH) triples,
where HEADWORD is the original non-normalized text. Multiple triples
may be returned when a dictionary contains duplicate headwords (e.g.,
from headword alternation) or when multiple entries share the same
normalized form. This function is used during the lookup flow in
johnson-lookup (Looking up words).
The function johnson-db-query-prefix queries the database for distinct
headwords whose normalized form begins with the normalized PREFIX. It
uses a SQL LIKE query on the indexed headword_normalized column for
efficient prefix matching. The optional LIMIT argument defaults to 100.
This function powers the dynamic completion table used by
johnson-lookup.
The function johnson-db-entry-count returns the total number of rows
in the entries table. It is used for display in the dictionary list
buffer (Dictionary list) and in indexing progress messages.
Staleness detection and reset
The function johnson-db-stale-p checks whether the index for a
dictionary file is stale or does not exist. It compares the file
modification time stored in the database metadata (under the key
"mtime") against the actual file’s modification time. If the sqlite
database file does not exist at all, the function returns non-nil. This
function is called during the indexing flow
(Indexing dictionaries) to determine which
dictionaries need re-indexing.
The function johnson-db-reset deletes all entries from a database in
preparation for re-indexing. It issues DELETE FROM entries but does
not drop the table or remove metadata. After a reset, the entry count
is zero and new entries can be inserted via
johnson-db-insert-entries-batch (Entry insertion).
The function johnson-db-stale-quick-p performs a fast,
filesystem-only staleness check. Unlike johnson-db-stale-p, it never
opens a sqlite database; it simply checks whether the index file exists
and whether the dictionary file’s modification time is newer than the
index file’s. This makes it suitable for rapid scanning during
discovery without the overhead of opening every database.
Wildcard and full-text search queries
The function johnson-db-query-wildcard queries the database for
headwords matching a wildcard PATTERN. The pattern uses ? for a
single character and * for zero or more characters, which are
translated to SQL _ and %. It returns a list of distinct headword
strings, with an optional LIMIT (default 200). This function powers the
wildcard search feature described in Wildcard search.
The function johnson-db-insert-fts inserts a headword and its
plain-text definition into the FTS (full-text search) virtual table.
It is called during indexing when johnson-fts-enabled is non-nil
(Full-text search).
The function johnson-db-query-fts queries the FTS table for a search
term and returns a list of (HEADWORD SNIPPET) pairs. The optional
LIMIT defaults to 50. This function powers johnson-search
(Searching definitions).
The function johnson-db-fts-indexed-p returns non-nil if the database
has been indexed for full-text search. The function
johnson-db-set-fts-indexed marks a database as having been FTS-indexed.
Completion index
The completion index is a unified sqlite database that aggregates
headwords from all per-dictionary indexes into a single file. This
enables fast cross-dictionary prefix queries for completing-read
without opening every individual database.
The function johnson-db-rebuild-completion-index builds or rebuilds
the unified index from a list of dictionary file paths. It returns
the total number of unique headwords aggregated.
The function johnson-db-query-completion queries the unified
completion index for headwords matching a PREFIX. It returns a list of
(HEADWORD DICT-COUNT) pairs, where DICT-COUNT indicates how many
dictionaries contain that headword. The optional LIMIT defaults to 200.
The function johnson-db-get-completion-db returns the cached
completion index database connection, opening it on first use. It
returns nil if the index file does not exist. The function
johnson-db-close-completion-db closes this cached connection.
The function johnson-db-completion-index-path returns the filesystem
path to the unified completion index database.
DSL format detection
The function johnson-dsl-detect determines whether a given file path
is a DSL dictionary file. It checks for the .dsl extension
(case-insensitively) and then verifies that the first non-BOM content in
the file starts with #, which indicates a DSL metadata header.
The detection handles all supported encodings: it reads the BOM to
determine the encoding, skips the appropriate number of BOM bytes, and
decodes the initial content to check for the # character. For UTF-16
files, it reads enough bytes (64) to ensure it captures the first
decoded characters.
This function is registered as the :detect handler in the format
registry and is called during dictionary discovery to identify DSL files
among all files found in the configured directories.
DSL metadata parsing
The function johnson-dsl-parse-metadata extracts metadata headers from
a DSL dictionary file. It reads only the first few kilobytes of the
file (4 KB for UTF-8, 8 KB for UTF-16) since headers always appear at
the top, making this operation efficient even for large files.
The function parses three header fields: #NAME (the dictionary’s
display name), #INDEX_LANGUAGE (the source language), and
#CONTENTS_LANGUAGE (the target language). Header values are enclosed
in double quotes in the file.
It returns a plist with keys :name, :source-lang, and
:target-lang. If a header is absent, the corresponding value defaults
to an empty string. The core module uses these values for dictionary
display names and for auto-detecting language-pair groups
(Search scope and groups).
DSL index building
The function johnson-dsl-build-index parses an entire DSL dictionary
file and calls a CALLBACK function for each headword entry found. The
callback receives three arguments: the headword string, the character
offset of the entry body in the decoded buffer (1-based), and the
character length of the body.
The parsing proceeds in a single pass through a decoded buffer cache.
It first skips metadata header lines (beginning with #), then
identifies entries by their structure: flush-left lines are headwords,
and indented lines (beginning with a tab or spaces) are the entry body.
Multiple consecutive flush-left lines before an indented body are treated
as multiple headwords for the same entry.
Each raw headword is expanded via the internal headword expansion pipeline, which handles three kinds of variants:
Split markers:
colour{/}colorproduces two separate headwords,colourandcolor.Alternation:
pre{a/b}sufexpands topreasufandprebsuf.Optional parts:
go(es)expands to bothgoandgoes.
Escaped characters (\[, \], \{, \}, \(, \)) are stripped
after expansion. Each expanded variant becomes a separate row in the
database, all pointing to the same entry body.
If a headword fails to parse, it is skipped and a count of skipped entries is logged as a warning message at the end of the indexing run.
A key design decision is the use of decoded buffers rather than raw byte access. Dictionary files are opened with their detected encoding (UTF-16LE, UTF-16BE, UTF-8 with BOM, or plain UTF-8) and cached as multibyte Emacs buffers. The index stores character positions rather than byte offsets, which ensures that UTF-16 encoded files work correctly for both indexing and retrieval.
DSL entry retrieval
The function johnson-dsl-retrieve-entry retrieves the raw entry body
from a DSL dictionary file. It takes the file path, a character offset
(CHAR-OFFSET, 1-based), and a character count (NCHARS), and returns the
corresponding substring from the decoded buffer cache.
Because the index stores character positions rather than byte offsets, this function works correctly with all supported encodings, including UTF-16 files where byte positions would not align with character boundaries.
The decoded buffer cache keeps dictionary files open as hidden Emacs
buffers (named " *johnson-cache: PATH") for fast repeated lookups.
All cache buffers can be killed via johnson-close-caches
(Cache management).
DSL entry rendering
The function johnson-dsl-render-entry transforms raw DSL entry text
into richly formatted content in the current buffer. It inserts the
rendered text at point using Emacs text properties and faces rather than
HTML or shr rendering.
The rendering proceeds in several steps. First, leading indentation
(tab or spaces) is stripped from each line. Media references
({{...}}) are removed. Then the text is inserted into the buffer and
processed in two passes.
The first pass handles <<...>> cross-reference markers, converting
them into clickable buttons with johnson-ref-face
(Link faces). Each button invokes johnson-lookup for the
referenced word when activated.
The second pass processes DSL tags using a stack-based parser. The
parser scans for tag patterns matching [tag] and [/tag], maintaining
a stack of active tags. When a closing tag is encountered, the
corresponding face or text property is applied to the enclosed region.
The supported tags include:
[b],[i],[u]: text styling (Text styling faces)[c]and[c NAME]: color (Color faces)[sup],[sub]: superscript and subscript viadisplayproperties[ex]: examples (Content type faces)[*]: optional/secondary text[ref]: cross-reference buttons (Link faces)[url]: external URL buttons[lang id=N]: language identifier (stored as text property)[trn],[!trn]: translation blocks (blank line separation)[com]: comments[m]and[m0]-[m9]: indentation levels vialine-prefixandwrap-prefixdisplay properties[p]: abbreviation markers (rendered as-is)[']: stress marks (Text styling faces)[s]: media references (content is suppressed)
StarDict backend
The StarDict backend (johnson-stardict.el) handles .ifo, .idx,
.dict, and .syn files. It supports dictzip-compressed .dict.dz
files via johnson-dictzip.el (Dictzip backend).
The function johnson-stardict-detect checks whether a file is a
StarDict .ifo file by verifying the extension and the presence of
the StarDict's dict ifo file magic line.
The function johnson-stardict-parse-metadata parses the .ifo file
to extract the dictionary name, source language, and target language.
The function johnson-stardict-build-index parses the binary .idx
file (or .idx.gz) to extract headword entries. Each entry’s byte
offset and size reference the uncompressed .dict data. The function
auto-detects non-standard 64-bit entry formats in dictionaries whose
.ifo files omit the idxoffsetbits=64 declaration.
The function johnson-stardict-retrieve-entry reads the raw entry data
from the .dict file at the given byte offset and size. For
dictzip-compressed files, it delegates to johnson-dictzip-read. It
also records the sametypesequence setting for use by the renderer.
The function johnson-stardict-render-entry renders raw StarDict data
into the current buffer. It interprets the sametypesequence field to
parse data fields, supporting plain text, HTML, Pango markup, phonetic,
and resource content types. HTML content is rendered using
johnson-html-render-region (HTML rendering).
MDict backend
The MDict backend (johnson-mdict.el) handles .mdx and .mdd
files, including encrypted files (Encrypted“2”). Encryption support uses the pure Elisp RIPEMD-128 implementation in =johnson-ripemd128.el (RIPEMD-128 hashing).
The function johnson-mdict-detect checks whether a file is an MDict
.mdx file by verifying the extension.
The function johnson-mdict-parse-metadata reads the MDict header to
extract the dictionary name, source language, and target language. If
the title field contains HTML, the function falls back to the filename.
The function johnson-mdict-build-index parses the MDict keyword
blocks to extract headwords and their record offsets. Entry sizes are
not stored in the keyword index; they are computed at retrieval time via
a sorted offset cache and binary search for the next offset.
The function johnson-mdict-retrieve-entry decompresses and decodes
the entry data at a given record offset. It handles zlib-compressed
record blocks.
The function johnson-mdict-render-entry renders MDict HTML content
into the current buffer using johnson-html-render-region. If the
entry data starts with @@@LINK=, the target entry is resolved and
rendered instead (following MDict’s redirect convention).
The function johnson-mdict-clear-caches clears all MDict-specific
caches (header data, record block info, offset caches).
BGL backend
The BGL backend (johnson-bgl.el) handles Babylon .bgl files.
The function johnson-bgl-detect checks whether a file is a BGL
dictionary by verifying the extension and the presence of magic bytes
(0x12 0x34) at the start of the file.
The function johnson-bgl-parse-metadata reads BGL header records to
extract the dictionary name, source language, and target language.
The function johnson-bgl-build-index decompresses the BGL file
(which uses gzip compression) and parses the entry records. Byte
offsets reference positions in the decompressed stream, which is cached
in a unibyte buffer.
The function johnson-bgl-retrieve-entry retrieves the raw definition
from the decompressed stream at the given offset and returns it as a
decoded string.
The function johnson-bgl-render-entry renders BGL HTML content into
the current buffer using johnson-html-render-region
(HTML rendering).
The function johnson-bgl-clear-caches clears all BGL-specific caches.
DICT protocol backend
The DICT protocol backend (johnson-dict.el) communicates with remote
dictionary servers using RFC 2229 over TCP. Unlike file-based backends,
it does not use the standard :detect / :build-index registration
keys (those are set to ignore). Instead, it has its own discovery and
query mechanisms.
The function johnson-dict-discover connects to each server listed in
johnson-dict-servers (DICT protocol), lists the
available databases, and returns a list of dictionary plists suitable
for the internal dictionary registry. Discovered DICT dictionaries
appear under the “DICT Servers” group.
The function johnson-dict-query-exact queries a DICT server for a
word and caches the results locally. It returns a list of
(HEADWORD OFFSET LENGTH) triples, where OFFSET encodes the word and
result index for deterministic cache retrieval.
The function johnson-dict-retrieve-entry retrieves a previously
cached DICT definition using the encoded offset string.
The function johnson-dict-render-entry inserts a DICT definition as
plain text, since DICT responses do not contain markup.
The function johnson-dict-clear-caches clears all DICT caches and
disconnects from servers.
Dictzip backend
The dictzip module (johnson-dictzip.el) provides random-access
decompression for dictzip-compressed files (.dsl.dz and .dict.dz).
Dictzip is a gzip-compatible format that partitions the compressed data
into fixed-size chunks, enabling decompression of arbitrary byte ranges
without reading the entire file.
The function johnson-dictzip-read reads a specified number of bytes
at a given uncompressed offset from a dictzip file. It decompresses
only the chunks that overlap the requested range, caching decompressed
chunks for subsequent reads.
The function johnson-dictzip-read-full reads the entire uncompressed
content of a dictzip file. This is used during indexing when the full
file must be scanned.
The function johnson-dictzip-clear-caches clears all cached dictzip
headers and decompressed chunks.
HTML rendering
The HTML rendering module (johnson-html.el) provides shared
infrastructure for rendering HTML content from StarDict, MDict, and BGL
entries. It converts HTML tags into Emacs text properties and faces,
without relying on shr or an external browser.
The function johnson-html-render-region processes HTML tags in a
buffer region, replacing them with appropriate text properties. It
handles common tags: <b>, <i>, <u>, <font>, <span>,
<div>, <br>, <p>, <img>, <a>, and others. CSS color and
font-weight style attributes are parsed and applied as face
properties. It also decodes HTML entities such as &, <,
>, , ", ', and numeric entities like
{ and «.
The function johnson-html-decode-entities-string decodes the same
set of HTML entities in a bare string and returns the result. Format
backends use it for metadata fields that come from raw headers rather
than entry bodies — for example, the MDict backend decodes entities
in the Title header before storing it as the dictionary name.
The function johnson-html-color-to-face maps an HTML color name or
hex code to a johnson face. It supports CSS named colors and falls
back to creating an anonymous face with the specified RGB foreground.
RIPEMD-128 hashing
The RIPEMD-128 module (johnson-ripemd128.el) provides a pure Elisp
implementation of the RIPEMD-128 hash algorithm. This is used by the
MDict backend (MDict backend) to decrypt MDict files
with Encrypted“2”= headers, which require RIPEMD-128 key derivation.
The function johnson-ripemd128-hash computes the RIPEMD-128 hash of a
unibyte string and returns a 16-byte unibyte digest.
The function johnson-ripemd128-hash-hex computes the RIPEMD-128 hash
as a hexadecimal string. It accepts both multibyte strings (encoding
to UTF-8 first) and unibyte strings.
Audio playback functions
The function johnson-play-sound plays an audio file using the
configured player. It respects johnson-audio-player
(Audio playback): when set to auto, it tries
play-sound-file first, then falls back to external players in
johnson-audio-external-players.
The function johnson-insert-audio-button inserts a clickable play
button (▶) for an audio file at point. The optional DICT-PATH
argument identifies the dictionary file, which is used to locate a
companion zip archive for on-demand audio extraction. This function
is called by format renderers when they encounter audio references.
EPWING backend
The EPWING backend (johnson-epwing.el) handles EPWING (JIS X 4081)
and EB format electronic dictionaries (EPWING).
The function johnson-epwing-detect checks whether a file is a valid
EPWING HONMON data file.
The function johnson-epwing-parse-metadata reads metadata from a
HONMON file and returns a plist with the dictionary name, source
language, and target language.
The function johnson-epwing-discover scans
johnson-dictionary-directories for CATALOGS and CATALOG files,
parses each to enumerate subbooks, and returns a list of dictionary
plists. Unlike file-based backends, EPWING discovery is
directory-based: each subbook within a book becomes a separate
dictionary entry.
The function johnson-epwing-build-index traverses the B-tree search
index of a HONMON file to extract headwords. Each entry callback
receives an absolute file position as the byte offset; the byte length
is always 0 because entries are delimited by stop codes rather than
fixed lengths.
The function johnson-epwing-retrieve-entry reads an entry from the
HONMON file at the given byte offset, decoding text until the stop
code. Escape sequences are preserved as character-31 markers for later
rendering.
The function johnson-epwing-render-entry interprets the character-31
escape markers to apply text formatting (bold, italic, emphasis,
references, sub/superscript, indentation, and newlines) and inserts the
result into the current buffer.
The function johnson-epwing-clear-caches clears all EPWING-specific
caches.
EBZIP backend
The EBZIP module (johnson-ebzip.el) provides random-access
decompression for EBZIP-compressed EPWING files (.ebz). EBZIP is a
compression format used by some EPWING distributions, similar in concept
to dictzip but specific to the EPWING ecosystem. The module uses zlib
decompression and caches decompressed slices for efficient repeated
access.
The function johnson-ebzip-read reads a specified number of
uncompressed bytes from an EBZIP file at a given offset. It
decompresses only the slices that overlap the requested range,
caching results for subsequent reads.
The function johnson-ebzip-uncompressed-size returns the
uncompressed size of an EBZIP file by reading its header.
The function johnson-ebzip-clear-caches clears all cached EBZIP
headers and decompressed slices.
Faces
DSL-specific faces defined in johnson-dsl.el belong to the
johnson-dsl-faces customization group. You can customize them via
M-x customize-group RET johnson-dsl-faces RET to match your
preferred color theme. Faces defined in johnson.el belong to the
johnson group.
Text styling faces
The face johnson-bold-face renders text enclosed in [b]...[/b] tags.
It inherits from bold.
The face johnson-italic-face renders text enclosed in [i]...[/i]
tags. It inherits from italic.
The face johnson-underline-face renders text enclosed in
[u]...[/u] tags. It sets the :underline attribute to t.
The face johnson-stress-face renders stress marks enclosed in
[']…=[/’]= tags. It inherits from bold, making accented
syllables visually prominent in pronunciation guides.
Content type faces
The face johnson-example-face renders example sentences enclosed in
[ex]...[/ex] tags. It inherits from italic with a dim gray
foreground, visually distinguishing examples from definitions.
The face johnson-optional-face renders optional or secondary text
enclosed in [*]...[/*] tags. It uses a muted gray foreground that
adapts to the background: gray50 on light backgrounds and gray60
on dark backgrounds.
The face johnson-comment-face renders comment annotations enclosed in
[com]...[/com] tags. It inherits from font-lock-comment-face,
giving comments a consistent appearance with the rest of Emacs.
The face johnson-abbreviation-face renders abbreviation markers
enclosed in [p]...[/p] tags. It uses a green foreground (dark green on light backgrounds, green3 on dark backgrounds), matching
the default color face convention for DSL dictionaries. Abbreviation
markers typically contain short labels (e.g., “noun”, “verb”) that are
expanded from a companion _abrv.dsl file when available.
Link faces
The face johnson-ref-face renders cross-reference links enclosed in
[ref]...[/ref] tags or <<...>> markers. It inherits from link.
Cross-references are rendered as clickable buttons that invoke
johnson-lookup for the referenced word
(Following cross-references).
The face johnson-url-face renders external URL links enclosed in
[url]...[/url] tags. It inherits from link. URL links are rendered
as clickable buttons that open the URL via browse-url.
Section header face
The face johnson-section-header-face renders dictionary section
headers in the results buffer (the lines using Unicode box-drawing
character ━). It inherits from bold with the :extend attribute
set to t, ensuring the face extends to the edge of the window.
This face is defined in johnson.el (for the results buffer headers)
and also in johnson-dsl.el (for DSL-specific section headers).
Color faces
DSL dictionaries use [c]...[/c] and [c NAME]...[/c] tags to apply
color to text. The module maps DSL color names (case-insensitively) to
a fixed set of customizable Emacs faces. All color faces define
separate foreground colors for light and dark backgrounds.
The face johnson-color-default-face is used when no color name is
specified (i.e., a bare [c] tag). It defaults to green (dark green
on light backgrounds, green3 on dark backgrounds).
The face johnson-color-green-face is used for the DSL color names
green and darkgreen.
The face johnson-color-red-face is used for red, darkred, and
crimson.
The face johnson-color-blue-face is used for blue, darkblue, and
steelblue.
The face johnson-color-gray-face is used for gray, darkgray, and
dimgray.
The face johnson-color-brown-face is used for brown and
saddlebrown.
The face johnson-color-violet-face is used for violet, purple,
and darkviolet.
The face johnson-color-orange-face is used for orange and
darkorange.
Any unrecognized color name falls back to johnson-color-default-face.
UI faces
The face johnson-toc-face renders table-of-contents entries at the
top of the results buffer. TOC entries are clickable links that jump
to the corresponding dictionary section. It inherits from link.
The face johnson-audio-button-face renders audio playback buttons
(▶) in dictionary entries (Audio playback). It
inherits from link.
The face johnson-reorder-unmatched-face highlights dictionaries in
the reorder buffer that could not be matched during a GoldenDict
import (Importing GoldenDict order). It
inherits from warning.
Roadmap
Version 0.1 — DSL
-
johnson-dsl.elbackend for.dslfiles. - Auto-discovery and indexing of uncompressed DSL dictionaries.
- Encoding auto-detection (UTF-8, UTF-16LE, UTF-16BE).
- Headword alternation (
{/}), optional parts ((...)), and multi-headword entries. - Case-insensitive and accent-insensitive search via NFKD normalization.
- Dynamic
completing-readwith sqlite-backed prefix matching. - Full DSL markup rendering with text properties and faces.
- Cross-reference navigation with back/forward history.
- Dictionary groups by language pair, with configurable scope.
- Collapsible dictionary sections in the results buffer.
- Dictionary list buffer (
tabulated-list-mode). - Completion-at-point (CAPF) backend.
- Plain-text entry copying.
Version 0.2 — StarDict and compressed DSL
-
johnson-stardict.elbackend for.ifo,.idx,.dict, and.synfiles. - Dictzip random-access support for compressed
.dict.dzand.dsl.dzfiles. - StarDict content types: plain text, HTML, Pango markup, phonetic.
- Include table of contents at the beginning of the buffer. Integrate it with ace-link for quick navigation.
- Allow collapsing all sections, not just individual ones (like org does).
- Display a buffer of all indexed dictionaries and allow the user to reorder them, so that the new order can be saved as the new order in
johnson-dictionary-priorities.- When saving, prompt the user whether they want to set the new value via defcustom, for the current session, or to set it manually in their init file; in the latter case, copy the new value to the kill ring and display instructions on how to use it.
- Provide basic media support (audio pronunciation).
Version 0.3 — MDict and DSL abbreviations
-
johnson-mdict.elbackend for.mdxand.mddfiles. - Support for MDict’s HTML+CSS content.
-
_abrv.dslabbreviation table support for DSL dictionaries ([p]tag expansion).
Version 0.4 — BGL, DICT, and more
-
johnson-bgl.elbackend for.bglfiles, with automatic detection via file extension and magic bytes (0x12 0x34). -
johnson-dict.elDICT protocol client (RFC 2229) for remote dictionary servers. - Inline image support across all format backends (DSL
[s]tags, HTML<img>tags in MDict, StarDict, and BGL). - Wildcard search using
?(single character) and*(multiple characters) in lookups. - Full-text search within dictionary definitions
(
johnson-search). - Eldoc integration via
johnson-eldoc-mode. - Scan-popup mode via
johnson-scan-mode(popup definitions on selection or idle). - Bookmarking entries for later reference.
- Browsable history buffer with timestamps and result counts.
- Auto-detection of non-standard 64-bit
.idxentry formats in StarDict dictionaries whose.ifofiles omit theidxoffsetbits=64declaration. - Clearer error message when a StarDict
.idxfile is missing (incomplete dictionary distribution).
Version 0.5 — EPWING
-
johnson-epwing.elbackend for EPWING (JIS X 4081) and EB format electronic dictionaries. - B-tree search index traversal for headword extraction.
- JIS X 0208 (EUC-JP) and ISO 8859-1 character encoding support.
- Fullwidth-to-ASCII normalization for readable output.
- Text rendering with bold, italic, emphasis, references, sub/superscript, indentation, and newlines.
-
johnson-ebzip.elmodule for EBZIP decompression (compressed.ebzfiles). - Cross-reference buttons navigating via
johnson-lookup.
Version 0.6 (current) — EPWING improvements and audits
- EBZIP decompression improvements and caching.
- Interpretability audit: improved code readability and intent transparency.
- Checkdoc and byte-compile cleanups across all backends.
Future features
The following features are planned but not yet scheduled for a specific version:
- EPWING gaiji (custom character) rendering with Unicode mapping.