diff options
| author | Dmitry Gutov | 2019-01-18 06:38:12 +0300 |
|---|---|---|
| committer | Dmitry Gutov | 2019-01-18 06:46:36 +0300 |
| commit | fbe87d0f8f8878b30b1dfe74f7eb369b569bab6b (patch) | |
| tree | 56c7e1e3277e1e738f7d2c552ad7e026eec5df1d | |
| parent | afc8a41f4889b0b207bbd1c30fa9f310437b439e (diff) | |
| download | emacs-fbe87d0f8f8878b30b1dfe74f7eb369b569bab6b.tar.gz emacs-fbe87d0f8f8878b30b1dfe74f7eb369b569bab6b.zip | |
Rebase project-find-regexp on top of project-files
* lisp/progmodes/project.el (project--files-in-directory):
New function.
(project-files, project-find-regexp): Use it.
(project--dir-ignores): New function.
(project--find-regexp-in): Remove.
(project--process-file-region): New function.
(project--find-regexp-in-files): New function.
(project-find-regexp, project-or-external-find-regexp): Use it, and
project-files as well.
| -rw-r--r-- | lisp/progmodes/project.el | 139 |
1 files changed, 107 insertions, 32 deletions
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index c16b2578ebf..f795c36fa06 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el | |||
| @@ -184,17 +184,30 @@ to find the list of ignores for each directory." | |||
| 184 | (require 'xref) | 184 | (require 'xref) |
| 185 | (cl-mapcan | 185 | (cl-mapcan |
| 186 | (lambda (dir) | 186 | (lambda (dir) |
| 187 | (let ((command | 187 | (project--files-in-directory dir (project-ignores project dir))) |
| 188 | (format "%s %s %s -type f -print0" | ||
| 189 | find-program | ||
| 190 | (shell-quote-argument | ||
| 191 | (expand-file-name dir)) | ||
| 192 | (xref--find-ignores-arguments | ||
| 193 | (project-ignores project dir) | ||
| 194 | (expand-file-name dir))))) | ||
| 195 | (split-string (shell-command-to-string command) "\0" t))) | ||
| 196 | (or dirs (project-roots project)))) | 188 | (or dirs (project-roots project)))) |
| 197 | 189 | ||
| 190 | (defun project--files-in-directory (dir ignores &optional files) | ||
| 191 | (require 'find-dired) | ||
| 192 | (defvar find-name-arg) | ||
| 193 | (let ((command (format "%s %s %s -type f %s -print0" | ||
| 194 | find-program | ||
| 195 | dir | ||
| 196 | (xref--find-ignores-arguments | ||
| 197 | ignores | ||
| 198 | (expand-file-name dir)) | ||
| 199 | (if files | ||
| 200 | (concat (shell-quote-argument "(") | ||
| 201 | " " find-name-arg " " | ||
| 202 | (mapconcat | ||
| 203 | #'shell-quote-argument | ||
| 204 | (split-string files) | ||
| 205 | (concat " -o " find-name-arg " ")) | ||
| 206 | " " | ||
| 207 | (shell-quote-argument ")"))"") | ||
| 208 | ))) | ||
| 209 | (split-string (shell-command-to-string command) "\0" t))) | ||
| 210 | |||
| 198 | (defgroup project-vc nil | 211 | (defgroup project-vc nil |
| 199 | "Project implementation using the VC package." | 212 | "Project implementation using the VC package." |
| 200 | :version "25.1" | 213 | :version "25.1" |
| @@ -320,11 +333,26 @@ triggers completion when entering a pattern, including it | |||
| 320 | requires quoting, e.g. `\\[quoted-insert]<space>'." | 333 | requires quoting, e.g. `\\[quoted-insert]<space>'." |
| 321 | (interactive (list (project--read-regexp))) | 334 | (interactive (list (project--read-regexp))) |
| 322 | (let* ((pr (project-current t)) | 335 | (let* ((pr (project-current t)) |
| 323 | (dirs (if current-prefix-arg | 336 | (files |
| 324 | (list (read-directory-name "Base directory: " | 337 | (if (not current-prefix-arg) |
| 325 | nil default-directory t)) | 338 | (project-files pr (project-roots pr)) |
| 326 | (project-roots pr)))) | 339 | (let ((dir (read-directory-name "Base directory: " |
| 327 | (project--find-regexp-in dirs regexp pr))) | 340 | nil default-directory t))) |
| 341 | (project--files-in-directory dir | ||
| 342 | (project--dir-ignores pr dir) | ||
| 343 | (grep-read-files regexp)))))) | ||
| 344 | (project--find-regexp-in-files regexp files))) | ||
| 345 | |||
| 346 | (defun project--dir-ignores (project dir) | ||
| 347 | (let* ((roots (project-roots project)) | ||
| 348 | (root (cl-find dir roots :test #'file-in-directory-p))) | ||
| 349 | (when root | ||
| 350 | (let ((ignores (project-ignores project root))) | ||
| 351 | (if (file-equal-p root dir) | ||
| 352 | ignores | ||
| 353 | ;; FIXME: Update the "rooted" ignores to relate to DIR instead. | ||
| 354 | (cl-delete-if (lambda (str) (string-prefix-p "./" str)) | ||
| 355 | ignores)))))) | ||
| 328 | 356 | ||
| 329 | ;;;###autoload | 357 | ;;;###autoload |
| 330 | (defun project-or-external-find-regexp (regexp) | 358 | (defun project-or-external-find-regexp (regexp) |
| @@ -333,29 +361,76 @@ With \\[universal-argument] prefix, you can specify the file name | |||
| 333 | pattern to search for." | 361 | pattern to search for." |
| 334 | (interactive (list (project--read-regexp))) | 362 | (interactive (list (project--read-regexp))) |
| 335 | (let* ((pr (project-current t)) | 363 | (let* ((pr (project-current t)) |
| 336 | (dirs (append | 364 | (files |
| 337 | (project-roots pr) | 365 | (project-files pr (append |
| 338 | (project-external-roots pr)))) | 366 | (project-roots pr) |
| 339 | (project--find-regexp-in dirs regexp pr))) | 367 | (project-external-roots pr))))) |
| 368 | (project--find-regexp-in-files regexp files))) | ||
| 369 | |||
| 370 | (defun project--find-regexp-in-files (regexp files) | ||
| 371 | (pcase-let* | ||
| 372 | ((output (get-buffer-create " *project grep output*")) | ||
| 373 | (`(,grep-re ,file-group ,line-group . ,_) (car grep-regexp-alist)) | ||
| 374 | (status nil) | ||
| 375 | (hits nil) | ||
| 376 | (xrefs nil) | ||
| 377 | (command (format "xargs -0 grep %s -nHe %s" | ||
| 378 | (if (and case-fold-search | ||
| 379 | (isearch-no-upper-case-p regexp t)) | ||
| 380 | "-i" | ||
| 381 | "") | ||
| 382 | (shell-quote-argument (xref--regexp-to-extended regexp))))) | ||
| 383 | (with-current-buffer output | ||
| 384 | (erase-buffer) | ||
| 385 | (with-temp-buffer | ||
| 386 | (insert (mapconcat #'identity files "\0")) | ||
| 387 | (setq status | ||
| 388 | (project--process-file-region (point-min) | ||
| 389 | (point-max) | ||
| 390 | shell-file-name | ||
| 391 | output | ||
| 392 | nil | ||
| 393 | shell-command-switch | ||
| 394 | command))) | ||
| 395 | (goto-char (point-min)) | ||
| 396 | (when (and (/= (point-min) (point-max)) | ||
| 397 | (not (looking-at grep-re)) | ||
| 398 | ;; TODO: Show these matches as well somehow? | ||
| 399 | (not (looking-at "Binary file .* matches"))) | ||
| 400 | (user-error "Search failed with status %d: %s" status | ||
| 401 | (buffer-substring (point-min) (line-end-position)))) | ||
| 402 | (while (re-search-forward grep-re nil t) | ||
| 403 | (push (list (string-to-number (match-string line-group)) | ||
| 404 | (match-string file-group) | ||
| 405 | (buffer-substring-no-properties (point) (line-end-position))) | ||
| 406 | hits))) | ||
| 407 | (setq xrefs (xref--convert-hits (nreverse hits) regexp)) | ||
| 408 | (unless xrefs | ||
| 409 | (user-error "No matches for: %s" regexp)) | ||
| 410 | (xref--show-xrefs xrefs nil))) | ||
| 411 | |||
| 412 | (defun project--process-file-region (start end program | ||
| 413 | &optional buffer display | ||
| 414 | &rest args) | ||
| 415 | ;; FIXME: This branching shouldn't be necessary, but | ||
| 416 | ;; call-process-region *is* measurably faster, even for a program | ||
| 417 | ;; doing some actual work (for a period of time). Even though | ||
| 418 | ;; call-process-region also creates a temp file internally | ||
| 419 | ;; (http://lists.gnu.org/archive/html/emacs-devel/2019-01/msg00211.html). | ||
| 420 | (if (not (file-remote-p default-directory)) | ||
| 421 | (apply #'call-process-region | ||
| 422 | start end program nil buffer display args) | ||
| 423 | (let ((infile (make-temp-file "ppfr"))) | ||
| 424 | (unwind-protect | ||
| 425 | (progn | ||
| 426 | (write-region start end infile nil 'silent) | ||
| 427 | (apply #'process-file program infile buffer display args)) | ||
| 428 | (delete-file infile))))) | ||
| 340 | 429 | ||
| 341 | (defun project--read-regexp () | 430 | (defun project--read-regexp () |
| 342 | (let ((id (xref-backend-identifier-at-point (xref-find-backend)))) | 431 | (let ((id (xref-backend-identifier-at-point (xref-find-backend)))) |
| 343 | (read-regexp "Find regexp" (and id (regexp-quote id))))) | 432 | (read-regexp "Find regexp" (and id (regexp-quote id))))) |
| 344 | 433 | ||
| 345 | (defun project--find-regexp-in (dirs regexp project) | ||
| 346 | (require 'grep) | ||
| 347 | (let* ((files (if current-prefix-arg | ||
| 348 | (grep-read-files regexp) | ||
| 349 | "*")) | ||
| 350 | (xrefs (cl-mapcan | ||
| 351 | (lambda (dir) | ||
| 352 | (xref-collect-matches regexp files dir | ||
| 353 | (project-ignores project dir))) | ||
| 354 | dirs))) | ||
| 355 | (unless xrefs | ||
| 356 | (user-error "No matches for: %s" regexp)) | ||
| 357 | (xref--show-xrefs xrefs nil))) | ||
| 358 | |||
| 359 | ;;;###autoload | 434 | ;;;###autoload |
| 360 | (defun project-find-file () | 435 | (defun project-find-file () |
| 361 | "Visit a file (with completion) in the current project's roots. | 436 | "Visit a file (with completion) in the current project's roots. |