summaryrefslogtreecommitdiff
path: root/arkta/arkta-project.el
blob: 18354a9174de3d4ea7c5e0d599508b471ce0f527 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
;; -*- lexical-binding: t -*-
;; Copyright © 2018-2025  Uko Koknevics

;; TODO: See about porting this to project.el:
;;     (define-key map (kbd "a") #'projectile-find-other-file)
;;     (define-key map (kbd "E") #'projectile-edit-dir-locals)
;;     (define-key map (kbd "g") #'projectile-find-file-dwim)
;;     (define-key map (kbd "i") #'projectile-invalidate-cache)
;;     (define-key map (kbd "I") #'projectile-ibuffer)
;;     (define-key map (kbd "j") #'projectile-find-tag)
;;     (define-key map (kbd "l") #'projectile-find-file-in-directory)
;;     (define-key map (kbd "m") #'projectile-commander)
;;     (define-key map (kbd "o") #'projectile-multi-occur)
;;     (define-key map (kbd "q") #'projectile-switch-open-project)
;;     (define-key map (kbd "R") #'projectile-regenerate-tags)

;;     (define-key map (kbd "s r") #'projectile-ripgrep)
;;     (define-key map (kbd "s s") #'projectile-ag)

;;     (define-key map (kbd "S") #'projectile-save-project-buffers)
;;     (define-key map (kbd "t") #'projectile-toggle-between-implementation-and-test)
;;     (define-key map (kbd "T") #'projectile-find-test-file)
;;     ;; project lifecycle external commands
;;     ;; TODO: Bundle those under some prefix key
;;     (define-key map (kbd "C") #'projectile-configure-project)
;;     (define-key map (kbd "K") #'projectile-package-project)
;;     (define-key map (kbd "L") #'projectile-install-project)
;;     (define-key map (kbd "P") #'projectile-test-project)
;;     (define-key map (kbd "u") #'projectile-run-project)

;;     ;; integration with utilities
;;     (define-key map (kbd "x i") #'projectile-run-ielm)
;;     (define-key map (kbd "x t") #'projectile-run-term)
;;     (define-key map (kbd "x g") #'projectile-run-gdb)
;;     (define-key map (kbd "x v") #'projectile-run-vterm)
;;     (define-key map (kbd "x 4 v") #'projectile-run-vterm-other-window)

;;     ;; misc
;;     (define-key map (kbd "z") #'projectile-cache-current-file)
;;     (define-key map (kbd "<left>") #'projectile-previous-project-buffer)
;;     (define-key map (kbd "<right>") #'projectile-next-project-buffer)
;;     (define-key map (kbd "ESC") #'projectile-project-buffers-other-buffer)

(cl-defun arkta/project-completing-read (prompt choices &key initial-input action (project (project-current t)))
  "Present a project tailored PROMPT with CHOICES."
  (require 'ivy)
  (let ((prompt (arkta/project-prepend-project-name prompt project)))
    (ivy-read prompt choices
              :initial-input initial-input
              :action action
              :caller 'arkta/project-completing-read)))

(defun arkta/project-expand-root (name &optional project)
  "Expand NAME to project root."
  (let (project (or project (project-current t)))
    (expand-file-name name (project-root project))))

(defun arkta/project-find-references (&optional symbol)
  "Find all references to SYMBOL in the current project.

A thin wrapper around `xref-references-in-directory'."
  (interactive)
  (require 'xref)
  (let ((project-root (project-root (project-current t)))
        (symbol (or symbol
                    (read-from-minibuffer "Lookup in project: " (arkta/symbol-at-point)))))
    (xref-show-xrefs (xref-references-in-directory symbol project-root) nil)))

(defun arkta/project-magit-status ()
  (interactive)
  (magit-status (project-root (project-current t))))

(defun arkta/project-prepend-project-name (string &optional project)
  "Prepend the current project's name to STRING."
  (let ((project (or project (project-current t))))
    (format "[%s] %s" (project-name project) string)))

(defun arkta/project-recentf ()
  "Show a list of recently visited files in a project."
  (interactive)
  (let ((project (project-current t)))
    (find-file (arkta/project-expand-root
                (arkta/project-completing-read
                 "Recently visited files: "
                 (arkta/project-recentf-files project)
                 :project project)
                project))))

(defun arkta/project-recentf-files (&optional project)
  "Return a list of recently visited files in a project."
  (require 'recentf)
  (let* ((project (or project (project-current t)))
         (project-root (expand-file-name (project-root project))))
    (mapcar
     (lambda (f) (file-relative-name f project-root))
     (cl-remove-if-not
      (lambda (f) (string-prefix-p project-root (expand-file-name f)))
      recentf-list))))

(defun arkta/symbol-at-point ()
  "Get the symbol at point and strip its properties."
  (substring-no-properties (or (thing-at-point 'symbol) "")))

(use-package project
  :ensure nil
  :config
  (defvar-keymap arkta/project-prefix-map
    :parent project-prefix-map
    "C-b" 'project-list-buffers

    "!" 'project-shell-command
    "&" 'project-async-shell-command

    "4" 'project-other-window-command
    "5" 'project-other-frame-command

    "D" 'project-dired
    "F" 'project-or-external-find-file
    "V" 'project-vc-dir

    "b" 'project-switch-to-buffer
    "c" 'project-compile
    "d" 'project-find-dir
    "e" 'arkta/project-recentf
    "f" 'project-find-file
    "k" 'project-kill-buffers
    "p" 'project-switch-project
    "r" 'project-query-replace-regexp
    "v" 'arkta/project-magit-status

    "M-x" 'project-execute-extended-command

    "s f" 'project-find-file
    "s g" 'project-find-regexp
    "s r" 'project-find-regexp
    "s x" 'arkta/project-find-references

    "x e" 'project-eshell
    "x s" 'project-shell)
  (fset 'arkta/project-prefix-map arkta/project-prefix-map)
  :bind (("C-c p" . arkta/project-prefix-map)))

(provide 'arkta-project)