Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

### Bugs fixed

- [#2170](https://github.com/bbatsov/projectile/pull/2170): Navigating a search results buffer while the scan is still running is no longer undone by the next chunk. Each redraw ended by going to the top of the buffer, so point jumped back on every batch of matches; it now stays where you left it.
- [#2167](https://github.com/bbatsov/projectile/pull/2167): The Emacs Lisp search scanner no longer decompresses or decrypts the files it reads. It went through `file-name-handler-alist`, so every archive in the candidate set was run through gzip and every `.gpg` handed to EPA - work discarded a moment later as binary, and on an encrypted file a passphrase prompt in the middle of a project search.
- Matches inside compressed files are no longer reported by that scanner, which is what the ripgrep path already did.
- [#2162](https://github.com/bbatsov/projectile/pull/2162): Listing another project's files now applies that project's own ignore rules instead of the rules of whichever project you happen to be visiting. `projectile-find-file-in-known-projects` and the sibling commands used to filter every other project by the current one's dirconfig, and with caching on they stored that wrong list under the other project's key - so an ordinary `projectile-find-file` there kept offering ignored files, and with persistent caching it survived a restart.
Expand Down
3 changes: 3 additions & 0 deletions doc/modules/ROOT/pages/usage.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ results stream in rather than happening on every chunk. The redraw that
settles a finished scan is unconditional, so throttling only ever delays an
intermediate state. Set the option to nil to redraw on every chunk.

A redraw keeps point where you left it, so you can start reading and
navigating the results while the rest of them are still arriving.

kbd:[!] applies the enabled matches with no external dependency. If you'd
rather edit the results as text, kbd:[e] exports the enabled matches (the
same set kbd:[!] would act on) to a `+*projectile-grep*+` buffer in `grep-mode`,
Expand Down
14 changes: 9 additions & 5 deletions projectile.el
Original file line number Diff line number Diff line change
Expand Up @@ -9799,7 +9799,7 @@ killed BUFFER (leaving no work behind) and against \\`C-g' during a chunk
(with-current-buffer buffer
(setq projectile-replace--scanning nil
projectile-replace--scan-timer nil)
(funcall projectile-replace--render-function)
(projectile-replace--render-preserve)
(when on-done (funcall on-done buffer)))))
(quit
(when (buffer-live-p buffer)
Expand Down Expand Up @@ -9969,9 +9969,13 @@ async scan streams matches in."
(goto-char (point-min))))

(defun projectile-replace--render-preserve ()
"Redraw the results buffer, keeping point on the same line."
"Redraw the results buffer, keeping point on the same line.

Restoring by line rather than by position is what makes this safe while a
scan is streaming: matches are appended and a file header keeps its line
count when its tally grows, so the lines already on screen do not move."
(let ((line (line-number-at-pos)))
(projectile-replace--render)
(funcall projectile-replace--render-function)
(goto-char (point-min))
(forward-line (1- line))))

Expand Down Expand Up @@ -10817,7 +10821,7 @@ against a killed BUFFER."
(setq projectile-replace--scanning nil
projectile-replace--scan-process nil
projectile-replace--scan-timer nil)
(funcall projectile-replace--render-function)
(projectile-replace--render-preserve)
(when on-done (funcall on-done buffer)))))

(defun projectile-search--gather-rg (buffer term on-done)
Expand Down Expand Up @@ -10957,7 +10961,7 @@ unconditional, so a skipped intermediate draw is never the last word."
(>= (- now projectile-replace--last-render)
projectile-search-render-interval))
(setq projectile-replace--last-render now)
(funcall projectile-replace--render-function))))
(projectile-replace--render-preserve))))

(defun projectile-replace--start (buffer candidates regexp on-done)
"Fill BUFFER's match list by scanning CANDIDATES for REGEXP.
Expand Down
51 changes: 51 additions & 0 deletions test/projectile-search-review-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,57 @@ REGEXP-P selects `projectile-search-regexp-review'."
(dotimes (_ 5) (projectile-replace--render-progress))
(expect 'projectile-search--render :to-have-been-called-times 5))))

(describe "point while results stream in"
(it "keeps point where the user put it across a streaming redraw"
;; The buffer is redrawn from scratch, and the redraw ends by going to
;; point-min - so navigating while a scan was still running used to be
;; undone by the next chunk.
(projectile-test-with-project
(("a.txt" . "foo\nfoo\nfoo\n"))
(let ((buf (get-buffer-create projectile-search-buffer-name))
(dir default-directory))
(with-current-buffer buf
(projectile-replace--seed buf #'projectile-search-mode
dir "foo" "foo" nil t t)
(setq projectile-replace--matches
(mapcar (lambda (i)
(projectile-replace--match-create
:file (expand-file-name "a.txt" dir)
:line i :column 0 :string "foo"
:context "foo here" :enabled t))
(number-sequence 1 6)))
(projectile-search--render)
(goto-char (point-min))
(dotimes (_ 3) (projectile-replace--goto-next-match))
(let ((line (line-number-at-pos))
(match (projectile-replace--match-at-point)))
(expect match :to-be-truthy)
;; another chunk lands and the buffer is redrawn
(setq projectile-replace--matches
(append projectile-replace--matches
(list (projectile-replace--match-create
:file (expand-file-name "a.txt" dir)
:line 7 :column 0 :string "foo"
:context "foo here" :enabled t))))
(let ((projectile-search-render-interval nil))
(projectile-replace--render-progress))
(expect (line-number-at-pos) :to-equal line)
(expect (projectile-replace--match-line
(projectile-replace--match-at-point))
:to-equal (projectile-replace--match-line match)))))))

(it "leaves point at the top when the user has not moved it"
(assume (executable-find "rg") "ripgrep is not installed")
(projectile-test-with-project
(("a.txt" . "foo bar\n"))
(let ((buf (get-buffer-create projectile-search-buffer-name)))
(projectile-replace--seed buf #'projectile-search-mode
default-directory "foo" "foo" nil t t)
(projectile-search--gather-rg buf "foo" nil)
(projectile-search-review-test--wait buf)
(with-current-buffer buf
(expect (line-number-at-pos) :to-equal 1))))))

(describe "streaming redraw throttling"
(it "always redraws when a scan finishes, however long the interval"
(assume (executable-find "rg") "ripgrep is not installed")
Expand Down
Loading