Here's what I would do, using the 466544-strong
words.txt file from english-words as an example:
(defun my-dictionary ()
"Return hash-table whose keys comprise words.txt."
(let ((table (make-hash-table :test #'equal :size 466544)))
(while (not (eobp))
(puthash (buffer-substring (point) (line-end-position)) nil table)
(lazy-completion-table my-dictionary my-dictionary)
"Lazy completion table for function `my-dictionary'.")
The basic idea is that you read and cache the contents of your dictionary file into a data structure (a hash-table in my example) which standard completion functions such as
completion-in-region can understand. The benefit of this is that you can pick and choose between minibuffer and in-buffer completion, as well as from many conforming completion frameworks, using the same completion collection. See
(elisp) Completion for more information on this.
After that, you can pass the lazy (cached) collection to whichever conforming completion frontend tickles your fancy. With
ivy-mode enabled, for example, a call to
(completion-in-region START END my-dictionary)
for some buffer positions
END will perform an in-place in-buffer completion like the one you describe.
Here is a more concrete example which completes in-buffer the word preceding point:
(defun my-complete-word-in-region ()
"Complete word preceding point under `my-dictionary'."
You could then bind this to some convenient key.
If the dictionary file you want to read in does not change very often, you can speed up the initial hash-table-creation latency of the function
my-dictionary by creating the hash-table and writing its printed representation to some auxiliary file ahead of time, and then reading that auxiliary file as Lisp instead, though it may not be worth the effort, given a subsequent caching of the hash-table in memory and the inherent latency of fuzzy completion.
See also issue #1174 for some
ivy-specific performance tips.