Skip to content

Commit 10fa399

Browse files
committed
Let comint mode cover the tasks too [Fix #2156]
`projectile-use-comint-mode' promises an interactive buffer for what Projectile runs, and `t' reads as all of it - but `projectile--run-task' never passed the flag on, so a task always landed in a read-only compilation buffer. The reporter's case is a system rebuild that needs a sudo password: there is nowhere to type it, so the task simply can't be run. Not a regression from folding the six per-command options into one in 3.4 - no per-task option ever existed - but the single option reads like it covers everything, and tasks go through the same runner and the same command history. So they're covered now: `task' can be named in the list, and `t' includes them. `projectile-use-comint-mode-p' also returns a real boolean now. It was handing back whatever `memq' found, so a list-valued option sent its own tail into `compile' as the COMINT argument. Harmless, since only its truthiness was ever read, but it made the new spec assert on `(task)'.
1 parent fcd8f2b commit 10fa399

6 files changed

Lines changed: 92 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
## master (unreleased)
88

9+
### Bugs fixed
10+
11+
- [#2157](https://github.com/bbatsov/projectile/pull/2157): `projectile-use-comint-mode` now covers the named tasks run by `projectile-run-task`, which were always given a read-only compilation buffer however it was set - so a task that needs to ask for a sudo password had nowhere to type one ([#2156](https://github.com/bbatsov/projectile/issues/2156)). Name `task` in the list, or set the option to `t`.
12+
913
## 3.4.0 (2026-08-10)
1014

1115
### New features

doc/modules/ROOT/pages/configuration_index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ and the other reference pages.
372372
| Controls whether Projectile will automatically register known projects.
373373

374374
| `projectile-use-comint-mode`
375-
| Which lifecycle commands get an interactive output buffer.
375+
| Which of the commands Projectile runs get an interactive output buffer.
376376

377377
| `projectile-use-git-grep`
378378
| Whether ‘projectile-grep’ delegates to ‘vc-git-grep’ in git projects.

doc/modules/ROOT/pages/projects.adoc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,20 +1259,25 @@ your project, you could customize it with the following:
12591259
By default, compilation buffers are not writable, which allows you to
12601260
e.g. press `g` to restart the last command. `projectile-use-comint-mode`
12611261
makes them interactive instead, letting you e.g. test a command-line
1262-
program with `projectile-run-project`. Set it to `t` for every lifecycle
1263-
command, or to a list of the ones you want:
1262+
program with `projectile-run-project`, or answer a task's sudo prompt.
1263+
Set it to `t` for everything Projectile runs, or to a list of the ones
1264+
you want:
12641265

12651266
[source,elisp]
12661267
----
12671268
;; only the compile buffer is interactive
12681269
(setq projectile-use-comint-mode '(compile))
12691270
1270-
;; all of them are
1271+
;; the named tasks are too
1272+
(setq projectile-use-comint-mode '(compile task))
1273+
1274+
;; everything is
12711275
(setq projectile-use-comint-mode t)
12721276
----
12731277

1274-
The phases you can name are `configure`, `compile`, `test`, `install`,
1275-
`package` and `run`.
1278+
You can name the lifecycle phases - `configure`, `compile`, `test`,
1279+
`install`, `package` and `run` - and `task` for the named tasks run by
1280+
`projectile-run-task`.
12761281

12771282
== Project buffers
12781283

projectile.el

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12448,26 +12448,28 @@ The command actually run is returned."
1244812448
command))
1244912449

1245012450
(defcustom projectile-use-comint-mode nil
12451-
"Which lifecycle commands get an interactive output buffer.
12451+
"Which of the commands Projectile runs get an interactive output buffer.
1245212452

12453-
The lifecycle commands report through `compilation-mode', which is
12454-
read-only. For a command covered here Projectile uses `comint-mode'
12455-
instead, so a build that asks a question, or a test runner that drops
12456-
into a debugger, can be typed at.
12453+
Projectile reports through `compilation-mode', which is read-only. For a
12454+
command covered here it uses `comint-mode' instead, so a build that asks a
12455+
question, a test runner that drops into a debugger, or a task that wants a
12456+
sudo password can be typed at.
1245712457

12458-
The value is nil (no command is interactive), t (all of them), or a
12459-
list naming the ones that are - `configure', `compile', `test',
12460-
`install', `package' and `run'."
12458+
The value is nil (nothing is interactive), t (everything is), or a list
12459+
naming what is - the lifecycle phases `configure', `compile', `test',
12460+
`install', `package' and `run', and `task' for the named tasks run by
12461+
`projectile-run-task'."
1246112462
:group 'projectile
12462-
:type '(choice (const :tag "No command" nil)
12463-
(const :tag "Every command" t)
12463+
:type '(choice (const :tag "Nothing" nil)
12464+
(const :tag "Everything" t)
1246412465
(set :tag "Selected commands"
1246512466
(const :tag "Configure" configure)
1246612467
(const :tag "Compile" compile)
1246712468
(const :tag "Test" test)
1246812469
(const :tag "Install" install)
1246912470
(const :tag "Package" package)
12470-
(const :tag "Run" run)))
12471+
(const :tag "Run" run)
12472+
(const :tag "Tasks" task)))
1247112473
:package-version '(projectile . "3.4.0"))
1247212474

1247312475
;; Remove in 4.0, this block and the fallback in
@@ -12501,13 +12503,19 @@ list naming the ones that are - `configure', `compile', `test',
1250112503

1250212504
(defun projectile-use-comint-mode-p (phase)
1250312505
"Return non-nil when PHASE's output buffer should be interactive.
12504-
PHASE is a lifecycle phase symbol such as `compile'. Reads
12505-
`projectile-use-comint-mode', falling back to the obsolete per-phase
12506-
option it replaced for a configuration that still sets one."
12507-
(or (eq projectile-use-comint-mode t)
12508-
(memq phase projectile-use-comint-mode)
12509-
(when-let* ((var (alist-get phase projectile--obsolete-comint-vars)))
12510-
(symbol-value var))))
12506+
PHASE is a lifecycle phase symbol such as `compile', or `task' for the
12507+
named tasks - which run through the same machinery and are covered by the
12508+
same option (see issue #2156). Reads `projectile-use-comint-mode',
12509+
falling back to the obsolete per-phase option it replaced for a
12510+
configuration that still sets one; the tasks never had one of those."
12511+
;; Normalized to a boolean: `memq' would otherwise hand the caller the
12512+
;; tail of the option's list, which then travels all the way into
12513+
;; `compile' as its COMINT argument.
12514+
(and (or (eq projectile-use-comint-mode t)
12515+
(memq phase projectile-use-comint-mode)
12516+
(when-let* ((var (alist-get phase projectile--obsolete-comint-vars)))
12517+
(symbol-value var)))
12518+
t))
1251112519

1251212520
(defun projectile--phase-command-dynamic-p (phase)
1251312521
"Non-nil when PHASE's command comes from a function for the current project.
@@ -13145,6 +13153,7 @@ the `%p' placeholder still intact."
1314513153
(concat "<" (projectile-project-name project-root) ">")))))
1314613154
(projectile--run-project-cmd command nil
1314713155
:save-buffers t
13156+
:use-comint-mode (projectile-use-comint-mode-p 'task)
1314813157
:buffer-name-function (lambda (_mode) buffer-name)))
1314913158
;; `command-map' is nil above, so `projectile--run-project-cmd' records
1315013159
;; nothing; record the command - before `%p' expansion, like the other

test/projectile-commands-test.el

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,19 @@
10611061
(let ((projectile-use-comint-mode nil)
10621062
(projectile-test-use-comint-mode t))
10631063
(expect (projectile-use-comint-mode-p 'test) :to-be-truthy)
1064-
(expect (projectile-use-comint-mode-p 'compile) :to-be nil))))
1064+
(expect (projectile-use-comint-mode-p 'compile) :to-be nil)))
1065+
1066+
(it "covers the named tasks too"
1067+
;; Tasks run through the same machinery as the lifecycle phases, so
1068+
;; `t' has to mean them as well (issue #2156).
1069+
(let ((projectile-use-comint-mode t))
1070+
(expect (projectile-use-comint-mode-p 'task) :to-be-truthy))
1071+
(let ((projectile-use-comint-mode '(task)))
1072+
(expect (projectile-use-comint-mode-p 'task) :to-be-truthy)
1073+
(expect (projectile-use-comint-mode-p 'compile) :to-be nil))
1074+
(let ((projectile-use-comint-mode '(compile)))
1075+
(expect (projectile-use-comint-mode-p 'task) :to-be nil))
1076+
(let ((projectile-use-comint-mode nil))
1077+
(expect (projectile-use-comint-mode-p 'task) :to-be nil))))
10651078

10661079
;;; projectile-commands-test.el ends here

test/projectile-tasks-test.el

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,42 @@ main.o: main.c
388388
(expect (ring-elements (projectile--get-command-history "/proj/"))
389389
:to-equal '("make lint"))))
390390

391+
(it "gives a task an interactive buffer when comint mode covers everything"
392+
;; A task can be something like a sudo rebuild, which is unusable in a
393+
;; read-only compilation buffer - there's nowhere to type the password
394+
;; (issue #2156).
395+
(spy-on 'projectile-run-compilation)
396+
(spy-on 'projectile-completing-read :and-return-value "rebuild")
397+
(let ((projectile-use-comint-mode t)
398+
(projectile-project-command-history (make-hash-table :test 'equal))
399+
(projectile-last-task-map (make-hash-table :test 'equal))
400+
(projectile-tasks '(("rebuild" . "sudo make install"))))
401+
(projectile-run-task nil)
402+
(expect 'projectile-run-compilation
403+
:to-have-been-called-with "sudo make install" t)))
404+
405+
(it "gives a task an interactive buffer when the list names `task'"
406+
(spy-on 'projectile-run-compilation)
407+
(spy-on 'projectile-completing-read :and-return-value "rebuild")
408+
(let ((projectile-use-comint-mode '(task))
409+
(projectile-project-command-history (make-hash-table :test 'equal))
410+
(projectile-last-task-map (make-hash-table :test 'equal))
411+
(projectile-tasks '(("rebuild" . "sudo make install"))))
412+
(projectile-run-task nil)
413+
(expect 'projectile-run-compilation
414+
:to-have-been-called-with "sudo make install" t)))
415+
416+
(it "leaves a task alone when the list names only lifecycle phases"
417+
(spy-on 'projectile-run-compilation)
418+
(spy-on 'projectile-completing-read :and-return-value "rebuild")
419+
(let ((projectile-use-comint-mode '(compile test))
420+
(projectile-project-command-history (make-hash-table :test 'equal))
421+
(projectile-last-task-map (make-hash-table :test 'equal))
422+
(projectile-tasks '(("rebuild" . "sudo make install"))))
423+
(projectile-run-task nil)
424+
(expect 'projectile-run-compilation
425+
:to-have-been-called-with "sudo make install" nil)))
426+
391427
(it "confirms the command before running by default"
392428
;; Task commands can come from a checked-out .dir-locals.el, so the
393429
;; run-time confirmation (like compile's) is a security requirement,

0 commit comments

Comments
 (0)