Open iquiw opened 5 years ago
Can't reproduce step 3. When I press m, there's no Info-menu
candidate, only Info
. Please clarify.
Sorry, the step is just pressing m
.
Thanks. I think it's a bug in info.el. They declare this variable, but don't define it. Then with C-g the let-bound var goes out of scope, but there's still a reference to the symbol somewhere. I've added a work-around that I think good enough. It might be worthwhile to investigate why there's still a reference to the symbol after C-g. But that would be a general Emacs bug, not related to ivy.
I think it's a bug in info.el. They declare this variable, but don't define it.
They do define it: it's dynamically bound in several places in lisp/info.el
. That info.el
was not designed with Ivy in mind, or that Ivy does not fully support info.el
completion (e.g. #1524, #1597), does not make this an Emacs bug.
Then with C-g the let-bound var goes out of scope, but there's still a reference to the symbol somewhere.
I think the "stale" reference is in ivy-recursive-last
, which is fed to ivy--reset-state
in ivy-recursive-restore
as part of ivy-call
: https://github.com/abo-abo/swiper/blob/cfef4724968e4968616947bbf0ed50d62ca5c4e1/ivy.el#L1272-L1273
I've added a work-around that I think good enough.
The workaround sets Info-read-node-completion-table
, not Info-complete-menu-buffer
, to nil
, so the issue is not solved.
But that would be a general Emacs bug, not related to ivy.
Though I haven't investigated this beyond what I outlined above, I'm not yet convinced this is Emacs' fault.
I've added a work-around that I think good enough.
The workaround sets
Info-read-node-completion-table
, notInfo-complete-menu-buffer
, tonil
, so the issue is not solved.
What's more, at the point where ivy--reset-state
sets Info-read-node-completion-table
, the variable is not yet declared special:
In ivy--reset-state:
ivy.el:1922:20:Warning: assignment to free variable
‘Info-read-node-completion-table’
In my understanding, (defvar x)
is a forward declation, not a definition. And (defvar x nil)
is a definition. And using an undefined variable is a bug. Of course, it may be legal, but it's unusual for me, i kind of see it as a bug.
In my understanding,
(defvar x)
is a forward declation
More specifically, it declares x
as being special in the current scope.
And
(defvar x nil)
is a definition.
It both makes x
special in the current scope and sets its value slot.
And using an undefined variable is a bug.
More specifically, trying to access the value of a void variable is a bug. But info.el
doesn't do this. What it does is analogous to the following perfectly valid Elisp:
(defvar my-special-var)
(defun my-foo ()
(message "%s" my-special-var))
(let ((my-special-var nil))
(my-foo))
Sure, it may not be the most straightforward way to program something, which is why lexical-binding
is generally a superior paradigm, but it's still idiomatic dynamic Elisp. In fact, every time (defvar some-externally-defined-special-var)
appears in the Ivy sources we're effectively relying on the same pattern.
Having gotten today's dose of pedantry out of my system, I of course agree that info.el
could be written in a nicer way, but that's not really an avenue worth pursuing given that the (potentially quite involved) change wouldn't be visible until Emacs 27.
Then with C-g the let-bound var goes out of scope, but there's still a reference to the symbol somewhere.
I think the "stale" reference is in
ivy-recursive-last
, which is fed toivy--reset-state
inivy-recursive-restore
as part ofivy-call
: https://github.com/abo-abo/swiper/blob/cfef4724968e4968616947bbf0ed50d62ca5c4e1/ivy.el#L1272-L1273
@abo-abo Why does ivy-recursive-restore
have to "reevaluate" the entire ivy-recursive-last
state?
Hopefully a less kludgy solution can be found, but until then, and before we forget about this, #1814 works around the OP.
From
make plain
(setq debug-on-error t)
info
(Info-menu
)a
) and press TABDebugger entered--Lisp error: (void-variable Info-complete-menu-buffer)
``` Debugger entered--Lisp error: (void-variable Info-complete-menu-buffer) Info-complete-menu-item("" nil t) all-completions("" Info-complete-menu-item nil) (setq coll (all-completions "" collection predicate)) (cond ((eq collection (function Info-read-node-name-1)) (setq coll (if (equal (and (boundp 'Info-current-file) Info-current-file) "dir") (mapcar (function (lambda (x) (format "(%s)" x))) (delete-dups (all-completions "(" collection predicate))) (all-completions "" collection predicate)))) ((eq collection (function read-file-name-internal)) (if (and (equal def initial-input) (member "./" ivy-extra-directories)) (progn (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 24 (setq def nil)))))) (setq ivy--directory default-directory) (if (and initial-input (not (equal initial-input ""))) (progn (cond ((file-directory-p initial-input) (if (equal (file-name-nondirectory initial-input) "") (progn (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 (setq preselect nil)))) (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 24 (setq def nil)))))) (setq ivy--directory (file-name-as-directory initial-input)) (setq initial-input nil) (if preselect (progn (let ((preselect-directory (file-name-directory preselect))) (if (and preselect-directory (not (equal (expand-file-name preselect-directory) (expand-file-name ivy--directory)))) (progn (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 (setq preselect nil)))))))))) ((condition-case nil (progn (file-exists-p (file-name-directory initial-input))) (error nil)) (setq ivy--directory (file-name-directory initial-input)) (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 (file-name-nondirectory initial-input)))))))) (require 'dired) (if preselect (progn (let ((preselect-directory (ivy--parent-dir preselect))) (if (and preselect-directory (not (string= preselect-directory default-directory))) (progn (setq ivy--directory preselect-directory))) (setq preselect (file-relative-name preselect preselect-directory)) (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 preselect)))))) (setq sort nil) (setq coll (ivy--sorted-files ivy--directory)) (if initial-input (progn (if (or require-match (equal initial-input default-directory) (equal initial-input "")) nil (setq coll (cons initial-input coll))) (if (or (not (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 15))) (equal (ivy--get-action ivy-last) 'identity)) (progn (setq initial-input nil)))))) ((eq collection (function internal-complete-buffer)) (setq coll (ivy--buffer-list "" ivy-use-virtual-buffers predicate))) (dynamic-collection (setq coll (funcall collection ivy-text))) ((consp (car-safe collection)) (setq collection (cl-remove-if-not predicate collection)) (if (and sort (setq sort-fn (ivy--sort-function caller))) (progn (setq collection (sort (copy-sequence collection) sort-fn)) (setq sort nil))) (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (let* ((v ivy-last)) (aset v 2 collection))) (setq coll (let ((i -1)) (mapcar (function (lambda (x) (propertize x 'idx (setq i (1+ i))))) (all-completions "" collection))))) ((or (functionp collection) (byte-code-function-p collection) (vectorp collection) (hash-table-p collection) (and (listp collection) (symbolp (car collection)))) (setq coll (all-completions "" collection predicate))) (t (setq coll (if predicate (cl-remove-if-not predicate collection) collection)))) (let (coll sort-fn) (cond ((eq collection (function Info-read-node-name-1)) (setq coll (if (equal (and (boundp 'Info-current-file) Info-current-file) "dir") (mapcar (function (lambda (x) (format "(%s)" x))) (delete-dups (all-completions "(" collection predicate))) (all-completions "" collection predicate)))) ((eq collection (function read-file-name-internal)) (if (and (equal def initial-input) (member "./" ivy-extra-directories)) (progn (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 24 (setq def nil)))))) (setq ivy--directory default-directory) (if (and initial-input (not (equal initial-input ""))) (progn (cond ((file-directory-p initial-input) (if (equal (file-name-nondirectory initial-input) "") (progn (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 (setq preselect nil)))) (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 24 (setq def nil)))))) (setq ivy--directory (file-name-as-directory initial-input)) (setq initial-input nil) (if preselect (progn (let ((preselect-directory (file-name-directory preselect))) (if (and preselect-directory (not (equal (expand-file-name preselect-directory) (expand-file-name ivy--directory)))) (progn (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 (setq preselect nil)))))))))) ((condition-case nil (progn (file-exists-p (file-name-directory initial-input))) (error nil)) (setq ivy--directory (file-name-directory initial-input)) (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 (file-name-nondirectory initial-input)))))))) (require 'dired) (if preselect (progn (let ((preselect-directory (ivy--parent-dir preselect))) (if (and preselect-directory (not (string= preselect-directory default-directory))) (progn (setq ivy--directory preselect-directory))) (setq preselect (file-relative-name preselect preselect-directory)) (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 preselect)))))) (setq sort nil) (setq coll (ivy--sorted-files ivy--directory)) (if initial-input (progn (if (or require-match (equal initial-input default-directory) (equal initial-input "")) nil (setq coll (cons initial-input coll))) (if (or (not (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 15))) (equal (ivy--get-action ivy-last) 'identity)) (progn (setq initial-input nil)))))) ((eq collection (function internal-complete-buffer)) (setq coll (ivy--buffer-list "" ivy-use-virtual-buffers predicate))) (dynamic-collection (setq coll (funcall collection ivy-text))) ((consp (car-safe collection)) (setq collection (cl-remove-if-not predicate collection)) (if (and sort (setq sort-fn (ivy--sort-function caller))) (progn (setq collection (sort (copy-sequence collection) sort-fn)) (setq sort nil))) (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (let* ((v ivy-last)) (aset v 2 collection))) (setq coll (let ((i -1)) (mapcar (function (lambda (x) (propertize x 'idx (setq i (1+ i))))) (all-completions "" collection))))) ((or (functionp collection) (byte-code-function-p collection) (vectorp collection) (hash-table-p collection) (and (listp collection) (symbolp (car collection)))) (setq coll (all-completions "" collection predicate))) (t (setq coll (if predicate (cl-remove-if-not predicate collection) collection)))) (if (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 19)) nil (setq coll (delete "" coll))) (if def (progn (cond ((listp def) (setq coll (cl-union def coll :test (function equal)))) ((not (member def coll)) (setq coll (cons def coll)))))) (if (and sort (or (functionp collection) (not (eq history 'org-refile-history))) (setq sort-fn (ivy--sort-function (if (functionp collection) collection caller))) (null (nthcdr ivy-sort-max-size coll))) (progn (setq coll (sort (copy-sequence coll) sort-fn)))) (setq coll (ivy--set-candidates coll)) (setq ivy--old-re nil) (setq ivy--old-cands nil) (if (integerp preselect) (progn (setq ivy--old-re "") (ivy-set-index preselect))) (if initial-input (progn (setq ivy--old-cands coll) (setq ivy--old-cands (ivy--filter initial-input coll)))) (setq ivy--all-candidates coll) (if (integerp preselect) nil (ivy-set-index (or (and dynamic-collection ivy--index) (and preselect (ivy--preselect-index preselect (if initial-input ivy--old-cands coll))) 0)))) (let* ((prompt (or (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 1)) "")) (collection (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 2))) (predicate (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 3))) (history (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 6))) (preselect (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 7))) (re-builder (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 17))) (dynamic-collection (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 19))) (require-match (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 4))) (caller (or (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 22)) this-command)) (sort (or (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 10)) (assoc caller ivy-sort-functions-alist))) (initial-input (or (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 5)) (cdr (assq caller ivy-initial-inputs-alist)))) (def (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (aref state 24)))) (setq ivy--directory nil) (setq ivy-case-fold-search ivy-case-fold-search-default) (setq ivy--regex-function (or re-builder (and (functionp collection) (cdr (assq collection ivy-re-builders-alist))) (ivy-alist-setting ivy-re-builders-alist) (function ivy--regex))) (setq ivy--subexps 0) (setq ivy--regexp-quote (function regexp-quote)) (setq ivy--old-text "") (setq ivy--full-length nil) (setq ivy-text "") (setq ivy--index 0) (setq ivy-calling nil) (setq ivy-use-ignore ivy-use-ignore-default) (setq ivy--highlight-function (or (cdr (assq ivy--regex-function ivy-highlight-functions-alist)) (function ivy--highlight-default))) (let (coll sort-fn) (cond ((eq collection (function Info-read-node-name-1)) (setq coll (if (equal (and (boundp 'Info-current-file) Info-current-file) "dir") (mapcar (function (lambda (x) (format "(%s)" x))) (delete-dups (all-completions "(" collection predicate))) (all-completions "" collection predicate)))) ((eq collection (function read-file-name-internal)) (if (and (equal def initial-input) (member "./" ivy-extra-directories)) (progn (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 24 (setq def nil)))))) (setq ivy--directory default-directory) (if (and initial-input (not (equal initial-input ""))) (progn (cond ((file-directory-p initial-input) (if (equal (file-name-nondirectory initial-input) "") (progn (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 (setq preselect nil)))) (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 24 (setq def nil)))))) (setq ivy--directory (file-name-as-directory initial-input)) (setq initial-input nil) (if preselect (progn (let ((preselect-directory (file-name-directory preselect))) (if (and preselect-directory (not (equal (expand-file-name preselect-directory) (expand-file-name ivy--directory)))) (progn (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 (setq preselect nil)))))))))) ((condition-case nil (progn (file-exists-p (file-name-directory initial-input))) (error nil)) (setq ivy--directory (file-name-directory initial-input)) (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 (file-name-nondirectory initial-input)))))))) (require 'dired) (if preselect (progn (let ((preselect-directory (ivy--parent-dir preselect))) (if (and preselect-directory (not (string= preselect-directory default-directory))) (progn (setq ivy--directory preselect-directory))) (setq preselect (file-relative-name preselect preselect-directory)) (progn (or (and (memq (type-of state) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state state))) (let* ((v state)) (aset v 7 preselect)))))) (setq sort nil) (setq coll (ivy--sorted-files ivy--directory)) (if initial-input (progn (if (or require-match (equal initial-input default-directory) (equal initial-input "")) nil (setq coll (cons initial-input coll))) (if (or (not (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 15))) (equal (ivy--get-action ivy-last) 'identity)) (progn (setq initial-input nil)))))) ((eq collection (function internal-complete-buffer)) (setq coll (ivy--buffer-list "" ivy-use-virtual-buffers predicate))) (dynamic-collection (setq coll (funcall collection ivy-text))) ((consp (car-safe collection)) (setq collection (cl-remove-if-not predicate collection)) (if (and sort (setq sort-fn (ivy--sort-function caller))) (progn (setq collection (sort (copy-sequence collection) sort-fn)) (setq sort nil))) (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (let* ((v ivy-last)) (aset v 2 collection))) (setq coll (let ((i -1)) (mapcar (function (lambda (x) (propertize x 'idx (setq i (1+ i))))) (all-completions "" collection))))) ((or (functionp collection) (byte-code-function-p collection) (vectorp collection) (hash-table-p collection) (and (listp collection) (symbolp (car collection)))) (setq coll (all-completions "" collection predicate))) (t (setq coll (if predicate (cl-remove-if-not predicate collection) collection)))) (if (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 19)) nil (setq coll (delete "" coll))) (if def (progn (cond ((listp def) (setq coll (cl-union def coll :test (function equal)))) ((not (member def coll)) (setq coll (cons def coll)))))) (if (and sort (or (functionp collection) (not (eq history 'org-refile-history))) (setq sort-fn (ivy--sort-function (if (functionp collection) collection caller))) (null (nthcdr ivy-sort-max-size coll))) (progn (setq coll (sort (copy-sequence coll) sort-fn)))) (setq coll (ivy--set-candidates coll)) (setq ivy--old-re nil) (setq ivy--old-cands nil) (if (integerp preselect) (progn (setq ivy--old-re "") (ivy-set-index preselect))) (if initial-input (progn (setq ivy--old-cands coll) (setq ivy--old-cands (ivy--filter initial-input coll)))) (setq ivy--all-candidates coll) (if (integerp preselect) nil (ivy-set-index (or (and dynamic-collection ivy--index) (and preselect (ivy--preselect-index preselect (if initial-input ivy--old-cands coll))) 0)))) (setq ivy-exit nil) (setq ivy--default (if (region-active-p) (buffer-substring (region-beginning) (region-end)) (ivy-thing-at-point))) (setq ivy--prompt (ivy-add-prompt-count prompt)) (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (let* ((v ivy-last)) (aset v 5 initial-input)))) ivy--reset-state(#s(ivy-state :prompt "Menu item: " :collection Info-complete-menu-item :predicate nil :require-match t :initial-input nil :history nil :preselect "" :keymap nil :update-fn nil :sort t :frame # :window #Emacs 26.1, swiper commit: d76968a85f9dc5dcebdc25eb8e3af2cd2775319e