Skip to content

Instantly share code, notes, and snippets.

@ackerleytng
Created December 15, 2020 04:17
Show Gist options
  • Save ackerleytng/290c29ac951c18d859593a9414f88fe7 to your computer and use it in GitHub Desktop.
Save ackerleytng/290c29ac951c18d859593a9414f88fe7 to your computer and use it in GitHub Desktop.
;;; projectile-ag.el --- The silver searcher with interactive searching with completing-read interface, for consult -*- lexical-binding: t; -*-
;; Copyright (C) 2020 by Ackerley TNG
;; Author: Ackerley TNG <[email protected]>
;; URL: https://github.com/ackerleytng/emacs-projectile-ag
;; Version: 0.0.1
;; Package-Requires: ((emacs "25.1") (projectile "2.0"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; projectile-ag calls the silver searcher and presents the results using
;; completing-read. This was written to work with consult
;;; Code:
(require 'projectile)
(defun projectile-ag--marked-string ()
"Return the marked input (anything highlighted in buffer), nil if nothing is marked."
(when (use-region-p)
(buffer-substring-no-properties (region-beginning) (region-end))))
(defun projectile-ag--search-string ()
"Get the input for search."
(or (projectile-ag--marked-string)
(thing-at-point 'symbol t)))
(defun projectile-ag--do-ag (s path)
"Call ag with search string S on PATH and return all results. Return a list of results (strings)."
(with-temp-buffer
(let ((exit-code (process-file "ag" nil t nil "--nocolor" "--nogroup" "--column" s path)))
(when (= exit-code 0)
(split-string (buffer-string) "\n" t)))))
(defun projectile-ag--show-search-completions (search-string)
"Call ag with SEARCH-STRING on projectile's project root."
(completing-read
"ag: "
(projectile-ag--do-ag search-string (projectile-project-root))
nil t search-string))
(defun projectile-ag--get-or-read-search-string ()
"Either get search string from or ask user for search string. For use in interactive."
(list (or (projectile-ag--search-string) (read-string "Search for: "))))
(defun projectile-ag--parse-filename-line-number (string)
"Parse filename and line number from STRING, which is assumed to be from ag output. Input format is expected to be <file path>:<line number>:<column number>:<line from file>. Return list of <file path>, <line number as int>, <column number as int>."
(let ((parts (split-string string ":")))
(list (car parts) (string-to-number (cadr parts)) (string-to-number (caddr parts)))))
(defun projectile-ag--open-user-choice (string)
"Open user choice STRING, which is assumed to be from ag output."
(let* ((parsed (projectile-ag--parse-filename-line-number string))
(filename (car parsed))
(line-number (cadr parsed))
(column-number (caddr parsed)))
(find-file filename)
;; Go back to the start of the file, then go to line-number
(goto-char (point-min)) (beginning-of-line line-number)
;; Go forward to the right column
(forward-char (1- column-number)))
(let ((parts (split-string string ":")))
(list (car parts) (string-to-number (cadr parts)) (string-to-number (caddr parts)))))
(defun projectile-ag--main (search-string)
"Search for the marked string, or symbol at point, or if neither of the above are available, a SEARCH-STRING from prompt, then presents the user with search results from ag, and then opens the user's choice in the buffer."
(interactive (projectile-ag--get-or-read-search-string))
(let ((user-choice (projectile-ag--show-search-completions search-string)))
(projectile-ag--open-user-choice user-choice)))
(provide 'projectile-ag)
;;; projectile-ag.el ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment