notes

johnson manual

First published: Last updated: 9107 words · 40 lines of code
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 (.ebz files).
  • 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).

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.

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 via customize-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) → BOOLEAN that determines whether a file at PATH is a dictionary in this format.
  • :parse-metadata — a function (PATH) → PLIST that extracts metadata from the dictionary file, returning at minimum :name, :source-lang, and :target-lang.
  • :build-index — a function (PATH CALLBACK) → NIL that parses the dictionary and calls (funcall CALLBACK HEADWORD OFFSET LENGTH) for each entry.
  • :retrieve-entry — a function (PATH OFFSET LENGTH) → STRING that retrieves the raw entry text at the given location.
  • :render-entry — a function (RAW-TEXT) → NIL that 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{/}color produces two separate headwords, colour and color.

  • Alternation: pre{a/b}suf expands to preasuf and prebsuf.

  • Optional parts: go(es) expands to both go and goes.

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 via display properties
  • [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 via line-prefix and wrap-prefix display 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 &amp;, &lt;, &gt;, &nbsp;, &quot;, &apos;, and numeric entities like &#123; and &#xAB;.

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.

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.el backend for .dsl files.
  • 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-read with 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.el backend for .ifo, .idx, .dict, and .syn files.
  • Dictzip random-access support for compressed .dict.dz and .dsl.dz files.
  • 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.el backend for .mdx and .mdd files.
  • Support for MDict’s HTML+CSS content.
  • _abrv.dsl abbreviation table support for DSL dictionaries ([p] tag expansion).

Version 0.4 — BGL, DICT, and more

  • johnson-bgl.el backend for .bgl files, with automatic detection via file extension and magic bytes (0x12 0x34).
  • johnson-dict.el DICT 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 .idx entry formats in StarDict dictionaries whose .ifo files omit the idxoffsetbits=64 declaration.
  • Clearer error message when a StarDict .idx file is missing (incomplete dictionary distribution).

Version 0.5 — EPWING

  • johnson-epwing.el backend 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.el module for EBZIP decompression (compressed .ebz files).
  • 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.

Indices

Function index

Variable index