0

I implemented a couple of extra functions that work with projectile and have been keeping them in my dotfiles as they are not yet a part of the original package. When the functions are defined in the file packages.el after line number 130, all lexical bindings work well.

However, when I tried extracting the two functions into a separate file here and require them somewhere down the line, it throws an error saying "Symbol’s value as variable is void: text-to-search". Apparently the closures are not getting created correctly.

(defun projectile-find-all-occurrences (&optional arg)
  "List all occurrences of the text being searched using completion.
With a prefix argument ARG prompts you for a directory on which to run search in."
  (interactive "P")
  (let* ((text-to-search (read-string
                          (projectile-prepend-project-name "Find all: ")
                          (projectile-symbol-or-selection-at-point)))
         (directory (if arg
                        (file-name-as-directory
                         (read-directory-name "Find in directory: "))
                      (projectile-project-root)))
         (file-to-search (projectile-files-with-string text-to-search directory)))
    (projectile-completing-read
     "All occurrences: "
     (reduce #'append
             (mapcar (lambda (file)
                       (with-temp-buffer
                         (insert-file-contents file)
                         (let ((lines (split-string (buffer-string) "\n")))
                           (cl-remove-if nil
                                         (mapcar (lambda (line)
                                                   (let ((present-in-linep (search text-to-search line)))
                                                     (cond (present-in-linep (concat (cadr (split-string file (projectile-project-root)))
                                                                                     " => line "
                                                                                     (number-to-string (1+ (cl-position line lines)))))
                                                           (t nil))))
                                                 lines)))))
                     file-to-search))
     :action `(lambda (item)
                (let ((file (car (split-string item " => line ")))
                      (line-number (string-to-number (cadr (split-string item " => line ")))))
                  (find-file (expand-file-name file ,(projectile-project-root)))
                  (beginning-of-buffer)
                  (forward-line (1- line-number))
                  (search-forward text-to-search)
                  (set-mark-command nil)
                  (search-backward text-to-search)
                  (run-hooks 'projectile-find-file-hook))))))

I have tried too many things to finally give up and cannot think of any way to make it work myself now. Seems to be that I am missing something too obvious to notice or is it...?

  • 1
    A question is more useful if the meaning is in the question itself. Here, it is impossible to know what the question is really about without following links. It doesn't help to refer readers to whole files (including dotfiles, no less). This question could benefit from being self-contained and pared down to what's relevant. Please see How To Ask. – Drew Oct 2 '17 at 2:56
  • This is not my first question on StackExchange and I have asked better questions earlier. I have been so frustrated with this problem of mine and after spending so much time to figure this out myself, I was not in a state to think more on breaking the scope of the question down to plain text, hence a question full of references. I certainly know how to ask a question, though I have not been my best this time. – myTerminal Oct 2 '17 at 4:12
  • If you don't mind me asking. Is it your intent that (projectile-project-root) be a compile-time constant ? – politza Oct 2 '17 at 15:44
  • @politza, I do not think I need it that way now. I remember I had issues with binding that expression earlier and ended up putting it that way. Thanks for pointing out though. – myTerminal Oct 2 '17 at 15:56
2

You have a quoted text-to-search in those :action forms (because the entire form is quoted), which therefore is not the same thing as your lexically-bound variable; so when those forms are called as functions, they are looking for a dynamic scope variable of that name.

So that looks like a bug no matter where the functions were defined. Presumably it was being masked in your original scenario.

  • It seems to work when I replace text-to-search with ,@(list text-to-search), something around what it shows here: gnu.org/software/emacs/manual/html_node/elisp/Backquote.html, but I am not convinced with creating a single element list for the purpose, any better ways to achieve that? – myTerminal Oct 2 '17 at 14:11
  • @myTerminal: I think ,@(list foo) should always be the same as just ,foo. – npostavs Oct 2 '17 at 14:29
  • @npostavs: ,foo does not seem to work, as I have tried it before – myTerminal Oct 2 '17 at 14:49
  • @myTerminal: I can't think of any case where the equivalence doesn't hold. As Drew suggested, posting a minimal example would be helpful. – npostavs Oct 2 '17 at 15:34
  • @npostavs, ,foo works, I missed to update another reference earlier (I had two of them) and made myself think that it did not work, Thanks. – myTerminal Oct 2 '17 at 16:09
0

Replacing instances of text-to-search with ,text-to-search solves the problem.

(lambda (item)
  (let ((file (car (split-string item " => line ")))
        (line-number (string-to-number (cadr (split-string item " => line ")))))
    (find-file (expand-file-name file (projectile-project-root)))
    (beginning-of-buffer)
    (forward-line (1- line-number))
    (search-forward ,text-to-search)
    (set-mark-command nil)
    (search-backward ,text-to-search)
    (run-hooks 'projectile-find-file-hook)))
  • This is not the way to create a closure (though perhaps that wasn't actually your intention) – npostavs Oct 3 '17 at 14:50
  • I'm still learning Emacs Lisp and this is apparently the only way it works for me. Any better alternatives are appreciated! :) – myTerminal Oct 3 '17 at 16:42
  • 1
    Remove the apostrophe in front of the lambda and all commas inside. – politza Oct 3 '17 at 17:35
  • Actually that seems to work. – myTerminal Oct 3 '17 at 18:17
  • (elisp) Lexical Binding has an example of creating a closure. – npostavs Oct 3 '17 at 19:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.