projectile icon indicating copy to clipboard operation
projectile copied to clipboard

cannot exit projectile-skel-dir-locals while keeping the variables already entered

Open st0nie opened this issue 1 year ago • 3 comments

Expected behavior

When I enter an empty variable name, the template should exit and retain the previous input.

Projectile works well, but there seems to be no detailed documentation on how to properly use edit .dir-locals.el, I tried to use projectile-skel-dir-locals to edit it, but I can't exit the loop of input variables, if I press C-g or esc, my minibuffer will If I press C-g or esc, my minibuffer will close and the variables that have been entered before will disappear. I tried to exit the switch-case skeleton as in sh-mode by typing an empty string, but that doesn't work either.

I use vertico, and when I try to use a minimal configuration with only projectile, I also cannot exit the input loop of the skeleton correctly. This causes me to enter the input loop, press c-g to exit, and only the first input variable is saved, the rest of the variable input disappears.

What is the correct way to use projectile-skel-dir-locals, please help me out and thanks in advance!

The correct behavior should be like this

((nil . ((compile-command . "make -k"))

Actual behavior

((nil . ((compile-command . "make -k")
           ( .

Steps to reproduce the problem

M-x projectile-skel-dir-locals <cr>
c-file-style <cr>
"llvm.org" <cr>
M-x vertico-exit-input

Environment & Version information

Projectile version information

Projectile 20230317.1101

Emacs version

GNU Emacs 29.0.91 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.38, cairo version 1.17.8) of 2023-05-26

Operating system

E.g. Gentoo Linux 2.13

st0nie avatar Jun 04 '23 02:06 st0nie

To add to that projectile-edit-dir-locals behaves similar, although M-x vertico-exit-input will also delete the fresh content (projectile 2.8.0).

neuhalje avatar Dec 15 '23 08:12 neuhalje

Also observing this. I'm not familiar with the Emacs skeleton system, but want to dig into the interaction between it and Projectile's define-skeleton to discover the root cause.

jaccarmac avatar Jan 07 '24 02:01 jaccarmac

I found that Emacs 27.2 and before did not have this problem, while Emacs 28.1 and after

  • Clears out the whole skeleton on C-g at the prompt.
  • Keeps on looping even with an empty variable name.

Looking at the diff of skeleton.el between 27.2 and 28.1, the most significant change seems to be this commit that migrates it to lexical binding, but I'm not sure if that's the change that causes the issue.

Missing str in sub-skeleton?

(define-skeleton projectile-skel-dir-locals
  "Insert a .dir-locals.el template."
  nil
  "((nil . ("
  ; This sub-skeleton doesn't have a `str`?
  ("" '(projectile-skel-variable-cons) \n)
  resume:
  ")))")

One thing I noticed is that in the current implementation, the skeleton has a sub-skeleton without a str. On the Skeleton Language page on the Emacs manual, it's stated that a sub-skeleton must have a str (emphasis by myself):

skeleton Subskeletons are inserted recursively, not once, but as often as the user enters something at the subskeletons interactor. Thus there must be a str in the subskeleton. They can also be used non-interactively, when prompt is a lisp-expression that returns successive list-elements.

The code of skeleton.el itself is too difficult for me to fully understand, so I'm not sure if this requirement stated on the manual is still the case.

I came up with a version of projectile-skel-dir-locals that, unlike the current implementation, has a str in the sub-skeleton. This version terminates properly on both empty variable name and empty value. It still wipes out the whole skeleton on C-g[^1], but terminating on empty variable name is good enough for me.

 (define-skeleton projectile-skel-dir-locals
   "Insert a .dir-locals.el template."
   nil
   "((nil . ("
-  ("" '(projectile-skel-variable-cons) \n)
+  ("Value: "
+   "("
+   (let ((var-name (projectile-read-variable)))
+                   (if (string-empty-p var-name)
+                       ;; Stop the sub-skeleton iteration on empty variable name.
+                       (signal 'quit t)
+                     var-name))
+   " . " str ")" \n)
   resume:
   ")))")

[^1]: python-skeleton-if also wipes out the whole skeleton on C-g starting from Emacs 28.1.

kotatsuyaki avatar May 16 '24 03:05 kotatsuyaki