From d01186e9d2d4bc8891b5e6379a8cf2f589162990 Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Fri, 24 Apr 2026 18:49:41 +0200 Subject: [PATCH] update helm-projectile --- .../helm-projectile-autoloads.el | 74 - .../helm-projectile-pkg.el | 10 - .../helm-projectile.el | 1198 ----------- .../helm-projectile-autoloads.el | 123 ++ .../helm-projectile-pkg.el | 10 + .../helm-projectile.el | 1819 +++++++++++++++++ 6 files changed, 1952 insertions(+), 1282 deletions(-) delete mode 100644 elpa/helm-projectile-20250218.1525/helm-projectile-autoloads.el delete mode 100644 elpa/helm-projectile-20250218.1525/helm-projectile-pkg.el delete mode 100644 elpa/helm-projectile-20250218.1525/helm-projectile.el create mode 100644 elpa/helm-projectile-20250902.830/helm-projectile-autoloads.el create mode 100644 elpa/helm-projectile-20250902.830/helm-projectile-pkg.el create mode 100644 elpa/helm-projectile-20250902.830/helm-projectile.el diff --git a/elpa/helm-projectile-20250218.1525/helm-projectile-autoloads.el b/elpa/helm-projectile-20250218.1525/helm-projectile-autoloads.el deleted file mode 100644 index 3df69601..00000000 --- a/elpa/helm-projectile-20250218.1525/helm-projectile-autoloads.el +++ /dev/null @@ -1,74 +0,0 @@ -;;; helm-projectile-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from helm-projectile.el - -(defvar helm-projectile-fuzzy-match t "\ -Enable fuzzy matching for Helm Projectile commands. -This needs to be set before loading helm-projectile.el.") -(custom-autoload 'helm-projectile-fuzzy-match "helm-projectile" t) -(autoload 'helm-projectile-find-file-dwim "helm-projectile" "\ -Find file at point based on context." t) -(autoload 'helm-projectile-find-other-file "helm-projectile" "\ -Switch between files with the same name but different extensions using Helm. -With FLEX-MATCHING, match any file that contains the base name of -current file. Other file extensions can be customized with the -variable `projectile-other-file-alist'. - -(fn &optional FLEX-MATCHING)" t) -(autoload 'helm-projectile-on "helm-projectile" "\ -Turn on `helm-projectile' key bindings." t) -(autoload 'helm-projectile-off "helm-projectile" "\ -Turn off `helm-projectile' key bindings." t) -(autoload 'helm-projectile-grep "helm-projectile" "\ -Helm version of `projectile-grep'. -DIR is the project root, if not set then current directory is used - -(fn &optional DIR)" t) -(autoload 'helm-projectile-ack "helm-projectile" "\ -Helm version of projectile-ack. -DIR directory where to search - -(fn &optional DIR)" t) -(autoload 'helm-projectile-ag "helm-projectile" "\ -Helm version of `projectile-ag'. -OPTIONS explicit command line arguments to ag - -(fn &optional OPTIONS)" t) -(autoload 'helm-projectile-rg "helm-projectile" "\ -Projectile version of `helm-rg'." t) -(autoload 'helm-projectile-toggle "helm-projectile" "\ -Toggle Helm version of Projectile commands. - -(fn TOGGLE)") -(autoload 'helm-projectile "helm-projectile" "\ -Use projectile with Helm instead of ido. - -With a prefix ARG invalidates the cache first. -If invoked outside of a project, displays a list of known projects to jump. - -(fn &optional ARG)" t) -(eval-after-load 'projectile '(progn (define-key projectile-command-map (kbd "h") #'helm-projectile))) -(register-definition-prefixes "helm-projectile" '("glob-quote" "helm-")) - -;;; End of scraped data - -(provide 'helm-projectile-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; helm-projectile-autoloads.el ends here diff --git a/elpa/helm-projectile-20250218.1525/helm-projectile-pkg.el b/elpa/helm-projectile-20250218.1525/helm-projectile-pkg.el deleted file mode 100644 index 67adb9ac..00000000 --- a/elpa/helm-projectile-20250218.1525/helm-projectile-pkg.el +++ /dev/null @@ -1,10 +0,0 @@ -;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "helm-projectile" "20250218.1525" - "Helm integration for Projectile." - '((helm "3.0") - (projectile "2.9") - (cl-lib "0.3")) - :url "https://github.com/bbatsov/helm-projectile" - :commit "041076e35a6663302a91a0fa672f847c7d64bf29" - :revdesc "041076e35a66" - :keywords '("project" "convenience")) diff --git a/elpa/helm-projectile-20250218.1525/helm-projectile.el b/elpa/helm-projectile-20250218.1525/helm-projectile.el deleted file mode 100644 index 9cd7a12c..00000000 --- a/elpa/helm-projectile-20250218.1525/helm-projectile.el +++ /dev/null @@ -1,1198 +0,0 @@ -;;; helm-projectile.el --- Helm integration for Projectile -*- lexical-binding: t; -*- - -;; Copyright (C) 2011-2025 Bozhidar Batsov - -;; Author: Bozhidar Batsov -;; URL: https://github.com/bbatsov/helm-projectile -;; Created: 2011-31-07 -;; Keywords: project, convenience -;; Package-Version: 20250218.1525 -;; Package-Revision: 041076e35a66 -;; Package-Requires: ((helm "3.0") (projectile "2.9") (cl-lib "0.3")) - -;; This file is NOT part of GNU Emacs. - -;;; License: - -;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. - -;;; Commentary: -;; -;; This library provides easy project management and navigation. The -;; concept of a project is pretty basic - just a folder containing -;; special file. Currently git, mercurial and bazaar repos are -;; considered projects by default. If you want to mark a folder -;; manually as a project just create an empty .projectile file in -;; it. See the README for more details. -;; -;;; Code: - -;; built-in libraries -(require 'subr-x) -(require 'cl-lib) -(require 'grep) ;; TODO: Probably we should defer this require - -(require 'helm-core) -(require 'helm-global-bindings) -(require 'helm-types) -(require 'helm-locate) -(require 'helm-buffers) -(require 'helm-files) - -(require 'projectile) - -(declare-function eshell "eshell") -(declare-function helm-do-ag "ext:helm-ag") -(declare-function dired-get-filename "dired") -(defvar helm-ag-base-command) - -(defvar grep-find-ignored-directories) -(defvar grep-find-ignored-files) - -(defgroup helm-projectile nil - "Helm support for projectile." - :prefix "helm-projectile-" - :group 'projectile - :group 'helm - :link `(url-link :tag "GitHub" "https://github.com/bbatsov/helm-projectile")) - -(defvar helm-projectile-current-project-root) - -(defcustom helm-projectile-truncate-lines nil - "Truncate lines in helm projectile commands when non--nil. - -Some `helm-projectile' commands have similar behavior with existing -Helms. In these cases their respective custom var for truncation -of lines will be honored. E.g. `helm-buffers-truncate-lines' -dictates the truncation in `helm-projectile-switch-to-buffer'." - :group 'helm-projectile - :type 'boolean) - -;;;###autoload -(defcustom helm-projectile-fuzzy-match t - "Enable fuzzy matching for Helm Projectile commands. -This needs to be set before loading helm-projectile.el." - :group 'helm-projectile - :type 'boolean) - -(defmacro helm-projectile-define-key (keymap key def &rest bindings) - "In KEYMAP, define KEY - DEF sequence KEY1 as DEF1, KEY2 as DEF2 ..." - (declare (indent defun)) - (let ((ret '(progn))) - (while key - (push - `(define-key ,keymap ,key - (lambda () - (interactive) - (helm-exit-and-execute-action ,def))) - ret) - (setq key (pop bindings) - def (pop bindings))) - (reverse ret))) - -(defun helm-projectile-hack-actions (actions &rest prescription) - "Given a Helm action list and a prescription, return a hacked Helm action list. -Optionally applies the PRESCRIPTION beforehand. - -The Helm action list ACTIONS is of the form: - -\(\(DESCRIPTION1 . FUNCTION1\) - \(DESCRIPTION2 . FUNCTION2\) - ... - \(DESCRIPTIONn . FUNCTIONn\)\) - -PRESCRIPTION is in the form: - -\(INSTRUCTION1 INSTRUCTION2 ... INSTRUCTIONn\) - -If an INSTRUCTION is a symbol, the action with function name -INSTRUCTION is deleted. - -If an INSTRUCTION is of the form \(FUNCTION1 . FUNCTION2\), the -action with function name FUNCTION1 will change it's function to -FUNCTION2. - -If an INSTRUCTION is of the form \(FUNCTION . DESCRIPTION\), and -if an action with function name FUNCTION exists in the original -Helm action list, the action in the Helm action list, with -function name FUNCTION will change it's description to -DESCRIPTION. Otherwise, (FUNCTION . DESCRIPTION) will be added to -the action list. - -Please check out how `helm-projectile-file-actions' is defined -for an example of how this function is being used." - (let* ((to-delete (cl-remove-if (lambda (entry) (listp entry)) prescription)) - (actions (cl-delete-if (lambda (action) (memq (cdr action) to-delete)) - (copy-alist actions))) - new) - (cl-dolist (action actions) - (when (setq new (cdr (assq (cdr action) prescription))) - (if (stringp new) - (setcar action new) - (setcdr action new)))) - ;; Add new actions from PRESCRIPTION - (setq new nil) - (cl-dolist (instruction prescription) - (when (and (listp instruction) - (null (rassq (car instruction) actions)) - (symbolp (car instruction)) (stringp (cdr instruction))) - (push (cons (cdr instruction) (car instruction)) new))) - (append actions (nreverse new)))) - -(defun helm-projectile-vc (dir) - "A Helm action for jumping to project root using `vc-dir' or Magit. -DIR is a directory to be switched" - (let ((projectile-require-project-root nil)) - (projectile-vc dir))) - -(defun helm-projectile-compile-project (dir) - "A Helm action for compile a project. -DIR is the project root." - (let ((helm--reading-passwd-or-string t) - (default-directory dir)) - (projectile-compile-project helm-current-prefix-arg))) - -(defun helm-projectile-test-project (dir) - "A Helm action for test a project. -DIR is the project root." - (let ((helm--reading-passwd-or-string t) - (default-directory dir)) - (projectile-test-project helm-current-prefix-arg))) - -(defun helm-projectile-run-project (dir) - "A Helm action for run a project. -DIR is the project root." - (let ((helm--reading-passwd-or-string t) - (default-directory dir)) - (projectile-run-project helm-current-prefix-arg))) - -(defun helm-projectile-remove-known-project (_ignore) - "Remove selected projects from projectile project list. -_IGNORE means the argument does not matter. -It is there because Helm requires it." - (let* ((projects (helm-marked-candidates :with-wildcard t)) - (len (length projects))) - (with-helm-display-marked-candidates - helm-marked-buffer-name - projects - (if (not (y-or-n-p (format "Remove *%s projects(s)? " len))) - (message "(No removal performed)") - (progn - (mapc (lambda (p) - (setq projectile-known-projects (delete p projectile-known-projects))) - projects) - (projectile-save-known-projects)) - (message "%s projects(s) removed" len))))) - -(defvar helm-projectile-projects-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map helm-map) - (helm-projectile-define-key map - (kbd "C-d") #'dired - (kbd "M-g") #'helm-projectile-vc - (kbd "M-e") #'helm-projectile-switch-to-shell - (kbd "C-s") #'helm-projectile-grep - (kbd "M-c") #'helm-projectile-compile-project - (kbd "M-t") #'helm-projectile-test-project - (kbd "M-r") #'helm-projectile-run-project - (kbd "M-D") #'helm-projectile-remove-known-project) - map) - "Mapping for known projectile projects.") - -(defcustom helm-source-projectile-projects-actions - (helm-make-actions - "Switch to project" (lambda (project) - (let ((projectile-completion-system 'helm)) - (projectile-switch-project-by-name project))) - "Open Dired in project's directory `C-d'" #'dired - "Open project root in vc-dir or magit `M-g'" #'helm-projectile-vc - "Switch to Eshell `M-e'" #'helm-projectile-switch-to-shell - "Grep in projects `C-s'" #'helm-projectile-grep - "Compile project `M-c'. With C-u, new compile command" #'helm-projectile-compile-project - "Remove project(s) from project list `M-D'" #'helm-projectile-remove-known-project) - "Actions for `helm-source-projectile-projects'." - :group 'helm-projectile - :type '(alist :key-type string :value-type function)) - -(defclass helm-projectile-projects-source (helm-source-sync helm-type-file) - ((candidates :initform (lambda () (with-helm-current-buffer - (mapcar #'copy-sequence - (projectile-known-projects))))) - (fuzzy-match :initform 'helm-projectile-fuzzy-match) - (keymap :initform 'helm-projectile-projects-map) - (mode-line :initform 'helm-read-file-name-mode-line-string) - (action :initform 'helm-source-projectile-projects-actions)) - "Helm source for known projectile projects.") - -(cl-defmethod helm--setup-source :after ((source helm-projectile-projects-source)) - "Make SOURCE specific to project switching. -The `helm-projectile-projects-source` inherits from -`helm-type-file` (which see), which sets up actions, keymap, and -help message slots to file specific ones. Override these slots -to be specific to `helm-projectile-projects-source'." - (setf (slot-value source 'action) 'helm-source-projectile-projects-actions) - (setf (slot-value source 'keymap) helm-projectile-projects-map) - ;; Use `ignore' as a persistent action, to actually keep `helm' session - ;; when `helm-execute-persistent-action' is executed. - (setf (slot-value source 'persistent-action) #'ignore) - (let ((persistent-help "Do Nothing")) - (setf (slot-value source 'persistent-help) persistent-help) - (setf (slot-value source 'header-line) - (helm-source--persistent-help-string - persistent-help - source))) - (setf (slot-value source 'mode-line) - (list "Project(s)" helm-mode-line-string))) - -(defvar helm-source-projectile-projects - (helm-make-source "Projectile projects" 'helm-projectile-projects-source)) - -(defvar helm-projectile-dirty-projects-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map helm-map) - (helm-projectile-define-key map - (kbd "C-d") #'dired - (kbd "M-o") #'(lambda (project) - (let ((projectile-completion-system 'helm)) - (projectile-switch-project-by-name project))) - (kbd "M-e") #'helm-projectile-switch-to-shell - (kbd "C-s") #'helm-projectile-grep - (kbd "M-c") #'helm-projectile-compile-project - (kbd "M-t") #'helm-projectile-test-project - (kbd "M-r") #'helm-projectile-run-project - (kbd "M-D") #'helm-projectile-remove-known-project) - map) - "Mapping for dirty projectile projects.") - -(defvar helm-source-projectile-dirty-projects - (helm-build-sync-source "Projectile dirty projects" - :candidates (lambda () (with-helm-current-buffer (helm-projectile-get-dirty-projects))) - :fuzzy-match helm-projectile-fuzzy-match - :keymap helm-projectile-dirty-projects-map - :mode-line helm-read-file-name-mode-line-string - :action '(("Open project root in vc-dir or magit" . helm-projectile-vc) - ("Switch to project `M-o'" . - (lambda (project) - (let ((projectile-completion-system 'helm)) - (projectile-switch-project-by-name project)))) - ("Open Dired in project's directory `C-d'" . dired) - ("Switch to Eshell `M-e'" . helm-projectile-switch-to-shell) - ("Grep in projects `C-s'" . helm-projectile-grep) - ("Compile project `M-c'. With C-u, new compile command" - . helm-projectile-compile-project))) - "Helm source for dirty version controlled projectile projects.") - -(defun helm-projectile-get-dirty-projects () - "Return dirty version controlled known projects as an alist to -have a nice display in Helm." - (message "Checking for dirty known projects...") - (let* ((status (projectile-check-vcs-status-of-known-projects)) - (proj-dir (cl-loop for stat in status - collect (car stat))) - (status-display (cl-loop for stat in status collect - (propertize (format "[%s]" (mapconcat 'identity (car (cdr stat)) ", ")) 'face 'helm-match))) - (max-status-display-length (cl-loop for sd in status-display - maximize (length sd))) - (status-display (cl-loop for sd in status-display collect - (format "%s%s " sd (make-string (- max-status-display-length (length sd)) ? )))) - (full-display (cl-mapcar 'concat status-display proj-dir)) - (helm-list (cl-pairlis full-display proj-dir))) - helm-list)) - -(define-key helm-etags-map (kbd "C-c p f") - (lambda () - (interactive) - (helm-run-after-exit 'helm-projectile-find-file nil))) - -(defun helm-projectile-file-persistent-action (candidate) - "Persistent action for file-related functionality. - -Previews the contents of a file in a temporary buffer." - (let ((buf (get-buffer-create " *helm-projectile persistent*"))) - (cl-flet ((preview (candidate) - (switch-to-buffer buf) - (setq inhibit-read-only t) - (erase-buffer) - (insert-file-contents candidate) - (let ((buffer-file-name candidate)) - (set-auto-mode)) - (font-lock-ensure) - (setq inhibit-read-only nil))) - (if (and (helm-get-attr 'previewp) - (string= candidate (helm-get-attr 'current-candidate))) - (progn - (kill-buffer buf) - (helm-set-attr 'previewp nil)) - (preview candidate) - (helm-set-attr 'previewp t))) - (helm-set-attr 'current-candidate candidate))) - -(defun helm-projectile-find-files-eshell-command-on-file-action (candidate) - (interactive) - (let* ((helm-ff-default-directory (file-name-directory candidate))) - (helm-find-files-eshell-command-on-file candidate))) - -(defun helm-projectile-ff-etags-select-action (candidate) - (interactive) - (let* ((helm-ff-default-directory (file-name-directory candidate))) - (helm-ff-etags-select candidate))) - -(defun helm-projectile-switch-to-shell (dir) - "Within DIR, switch to a shell corresponding to `helm-ff-preferred-shell-mode'." - (interactive) - (let* ((projectile-require-project-root nil) - (helm-ff-default-directory (file-name-directory (projectile-expand-root dir)))) - (helm-ff-switch-to-shell dir))) - -(defun helm-projectile-files-in-current-dired-buffer () - "Return a list of files (only) in the current dired buffer." - (let (flist) - (cl-flet ((fpush (fname) (push fname flist))) - (save-excursion - (let (file buffer-read-only) - (goto-char (point-min)) - (while (not (eobp)) - (save-excursion - (and (not (eolp)) - (setq file (dired-get-filename t t)) ; nil on non-file - (progn (end-of-line) - (funcall #'fpush file)))) - (forward-line 1))))) - (mapcar 'file-truename (nreverse flist)))) - -(defun helm-projectile-all-dired-buffers () - "Get all current Dired buffers." - (mapcar (lambda (b) - (with-current-buffer b (buffer-name))) - (cl-remove-if-not - (lambda (b) - (with-current-buffer b - (and (eq major-mode 'dired-mode) - (buffer-name)))) - (buffer-list)))) - -(defvar helm-projectile-virtual-dired-remote-enable nil - "Enable virtual Dired manager on remote host. -Disabled by default.") - -(defun helm-projectile-dired-files-new-action (candidate) - "Create a Dired buffer from chosen files. -CANDIDATE is the selected file, but choose the marked files if available." - (if (and (file-remote-p (projectile-project-root)) - (not helm-projectile-virtual-dired-remote-enable)) - (message "Virtual Dired manager is disabled in remote host. Enable with %s." - (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face)) - (let ((files (cl-remove-if-not - (lambda (f) - (not (string= f ""))) - (mapcar (lambda (file) - (replace-regexp-in-string (projectile-project-root) "" file)) - (helm-marked-candidates :with-wildcard t)))) - (new-name (completing-read "Select or enter a new buffer name: " - (helm-projectile-all-dired-buffers))) - (helm--reading-passwd-or-string t) - (default-directory (projectile-project-root))) - ;; create a unique buffer that is unique to any directory in default-directory - ;; or opened buffer; when Dired is passed with a non-existence directory name, - ;; it only creates a buffer and insert everything. If a new name user supplied - ;; exists as default-directory, Dired throws error when insert anything that - ;; does not exist in current directory. - (with-current-buffer (dired (cons (make-temp-name new-name) - (if files - files - (list candidate)))) - (when (get-buffer new-name) - (kill-buffer new-name)) - (rename-buffer new-name))))) - -(defun helm-projectile-dired-files-add-action (candidate) - "Add files to a Dired buffer. -CANDIDATE is the selected file. Used when no file is explicitly marked." - (if (and (file-remote-p (projectile-project-root)) - (not helm-projectile-virtual-dired-remote-enable)) - (message "Virtual Dired manager is disabled in remote host. Enable with %s." - (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face)) - (if (eq (with-helm-current-buffer major-mode) 'dired-mode) - (let* ((marked-files (helm-marked-candidates :with-wildcard t)) - (helm--reading-passwd-or-string t) - (root (projectile-project-root)) ; store root for later use - (dired-buffer-name (or (and (eq major-mode 'dired-mode) (buffer-name)) - (completing-read "Select a Dired buffer:" - (helm-projectile-all-dired-buffers)))) - (dired-files (with-current-buffer dired-buffer-name - (helm-projectile-files-in-current-dired-buffer))) - (files (sort (mapcar (lambda (file) - (replace-regexp-in-string (projectile-project-root) "" file)) - (cl-nunion (if marked-files - marked-files - (list candidate)) - dired-files - :test #'string-equal)) - 'string-lessp))) - (kill-buffer dired-buffer-name) - ;; Rebind default-directory because after killing a buffer, we - ;; could be in any buffer and default-directory is set to that - ;; random buffer - ;; - ;; Also use saved root directory, because after killing a buffer, - ;; we could be outside of current project - (let ((default-directory root)) - (with-current-buffer (dired (cons (make-temp-name dired-buffer-name) - (if files - (mapcar (lambda (file) - (replace-regexp-in-string root "" file)) - files) - (list candidate)))) - (rename-buffer dired-buffer-name)))) - (error "You're not in a Dired buffer to add")))) - -(defun helm-projectile-dired-files-delete-action (candidate) - "Delete selected entries from a Dired buffer. -CANDIDATE is the selected file. Used when no file is explicitly marked." - (if (and (file-remote-p (projectile-project-root)) - (not helm-projectile-virtual-dired-remote-enable)) - (message "Virtual Dired manager is disabled in remote host. Enable with %s." - (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face)) - (let* ((helm--reading-passwd-or-string t) - (root (projectile-project-root)) - (dired-buffer-name (with-helm-current-buffer (buffer-name))) - (dired-files (with-current-buffer dired-buffer-name - (helm-projectile-files-in-current-dired-buffer))) - (files (sort (cl-set-exclusive-or (helm-marked-candidates :with-wildcard t) - dired-files - :test #'string-equal) #'string-lessp))) - (kill-buffer dired-buffer-name) - ;; similar reason to `helm-projectile-dired-files-add-action' - (let ((default-directory root)) - (with-current-buffer (dired (cons (make-temp-name dired-buffer-name) - (if files - (mapcar (lambda (file) - (replace-regexp-in-string root "" file)) - files) - (list candidate)))) - (rename-buffer dired-buffer-name)))))) - -(defun helm-projectile-run-projectile-hooks-after-find-file (_orig-fun &rest _args) - "Run `projectile-find-file-hook' if using projectile." - (when (and projectile-mode (projectile-project-p)) - (run-hooks 'projectile-find-file-hook))) - -(advice-add 'helm-find-file-or-marked - :after #'helm-projectile-run-projectile-hooks-after-find-file) - -(defvar helm-projectile-find-file-map - (let ((map (copy-keymap helm-find-files-map))) - (helm-projectile-define-key map - (kbd "C-c f") #'helm-projectile-dired-files-new-action - (kbd "C-c a") #'helm-projectile-dired-files-add-action - (kbd "M-e") #'helm-projectile-switch-to-shell - (kbd "M-.") #'helm-projectile-ff-etags-select-action - (kbd "M-!") #'helm-projectile-find-files-eshell-command-on-file-action) - (define-key map (kbd "") #'helm-previous-source) - (define-key map (kbd "") #'helm-next-source) - (dolist (cmd '(helm-find-files-up-one-level - helm-find-files-down-last-level)) - (substitute-key-definition cmd nil map)) - map) - "Mapping for file commands in Helm Projectile.") - -(defvar helm-projectile-file-actions - (helm-projectile-hack-actions - helm-find-files-actions - ;; Delete these actions - 'helm-ff-browse-project - 'helm-insert-file-name-completion-at-point - 'helm-ff-find-sh-command - 'helm-ff-cache-add-file - ;; Substitute these actions - '(helm-ff-switch-to-shell . helm-projectile-switch-to-shell) - '(helm-ff-etags-select . helm-projectile-ff-etags-select-action) - '(helm-find-files-eshell-command-on-file - . helm-projectile-find-files-eshell-command-on-file-action) - ;; Change action descriptions - '(helm-find-file-as-root . "Find file as root `C-c r'") - ;; New actions - '(helm-projectile-dired-files-new-action - . "Create Dired buffer from files `C-c f'") - '(helm-projectile-dired-files-add-action - . "Add files to Dired buffer `C-c a'")) - "Action for files.") - -(defun helm-projectile--move-selection-p (selection) - "Return non-nil if should move Helm selector after SELECTION. - -SELECTION should be moved unless it's one of: - -- Non-string -- Existing file -- Non-remote file that matches `helm-tramp-file-name-regexp'" - (not (or (not (stringp selection)) - (file-exists-p selection) - (and (string-match helm-tramp-file-name-regexp selection) - (not (file-remote-p selection nil t)))))) - -(defun helm-projectile--move-to-real () - "Move to first real candidate. - -Similar to `helm-ff--move-to-first-real-candidate', but without -unnecessary complexity." - (while (let* ((src (helm-get-current-source)) - (selection (and (not (helm-empty-source-p)) - (helm-get-selection nil nil src)))) - (and (not (helm-end-of-source-p)) - (helm-projectile--move-selection-p selection))) - (helm-next-line))) - -(defun helm-projectile--remove-move-to-real () - "Hook function to remove `helm-projectile--move-to-real'. - -Meant to be added to `helm-cleanup-hook', from which it removes - itself at the end." - (remove-hook 'helm-after-update-hook #'helm-projectile--move-to-real) - (remove-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real)) - -(defvar helm-source-projectile-files-list-before-init-hook - (lambda () - (add-hook 'helm-after-update-hook #'helm-projectile--move-to-real) - (add-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real))) - -(defvar helm-source-projectile-files-list - (helm-build-sync-source "Projectile files" - :before-init-hook 'helm-source-projectile-files-list-before-init-hook - :candidates (lambda () - (when (projectile-project-p) - (with-helm-current-buffer - (cl-loop with root = (projectile-project-root) - for display in (projectile-current-project-files) - collect (cons display (expand-file-name display root)))))) - :filtered-candidate-transformer - (lambda (files _source) - (with-helm-current-buffer - (let* ((root (projectile-project-root)) - (file-at-root (file-relative-name (expand-file-name helm-pattern root)))) - (if (or (string-empty-p helm-pattern) - (assoc helm-pattern files)) - files - (if (equal helm-pattern file-at-root) - (cl-acons (helm-ff-prefix-filename helm-pattern nil t) - (expand-file-name helm-pattern) - files) - (cl-pairlis (list (helm-ff-prefix-filename helm-pattern nil t) - (helm-ff-prefix-filename file-at-root nil t)) - (list (expand-file-name helm-pattern) - (expand-file-name helm-pattern root)) - files)))))) - :fuzzy-match helm-projectile-fuzzy-match - :keymap helm-projectile-find-file-map - :help-message 'helm-ff-help-message - :mode-line helm-read-file-name-mode-line-string - :action helm-projectile-file-actions - :persistent-action #'helm-projectile-file-persistent-action - :persistent-help "Preview file") - "Helm source definition for Projectile files.") - -(defvar helm-source-projectile-files-in-all-projects-list - (helm-build-sync-source "Projectile files in all Projects" - :candidates (lambda () - (with-helm-current-buffer - (let ((projectile-require-project-root nil)) - (projectile-all-project-files)))) - :keymap helm-projectile-find-file-map - :help-message 'helm-ff-help-message - :mode-line helm-read-file-name-mode-line-string - :action helm-projectile-file-actions - :persistent-action #'helm-projectile-file-persistent-action - :persistent-help "Preview file") - "Helm source definition for all Projectile files in all projects.") - -(defvar helm-projectile-dired-file-actions - (helm-projectile-hack-actions - helm-projectile-file-actions - ;; New actions - '(helm-projectile-dired-files-delete-action . "Remove entry(s) from Dired buffer `C-c d'"))) - -(defvar helm-source-projectile-dired-files-list - (helm-build-in-buffer-source "Projectile files in current Dired buffer" - :data (lambda () - (if (and (file-remote-p (projectile-project-root)) - (not helm-projectile-virtual-dired-remote-enable)) - nil - (when (eq major-mode 'dired-mode) - (helm-projectile-files-in-current-dired-buffer)))) - :filter-one-by-one (lambda (file) - (let ((helm-ff-transformer-show-only-basename t)) - (helm-ff-filter-candidate-one-by-one file))) - :action-transformer 'helm-find-files-action-transformer - :keymap (let ((map (copy-keymap helm-projectile-find-file-map))) - (helm-projectile-define-key map - (kbd "C-c d") 'helm-projectile-dired-files-delete-action) - map) - :help-message 'helm-ff-help-message - :mode-line helm-read-file-name-mode-line-string - :action helm-projectile-dired-file-actions) - "Helm source definition for Projectile delete files.") - -(defun helm-projectile-dired-find-dir (dir) - "Jump to a selected directory DIR from `helm-projectile'." - (dired (expand-file-name dir (projectile-project-root))) - (run-hooks 'projectile-find-dir-hook)) - -(defun helm-projectile-dired-find-dir-other-window (dir) - "Jump to a selected directory DIR from `helm-projectile'." - (dired-other-window (expand-file-name dir (projectile-project-root))) - (run-hooks 'projectile-find-dir-hook)) - -(defvar helm-source-projectile-directories-list - (helm-build-sync-source "Projectile directories" - :candidates (lambda () - (when (projectile-project-p) - (with-helm-current-buffer - (let ((dirs (if projectile-find-dir-includes-top-level - (append '("./") (projectile-current-project-dirs)) - (projectile-current-project-dirs)))) - (helm-projectile--files-display-real dirs (projectile-project-root)))))) - :fuzzy-match helm-projectile-fuzzy-match - :action-transformer 'helm-find-files-action-transformer - :keymap (let ((map (make-sparse-keymap))) - (set-keymap-parent map helm-map) - (helm-projectile-define-key map - (kbd "") #'helm-previous-source - (kbd "") #'helm-next-source - (kbd "C-c o") #'helm-projectile-dired-find-dir-other-window - (kbd "M-e") #'helm-projectile-switch-to-shell - (kbd "C-c f") #'helm-projectile-dired-files-new-action - (kbd "C-c a") #'helm-projectile-dired-files-add-action - (kbd "C-s") #'helm-projectile-grep) - map) - :help-message 'helm-ff-help-message - :mode-line helm-read-file-name-mode-line-string - :action '(("Open Dired" . helm-projectile-dired-find-dir) - ("Open Dired in other window `C-c o'" . helm-projectile-dired-find-dir) - ("Switch to Eshell `M-e'" . helm-projectile-switch-to-shell) - ("Grep in projects `C-s'" . helm-projectile-grep) - ("Create Dired buffer from files `C-c f'" . helm-projectile-dired-files-new-action) - ("Add files to Dired buffer `C-c a'" . helm-projectile-dired-files-add-action))) - "Helm source for listing project directories.") - -(defvar helm-projectile-buffers-list-cache nil) - -(defclass helm-source-projectile-buffer (helm-source-sync helm-type-buffer) - ((init :initform (lambda () - ;; Issue #51 Create the list before `helm-buffer' creation. - (setq helm-projectile-buffers-list-cache - (ignore-errors (remove (buffer-name) (projectile-project-buffer-names)))) - (let ((result (cl-loop for b in helm-projectile-buffers-list-cache - maximize (length b) into len-buf - maximize (length (with-current-buffer b - (symbol-name major-mode))) - into len-mode - finally return (cons len-buf len-mode)))) - (unless helm-buffer-max-length - (setq helm-buffer-max-length (car result))) - (unless helm-buffer-max-len-mode - ;; If a new buffer is longer that this value - ;; this value will be updated - (setq helm-buffer-max-len-mode (cdr result)))))) - (candidates :initform 'helm-projectile-buffers-list-cache) - (matchplugin :initform nil) - (match :initform 'helm-buffers-match-function) - (persistent-action :initform 'helm-buffers-list-persistent-action) - (keymap :initform 'helm-buffer-map) - (volatile :initform t) - (persistent-help - :initform - "Show this buffer / C-u \\[helm-execute-persistent-action]: Kill this buffer"))) - -(defvar helm-source-projectile-buffers-list (helm-make-source "Project buffers" 'helm-source-projectile-buffer)) - -(defvar helm-source-projectile-recentf-list - (helm-build-sync-source "Projectile recent files" - :candidates (lambda () - (when (projectile-project-p) - (with-helm-current-buffer - (helm-projectile--files-display-real (projectile-recentf-files) - (projectile-project-root))))) - :fuzzy-match helm-projectile-fuzzy-match - :keymap helm-projectile-find-file-map - :help-message 'helm-ff-help-message - :mode-line helm-read-file-name-mode-line-string - :action helm-projectile-file-actions - :persistent-action #'helm-projectile-file-persistent-action - :persistent-help "Preview file") - "Helm source definition for recent files in current project.") - -(defvar helm-source-projectile-files-and-dired-list - '(helm-source-projectile-dired-files-list - helm-source-projectile-files-list)) - -(defvar helm-source-projectile-directories-and-dired-list - '(helm-source-projectile-dired-files-list - helm-source-projectile-directories-list)) - -(defcustom helm-projectile-git-grep-command - "git --no-pager grep --no-color -n%c -e %p -- %f" - "Command to execute when performing `helm-grep' inside a projectile git project. -See documentation of `helm-grep-default-command' for the format." - :type 'string - :group 'helm-projectile - ) - -(defcustom helm-projectile-grep-command - "grep -a -r %e -n%cH -e %p %f ." - "Command to execute when performing `helm-grep' outside a projectile git project. -See documentation of `helm-grep-default-command' for the format." - :type 'string - :group 'helm-projectile - ) - - -(defcustom helm-projectile-sources-list - '(helm-source-projectile-buffers-list - helm-source-projectile-files-list - helm-source-projectile-projects) - "Default sources for `helm-projectile'." - :type '(repeat symbol) - :group 'helm-projectile) - -(defmacro helm-projectile-command (command source prompt &optional not-require-root truncate-lines-var) - "Template for generic `helm-projectile' commands. -COMMAND is a command name to be appended with \"helm-projectile\" prefix. -SOURCE is a Helm source that should be Projectile specific. -PROMPT is a string for displaying as a prompt. -NOT-REQUIRE-ROOT specifies the command doesn't need to be used in a -project root. -TRUNCATE-LINES-VAR is the symbol used dictate truncation of lines. -Defaults is `helm-projectile-truncate-lines'." - (unless truncate-lines-var (setq truncate-lines-var 'helm-projectile-truncate-lines)) - `(defun ,(intern (concat "helm-projectile-" command)) (&optional arg) - "Use projectile with Helm for finding files in project - -With a prefix ARG invalidates the cache first." - (interactive "P") - (if (projectile-project-p) - (projectile-maybe-invalidate-cache arg) - (unless ,not-require-root - (error "You're not in a project"))) - (let ((helm-ff-transformer-show-only-basename nil) - ;; for consistency, we should just let Projectile take care of ignored files - (helm-boring-file-regexp-list nil)) - (helm :sources ,source - :buffer (concat "*helm projectile: " (projectile-project-name) "*") - :truncate-lines ,truncate-lines-var - :prompt (projectile-prepend-project-name ,prompt))))) - -(helm-projectile-command "switch-project" 'helm-source-projectile-projects "Switch to project: " t) -(helm-projectile-command "find-file" helm-source-projectile-files-and-dired-list "Find file: ") -(helm-projectile-command "find-file-in-known-projects" 'helm-source-projectile-files-in-all-projects-list "Find file in projects: " t) -(helm-projectile-command "find-dir" helm-source-projectile-directories-and-dired-list "Find dir: ") -(helm-projectile-command "recentf" 'helm-source-projectile-recentf-list "Recently visited file: ") -(helm-projectile-command "switch-to-buffer" 'helm-source-projectile-buffers-list "Switch to buffer: " nil helm-buffers-truncate-lines) -(helm-projectile-command "browse-dirty-projects" 'helm-source-projectile-dirty-projects "Select a project: " t) - -(defun helm-projectile--files-display-real (files root) - "Create (DISPLAY . REAL) pairs with FILES and ROOT. - - DISPLAY is the short file name. REAL is the full path." - (cl-loop for display in files - collect (cons display (expand-file-name display root)))) - -;;;###autoload -(defun helm-projectile-find-file-dwim () - "Find file at point based on context." - (interactive) - (let* ((project-root (projectile-project-root)) - (project-files (projectile-current-project-files)) - (files (projectile-select-files project-files))) - (if (= (length files) 1) - (find-file (expand-file-name (car files) (projectile-project-root))) - (helm :sources (helm-build-sync-source "Projectile files" - :candidates (if (> (length files) 1) - (helm-projectile--files-display-real files project-root) - (helm-projectile--files-display-real project-files project-root)) - :fuzzy-match helm-projectile-fuzzy-match - :action-transformer 'helm-find-files-action-transformer - :keymap helm-projectile-find-file-map - :help-message helm-ff-help-message - :mode-line helm-read-file-name-mode-line-string - :action helm-projectile-file-actions - :persistent-action #'helm-projectile-file-persistent-action - :persistent-help "Preview file") - :buffer "*helm projectile*" - :truncate-lines helm-projectile-truncate-lines - :prompt (projectile-prepend-project-name "Find file: "))))) - -;;;###autoload -(defun helm-projectile-find-other-file (&optional flex-matching) - "Switch between files with the same name but different extensions using Helm. -With FLEX-MATCHING, match any file that contains the base name of -current file. Other file extensions can be customized with the -variable `projectile-other-file-alist'." - (interactive "P") - (let* ((project-root (projectile-project-root)) - (other-files (projectile-get-other-files (buffer-file-name) - flex-matching))) - (if other-files - (if (= (length other-files) 1) - (find-file (expand-file-name (car other-files) project-root)) - (progn - (let* ((helm-ff-transformer-show-only-basename nil)) - (helm :sources (helm-build-sync-source "Projectile other files" - :candidates (helm-projectile--files-display-real other-files project-root) - :keymap helm-projectile-find-file-map - :help-message helm-ff-help-message - :mode-line helm-read-file-name-mode-line-string - :action helm-projectile-file-actions - :persistent-action #'helm-projectile-file-persistent-action - :persistent-help "Preview file") - :buffer "*helm projectile*" - :truncate-lines helm-projectile-truncate-lines - :prompt (projectile-prepend-project-name "Find other file: "))))) - (error "No other file found")))) - -(defcustom helm-projectile-ignore-strategy 'projectile - "Allow projectile to compute ignored files and directories. - -When set to `projectile', the package will compute ignores and -explicitely add additionally command line arguments to the search -tool. Note that this might override search tool specific -behaviors (for instance ag would not use VCS ignore files). - -When set to `search-tool', the above does not happen." - :group 'helm-projectile - :type '(choice (const :tag "Allow projectile to compute ignores" projectile) - (const :tag "Let the search tool compute ignores" search-tool))) - -(defun helm-projectile--projectile-ignore-strategy () - "True if the ignore strategy is `projectile'." - (eq 'projectile helm-projectile-ignore-strategy)) - -(defun helm-projectile--ignored-files () - "Compute ignored files." - (cl-union (projectile-ignored-files-rel) grep-find-ignored-files)) - -(defun helm-projectile--ignored-directories () - "Compute ignored directories." - (cl-union (projectile-ignored-directories-rel) grep-find-ignored-directories)) - -(defcustom helm-projectile-grep-or-ack-actions - '("Find file" helm-grep-action - "Find file other frame" helm-grep-other-frame - (lambda () (and (locate-library "elscreen") - "Find file in Elscreen")) - helm-grep-jump-elscreen - "Save results in grep buffer" helm-grep-save-results - "Find file other window" helm-grep-other-window) - "Available actions for `helm-projectile-grep-or-ack'. -The contents of this list are passed as the arguments to `helm-make-actions'." - :type 'symbol - :group 'helm-projectile) - -(defcustom helm-projectile-set-input-automatically t - "If non-nil, attempt to set search input automatically. -Automatic input selection uses the region (if there is an active -region), otherwise it uses the current symbol at point (if there -is one). Applies to `helm-projectile-grep' and -`helm-projectile-ack' only. If the `helm-ag' package is -installed, then automatic input behavior for `helm-projectile-ag' -can be customized using `helm-ag-insert-at-point'." - :group 'helm-projectile - :type 'boolean) - -(defun helm-projectile-grep-or-ack (&optional dir use-ack-p ack-ignored-pattern ack-executable) - "Perform helm-grep at project root. -DIR directory where to search -USE-ACK-P indicates whether to use ack or not. -ACK-IGNORED-PATTERN is a file regex to exclude from searching. -ACK-EXECUTABLE is the actual ack binary name. -It is usually \"ack\" or \"ack-grep\". -If it is nil, or ack/ack-grep not found then use default grep command." - (when (helm-projectile--projectile-ignore-strategy) - (setq helm-grep-ignored-files (helm-projectile--ignored-files) - helm-grep-ignored-directories (mapcar 'directory-file-name (helm-projectile--ignored-directories)))) - - (let* ((default-directory (or dir (projectile-project-root))) - (helm-ff-default-directory default-directory) - (helm-grep-in-recurse t) - (helm-grep-default-command (if use-ack-p - (concat ack-executable " -H --no-group --no-color " - (when ack-ignored-pattern (concat ack-ignored-pattern " ")) - "%p %f") - (if (and projectile-use-git-grep (eq (projectile-project-vcs) 'git)) - helm-projectile-git-grep-command - helm-projectile-grep-command))) - (helm-grep-default-recurse-command helm-grep-default-command)) - - (setq helm-source-grep - (helm-build-async-source - (capitalize (helm-grep-command t)) - :header-name (lambda (_name) - (let ((name (if use-ack-p - "Helm Projectile Ack" - "Helm Projectile Grep"))) - (concat name " " "(C-c ? Help)"))) - :candidates-process 'helm-grep-collect-candidates - :filter-one-by-one 'helm-grep-filter-one-by-one - :candidate-number-limit 9999 - :nohighlight t - ;; We need to specify keymap here and as :keymap arg [1] - ;; to make it available in further resuming. - :keymap helm-grep-map - :history 'helm-grep-history - :action (apply #'helm-make-actions helm-projectile-grep-or-ack-actions) - :persistent-action 'helm-grep-persistent-action - :persistent-help "Jump to line (`C-u' Record in mark ring)" - :requires-pattern 2)) - (helm - :sources 'helm-source-grep - :input (when helm-projectile-set-input-automatically - (if (region-active-p) - (buffer-substring-no-properties (region-beginning) (region-end)) - (thing-at-point 'symbol))) - :buffer (format "*helm %s*" (if use-ack-p - "ack" - "grep")) - :default-directory default-directory - :keymap helm-grep-map - :history 'helm-grep-history - :truncate-lines helm-grep-truncate-lines))) - -;;;###autoload -(defun helm-projectile-on () - "Turn on `helm-projectile' key bindings." - (interactive) - (message "Turn on helm-projectile key bindings") - (helm-projectile-toggle 1)) - -;;;###autoload -(defun helm-projectile-off () - "Turn off `helm-projectile' key bindings." - (interactive) - (message "Turn off helm-projectile key bindings") - (helm-projectile-toggle -1)) - -;;;###autoload -(defun helm-projectile-grep (&optional dir) - "Helm version of `projectile-grep'. -DIR is the project root, if not set then current directory is used" - (interactive) - (let ((project-root (or dir (projectile-project-root) (error "You're not in a project")))) - (funcall 'run-with-timer 0.01 nil - #'helm-projectile-grep-or-ack project-root nil))) - -;;;###autoload -(defun helm-projectile-ack (&optional dir) - "Helm version of projectile-ack. -DIR directory where to search" - (interactive) - (let* ((project-root (or dir (projectile-project-root) (error "You're not in a project"))) - (ignored (when (helm-projectile--projectile-ignore-strategy) - (mapconcat - 'identity - (cl-union (mapcar (lambda (path) - (concat "--ignore-dir=" (file-name-nondirectory (directory-file-name path)))) - (helm-projectile--ignored-directories)) - (mapcar (lambda (path) - (concat "--ignore-file=match:" (shell-quote-argument path))) - (append (helm-projectile--ignored-files) - (projectile-patterns-to-ignore)))) - " "))) - (helm-ack-grep-executable (cond - ((executable-find "ack") "ack") - ((executable-find "ack-grep") "ack-grep") - (t (error "ack or ack-grep is not available"))))) - (funcall 'run-with-timer 0.01 nil - #'helm-projectile-grep-or-ack project-root t ignored helm-ack-grep-executable))) - -;;;###autoload - -(defun helm-projectile-ag (&optional options) - "Helm version of `projectile-ag'. -OPTIONS explicit command line arguments to ag" - (interactive (if current-prefix-arg (list (helm-read-string "option: " "" 'helm-ag--extra-options-history)))) - (if (require 'helm-ag nil t) - (if (projectile-project-p) - (progn - (when (helm-projectile--projectile-ignore-strategy) - (setq grep-find-ignored-files (helm-projectile--ignored-files) - grep-find-ignored-directories (helm-projectile--ignored-directories))) - (let* ((ignored (when (helm-projectile--projectile-ignore-strategy) - (mapconcat (lambda (i) - (concat "--ignore " i)) - (append grep-find-ignored-files - grep-find-ignored-directories - (cadr (projectile-parse-dirconfig-file))) - " "))) - (helm-ag-base-command (concat helm-ag-base-command - (when ignored (concat " " ignored)) - " " options)) - (current-prefix-arg nil)) - (helm-do-ag (projectile-project-root) (car (projectile-parse-dirconfig-file))))) - (error "You're not in a project")) - (when (yes-or-no-p "`helm-ag' is not installed. Install? ") - (condition-case nil - (progn - (package-install 'helm-ag) - (helm-projectile-ag options)) - (error (error "`helm-ag' is not available. Is MELPA in your `package-archives'?")))))) - -;; Declare/define these to satisfy the byte compiler -(defvar helm-rg-prepend-file-name-line-at-top-of-matches) -(defvar helm-rg-include-file-on-every-match-line) -(declare-function helm-rg "ext:helm-rg") -(declare-function helm-rg--get-thing-at-pt "ext:helm-rg") - -(defun helm-projectile-rg--region-selection () - (when helm-projectile-set-input-automatically - (if (region-active-p) - (buffer-substring-no-properties (region-beginning) (region-end)) - (helm-rg--get-thing-at-pt)))) - -(defun glob-quote (string) - "Quote the special glob characters: *, ?, [, and ]. -STRING the string in which to escape special characters." - (replace-regexp-in-string "[]*?[]" "\\\\\\&" string)) - -;;;###autoload -(defun helm-projectile-rg () - "Projectile version of `helm-rg'." - (interactive) - (if (require 'helm-rg nil t) - (if (projectile-project-p) - (let* ((helm-rg-prepend-file-name-line-at-top-of-matches nil) - (helm-rg-include-file-on-every-match-line t) - (default-directory (projectile-project-root))) - (when (helm-projectile--projectile-ignore-strategy) - (setq helm-rg--extra-args - (mapconcat - 'identity - (cl-union (mapcan (lambda (path) - (list "--glob" (concat "!" (glob-quote path)))) - (helm-projectile--ignored-files)) - (mapcan (lambda (path) - (list "--glob" (concat "!" (glob-quote path) "/**"))) - (mapcar 'directory-file-name (helm-projectile--ignored-directories)))) - " "))) - (helm-rg (helm-projectile-rg--region-selection) - nil)) - (error "You're not in a project")) - (when (yes-or-no-p "`helm-rg' is not installed. Install? ") - (condition-case nil - (progn - (package-install 'helm-rg) - (helm-projectile-rg)) - (error "`helm-rg' is not available. Is MELPA in your `package-archives'?"))))) - -(defun helm-projectile-commander-bindings () - (def-projectile-commander-method ?a - "Run ack on project." - (call-interactively 'helm-projectile-ack)) - - (def-projectile-commander-method ?A - "Find ag on project." - (call-interactively 'helm-projectile-ag)) - - (def-projectile-commander-method ?f - "Find file in project." - (helm-projectile-find-file)) - - (def-projectile-commander-method ?b - "Switch to project buffer." - (helm-projectile-switch-to-buffer)) - - (def-projectile-commander-method ?d - "Find directory in project." - (helm-projectile-find-dir)) - - (def-projectile-commander-method ?g - "Run grep on project." - (helm-projectile-grep)) - - (def-projectile-commander-method ?s - "Switch project." - (helm-projectile-switch-project)) - - (def-projectile-commander-method ?e - "Find recently visited file in project." - (helm-projectile-recentf)) - - (def-projectile-commander-method ?V - "Find dirty projects." - (helm-projectile-browse-dirty-projects))) - -;;;###autoload -(defun helm-projectile-toggle (toggle) - "Toggle Helm version of Projectile commands." - (if (> toggle 0) - (progn - (when (eq projectile-switch-project-action #'projectile-find-file) - (setq projectile-switch-project-action #'helm-projectile-find-file)) - (define-key projectile-mode-map [remap projectile-find-other-file] #'helm-projectile-find-other-file) - (define-key projectile-mode-map [remap projectile-find-file] #'helm-projectile-find-file) - (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] #'helm-projectile-find-file-in-known-projects) - (define-key projectile-mode-map [remap projectile-find-file-dwim] #'helm-projectile-find-file-dwim) - (define-key projectile-mode-map [remap projectile-find-dir] #'helm-projectile-find-dir) - (define-key projectile-mode-map [remap projectile-switch-project] #'helm-projectile-switch-project) - (define-key projectile-mode-map [remap projectile-recentf] #'helm-projectile-recentf) - (define-key projectile-mode-map [remap projectile-switch-to-buffer] #'helm-projectile-switch-to-buffer) - (define-key projectile-mode-map [remap projectile-grep] #'helm-projectile-grep) - (define-key projectile-mode-map [remap projectile-ack] #'helm-projectile-ack) - (define-key projectile-mode-map [remap projectile-ag] #'helm-projectile-ag) - (define-key projectile-mode-map [remap projectile-ripgrep] #'helm-projectile-rg) - (define-key projectile-mode-map [remap projectile-browse-dirty-projects] #'helm-projectile-browse-dirty-projects) - (helm-projectile-commander-bindings)) - (progn - (when (eq projectile-switch-project-action #'helm-projectile-find-file) - (setq projectile-switch-project-action #'projectile-find-file)) - (define-key projectile-mode-map [remap projectile-find-other-file] nil) - (define-key projectile-mode-map [remap projectile-find-file] nil) - (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] nil) - (define-key projectile-mode-map [remap projectile-find-file-dwim] nil) - (define-key projectile-mode-map [remap projectile-find-dir] nil) - (define-key projectile-mode-map [remap projectile-switch-project] nil) - (define-key projectile-mode-map [remap projectile-recentf] nil) - (define-key projectile-mode-map [remap projectile-switch-to-buffer] nil) - (define-key projectile-mode-map [remap projectile-grep] nil) - (define-key projectile-mode-map [remap projectile-ag] nil) - (define-key projectile-mode-map [remap projectile-ripgrep] nil) - (define-key projectile-mode-map [remap projectile-browse-dirty-projects] nil) - (projectile-commander-bindings)))) - -;;;###autoload -(defun helm-projectile (&optional arg) - "Use projectile with Helm instead of ido. - -With a prefix ARG invalidates the cache first. -If invoked outside of a project, displays a list of known projects to jump." - (interactive "P") - (if (not (projectile-project-p)) - (helm-projectile-switch-project arg) - (projectile-maybe-invalidate-cache arg) - (let ((helm-ff-transformer-show-only-basename nil)) - (helm :sources helm-projectile-sources-list - :buffer "*helm projectile*" - :truncate-lines helm-projectile-truncate-lines - :prompt (projectile-prepend-project-name (if (projectile-project-p) - "pattern: " - "Switch to project: ")))))) - -;;;###autoload -(eval-after-load 'projectile - '(progn - (define-key projectile-command-map (kbd "h") #'helm-projectile))) - -(provide 'helm-projectile) - -;;; helm-projectile.el ends here diff --git a/elpa/helm-projectile-20250902.830/helm-projectile-autoloads.el b/elpa/helm-projectile-20250902.830/helm-projectile-autoloads.el new file mode 100644 index 00000000..b520061a --- /dev/null +++ b/elpa/helm-projectile-20250902.830/helm-projectile-autoloads.el @@ -0,0 +1,123 @@ +;;; helm-projectile-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; Generated autoloads from helm-projectile.el + +(defvar helm-projectile-fuzzy-match t "\ +Enable fuzzy matching for Helm Projectile commands. +This needs to be set before loading helm-projectile.el.") +(custom-autoload 'helm-projectile-fuzzy-match "helm-projectile" t) +(autoload 'helm-projectile-switch-project "helm-projectile" nil t) +(autoload 'helm-projectile-switch-project-other-window "helm-projectile" nil t) +(autoload 'helm-projectile-switch-project-other-frame "helm-projectile" nil t) +(autoload 'helm-projectile-find-file "helm-projectile" nil t) +(autoload 'helm-projectile-find-file-other-window "helm-projectile" nil t) +(autoload 'helm-projectile-find-file-other-frame "helm-projectile" nil t) +(autoload 'helm-projectile-find-file-in-known-projects "helm-projectile" nil t) +(autoload 'helm-projectile-find-dir "helm-projectile" nil t) +(autoload 'helm-projectile-find-dir-other-window "helm-projectile" nil t) +(autoload 'helm-projectile-find-dir-other-frame "helm-projectile" nil t) +(autoload 'helm-projectile-recentf "helm-projectile" nil t) +(autoload 'helm-projectile-switch-to-buffer "helm-projectile" nil t) +(autoload 'helm-projectile-switch-to-buffer-other-window "helm-projectile" nil t) +(autoload 'helm-projectile-switch-to-buffer-other-frame "helm-projectile" nil t) +(autoload 'helm-projectile-browse-dirty-projects "helm-projectile" nil t) +(autoload 'helm-projectile-find-file-dwim "helm-projectile" "\ +Find file at point based on context." t) +(autoload 'helm-projectile-find-file-dwim-other-window "helm-projectile" "\ +Find file at point based on context." t) +(autoload 'helm-projectile-find-file-dwim-other-frame "helm-projectile" "\ +Find file at point based on context." t) +(autoload 'helm-projectile-find-other-file "helm-projectile" "\ +Switch between files with the same name but different extensions using Helm. +With FLEX-MATCHING, match any file that contains the base name of +current file. Other file extensions can be customized with the +variable `projectile-other-file-alist'. + +(fn &optional FLEX-MATCHING)" t) +(autoload 'helm-projectile-find-other-file-other-window "helm-projectile" "\ +Switch between files with the same name but different extensions using Helm. +With FLEX-MATCHING, match any file that contains the base name of +current file. Other file extensions can be customized with the +variable `projectile-other-file-alist'. + +(fn &optional FLEX-MATCHING)" t) +(autoload 'helm-projectile-find-other-file-other-frame "helm-projectile" "\ +Switch between files with the same name but different extensions using Helm. +With FLEX-MATCHING, match any file that contains the base name of +current file. Other file extensions can be customized with the +variable `projectile-other-file-alist'. + +(fn &optional FLEX-MATCHING)" t) +(autoload 'helm-projectile-on "helm-projectile" "\ +Turn on `helm-projectile' key bindings." t) +(autoload 'helm-projectile-off "helm-projectile" "\ +Turn off `helm-projectile' key bindings." t) +(autoload 'helm-projectile-grep "helm-projectile" "\ +Helm version of `projectile-grep'. +DIR is the project root, if not set then current project root is used. +FILES is a list of file patterns to search in. When called with a +prefix argument then ask for FILES. + +(fn &optional DIR FILES)" t) +(autoload 'helm-projectile-ack "helm-projectile" "\ +Helm version of `projectile-ack'. +DIR directory where to search, if not set then current project root is +used. TYPES is a list of types to include in search. When called with +a prefix argument, then ask for TYPES. + +(fn &optional DIR TYPES)" t) +(autoload 'helm-projectile-ag "helm-projectile" "\ +Helm version of `projectile-ag'. +When called with a single or a triple prefix argument, ask for OPTIONS. +When called with a double or a triple prefix argument, ask for +TYPES (see `helm-grep-ag').' + +This command uses `helm-grep-ag' to perform the search, so the actual +searcher used is determined by the value of `helm-grep-ag-command'. + +(fn &optional OPTIONS)" t) +(autoload 'helm-projectile-rg "helm-projectile" "\ +Projectile version of `helm-rg'. +When package `helm-rg' is not installed and `helm-grep-ag-command' is +pointing to ripgrep (rg) then use `helm-projectile-ag'. Otherwise if +`helm-projectile-ask-to-install-helm-rg' is non nil then ask to install +package `helm-rg'." t) +(autoload 'helm-projectile-toggle "helm-projectile" "\ +Toggle Helm version of Projectile commands. +When TOGGLE is greater than 0 turn Helm version of Projectile commands +on. When TOGGLE is is less or equal to 0 turn Helm version of commands +off. + +(fn TOGGLE)") +(autoload 'helm-projectile "helm-projectile" "\ +Use projectile with Helm instead of ido. + +With a prefix ARG invalidates the cache first. +If invoked outside of a project, displays a list of known projects to jump. + +(fn &optional ARG)" t) +(eval-after-load 'projectile '(progn (define-key projectile-command-map (kbd "h") #'helm-projectile))) +(register-definition-prefixes "helm-projectile" '("helm-")) + +;;; End of scraped data + +(provide 'helm-projectile-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; helm-projectile-autoloads.el ends here diff --git a/elpa/helm-projectile-20250902.830/helm-projectile-pkg.el b/elpa/helm-projectile-20250902.830/helm-projectile-pkg.el new file mode 100644 index 00000000..118bf5cc --- /dev/null +++ b/elpa/helm-projectile-20250902.830/helm-projectile-pkg.el @@ -0,0 +1,10 @@ +;; -*- no-byte-compile: t; lexical-binding: nil -*- +(define-package "helm-projectile" "20250902.830" + "Helm integration for Projectile." + '((emacs "26.1") + (helm "3.0") + (projectile "2.9")) + :url "https://github.com/bbatsov/helm-projectile" + :commit "0ffb6b5f09c1d65d721c1111ebfa6cec0ba63234" + :revdesc "0ffb6b5f09c1" + :keywords '("project" "convenience")) diff --git a/elpa/helm-projectile-20250902.830/helm-projectile.el b/elpa/helm-projectile-20250902.830/helm-projectile.el new file mode 100644 index 00000000..bc73ca4e --- /dev/null +++ b/elpa/helm-projectile-20250902.830/helm-projectile.el @@ -0,0 +1,1819 @@ +;;; helm-projectile.el --- Helm integration for Projectile -*- lexical-binding: t; -*- + +;; Copyright (C) 2011-2025 Bozhidar Batsov + +;; Author: Bozhidar Batsov +;; URL: https://github.com/bbatsov/helm-projectile +;; Maintainer: Przemysław Kryger +;; Created: 2011-31-07 +;; Keywords: project, convenience +;; Package-Version: 20250902.830 +;; Package-Revision: 0ffb6b5f09c1 +;; Package-Requires: ((emacs "26.1") (helm "3.0") (projectile "2.9")) + +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; This library provides easy project management and navigation. The +;; concept of a project is pretty basic - just a folder containing +;; special file. Currently git, mercurial and bazaar repos are +;; considered projects by default. If you want to mark a folder +;; manually as a project just create an empty .projectile file in +;; it. See the README for more details. +;; +;;; Code: + +;; built-in libraries +(require 'subr-x) +(require 'cl-lib) +(require 'grep) ;; TODO: Probably we should defer this require + +(require 'helm-core) +(require 'helm-global-bindings) +(require 'helm-types) +(require 'helm-locate) +(require 'helm-buffers) +(require 'helm-files) +(require 'helm-grep) + +(require 'projectile) + +(declare-function eshell "eshell") +(declare-function dired-get-filename "dired") + +(defvar grep-find-ignored-directories) +(defvar grep-find-ignored-files) + +(defgroup helm-projectile nil + "Helm support for projectile." + :prefix "helm-projectile-" + :group 'projectile + :group 'helm + :link `(url-link :tag "GitHub" "https://github.com/bbatsov/helm-projectile")) + +(defvar helm-projectile-current-project-root) + +(defcustom helm-projectile-truncate-lines nil + "Truncate lines in helm projectile commands when non--nil. + +Some `helm-projectile' commands have similar behavior with existing +Helms. In these cases their respective custom var for truncation +of lines will be honored. E.g. `helm-buffers-truncate-lines' +dictates the truncation in `helm-projectile-switch-to-buffer'." + :group 'helm-projectile + :type 'boolean) + +(defcustom helm-projectile-remove-current-buffer nil + "Non-nil if current buffer should be removed from buffer sources. +For example from `helm-projectile' (when invoked from a project) as well +as from `helm-projectile-switch-to-buffer', +`helm-projectile-switch-to-buffer-other-window', and +`helm-projectile-switch-to-buffer-other-frame'." + :type 'boolean + :group 'helm-projectile) + +;;;###autoload +(defcustom helm-projectile-fuzzy-match t + "Enable fuzzy matching for Helm Projectile commands. +This needs to be set before loading helm-projectile.el." + :group 'helm-projectile + :type 'boolean) + +(defmacro helm-projectile-define-key (keymap &rest bindings) + "In KEYMAP, define BINDINGS. +BINDINS is a list in a form of (KEY1 DEF1 KEY2 DEF2 ...)." + (declare (indent defun)) + (when (or (< (length bindings) 2) + (= 1 (% 2 (length bindings)))) + (error "Expected BINDINGS to be KEY1 DEF1 KEY2 DEF2 ... ")) + (let ((ret '(progn))) + (while-let ((key (car bindings)) + (def (cadr bindings))) + (push + `(define-key ,keymap ,key + (lambda () + (interactive) + (helm-exit-and-execute-action ,def))) + ret) + (setq bindings (cddr bindings))) + (reverse ret))) + +(defun helm-projectile-hack-actions (actions &rest prescription) + "Given a Helm action list and a prescription, return a hacked Helm action list. +Optionally applies the PRESCRIPTION beforehand. + +The Helm action list ACTIONS is of the form: + +\(\(DESCRIPTION1 . FUNCTION1\) + \(DESCRIPTION2 . FUNCTION2\) + ... + \(DESCRIPTIONn . FUNCTIONn\)\) + +PRESCRIPTION is in the form: + +\(INSTRUCTION1 INSTRUCTION2 ... INSTRUCTIONn\) + +If an INSTRUCTION is a symbol, the action with function name +INSTRUCTION is deleted. + +If an INSTRUCTION is of the form \(FUNCTION1 . FUNCTION2\), the +action with function name FUNCTION1 will change it's function to +FUNCTION2. + +If an INSTRUCTION is of the form \(FUNCTION . DESCRIPTION\), and +if an action with function name FUNCTION exists in the original +Helm action list, the action in the Helm action list, with +function name FUNCTION will change it's description to +DESCRIPTION. Otherwise, (FUNCTION . DESCRIPTION) will be added to +the action list. + +If an INSTRUCTION is of the form \(FUNCTION . :make-first\), and if the +an action with function name FUNCTION exists in the th Helm action list +concatenated with new actions from PRESCRIPTION, the action will become +the first (default) action. + +Please check out how `helm-projectile-file-actions' is defined +for an example of how this function is being used." + (let* ((to-delete (cl-remove-if (lambda (entry) (listp entry)) prescription)) + (actions (cl-delete-if (lambda (action) (memq (cdr action) to-delete)) + (copy-alist actions))) + new) + (cl-dolist (action actions) + (when (setq new (cdr (assq (cdr action) prescription))) + (cond + ((stringp new) (setcar action new)) + ((functionp new) (setcdr action new))))) + ;; Add new actions from PRESCRIPTION + (setq new nil) + (cl-dolist (instruction prescription) + (when (and (listp instruction) + (null (rassq (car instruction) actions)) + (symbolp (car instruction)) (stringp (cdr instruction))) + (push (cons (cdr instruction) (car instruction)) new))) + ;; Push to front the desired action + (let ((actions (append actions (nreverse new)))) + (if-let* ((first-function (car (rassq :make-first prescription))) + (first-action-p (lambda (action) + (eq (cdr action) + first-function))) + (first-action (cl-find-if first-action-p actions))) + (cons first-action + (cl-remove-if first-action-p actions)) + actions)))) + +(defun helm-projectile-vc (dir) + "A Helm action for jumping to project root using `vc-dir' or Magit. +DIR is a directory to be switched" + (let ((projectile-require-project-root nil)) + (projectile-vc dir))) + +(defun helm-projectile-compile-project (dir) + "A Helm action for compile a project. +DIR is the project root." + (let ((helm--reading-passwd-or-string t) + (default-directory dir)) + (projectile-compile-project helm-current-prefix-arg))) + +(defun helm-projectile-test-project (dir) + "A Helm action for test a project. +DIR is the project root." + (let ((helm--reading-passwd-or-string t) + (default-directory dir)) + (projectile-test-project helm-current-prefix-arg))) + +(defun helm-projectile-run-project (dir) + "A Helm action for run a project. +DIR is the project root." + (let ((helm--reading-passwd-or-string t) + (default-directory dir)) + (projectile-run-project helm-current-prefix-arg))) + +(defun helm-projectile-remove-known-project (_ignore) + "Remove selected projects from projectile project list. +_IGNORE means the argument does not matter. +It is there because Helm requires it." + (let* ((projects (helm-marked-candidates :with-wildcard t)) + (len (length projects))) + (with-helm-display-marked-candidates + helm-marked-buffer-name + projects + (if (not (y-or-n-p (format "Remove *%s projects(s)? " len))) + (message "(No removal performed)") + (progn + (mapc (lambda (p) + (setq projectile-known-projects (delete p projectile-known-projects))) + projects) + (projectile-save-known-projects)) + (message "%s projects(s) removed" len))))) + +(defun helm-projectile-switch-project-by-name (project) + "Switch to PROJECT and execute `projectile-switch-project-action' in it. +When `projectile-switch-project-action' is `projectile-find-file' a +`helm-projectile-find-file' will be used instead." + (let ((projectile-completion-system 'helm) + (projectile-switch-project-action + (if (eq projectile-switch-project-action 'projectile-find-file) + #'helm-projectile-find-file + projectile-switch-project-action))) + (projectile-switch-project-by-name project))) + +(defun helm-projectile-switch-project-by-name-other-window (project) + "Switch to PROJECT and find file in it in other window." + (let ((projectile-completion-system 'helm) + (projectile-switch-project-action #'helm-projectile-find-file-other-window)) + (projectile-switch-project-by-name project))) + +(defun helm-projectile-switch-project-by-name-other-frame (project) + "Switch to PROJECT and find file in it in other frame." + (let ((projectile-completion-system 'helm) + (projectile-switch-project-action #'helm-projectile-find-file-other-frame)) + (projectile-switch-project-by-name project))) + +(defvar helm-projectile-projects-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (helm-projectile-define-key map + (kbd "C-d") #'dired + (kbd "C-c o") #'helm-projectile-switch-project-by-name-other-window + (kbd "C-c C-o") #'helm-projectile-switch-project-by-name-other-frame + (kbd "M-g") #'helm-projectile-vc + (kbd "M-e") #'helm-projectile-switch-to-shell + (kbd "C-s") #'helm-projectile-grep + (kbd "M-c") #'helm-projectile-compile-project + (kbd "M-t") #'helm-projectile-test-project + (kbd "M-r") #'helm-projectile-run-project + (kbd "M-D") #'helm-projectile-remove-known-project + (kbd "C-S-a") #'helm-projectile--switch-project-and-ag-action + (kbd "C-S-r") #'helm-projectile--switch-project-and-rg-action) + map) + "Mapping for known projectile projects.") + +(defcustom helm-source-projectile-projects-actions + (helm-make-actions + "Switch to project" #'helm-projectile-switch-project-by-name + "Switch to project other window `C-c o'" #'helm-projectile-switch-project-by-name-other-window + "Switch to project other frame `C-c C-o'" #'helm-projectile-switch-project-by-name-other-frame + "Open Dired in project's directory `C-d'" #'dired + "Open project root in vc-dir or magit `M-g'" #'helm-projectile-vc + "Switch to Eshell `M-e'" #'helm-projectile-switch-to-shell + "Grep in projects `C-s'" #'helm-projectile-grep + "Compile project `M-c'. With C-u, new compile command" #'helm-projectile-compile-project + "Remove project(s) from project list `M-D'" #'helm-projectile-remove-known-project + "Silver searcher (ag) in project `C-S-a'" #'helm-projectile--switch-project-and-ag-action + "Ripgrep (rg) in project `C-S-r'" #'helm-projectile--switch-project-and-rg-action) + "Actions for `helm-source-projectile-projects'." + :group 'helm-projectile + :type '(alist :key-type string :value-type function)) + +(defclass helm-projectile-projects-source (helm-source-sync helm-type-file) + ((candidates :initform (lambda () (with-helm-current-buffer + (mapcar #'copy-sequence + (projectile-known-projects))))) + (fuzzy-match :initform 'helm-projectile-fuzzy-match) + (keymap :initform 'helm-projectile-projects-map) + (mode-line :initform 'helm-read-file-name-mode-line-string) + (action :initform 'helm-source-projectile-projects-actions)) + "Helm source for known projectile projects.") + +(cl-defmethod helm-setup-user-source ((source helm-projectile-projects-source)) + "Make SOURCE specific to project switching. +The `helm-projectile-projects-source` inherits from +`helm-type-file` (which see), which sets up actions, keymap, and +help message slots to file specific ones. Override these slots +to be specific to `helm-projectile-projects-source'." + (setf (slot-value source 'action) 'helm-source-projectile-projects-actions) + (setf (slot-value source 'keymap) helm-projectile-projects-map) + ;; Use `ignore' as a persistent action, to actually keep `helm' session + ;; when `helm-execute-persistent-action' is executed. + (setf (slot-value source 'persistent-action) #'ignore) + (let ((persistent-help "Do Nothing")) + (setf (slot-value source 'persistent-help) persistent-help) + (setf (slot-value source 'header-line) + (helm-source--persistent-help-string + persistent-help + source))) + (setf (slot-value source 'mode-line) + (list "Project(s)" helm-mode-line-string))) + +(defvar helm-source-projectile-projects + (helm-make-source "Projectile projects" 'helm-projectile-projects-source)) + +(defclass helm-projectile-projects-other-window-source (helm-projectile-projects-source) + ()) + +(cl-defmethod helm-setup-user-source :after ((source helm-projectile-projects-other-window-source)) + "Set `helm-projectile-switch-project-by-name-other-window' as the first action." + (setf (slot-value source 'action) + (helm-projectile-hack-actions + helm-source-projectile-projects-actions + '(helm-projectile-switch-project-by-name-other-window . :make-first)))) + +(defclass helm-projectile-projects-other-frame-source (helm-projectile-projects-source) + ()) + +(cl-defmethod helm-setup-user-source :after ((source helm-projectile-projects-other-frame-source)) + "Set `helm-projectile-switch-project-by-name-other-frame' as the first action." + (setf (slot-value source 'action) + (helm-projectile-hack-actions + helm-source-projectile-projects-actions + '(helm-projectile-switch-project-by-name-other-frame . :make-first)))) + +(defvar helm-projectile-dirty-projects-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (helm-projectile-define-key map + (kbd "C-d") #'dired + (kbd "M-o") #'helm-projectile-switch-project-by-name + (kbd "C-c o") #'helm-projectile-switch-project-by-name-other-window + (kbd "C-c C-o") #'helm-projectile-switch-project-by-name-other-frame + (kbd "M-e") #'helm-projectile-switch-to-shell + (kbd "C-s") #'helm-projectile-grep + (kbd "M-c") #'helm-projectile-compile-project + (kbd "M-t") #'helm-projectile-test-project + (kbd "M-r") #'helm-projectile-run-project + (kbd "M-D") #'helm-projectile-remove-known-project + (kbd "C-S-a") #'helm-projectile--switch-project-and-ag-action + (kbd "C-S-r") #'helm-projectile--switch-project-and-rg-action) + map) + "Mapping for dirty projectile projects.") + +(defvar helm-source-projectile-dirty-projects + (helm-build-sync-source "Projectile dirty projects" + :candidates (lambda () (with-helm-current-buffer (helm-projectile-get-dirty-projects))) + :fuzzy-match helm-projectile-fuzzy-match + :keymap helm-projectile-dirty-projects-map + :mode-line helm-read-file-name-mode-line-string + :action '(("Open project root in vc-dir or magit" . helm-projectile-vc) + ("Switch to project `M-o'" . helm-projectile-switch-project-by-name) + ("Switch to project other window `C-c o'" . helm-projectile-switch-project-by-name-other-window) + ("Switch to project other frame `C-c C-o'" . helm-projectile-switch-project-by-name-other-frame) + ("Open Dired in project's directory `C-d'" . dired) + ("Switch to Eshell `M-e'" . helm-projectile-switch-to-shell) + ("Grep in projects `C-s'" . helm-projectile-grep) + ("Compile project `M-c'. With C-u, new compile command" + . helm-projectile-compile-project) + ("Silver searcher (ag) in project `C-S-a'" . helm-projectile--switch-project-and-ag-action) + ("Ripgrep (rg) in project `C-S-r'" . helm-projectile--switch-project-and-rg-action))) + "Helm source for dirty version controlled projectile projects.") + +(defun helm-projectile-get-dirty-projects () + "Return dirty version controlled known projects. +The value is returned as an alist to have a nice display in Helm." + (message "Checking for dirty known projects...") + (let* ((status (projectile-check-vcs-status-of-known-projects)) + (proj-dir (cl-loop for stat in status + collect (car stat))) + (status-display (cl-loop for stat in status collect + (propertize (format "[%s]" + (mapconcat 'identity + (car (cdr stat)) ", ")) + 'face 'helm-match))) + (max-status-display-length (cl-loop for sd in status-display + maximize (length sd))) + (status-display (cl-loop for sd in status-display collect + (format "%s%s " + sd + (make-string + (- max-status-display-length (length sd)) ? )))) + (full-display (cl-mapcar 'concat + status-display + (mapcar (lambda (dir) + (propertize dir 'face 'helm-ff-directory)) + proj-dir)))) + (cl-pairlis full-display proj-dir))) + +(define-key helm-etags-map (kbd "C-c p f") + (lambda () + (interactive) + (helm-run-after-exit 'helm-projectile-find-file nil))) + +(defun helm-projectile-file-persistent-action (candidate) + "Previews the contents of a file CANDIDATE in a temporary buffer. +This is a persistent action for file-related functionality." + (let ((buf (get-buffer-create " *helm-projectile persistent*"))) + (cl-flet ((preview (candidate) + (switch-to-buffer buf) + (setq inhibit-read-only t) + (erase-buffer) + (insert-file-contents candidate) + (let ((buffer-file-name candidate)) + (set-auto-mode)) + (font-lock-ensure) + (setq inhibit-read-only nil))) + (if (and (helm-get-attr 'previewp) + (string= candidate (helm-get-attr 'current-candidate))) + (progn + (kill-buffer buf) + (helm-set-attr 'previewp nil)) + (preview candidate) + (helm-set-attr 'previewp t))) + (helm-set-attr 'current-candidate candidate))) + +(defun helm-projectile-find-files-eshell-command-on-file-action (candidate) + "Execute an eshell command on a file CANDIDATE." + (interactive) + (let* ((helm-ff-default-directory (file-name-directory candidate))) + (helm-find-files-eshell-command-on-file candidate))) + +(defun helm-projectile-ff-etags-select-action (candidate) + "Jump to etags for file CANDIDATE. +See also `helm-etags-select'." + (interactive) + (let* ((helm-ff-default-directory (file-name-directory candidate))) + (helm-ff-etags-select candidate))) + +(defun helm-projectile-switch-to-shell (dir) + "Within DIR, switch to a shell corresponding to `helm-ff-preferred-shell-mode'." + (interactive) + (let* ((projectile-require-project-root nil) + (helm-ff-default-directory (file-name-directory (projectile-expand-root dir)))) + (helm-ff-switch-to-shell dir))) + +(defun helm-projectile-files-in-current-dired-buffer () + "Return a list of files (only) in the current Dired buffer." + (let (flist) + (cl-flet ((fpush (fname) (push fname flist))) + (save-excursion + (let (file buffer-read-only) + (goto-char (point-min)) + (while (not (eobp)) + (save-excursion + (and (not (eolp)) + (setq file (dired-get-filename t t)) ; nil on non-file + (progn (end-of-line) + (funcall #'fpush file)))) + (forward-line 1))))) + (mapcar 'file-truename (nreverse flist)))) + +(defun helm-projectile-all-dired-buffers () + "Get all current Dired buffers." + (mapcar (lambda (b) + (with-current-buffer b (buffer-name))) + (cl-remove-if-not + (lambda (b) + (with-current-buffer b + (and (eq major-mode 'dired-mode) + (buffer-name)))) + (buffer-list)))) + +(defvar helm-projectile-virtual-dired-remote-enable nil + "Enable virtual Dired manager on remote host. +Disabled by default.") + +(defun helm-projectile-dired-files-new-action (candidate) + "Create a Dired buffer from chosen files. +CANDIDATE is the selected file, but choose the marked files if available." + (if (and (file-remote-p (projectile-project-root)) + (not helm-projectile-virtual-dired-remote-enable)) + (message "Virtual Dired manager is disabled in remote host. Enable with %s." + (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face)) + (let ((files (cl-remove-if-not + (lambda (f) + (not (string= f ""))) + (mapcar (lambda (file) + (replace-regexp-in-string (projectile-project-root) "" file)) + (helm-marked-candidates :with-wildcard t)))) + (new-name (completing-read "Select or enter a new buffer name: " + (helm-projectile-all-dired-buffers))) + (helm--reading-passwd-or-string t) + (default-directory (projectile-project-root))) + ;; create a unique buffer that is unique to any directory in default-directory + ;; or opened buffer; when Dired is passed with a non-existence directory name, + ;; it only creates a buffer and insert everything. If a new name user supplied + ;; exists as default-directory, Dired throws error when insert anything that + ;; does not exist in current directory. + (with-current-buffer (dired (cons (make-temp-name new-name) + (if files + files + (list candidate)))) + (when (get-buffer new-name) + (kill-buffer new-name)) + (rename-buffer new-name))))) + +(defun helm-projectile-dired-files-add-action (candidate) + "Add files to a Dired buffer. +CANDIDATE is the selected file. Used when no file is explicitly marked." + (if (and (file-remote-p (projectile-project-root)) + (not helm-projectile-virtual-dired-remote-enable)) + (message "Virtual Dired manager is disabled in remote host. Enable with %s." + (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face)) + (if (eq (with-helm-current-buffer major-mode) 'dired-mode) + (let* ((marked-files (helm-marked-candidates :with-wildcard t)) + (helm--reading-passwd-or-string t) + (root (projectile-project-root)) ; store root for later use + (dired-buffer-name (or (and (eq major-mode 'dired-mode) (buffer-name)) + (completing-read "Select a Dired buffer:" + (helm-projectile-all-dired-buffers)))) + (dired-files (with-current-buffer dired-buffer-name + (helm-projectile-files-in-current-dired-buffer))) + (files (sort (mapcar (lambda (file) + (replace-regexp-in-string (projectile-project-root) "" file)) + (cl-nunion (if marked-files + marked-files + (list candidate)) + dired-files + :test #'string-equal)) + 'string-lessp))) + (kill-buffer dired-buffer-name) + ;; Rebind default-directory because after killing a buffer, we + ;; could be in any buffer and default-directory is set to that + ;; random buffer + ;; + ;; Also use saved root directory, because after killing a buffer, + ;; we could be outside of current project + (let ((default-directory root)) + (with-current-buffer (dired (cons (make-temp-name dired-buffer-name) + (if files + (mapcar (lambda (file) + (replace-regexp-in-string root "" file)) + files) + (list candidate)))) + (rename-buffer dired-buffer-name)))) + (error "You're not in a Dired buffer to add")))) + +(defun helm-projectile-dired-files-delete-action (candidate) + "Delete selected entries from a Dired buffer. +CANDIDATE is the selected file. Used when no file is explicitly marked." + (if (and (file-remote-p (projectile-project-root)) + (not helm-projectile-virtual-dired-remote-enable)) + (message "Virtual Dired manager is disabled in remote host. Enable with %s." + (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face)) + (let* ((helm--reading-passwd-or-string t) + (root (projectile-project-root)) + (dired-buffer-name (with-helm-current-buffer (buffer-name))) + (dired-files (with-current-buffer dired-buffer-name + (helm-projectile-files-in-current-dired-buffer))) + (files (sort (cl-set-exclusive-or (helm-marked-candidates :with-wildcard t) + dired-files + :test #'string-equal) #'string-lessp))) + (kill-buffer dired-buffer-name) + ;; similar reason to `helm-projectile-dired-files-add-action' + (let ((default-directory root)) + (with-current-buffer (dired (cons (make-temp-name dired-buffer-name) + (if files + (mapcar (lambda (file) + (replace-regexp-in-string root "" file)) + files) + (list candidate)))) + (rename-buffer dired-buffer-name)))))) + +(defun helm-projectile-run-projectile-hooks-after-find-file (_orig-fun &rest _args) + "Run `projectile-find-file-hook' if using projectile." + (when (and projectile-mode (projectile-project-p)) + (run-hooks 'projectile-find-file-hook))) + +(advice-add 'helm-find-file-or-marked + :after #'helm-projectile-run-projectile-hooks-after-find-file) + +(defvar helm-projectile-find-file-map + (let ((map (copy-keymap helm-find-files-map))) + (helm-projectile-define-key map + (kbd "C-c f") #'helm-projectile-dired-files-new-action + (kbd "C-c a") #'helm-projectile-dired-files-add-action + (kbd "M-e") #'helm-projectile-switch-to-shell + (kbd "M-.") #'helm-projectile-ff-etags-select-action + (kbd "M-!") #'helm-projectile-find-files-eshell-command-on-file-action) + (define-key map (kbd "") #'helm-previous-source) + (define-key map (kbd "") #'helm-next-source) + (dolist (cmd '(helm-find-files-up-one-level + helm-find-files-down-last-level)) + (substitute-key-definition cmd nil map)) + map) + "Mapping for file commands in Helm Projectile.") + +(defvar helm-projectile-file-actions + (helm-projectile-hack-actions + helm-find-files-actions + ;; Delete these actions + 'helm-ff-browse-project + 'helm-insert-file-name-completion-at-point + 'helm-ff-find-sh-command + 'helm-ff-cache-add-file + ;; Substitute these actions + '(helm-ff-switch-to-shell . helm-projectile-switch-to-shell) + '(helm-ff-etags-select . helm-projectile-ff-etags-select-action) + '(helm-find-files-eshell-command-on-file + . helm-projectile-find-files-eshell-command-on-file-action) + ;; Change action descriptions + '(helm-find-file-as-root . "Find file as root `C-c r'") + ;; New actions + '(helm-projectile-dired-files-new-action + . "Create Dired buffer from files `C-c f'") + '(helm-projectile-dired-files-add-action + . "Add files to Dired buffer `C-c a'")) + "Action for files.") + +(defun helm-projectile--move-selection-p (selection) + "Return non-nil if should move Helm selector after SELECTION. + +SELECTION should be moved unless it's one of: + +- Non-string +- Existing file +- Non-remote file that matches `helm-tramp-file-name-regexp'" + (not (or (not (stringp selection)) + (file-exists-p selection) + (and (string-match helm-tramp-file-name-regexp selection) + (not (file-remote-p selection nil t)))))) + +(defun helm-projectile--move-to-real () + "Move to first real candidate. + +Similar to `helm-ff--move-to-first-real-candidate', but without +unnecessary complexity." + (while (let* ((src (helm-get-current-source)) + (selection (and (not (helm-empty-source-p)) + (helm-get-selection nil nil src)))) + (and (not (helm-end-of-source-p)) + (helm-projectile--move-selection-p selection))) + (helm-next-line))) + +(defun helm-projectile--remove-move-to-real () + "Hook function to remove `helm-projectile--move-to-real'. + +Meant to be added to `helm-cleanup-hook', from which it removes + itself at the end." + (remove-hook 'helm-after-update-hook #'helm-projectile--move-to-real) + (remove-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real)) + +(defvar helm-source-projectile-files-list-before-init-hook + (lambda () + (add-hook 'helm-after-update-hook #'helm-projectile--move-to-real) + (add-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real))) + +(defclass helm-source-projectile-file (helm-source-sync) + ((before-init-hook :initform 'helm-source-projectile-files-list-before-init-hook) + (candidates + :initform (lambda () + (when (projectile-project-p) + (with-temp-buffer + (hack-dir-local-variables-non-file-buffer) + (helm-projectile--files-display-real (projectile-current-project-files) + (projectile-project-root)))))) + (filtered-candidate-transformer + :initform (lambda (files _source) + (with-temp-buffer + (hack-dir-local-variables-non-file-buffer) + (let* ((root (projectile-project-root)) + (file-at-root (file-relative-name (expand-file-name helm-pattern root)))) + (if (or (string-empty-p helm-pattern) + (assoc helm-pattern files)) + files + (if (equal helm-pattern file-at-root) + (cl-acons (helm-ff-prefix-filename helm-pattern nil t) + (expand-file-name helm-pattern) + files) + (cl-pairlis (list (helm-ff-prefix-filename helm-pattern nil t) + (helm-ff-prefix-filename file-at-root nil t)) + (list (expand-file-name helm-pattern) + (expand-file-name helm-pattern root)) + files))))))) + (fuzzy-match :initform 'helm-projectile-fuzzy-match) + (keymap :initform 'helm-projectile-find-file-map) + (help-message :initform 'helm-ff-help-message) + (mode-line :initform 'helm-read-file-name-mode-line-string) + (action :initform 'helm-projectile-file-actions) + (persistent-action :initform #'helm-projectile-file-persistent-action) + (persistent-help :initform "Preview file"))) + +(defvar helm-source-projectile-files-list + (helm-make-source "Projectile files" 'helm-source-projectile-file) + "Helm source definition for Projectile files.") + +(defclass helm-source-projectile-file-other-window (helm-source-projectile-file) + ()) + +(cl-defmethod helm-setup-user-source ((source helm-source-projectile-file-other-window)) + "Make `helm-find-files-other-window' the first action in SOURCE." + (setf (slot-value source 'action) + (helm-projectile-hack-actions + helm-projectile-file-actions + '(helm-find-files-other-window . :make-first)))) + +(defvar helm-source-projectile-files-other-window-list + (helm-make-source "Projectile files" 'helm-source-projectile-file-other-window) + "Helm source definition for Projectile files.") + +(defclass helm-source-projectile-file-other-frame (helm-source-projectile-file) + ()) + +(cl-defmethod helm-setup-user-source ((source helm-source-projectile-file-other-frame)) + "Make `find-file-other-frame' the first action in SOURCE." + (setf (slot-value source 'action) + (helm-projectile-hack-actions + helm-projectile-file-actions + '(find-file-other-frame . :make-first)))) + +(defvar helm-source-projectile-files-other-frame-list + (helm-make-source "Projectile files" 'helm-source-projectile-file-other-frame) + "Helm source definition for Projectile files.") + +(defvar helm-source-projectile-files-in-all-projects-list + (helm-build-sync-source "Projectile files in all Projects" + :candidates (lambda () + (with-helm-current-buffer + (let ((projectile-require-project-root nil)) + (projectile-all-project-files)))) + :keymap helm-projectile-find-file-map + :help-message 'helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action helm-projectile-file-actions + :persistent-action #'helm-projectile-file-persistent-action + :persistent-help "Preview file") + "Helm source definition for all Projectile files in all projects.") + +(defvar helm-projectile-dired-file-actions + (helm-projectile-hack-actions + helm-projectile-file-actions + ;; New actions + '(helm-projectile-dired-files-delete-action . "Remove entry(s) from Dired buffer `C-c d'"))) + +(defclass helm-source-projectile-dired-file (helm-source-in-buffer) + ((data :initform (lambda () + (if (and (file-remote-p (projectile-project-root)) + (not helm-projectile-virtual-dired-remote-enable)) + nil + (when (eq major-mode 'dired-mode) + (helm-projectile-files-in-current-dired-buffer))))) + (filter-one-by-one :initform (lambda (file) + (let ((helm-ff-transformer-show-only-basename t)) + (helm-ff-filter-candidate-one-by-one file)))) + (action-transformer :initform (lambda (actions candidate) + (let ((actions (helm-find-files-action-transformer actions candidate))) + (if (file-directory-p candidate) + (append + actions + '(("Silver searcher (ag) in directory `C-S-a'" . helm-projectile--switch-project-and-ag-action) + ("Ripgrep (rg) in directory `C-S-r'" . helm-projectile--switch-project-and-rg-action))) + actions)))) + (keymap :initform (let ((map (copy-keymap helm-projectile-find-file-map))) + (helm-projectile-define-key map + (kbd "C-c d") #'helm-projectile-dired-files-delete-action + (kbd "C-S-a") #'helm-projectile--switch-project-and-ag-action + (kbd "C-S-r") #'helm-projectile--switch-project-and-rg-action) + map)) + (help-message :initform 'helm-ff-help-message) + (mode-line :initform 'helm-read-file-name-mode-line-string) + (action :initform 'helm-projectile-dired-file-actions))) + +(defvar helm-source-projectile-dired-files-list + (helm-make-source "Projectile files in current Dired buffer" + 'helm-source-projectile-dired-file) + "Helm source definition for Projectile delete files.") + +(defclass helm-source-projectile-dired-file-other-window (helm-source-projectile-dired-file) + ()) + +(cl-defmethod helm-setup-user-source ((source helm-source-projectile-dired-file-other-window)) + "Make `helm-find-files-other-window' the first action in SOURCE." + (setf (slot-value source 'action) + (helm-projectile-hack-actions + helm-projectile-dired-file-actions + '(helm-find-files-other-window . :make-first)))) + +(defvar helm-source-projectile-dired-files-other-window-list + (helm-make-source "Projectile files in current Dired buffer" + 'helm-source-projectile-dired-file-other-window) + "Helm source definition for Projectile delete files.") + +(defclass helm-source-projectile-dired-file-other-frame (helm-source-projectile-dired-file) + ()) + +(cl-defmethod helm-setup-user-source ((source helm-source-projectile-dired-file-other-frame)) + "Make `find-file-other-frame' the first action in SOURCE." + (setf (slot-value source 'action) + (helm-projectile-hack-actions + helm-projectile-dired-file-actions + '(find-file-other-frame . :make-first)))) + +(defvar helm-source-projectile-dired-files-other-frame-list + (helm-make-source "Projectile files in current Dired buffer" + 'helm-source-projectile-dired-file-other-frame) + "Helm source definition for Projectile delete files.") + +(defun helm-projectile-dired-find-dir (dir) + "Jump to a selected directory DIR from `helm-projectile'." + (dired (expand-file-name dir (projectile-project-root))) + (run-hooks 'projectile-find-dir-hook)) + +(defun helm-projectile-dired-find-dir-other-window (dir) + "Jump to a selected directory DIR from `helm-projectile' (in other window)." + (dired-other-window (expand-file-name dir (projectile-project-root))) + (run-hooks 'projectile-find-dir-hook)) + +(defun helm-projectile-dired-find-dir-other-frame (dir) + "Jump to a selected directory DIR from `helm-projectile' (in other frame)." + (dired-other-frame (expand-file-name dir (projectile-project-root))) + (run-hooks 'projectile-find-dir-hook)) + +(defvar helm-source-projectile-directory-actions + '(("Open Dired" . helm-projectile-dired-find-dir) + ("Open Dired in other window `C-c o'" . helm-projectile-dired-find-dir-other-window) + ("Open Dired in other frame `C-c C-o'" . helm-projectile-dired-find-dir-other-frame) + ("Switch to Eshell `M-e'" . helm-projectile-switch-to-shell) + ("Grep in projects `C-s'" . helm-projectile-grep) + ("Create Dired buffer from files `C-c f'" . helm-projectile-dired-files-new-action) + ("Add files to Dired buffer `C-c a'" . helm-projectile-dired-files-add-action) + ("Silver searcher (ag) in directory `C-S-a'" . helm-projectile--switch-project-and-ag-action) + ("Ripgrep (rg) in directory `C-S-r'" . helm-projectile--switch-project-and-rg-action))) + +(defclass helm-source-projectile-directory (helm-source-sync) + ((candidates :initform (lambda () + (when (projectile-project-p) + (with-temp-buffer + (hack-dir-local-variables-non-file-buffer) + (let ((dirs (if projectile-find-dir-includes-top-level + (append '("./") (projectile-current-project-dirs)) + (projectile-current-project-dirs)))) + (helm-projectile--files-display-real dirs (projectile-project-root))))))) + (fuzzy-match :initform 'helm-projectile-fuzzy-match) + (action-transformer :initform 'helm-find-files-action-transformer) + (keymap :initform (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (helm-projectile-define-key map + (kbd "") #'helm-previous-source + (kbd "") #'helm-next-source + (kbd "C-c o") #'helm-projectile-dired-find-dir-other-window + (kbd "C-c C-o") #'helm-projectile-dired-find-dir-other-frame + (kbd "M-e") #'helm-projectile-switch-to-shell + (kbd "C-c f") #'helm-projectile-dired-files-new-action + (kbd "C-c a") #'helm-projectile-dired-files-add-action + (kbd "C-s") #'helm-projectile-grep + (kbd "C-S-a") #'helm-projectile--switch-project-and-ag-action + (kbd "C-S-r") #'helm-projectile--switch-project-and-rg-action) + map)) + (help-message :initform 'helm-ff-help-message) + (mode-line :initform 'helm-read-file-name-mode-line-string) + (action :initform 'helm-source-projectile-directory-actions))) + +(defvar helm-source-projectile-directories-list + (helm-make-source "Projectile directories" 'helm-source-projectile-directory) + "Helm source for listing project directories.") + +(defclass helm-source-projectile-directory-other-window (helm-source-projectile-directory) + ()) + +(cl-defmethod helm-setup-user-source ((source helm-source-projectile-directory-other-window)) + "Make `helm-projectile-dired-find-dir-other-window' the first action in SOURCE." + (setf (slot-value source 'action) + (helm-projectile-hack-actions + helm-source-projectile-directory-actions + '(helm-projectile-dired-find-dir-other-window . :make-first)))) + +(defvar helm-source-projectile-directories-other-window-list + (helm-make-source "Projectile directories" 'helm-source-projectile-directory-other-window) + "Helm source for listing project directories.") + +(defclass helm-source-projectile-directory-other-frame (helm-source-projectile-directory) + ()) + +(cl-defmethod helm-setup-user-source ((source helm-source-projectile-directory-other-frame)) + "Make `helm-projectile-dired-find-dir-other-frame' the first action in SOURCE." + (setf (slot-value source 'action) + (helm-projectile-hack-actions + helm-source-projectile-directory-actions + '(helm-projectile-dired-find-dir-other-frame . :make-first)))) + +(defvar helm-source-projectile-directories-other-frame-list + (helm-make-source "Projectile directories" 'helm-source-projectile-directory-other-frame) + "Helm source for listing project directories.") + +(defvar helm-projectile-buffers-list-cache nil) + +(defclass helm-source-projectile-buffer (helm-source-sync helm-type-buffer) + ((init :initform (lambda () + ;; Issue #51 Create the list before `helm-buffer' creation. + (setq helm-projectile-buffers-list-cache + (ignore-errors + (let* ((buffers (projectile-project-buffer-names)) + (current-buffer (buffer-name))) + (append (remove current-buffer buffers) + (unless helm-projectile-remove-current-buffer + ;; Allow current buffer to be shown, + ;; but push it to the end. + (list current-buffer)))))) + (let ((result (cl-loop for b in helm-projectile-buffers-list-cache + maximize (length b) into len-buf + maximize (length (with-current-buffer b + (symbol-name major-mode))) + into len-mode + finally return (cons len-buf len-mode)))) + (unless (default-value 'helm-buffer-max-length) + (helm-set-local-variable 'helm-buffer-max-length (car result))) + (unless (default-value 'helm-buffer-max-len-mode) + ;; If a new buffer is longer that this value + ;; this value will be updated + (helm-set-local-variable 'helm-buffer-max-len-mode (cdr result)))))) + (candidates :initform 'helm-projectile-buffers-list-cache) + (matchplugin :initform nil) + (match :initform 'helm-buffers-match-function) + (persistent-action :initform 'helm-buffers-list-persistent-action) + (keymap :initform 'helm-buffer-map) + (volatile :initform t) + (persistent-help + :initform + "Show this buffer / C-u \\[helm-execute-persistent-action]: Kill this buffer"))) + +(defvar helm-source-projectile-buffers-list + (helm-make-source "Project buffers" 'helm-source-projectile-buffer)) + +(defclass helm-source-projectile-buffer-other-window (helm-source-projectile-buffer) + ()) + +(cl-defmethod helm-setup-user-source ((source helm-source-projectile-buffer-other-window)) + "Make `helm-buffer-switch-buffers-other-window' first action in SOURCE." + (setf (slot-value source 'action) + (helm-projectile-hack-actions + helm-type-buffer-actions + '(helm-buffer-switch-buffers-other-window . :make-first)))) + +(defvar helm-source-projectile-buffers-other-window-list + (helm-make-source "Project buffers" 'helm-source-projectile-buffer-other-window)) + +(defclass helm-source-projectile-buffer-other-frame (helm-source-projectile-buffer) + ()) + +(cl-defmethod helm-setup-user-source ((source helm-source-projectile-buffer-other-frame)) + "Make `helm-buffer-switch-to-buffer-other-frame' first action in SOURCE." + (setf + (slot-value source 'action) + (helm-projectile-hack-actions + helm-type-buffer-actions + '(helm-buffer-switch-to-buffer-other-frame . :make-first)))) + +(defvar helm-source-projectile-buffers-other-frame-list + (helm-make-source "Project buffers" 'helm-source-projectile-buffer-other-frame)) + +(defvar helm-source-projectile-recentf-list + (helm-build-sync-source "Projectile recent files" + :candidates (lambda () + (when (projectile-project-p) + (with-temp-buffer + (hack-dir-local-variables-non-file-buffer) + (helm-projectile--files-display-real (projectile-recentf-files) + (projectile-project-root))))) + :fuzzy-match helm-projectile-fuzzy-match + :keymap helm-projectile-find-file-map + :help-message 'helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action helm-projectile-file-actions + :persistent-action #'helm-projectile-file-persistent-action + :persistent-help "Preview file") + "Helm source definition for recent files in current project.") + +(defcustom helm-projectile-git-grep-command + "git --no-pager grep --no-color -n%c -e %p -- %f" + "Command to execute when performing `helm-grep' inside a projectile git project. +See documentation of `helm-grep-default-command' for the format." + :type 'string + :group 'helm-projectile) + +(defcustom helm-projectile-grep-command + "grep -a -r %e -n%cH -e %p %f ." + "Command to execute when performing `helm-grep' outside a projectile git project. +See documentation of `helm-grep-default-command' for the format." + :type 'string + :group 'helm-projectile) + + +(defcustom helm-projectile-sources-list + '(helm-source-projectile-buffers-list + helm-source-projectile-files-list + helm-source-projectile-projects) + "Default sources for `helm-projectile'." + :type '(repeat symbol) + :group 'helm-projectile) + +(defmacro helm-projectile-command (command source prompt &optional not-require-root truncate-lines-var) + "Template for generic `helm-projectile' commands. +COMMAND is a command name to be appended with \"helm-projectile\" prefix. +SOURCE is a Helm source that should be Projectile specific. +PROMPT is a string for displaying as a prompt. +NOT-REQUIRE-ROOT specifies the command doesn't need to be used in a +project root. +TRUNCATE-LINES-VAR is the symbol used dictate truncation of lines. +Defaults is `helm-projectile-truncate-lines'." + (unless truncate-lines-var (setq truncate-lines-var 'helm-projectile-truncate-lines)) + `(defun ,(intern (concat "helm-projectile-" command)) (&optional arg) + "Use projectile with Helm for finding files in project + +With a prefix ARG invalidates the cache first." + (interactive "P") + (if (projectile-project-p) + (projectile-maybe-invalidate-cache arg) + (unless ,not-require-root + (error "You're not in a project"))) + (let ((helm-ff-transformer-show-only-basename nil) + ;; for consistency, we should just let Projectile take care of ignored files + (helm-boring-file-regexp-list nil)) + (helm :sources ,source + :buffer (concat "*helm projectile: " (projectile-project-name) "*") + :truncate-lines ,truncate-lines-var + :prompt (projectile-prepend-project-name ,prompt))))) + +;; You can evaluate the following command to help inserting autoloads for the +;; `helm-projectile-command' macro. To use: move point to a form that calls +;; `helm-projectile-command' and type: +;; +;; M-x helm-projectile-command-insert-autoload +;; +;; (defun helm-projectile-command-insert-autoload () +;; "Insert autoload for a helm-projectile-command at point." +;; (interactive) +;; (save-excursion +;; (end-of-defun) +;; (beginning-of-defun) +;; (when-let* ((beg (point)) +;; (form (funcall load-read-function (current-buffer))) +;; ((eq (car form) 'helm-projectile-command)) +;; (command (concat "helm-projectile-" (cadr form))) +;; (file (file-name-sans-extension +;; (file-name-nondirectory +;; (buffer-file-name))))) +;; (goto-char beg) +;; (insert ";;;###autoload(autoload '" command " \"" file "\" nil t)\n")))) + +;;;###autoload(autoload 'helm-projectile-switch-project "helm-projectile" nil t) +(helm-projectile-command "switch-project" + 'helm-source-projectile-projects + "Switch to project: " t) + +;;;###autoload(autoload 'helm-projectile-switch-project-other-window "helm-projectile" nil t) +(helm-projectile-command "switch-project-other-window" + (helm-make-source + "Projectile projects" + 'helm-projectile-projects-other-window-source) + "Switch to project: " t) + +;;;###autoload(autoload 'helm-projectile-switch-project-other-frame "helm-projectile" nil t) +(helm-projectile-command "switch-project-other-frame" + (helm-make-source + "Projectile projects" + 'helm-projectile-projects-other-frame-source) + "Switch to project: " t) + +;;;###autoload(autoload 'helm-projectile-find-file "helm-projectile" nil t) +(helm-projectile-command "find-file" + '(helm-source-projectile-dired-files-list + helm-source-projectile-files-list) + "Find file: ") +;;;###autoload(autoload 'helm-projectile-find-file-other-window "helm-projectile" nil t) +(helm-projectile-command "find-file-other-window" + '(helm-source-projectile-dired-files-other-window-list + helm-source-projectile-files-other-window-list) + "Find file (other window): ") + +;;;###autoload(autoload 'helm-projectile-find-file-other-frame "helm-projectile" nil t) +(helm-projectile-command "find-file-other-frame" + '(helm-source-projectile-dired-files-other-frame-list + helm-source-projectile-files-other-frame-list) + "Find file (other frame): ") + +;;;###autoload(autoload 'helm-projectile-find-file-in-known-projects "helm-projectile" nil t) +(helm-projectile-command "find-file-in-known-projects" 'helm-source-projectile-files-in-all-projects-list "Find file in projects: " t) + +;;;###autoload(autoload 'helm-projectile-find-dir "helm-projectile" nil t) +(helm-projectile-command "find-dir" + '(helm-source-projectile-dired-files-list + helm-source-projectile-directories-list) + "Find dir: ") + +;;;###autoload(autoload 'helm-projectile-find-dir-other-window "helm-projectile" nil t) +(helm-projectile-command "find-dir-other-window" + '(helm-source-projectile-dired-files-other-window-list + helm-source-projectile-directories-other-window-list) + "Find dir (other window): ") + +;;;###autoload(autoload 'helm-projectile-find-dir-other-frame "helm-projectile" nil t) +(helm-projectile-command "find-dir-other-frame" + '(helm-source-projectile-dired-files-other-frame-list + helm-source-projectile-directories-other-frame-list) + "Find dir (other frame): ") + +;;;###autoload(autoload 'helm-projectile-recentf "helm-projectile" nil t) +(helm-projectile-command "recentf" 'helm-source-projectile-recentf-list "Recently visited file: ") + +;;;###autoload(autoload 'helm-projectile-switch-to-buffer "helm-projectile" nil t) +(helm-projectile-command "switch-to-buffer" + 'helm-source-projectile-buffers-list + "Switch to buffer: " nil helm-buffers-truncate-lines) + +;;;###autoload(autoload 'helm-projectile-switch-to-buffer-other-window "helm-projectile" nil t) +(helm-projectile-command "switch-to-buffer-other-window" + 'helm-source-projectile-buffers-other-window-list + "Switch to buffer (other window): " nil helm-buffers-truncate-lines) + +;;;###autoload(autoload 'helm-projectile-switch-to-buffer-other-frame "helm-projectile" nil t) +(helm-projectile-command "switch-to-buffer-other-frame" + 'helm-source-projectile-buffers-other-frame-list + "Switch to buffer (other frame): " nil helm-buffers-truncate-lines) + +;;;###autoload(autoload 'helm-projectile-browse-dirty-projects "helm-projectile" nil t) +(helm-projectile-command "browse-dirty-projects" 'helm-source-projectile-dirty-projects "Select a project: " t) + +(defun helm-projectile--files-display-real (files root) + "Create (DISPLAY . REAL) pairs with FILES and ROOT. + + DISPLAY is the short file name. REAL is the full path." + ;; Use `helm-ff-filter-candidate-one-by-one' (just like `helm-find-files-get-candidates' does). + ;; With a twist that some of files may contain a directory component. + ;; In such a case `helm-ff-filter-candidate-one-by-one' just returns a file component, + ;; so we the do a concatenation of file and directory components manually. + (cl-loop with default-directory = root + for file in files + collect (let ((file-res (helm-ff-filter-candidate-one-by-one file nil t))) + (setcdr file-res (expand-file-name file root)) + (if-let* ((directory (file-name-directory file))) + (cons (concat (if-let* ((face (get-text-property + 0 'face (car file-res)))) + (propertize directory 'face face) + directory) + (unless (file-directory-p file) + (car file-res))) + (cdr file-res)) + file-res)))) + +(defun helm-projectile--find-file-dwim-1 (one-candidate-action actions prompt) + "Find file at point based on context. +Execute ONE-CANDIDATE-ACTION when there is a single file returned by +`projectile-select-files' (which see). Otherwise display a Helm with +ACTIONS and PROMPT with other selected files." + (let* ((project-root (projectile-project-root)) + (project-files (projectile-current-project-files)) + (files (projectile-select-files project-files))) + (if (= (length files) 1) + (funcall one-candidate-action (expand-file-name (car files) (projectile-project-root))) + (helm :sources (helm-build-sync-source "Projectile files" + :candidates (if (> (length files) 1) + (helm-projectile--files-display-real files project-root) + (helm-projectile--files-display-real project-files project-root)) + :fuzzy-match helm-projectile-fuzzy-match + :action-transformer 'helm-find-files-action-transformer + :keymap helm-projectile-find-file-map + :help-message helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action actions + :persistent-action #'helm-projectile-file-persistent-action + :persistent-help "Preview file") + :buffer "*helm projectile*" + :truncate-lines helm-projectile-truncate-lines + :prompt (projectile-prepend-project-name prompt))))) + +;;;###autoload +(defun helm-projectile-find-file-dwim () + "Find file at point based on context." + (interactive) + (helm-projectile--find-file-dwim-1 + #'find-file helm-projectile-file-actions "Find file: ")) + +;;;###autoload +(defun helm-projectile-find-file-dwim-other-window () + "Find file at point based on context." + (interactive) + (helm-projectile--find-file-dwim-1 + #'find-file-other-window + (helm-projectile-hack-actions + helm-projectile-file-actions + '(helm-find-files-other-window . :make-first)) + "Find file (other window): ")) + +;;;###autoload +(defun helm-projectile-find-file-dwim-other-frame () + "Find file at point based on context." + (interactive) + (helm-projectile--find-file-dwim-1 + #'find-file-other-frame + (helm-projectile-hack-actions + helm-projectile-file-actions + '(find-file-other-frame . :make-first)) + "Find file (other frame): ")) + +(defun helm-projectile--find-other-file-1 (one-candidate-action actions prompt flex-matching) + "Switch between files with the same name but different extensions using Helm. +Execute ONE-CANDIDATE-ACTION when there is a single file returned by +`projectile-get-other-files' (which see). Otherwise display a Helm with +ACTIONS and PROMPT with other selected files. + +With FLEX-MATCHING, match any file that contains the base name of +current file. Other file extensions can be customized with the +variable `projectile-other-file-alist'." + (interactive "P") + (let* ((project-root (projectile-project-root)) + (other-files (projectile-get-other-files (buffer-file-name) + flex-matching))) + (if other-files + (if (= (length other-files) 1) + (funcall one-candidate-action (expand-file-name (car other-files) project-root)) + (progn + (let* ((helm-ff-transformer-show-only-basename nil)) + (helm :sources (helm-build-sync-source "Projectile other files" + :candidates (helm-projectile--files-display-real other-files project-root) + :keymap helm-projectile-find-file-map + :help-message helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action actions + :persistent-action #'helm-projectile-file-persistent-action + :persistent-help "Preview file") + :buffer "*helm projectile*" + :truncate-lines helm-projectile-truncate-lines + :prompt (projectile-prepend-project-name prompt))))) + (error "No other file found")))) + +;;;###autoload +(defun helm-projectile-find-other-file (&optional flex-matching) + "Switch between files with the same name but different extensions using Helm. +With FLEX-MATCHING, match any file that contains the base name of +current file. Other file extensions can be customized with the +variable `projectile-other-file-alist'." + (interactive "P") + (helm-projectile--find-other-file-1 + #'find-file + helm-projectile-file-actions + "Find other file: " + flex-matching)) + +;;;###autoload +(defun helm-projectile-find-other-file-other-window (&optional flex-matching) + "Switch between files with the same name but different extensions using Helm. +With FLEX-MATCHING, match any file that contains the base name of +current file. Other file extensions can be customized with the +variable `projectile-other-file-alist'." + (interactive "P") + (helm-projectile--find-other-file-1 + #'find-file-other-window + (helm-projectile-hack-actions + helm-projectile-file-actions + '(helm-find-files-other-window . :make-first)) + "Find other file (other window): " + flex-matching)) + +;;;###autoload +(defun helm-projectile-find-other-file-other-frame (&optional flex-matching) + "Switch between files with the same name but different extensions using Helm. +With FLEX-MATCHING, match any file that contains the base name of +current file. Other file extensions can be customized with the +variable `projectile-other-file-alist'." + (interactive "P") + (helm-projectile--find-other-file-1 + #'find-file-other-frame + (helm-projectile-hack-actions + helm-projectile-file-actions + '(find-file-other-frame . :make-first)) + "Find other file (other frame): " + flex-matching)) + +(defcustom helm-projectile-ignore-strategy 'projectile + "Allow projectile to compute ignored files and directories. + +When set to `projectile', the package will compute ignores and +explicitly add additionally command line arguments to the search +tool. Note that this might override search tool specific +behaviors (for instance ag would not use VCS ignore files). + +When set to `search-tool', the above does not happen." + :group 'helm-projectile + :type '(choice (const :tag "Allow projectile to compute ignores" projectile) + (const :tag "Let the search tool compute ignores" search-tool))) + +(defun helm-projectile--projectile-ignore-strategy () + "True if the ignore strategy is `projectile'." + (eq 'projectile helm-projectile-ignore-strategy)) + +(defun helm-projectile--ignored-files () + "Compute ignored files." + (cl-union (projectile-ignored-files-rel) grep-find-ignored-files + :test #'equal)) + +(defun helm-projectile--ignored-directories () + "Compute ignored directories." + (cl-union (mapcar #'file-name-as-directory (projectile-ignored-directories-rel)) + (mapcar #'file-name-as-directory grep-find-ignored-directories) + :test #'equal)) + +(defcustom helm-projectile-grep-or-ack-actions + '("Find file" helm-grep-action + "Find file other frame" helm-grep-other-frame + (lambda () (and (locate-library "elscreen") + "Find file in Elscreen")) + helm-grep-jump-elscreen + "Save results in grep buffer" helm-grep-save-results + "Find file other window" helm-grep-other-window) + "Available actions for `helm-projectile-grep-or-ack'. +The contents of this list are passed as the arguments to `helm-make-actions'." + :type 'symbol + :group 'helm-projectile) + +(defcustom helm-projectile-set-input-automatically t + "If non-nil, attempt to set search input automatically. +Automatic input selection uses the region (if there is an active +region), otherwise it uses the current symbol at point (if there is +one). Applies to `helm-projectile-grep', `helm-projectile-ack', and +`helm-projectile-ag'." + :group 'helm-projectile + :type 'boolean) + +(defun helm-projectile-grep-or-ack (&optional dir use-ack-p ack-ignored-pattern ack-executable include) + "Perform helm-grep at project root. +DIR directory where to search. USE-ACK-P indicates whether to use ack +or not. ACK-IGNORED-PATTERN is a file regex to exclude from searching. +ACK-EXECUTABLE is the actual ack binary name. It is usually \"ack\" or +\"ack-grep\". If it is nil, or ack/ack-grep not found then use default +grep command. INCLUDE is a string with patterns (for \"grep\") or +types (for \"ack\") to include in search." + (let* ((default-directory (or dir (projectile-project-root))) + (helm-ff-default-directory default-directory) + (helm-grep-in-recurse t) + (helm-grep-ignored-files (if (helm-projectile--projectile-ignore-strategy) + (helm-projectile--ignored-files) + helm-grep-ignored-files)) + (helm-grep-ignored-directories (if (helm-projectile--projectile-ignore-strategy) + (mapcar 'directory-file-name + (helm-projectile--ignored-directories)) + helm-grep-ignored-directories)) + (helm-grep-default-command + (if use-ack-p + (concat ack-executable " -H --no-group --no-color " + (when include "%e ") + (when ack-ignored-pattern (concat ack-ignored-pattern " ")) + "%p %f") + (if (and projectile-use-git-grep (eq (projectile-project-vcs) 'git)) + helm-projectile-git-grep-command + (if include + (replace-regexp-in-string (rx (one-or-more whitespace) + "." + (zero-or-more whitespace) + string-end) + "" + helm-projectile-grep-command) + helm-projectile-grep-command)))) + (helm-grep-default-recurse-command helm-grep-default-command) + (helm-grep-include-files include) + (helm-source-grep + (helm-build-async-source + (capitalize (helm-grep-command t)) + :header-name (lambda (_name) + (let ((name (if use-ack-p + "Helm Projectile Ack" + "Helm Projectile Grep"))) + (concat name " " "(C-c ? Help)"))) + :candidates-process 'helm-grep-collect-candidates + :filter-one-by-one 'helm-grep-filter-one-by-one + :candidate-number-limit 9999 + :nohighlight t + ;; We need to specify keymap here and as :keymap arg [1] + ;; to make it available in further resuming. + :keymap helm-grep-map + :history 'helm-grep-history + :action (apply #'helm-make-actions helm-projectile-grep-or-ack-actions) + :persistent-action 'helm-grep-persistent-action + :persistent-help "Jump to line (`C-u' Record in mark ring)" + :requires-pattern 2))) + (helm + :sources 'helm-source-grep + :input (when helm-projectile-set-input-automatically + (if (region-active-p) + (buffer-substring-no-properties (region-beginning) (region-end)) + (thing-at-point 'symbol))) + :buffer (format "*helm %s*" (if use-ack-p + "ack" + "grep")) + :default-directory default-directory + :keymap helm-grep-map + :history 'helm-grep-history + :truncate-lines helm-grep-truncate-lines))) + +;;;###autoload +(defun helm-projectile-on () + "Turn on `helm-projectile' key bindings." + (interactive) + (message "Turn on helm-projectile key bindings") + (helm-projectile-toggle 1)) + +;;;###autoload +(defun helm-projectile-off () + "Turn off `helm-projectile' key bindings." + (interactive) + (message "Turn off helm-projectile key bindings") + (helm-projectile-toggle -1)) + +;;;###autoload +(defun helm-projectile-grep (&optional dir files) + "Helm version of `projectile-grep'. +DIR is the project root, if not set then current project root is used. +FILES is a list of file patterns to search in. When called with a +prefix argument then ask for FILES." + (interactive) + (let* ((project-root (or dir (projectile-project-root) (error "You're not in a project"))) + (include (if (equal current-prefix-arg '(4)) + (read-string (projectile-prepend-project-name "Grep in: ")) + files))) + (funcall 'run-with-timer 0.01 nil + #'helm-projectile-grep-or-ack project-root nil nil nil include))) + +(defun helm-projectile--wildcard-to-ack-match (wildcard) + "Transform WILDCARD into a form expected by \"match:\" filter of \"ack\"." + ;; FIXME: Make the following more robust such that [...[!...] won't be + ;; changed. + (thread-last + wildcard + (replace-regexp-in-string (rx ".") ;; "." -> "\." + "\\\\.") + (replace-regexp-in-string (rx "?") ;; "?" -> "." + ".") + (replace-regexp-in-string (rx "*") ;; "*" -> ".*" + ".*") + (replace-regexp-in-string (rx (or string-start (not "[")) "[!") ;; [!...] -> [^...], but don't change [[!] + "[^") + (replace-regexp-in-string (rx (group (one-or-more any))) ;; Wrap everything in ^...$ + "^\\1$"))) + +;;;###autoload +(defun helm-projectile-ack (&optional dir types) + "Helm version of `projectile-ack'. +DIR directory where to search, if not set then current project root is +used. TYPES is a list of types to include in search. When called with +a prefix argument, then ask for TYPES." + (interactive) + (let* ((project-root (or dir (projectile-project-root) (error "You're not in a project"))) + (ignored (when (helm-projectile--projectile-ignore-strategy) + (mapconcat + 'identity + (cl-union (mapcar (lambda (path) + (concat "--ignore-dir=" + (file-name-nondirectory + (directory-file-name path)))) + (helm-projectile--ignored-directories)) + (mapcar (lambda (path) + (concat "--ignore-file=match:" + (thread-last + path + (helm-projectile--wildcard-to-ack-match) + (shell-quote-argument)))) + (append + (helm-projectile--ignored-files) + (projectile-patterns-to-ignore))) + :test #'equal) + " "))) + (helm-ack-grep-executable (cond + ((executable-find "ack") "ack") + ((executable-find "ack-grep") "ack-grep") + (t (error "Neither 'ack' nor 'ack-grep' is available")))) + (include (if (equal current-prefix-arg '(4)) + (let ((helm-grep-default-recurse-command helm-ack-grep-executable) + (helm-grep-default-command helm-ack-grep-executable)) + (helm-grep-read-ack-type)) + types))) + (funcall 'run-with-timer 0.01 nil + #'helm-projectile-grep-or-ack project-root t ignored helm-ack-grep-executable include))) + +(defvar helm-projectile--ag-input nil + "The value of input to be used in a next `helm-projectile-ag' call.") + +(defun helm-projectile--ag-automatic-input (args) + "Use active region or a symbol at point as a third element in ARGS. +This function has been designed as an advice to `helm-grep-ag-1'. Do not +use directly." + (pcase-let ((`(,directory ,type ,input) args)) + (list directory + type + (or input helm-projectile--ag-input)))) + +;; When calling `helm', the function `helm-grep-ag' uses symbol at point as an +;; argument `:default-input' (via `helm-sources-using-default-as-input'). This +;; however sets argument `:input' to an empty string. As a result the shell +;; command `ag' (or `rg', or `pt') is being run with the active region or a +;; symbol at point as a search pattern, but typing in minibuffer starts search +;; from scratch. This advice will use symbol active region or a symbol point +;; as an `input' argument to `helm-grep-ag-1', which will ensure both `helm' +;; arguments `:default-input' and `:input' are populated. +(advice-add #'helm-grep-ag-1 + :filter-args #'helm-projectile--ag-automatic-input) + +(defun helm-projectile--ag--region-selection () + "Return a default input for `helm-grep-ag'." + (when helm-projectile-set-input-automatically + (if (region-active-p) + (buffer-substring-no-properties (region-beginning) (region-end)) + (thing-at-point 'symbol)))) + +(defun helm-projectile--ag-1 (directory input &optional options) + "Call `helm-grep-ag' with DIRECTORY. +DIRECTORY is the directory to search in. INPUT is the value of default +input to use for the search. OPTIONS are explicit command line +arguments to `helm-grep-ag-command'. When called with a double or a +triple prefix argument, ask for TYPES (see `helm-grep-ag')." + (let* ((ignored (when (helm-projectile--projectile-ignore-strategy) + (mapconcat (lambda (i) + (helm-acase (helm-grep--ag-command) + ;; `helm-grep-ag-command' suggests + ;; that PT is obsolete, but support + ;; still persist in Helm. Likely + ;; remove after Helm drops support. + (("ag" "pt") + (concat "--ignore " (shell-quote-argument i))) + ("rg" + (concat "--glob !" (shell-quote-argument i))))) + (append (helm-projectile--ignored-files) + (helm-projectile--ignored-directories)) + " "))) + (helm-grep-ag-command (format helm-grep-ag-command + (mapconcat #'identity + (delq nil (list ignored options "%s")) + " ") + "%s" "%s")) + (with-types (member current-prefix-arg '((16) (64)))) + (current-prefix-arg nil) + (helm-projectile--ag-input input)) + (helm-grep-ag directory with-types))) + +;;;###autoload +(defun helm-projectile-ag (&optional options) + "Helm version of `projectile-ag'. +When called with a single or a triple prefix argument, ask for OPTIONS. +When called with a double or a triple prefix argument, ask for +TYPES (see `helm-grep-ag').' + +This command uses `helm-grep-ag' to perform the search, so the actual +searcher used is determined by the value of `helm-grep-ag-command'." + (interactive (if (member current-prefix-arg '((4) (64))) + (list (helm-read-string "option: " "" + 'helm-ag--extra-options-history)))) + (if (projectile-project-p) + (helm-projectile--ag-1 (projectile-project-root) + (helm-projectile--ag--region-selection) + options) + (error "You're not in a project"))) + +(defun helm-projectile--switch-project-and-ag-action (directory) + "Switch to a project containing DIRECTORY then run ag in the DIRECTORY." + (if (and (file-directory-p directory) + (projectile-project-p directory)) + (let* ((input (helm-projectile--ag--region-selection)) + (projectile-switch-project-action + (lambda () + (helm-projectile--ag-1 (file-truename directory) input)))) + (projectile-switch-project-by-name (projectile-project-root directory))) + (error (if (file-directory-p directory) + (format "Directory %s is not in any project!" directory) + (format "Not a directory %s" directory))))) + +;; Declare/define these to satisfy the byte compiler +(defvar helm-rg-prepend-file-name-line-at-top-of-matches) +(defvar helm-rg-include-file-on-every-match-line) +(defvar helm-rg-default-directory) +(defvar helm-rg--extra-args) +(declare-function helm-rg "ext:helm-rg") +(declare-function helm-rg--get-thing-at-pt "ext:helm-rg") + +(defcustom helm-projectile-ask-to-install-helm-rg t + "Whether to ask user to install `helm-rg' when searching with ripgrep (rg). +The question is presented to user when the `helm-grep-ag-command' is not +pointing to ripgrep (rg) executable and the package `helm-rg' is not +installed." + :type 'boolean + :group 'helm-projectile) + +(defun helm-projectile-rg--region-selection () + "Return a default input for `helm-rg'." + (when helm-projectile-set-input-automatically + (if (region-active-p) + (buffer-substring-no-properties (region-beginning) (region-end)) + (helm-rg--get-thing-at-pt)))) + +(defmacro helm-projectile--with-helm-rg (fallback &rest body) + "Execute BODY if package `helm-rg'is installed. +Otherwise if `helm-grep-ag-command' is pointing to ripgrep (rg) call the +FALLBACK, which is expected to be some version of `helm-projectile-ag'. +Otherwise ask to install package`helm-rg' and execute BODY." + (declare (indent 1) (debug t)) + `(cond ((require 'helm-rg nil t) + (progn ,@body)) + ((equal "rg" (helm-grep--ag-command)) + (funcall ,fallback)) + ((and + helm-projectile-ask-to-install-helm-rg + (yes-or-no-p "`helm-rg' is not installed. Install it? ")) + (condition-case nil + (progn + (package-refresh-contents) + (package-install 'helm-rg) + ,@body) + (error "`helm-rg' is not available. Is MELPA in your `package-archives'?"))) + (t + (error "Cannot execute search with ripgrep (rg)")))) + +(defun helm-projectile--rg-1 (directory input) + "Call `helm-rg' with DIRECTORY. +DIRECTORY is the directory to search in. INPUT is the value of default +input to use for the search." + (let* ((helm-rg-prepend-file-name-line-at-top-of-matches nil) + (helm-rg-include-file-on-every-match-line t) + (helm-rg-default-directory 'default) + (default-directory directory) + (helm-rg--extra-args + (if (helm-projectile--projectile-ignore-strategy) + (mapcan (lambda (path) (list "--glob" path)) + (mapcar (lambda (path) + (concat "!" path)) + (append (helm-projectile--ignored-files) + (helm-projectile--ignored-directories)))) + helm-rg--extra-args))) + (helm-rg input nil))) + +;;;###autoload +(defun helm-projectile-rg () + "Projectile version of `helm-rg'. +When package `helm-rg' is not installed and `helm-grep-ag-command' is +pointing to ripgrep (rg) then use `helm-projectile-ag'. Otherwise if +`helm-projectile-ask-to-install-helm-rg' is non nil then ask to install +package `helm-rg'." + (interactive) + (helm-projectile--with-helm-rg + #'helm-projectile-ag + (if (projectile-project-p) + (helm-projectile--rg-1 (projectile-project-root) + (helm-projectile-rg--region-selection)) + (error "You're not in a project")))) + +(defun helm-projectile--switch-project-and-rg-action (directory) + "Switch to a project containing DIRECTORY then run rg in the DIRECTORY. +When package `helm-rg' is not installed and `helm-grep-ag-command' is +pointing to ripgrep (rg) then use +`helm-projectile--switch-project-and-ag-action'. Otherwise if +`helm-projectile-ask-to-install-helm-rg' is non nil then ask to install +package `helm-rg'." + (helm-projectile--with-helm-rg + (lambda () + (helm-projectile--switch-project-and-ag-action directory)) + (if (and (file-directory-p directory) + (projectile-project-p directory)) + (let* ((input (helm-projectile-rg--region-selection)) + (projectile-switch-project-action + (lambda () + (helm-projectile--rg-1 (file-truename directory) input)))) + (projectile-switch-project-by-name (projectile-project-root directory))) + (error (if (file-directory-p directory) + (format "Directory %s is not in any project!" directory) + (format "Not a directory %s" directory)))))) + +(defun helm-projectile-commander-bindings () + "Define Helm versions of Projectile commands in `projectile-commander'." + (def-projectile-commander-method ?a + "Run ack on project." + (call-interactively 'helm-projectile-ack)) + + (def-projectile-commander-method ?A + "Find ag on project." + (call-interactively 'helm-projectile-ag)) + + (def-projectile-commander-method ?f + "Find file in project." + (helm-projectile-find-file)) + + (def-projectile-commander-method ?b + "Switch to project buffer." + (helm-projectile-switch-to-buffer)) + + (def-projectile-commander-method ?d + "Find directory in project." + (helm-projectile-find-dir)) + + (def-projectile-commander-method ?g + "Run grep on project." + (helm-projectile-grep)) + + (def-projectile-commander-method ?s + "Switch project." + (helm-projectile-switch-project)) + + (def-projectile-commander-method ?e + "Find recently visited file in project." + (helm-projectile-recentf)) + + (def-projectile-commander-method ?V + "Find dirty projects." + (helm-projectile-browse-dirty-projects))) + +;;;###autoload +(defun helm-projectile-toggle (toggle) + "Toggle Helm version of Projectile commands. +When TOGGLE is greater than 0 turn Helm version of Projectile commands +on. When TOGGLE is is less or equal to 0 turn Helm version of commands +off." + (if (> toggle 0) + (progn + (when (eq projectile-switch-project-action #'projectile-find-file) + (setq projectile-switch-project-action #'helm-projectile-find-file)) + (define-key projectile-mode-map [remap projectile-find-other-file] #'helm-projectile-find-other-file) + (define-key projectile-mode-map [remap projectile-find-other-file-other-window] #'helm-projectile-find-other-file-other-window) + (define-key projectile-mode-map [remap projectile-find-other-file-other-frame] #'helm-projectile-find-other-file-other-frame) + (define-key projectile-mode-map [remap projectile-find-file] #'helm-projectile-find-file) + (define-key projectile-mode-map [remap projectile-find-file-other-window] #'helm-projectile-find-file-other-window) + (define-key projectile-mode-map [remap projectile-find-file-other-frame] #'helm-projectile-find-file-other-frame) + (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] #'helm-projectile-find-file-in-known-projects) + (define-key projectile-mode-map [remap projectile-find-file-dwim] #'helm-projectile-find-file-dwim) + (define-key projectile-mode-map [remap projectile-find-file-dwim-other-window] #'helm-projectile-find-file-dwim-other-window) + (define-key projectile-mode-map [remap projectile-find-file-dwim-other-frame] #'helm-projectile-find-file-dwim-other-frame) + (define-key projectile-mode-map [remap projectile-find-dir] #'helm-projectile-find-dir) + (define-key projectile-mode-map [remap projectile-find-dir-other-window] #'helm-projectile-find-dir-other-window) + (define-key projectile-mode-map [remap projectile-find-dir-other-frame] #'helm-projectile-find-dir-other-frame) + (define-key projectile-mode-map [remap projectile-switch-project] #'helm-projectile-switch-project) + ;; At the time of writing projectile didn't have neither + ;; `projectile-switch-to-project-other-window' nor + ;; `projectile-switch-to-project-other-frame' (hopefully these will be + ;; names should they be added). Adding `helm-projectile' bindings in a + ;; - hopefully - backward compatible way, by setting up keys in + ;; `projectile-command-map'. + (if (where-is-internal 'projectile-switch-project-other-window projectile-mode-map nil t t) + (define-key projectile-mode-map [remap projectile-switch-project-other-window] #'helm-projectile-switch-project-other-window) + (define-key projectile-command-map (kbd "4 p") #'helm-projectile-switch-project-other-window)) + (if (where-is-internal 'projectile-switch-project-other-frame projectile-mode-map nil t t) + (define-key projectile-mode-map [remap projectile-switch-project-other-frame] #'helm-projectile-switch-project-other-frame) + (define-key projectile-command-map (kbd "5 p") #'helm-projectile-switch-project-other-frame)) + (define-key projectile-mode-map [remap projectile-recentf] #'helm-projectile-recentf) + (define-key projectile-mode-map [remap projectile-switch-to-buffer] #'helm-projectile-switch-to-buffer) + (define-key projectile-mode-map [remap projectile-switch-to-buffer-other-window] #'helm-projectile-switch-to-buffer-other-window) + (define-key projectile-mode-map [remap projectile-switch-to-buffer-other-frame] #'helm-projectile-switch-to-buffer-other-frame) + (define-key projectile-mode-map [remap projectile-grep] #'helm-projectile-grep) + (define-key projectile-mode-map [remap projectile-ack] #'helm-projectile-ack) + (define-key projectile-mode-map [remap projectile-ag] #'helm-projectile-ag) + (define-key projectile-mode-map [remap projectile-ripgrep] #'helm-projectile-rg) + (define-key projectile-mode-map [remap projectile-browse-dirty-projects] #'helm-projectile-browse-dirty-projects) + (helm-projectile-commander-bindings)) + (progn + (when (eq projectile-switch-project-action #'helm-projectile-find-file) + (setq projectile-switch-project-action #'projectile-find-file)) + (define-key projectile-mode-map [remap projectile-find-other-file] nil) + (define-key projectile-mode-map [remap projectile-find-other-file-other-window] nil) + (define-key projectile-mode-map [remap projectile-find-other-file-other-frame] nil) + (define-key projectile-mode-map [remap projectile-find-file] nil) + (define-key projectile-mode-map [remap projectile-find-file-other-window] nil) + (define-key projectile-mode-map [remap projectile-find-file-other-frame] nil) + (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] nil) + (define-key projectile-mode-map [remap projectile-find-file-dwim] nil) + (define-key projectile-mode-map [remap projectile-find-file-dwim-other-window] nil) + (define-key projectile-mode-map [remap projectile-find-file-dwim-other-frame] nil) + (define-key projectile-mode-map [remap projectile-find-dir] nil) + (define-key projectile-mode-map [remap projectile-find-dir-other-window] nil) + (define-key projectile-mode-map [remap projectile-find-dir-other-frame] nil) + (define-key projectile-mode-map [remap projectile-switch-project] nil) + (if (where-is-internal 'helm-projectile-switch-project-other-window projectile-command-map nil t t) + (define-key projectile-mode-map (kbd "4 p") nil) + (define-key projectile-mode-map [remap projectile-switch-project-other-window] nil)) + (if (where-is-internal 'helm-projectile-switch-project-other-frame projectile-command-map nil t t) + (define-key projectile-mode-map (kbd "5 p") nil) + (define-key projectile-mode-map [remap projectile-switch-project-other-frame] nil)) + (define-key projectile-mode-map [remap projectile-recentf] nil) + (define-key projectile-mode-map [remap projectile-switch-to-buffer] nil) + (define-key projectile-mode-map [remap projectile-switch-to-buffer-other-window] nil) + (define-key projectile-mode-map [remap projectile-switch-to-buffer-other-frame] nil) + (define-key projectile-mode-map [remap projectile-grep] nil) + (define-key projectile-mode-map [remap projectile-ag] nil) + (define-key projectile-mode-map [remap projectile-ripgrep] nil) + (define-key projectile-mode-map [remap projectile-browse-dirty-projects] nil) + (projectile-commander-bindings)))) + +;;;###autoload +(defun helm-projectile (&optional arg) + "Use projectile with Helm instead of ido. + +With a prefix ARG invalidates the cache first. +If invoked outside of a project, displays a list of known projects to jump." + (interactive "P") + (if (not (projectile-project-p)) + (helm-projectile-switch-project arg) + (projectile-maybe-invalidate-cache arg) + (let ((helm-ff-transformer-show-only-basename nil)) + (helm :sources helm-projectile-sources-list + :buffer "*helm projectile*" + :truncate-lines helm-projectile-truncate-lines + :prompt (projectile-prepend-project-name (if (projectile-project-p) + "pattern: " + "Switch to project: ")))))) + +;;;###autoload +(eval-after-load 'projectile + '(progn + (define-key projectile-command-map (kbd "h") #'helm-projectile))) + +(provide 'helm-projectile) + +;;; helm-projectile.el ends here -- 2.47.3