@@ -90,6 +90,8 @@ name and a count."
9090Fontification is done using the `org-src' library, which see."
9191 :type 'boolean )
9292
93+ (defvar devdocs-buffer-name " *devdocs*" )
94+
9395(defvar devdocs-history nil
9496 " History of documentation entries." )
9597
@@ -432,7 +434,7 @@ Interactively, read a page name with completion."
432434ENTRY is an alist like those in the entry index of the document,
433435possibly with an additional ENTRY.fragment which overrides the
434436fragment part of ENTRY.path."
435- (with-current-buffer (get-buffer-create " * devdocs* " )
437+ (with-current-buffer (get-buffer-create devdocs-buffer-name )
436438 (unless (eq major-mode 'devdocs-mode )
437439 (devdocs-mode))
438440 (let-alist entry
@@ -567,6 +569,74 @@ If INITIAL-INPUT is not nil, insert it into the minibuffer."
567569 (interactive (list (devdocs--read-document " Peruse documentation: " )))
568570 (pop-to-buffer (devdocs-goto-page doc 0 )))
569571
572+ (defun devdocs--next-error-function (n &optional reset )
573+ " A `next-error-function' suitable for *devdocs-grep* buffers."
574+ (cl-letf (((symbol-function 'compilation-find-file )
575+ (lambda (_marker filename &rest _ )
576+ (string-match " \\ ([^/]*\\ )/\\ (.*\\ )" filename)
577+ ; ; TODO: use goto-page
578+ (devdocs--render
579+ `((doc . ,(devdocs--doc-metadata (match-string 1 filename)))
580+ (path . ,(match-string 2 filename)))))))
581+ (compilation-next-error-function n reset)))
582+
583+ ;;;### autoload
584+ (defun devdocs-grep (docs regexp )
585+ " Perform full-text search in a collection of documents."
586+ (interactive (list (devdocs--relevant-docs current-prefix-arg)
587+ (read-regexp " Search for regexp: " )))
588+ (let* ((slugs (mapcar (lambda (doc ) (alist-get 'slug doc)) docs))
589+ (outbuf (get-buffer-create " *devdocs-grep*" ))
590+ (pages (mapcan (lambda (doc )
591+ (mapcar (lambda (path ) `((doc . , doc ) (path . , path )))
592+ (devdocs--index doc 'pages )))
593+ docs))
594+ (npages (length pages))
595+ (nmatches 0 )
596+ (progress (make-progress-reporter " Searching" 0 npages)))
597+ (pop-to-buffer outbuf)
598+ (let ((inhibit-read-only t ))
599+ (erase-buffer )
600+ (grep-mode )
601+ (buffer-disable-undo )
602+ (setq-local next-error-function #'devdocs--next-error-function )
603+ (insert (format " Search results for ‘%s ’ in the following documents: %s .\n\n "
604+ regexp (string-join slugs " , " ))))
605+ (letrec ((worker (pcase-lambda (`(, page . , rest ))
606+ (unless (buffer-live-p outbuf)
607+ (user-error " Grep buffer killed" ))
608+ (progress-reporter-update progress (- npages (length rest) 1 ))
609+ (with-temp-buffer
610+ (let ((devdocs-buffer-name (current-buffer ))
611+ (devdocs-fontify-code-blocks nil ))
612+ (devdocs--render page))
613+ (while (re-search-forward regexp nil t )
614+ (setq nmatches (1+ nmatches))
615+ (goto-char (match-beginning 0 ))
616+ (end-of-line )
617+ (let* ((text (buffer-substring (line-beginning-position )
618+ (point )))
619+ (result (let-alist page
620+ (format " %s /%s :%s :%s \n "
621+ .doc.slug .path
622+ (line-number-at-pos )
623+ text))))
624+ (with-current-buffer outbuf
625+ (save-excursion
626+ (goto-char (point-max ))
627+ (let ((inhibit-read-only t ))
628+ (insert result)))))))
629+ (if rest
630+ (run-with-idle-timer 0.2 nil worker rest)
631+ (progress-reporter-done progress)
632+ (with-current-buffer outbuf
633+ (save-excursion
634+ (goto-char (point-max ))
635+ (let ((inhibit-read-only t ))
636+ (insert (format " \n Search finished with %s results.\n "
637+ nmatches)))))))))
638+ (funcall worker pages))))
639+
570640; ;; Compatibility with the old devdocs package
571641
572642;;;### autoload
0 commit comments