3

I'm trying to ignore some file patterns while performing helm-projectile-grep and some with helm-projectile-find-file. I've found that it could be achieved using .dir-locals.el here.
Following this chunk of documentation I've made my .dir-locals.el looks like:

((nil
  (grep-find-ignored-files . '("*.log"))
  (projectile-globally-ignored-files . '("*.log"))))

When helm-projectile-grep is called it yields an error in helm-projectile:

Error running timer `helm-projectile-grep-or-ack': (wrong-type-argument stringp quote)

Since both emacs manual and projectile docs are sketchy about .dir-locals.el and it's usage I have few questions?

  • How .dir-locals.el works? Does it merge global and dir-local value of an variable? If I have a variable a which is '("a" "b") and set it in dir-local to '("c"), what is its value that Emacs use when I perform an operation in certain scope of .dir-locals.el?
  • What kind of structure is expected in .dir-locals.el for grep-find-ignored-files and projectile-globally-ignored-files?

On Projectile itself:

  • Why Projectile helm-projectile-grep and helm-projectile-find-file don't ignore file patterns which are already ignored in .gitignore? Is it turned off somewhere in my config?

Solution

According to accepted answer, if you want to ignore e.g all the .md files from Projectile operations in your project using .dir-locals.el you should do something like this:

((nil
  (eval . (set (make-local-variable 'projectile-globally-ignored-files)
               (append projectile-globally-ignored-files
                       (f-entries (projectile-project-root)
                                  (lambda (f) (string-match "\\.md$" f)) t))))))
  • 1. I can't reproduce your error with the same setup. You need to toggle-debug-on-error to see where it's coming from. 2. projectile does ignore files in .gitignore, at least I am certain about helm-projectile-find-files. It's impossible to say why you see a different behavior without dissecting your init.el. – Yuri Steinschreiber Jul 28 '16 at 20:14
  • @YuriSteinschreiber Sorry, I've forgot to mention that one should call helm-projectile-grep after visiting some file in project to see exactly the same error as I've seen. Question is updated now. – foki Jul 28 '16 at 21:47
  • @YuriSteinschreiber You didn't even got wrong-type-argument stringp quote on visiting file when you use .dir-locals.el from above? Debugger says that projectile try to expand-file-name(quote "/my/project/root/"). It would be very strange to me if your Projectile doesn't try to do the same. – foki Jul 28 '16 at 21:57
  • Now after visiting a file I get the same behavior, and it is coming from the fact that the value is interpreted literally, so it's a list with a quote symbol as the first element. Which is not a string, and that's the error. Looks weird and doesn't match either Projectile docs or my understanding. I have deleted my partial answer while I am trying to understand the reason (and maybe correct myself if I was wrong somewhere.) – Yuri Steinschreiber Jul 28 '16 at 22:19
  • Yes, that's what confusing me - it looks like variable values are interpreted literally. Thank you for your effort anyway. – foki Jul 28 '16 at 22:30
7
  1. .dir-locals.el does not care about the meaning of the variables that it sets, nor does it assume their values are lists (they do not have to be.) So it does not perform any merge or other processing. The only thing that's been taken into account is precedence which is described in the documentation you have linked to.

  2. The values are interpreted as-is, emacs uses read function to get them, so they are unevaluated. That's what causes the error - do not use a quote, use straight values.

  3. To get the effect of merging, you need to do it yourself, and since the values are unevaluated you need to use a special variable eval, like this:

((nil
   (eval . (setq a-variable-you-need-to-merge (add-to-list whatever-you-need))))

Update: as pointed out in the comments, using setq in eval will change the global value of the variable (unless of course the variable was declared local to begin with.) To make the change local, make-local-variable can be used, so for example changing projectile-globally-ignored-files locally can be done like this:

((nil
   (eval . 
         (set 
          (make-local-variable 'projectile-globally-ignored-files)
          (push "SOME-VALUE" projectile-globally-ignored-files)))))

It is a bit sloppy, and can certainly be done shorter, but I just wanted to make a point of using make-local-variable.

(I would definitely make a helper out of this kind of functions and put it in init.el and just called from .dir-locals.el)

  1. As I said, .dir-locals.el does not assume anything about the variables, so the requirements are the same as if they were set in any other way. And as far as I can tell from looking at projectile source, projectile-globally-ignored-files does not expect blobs (asterisks etc.) but rather single file names, so you will have to either list them all or write an expression that calculates them and use eval with it.

  2. You should have gotten a warning from Emacs about some local variables not being safe (because of *.) Make sure you haven't ignored the variable list, otherwise your .dir-locals.el will have no effect.

Update regarding .gitignore:

Seems that Projectile takes .gitignore file into account only if the project is indeed under git, that is, a git repository exists.

  • Doesn't this setq should looks like (setq a-variable-you-need-to-merge (add-to-list a-variable-you-need-to-merge whatever-you-need)) ? Does this mean that Projectile's docs is wrong, do we need to open an GH issue on this? – foki Jul 29 '16 at 9:32
  • 1. Yes, you can setq like this. You can actually put an arbitrary expression there, so you can create a helper function and just call it from there. 2. Yes, I think it's a documentation bug, – Yuri Steinschreiber Jul 29 '16 at 17:25
  • 1
    Yes, it works with make-local-variable. I've updated the answer. – Yuri Steinschreiber Jul 29 '16 at 21:35
  • 1
    Sure, I opened a project file, checked that the projectile-globally-ignored-files got the value set in .dir-local.el (and emacs correctly said it was buffer local,) and then checked it in another buffer outside of the project - no change. The point is make-local-variable is evaluated before push, so it's a local variable that's affected. So set in this particular case is not necessary, I said it could be done shorter - it's just an example. – Yuri Steinschreiber Jul 30 '16 at 17:16
  • 1
    Weird indeed. I noticed I was using a destructive function myself, but was in a hurry to check it and since make-local-variable was first to be evaluated decided to give it a go. Testing was a bit of a headache by itself, I needed to make sure all buffers are killed before testing changes, ended up killing and restarting emacs to make certain. As soon as I saw it worked I went ahead and posted it. (I am not fluent enough in elisp, and working in a few Lisps simultaneously doesn't make it easier. Sorry if it caused a few more attempts on your side.) – Yuri Steinschreiber Jul 30 '16 at 21:13

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.