Project management with org, projectile and skeletor
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

176 lignes
6.9KB

  1. ;;; projectorg.el ---
  2. ;; Copyright (C) 2019 Maxime Wack
  3. ;; Author: Maxime Wack <maximewack@free.fr>
  4. ;; Version: 0.1
  5. ;; This file is not part of GNU Emacs.
  6. ;; This program is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; This program is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; Project management using org, projectile and skeletor
  18. ;;; Code:
  19. ;;;; Variables
  20. (defvar projectorg/projects-root ""
  21. "Where all projects are rooted.
  22. This is the directory where projects are stored.
  23. Can contain subdirectories.
  24. The projectorg/org-dir subdirectory contains the notes file for each project, as well as the general notes files and the projects list file.")
  25. (defvar projectorg/org-dir "org/"
  26. "Directory inside projectorg/projects-root containing the org files.
  27. Defaults to \"org\"
  28. Contains the general notes files, the projects list files and each project notes file.")
  29. (defvar projectorg/counsel-org-capture-templates-contexts nil)
  30. (defvar projectorg/counsel-org-capture-templates nil)
  31. ;;;; Functions
  32. (defun projectorg/project-name ()
  33. "Return the full project name.
  34. It is the result of substracting the projectorg/projects-root string from the beginning of the path of the project, as returned by projectile-project-p."
  35. (when (projectile-project-p)
  36. (let ((project-path (projectile-project-p)))
  37. (and
  38. (string= (substring project-path 0 (length projectorg/projects-root))
  39. projectorg/projects-root)
  40. (substring project-path (length projectorg/projects-root) -1)))))
  41. (defun projectorg/notes-file ()
  42. "Returns the notes-file location.
  43. It is an org file, with the same name as the project (including subdirectories), located in the *org* directory in projectorg/projects-root."
  44. (let ((project-name (projectorg/project-name)))
  45. (when project-name
  46. (concat projectorg/projects-root "org/" project-name ".org"))))
  47. (defun projectorg/go-to-inbox ()
  48. "Go to org-default-notes-file."
  49. (interactive)
  50. (find-file-other-window org-default-notes-file))
  51. (defun projectorg/go-to-notes ()
  52. "Go to the projects' notes file if it exists in the *org* directory, or go to default notes file."
  53. (interactive)
  54. (let ((notes-file (projectorg/notes-file)))
  55. (if (and (eq projectile-require-project-root 'prompt)
  56. (not (projectile-project-p)))
  57. (projectorg/go-to-inbox)
  58. (find-file-other-window notes-file))))
  59. (defun projectorg/add-to-project-list (FILENAME &optional WILDCARDS)
  60. "Add the currently visited project to the projectile list.
  61. And switch to a perspective for the project."
  62. (when (projectile-project-p FILENAME)
  63. (persp-switch (projectile-project-name (projectile-project-root FILENAME)))
  64. (projectile-add-known-project (projectile-project-root FILENAME))))
  65. (defun tree-alist-get-all (key list acc)
  66. (cond ((or (not list) (not (listp list))) nil)
  67. ((eq key (car list)) (cons (cadr list) acc))
  68. (t (append (tree-alist-get-all key (car list) acc) (tree-alist-get-all key (cdr list) acc)))))
  69. (defun persp-kill-all-buffers (persp-name)
  70. (mapc '(lambda (buffer) (when (get-buffer buffer) (kill-buffer buffer)))
  71. (tree-alist-get-all 'buffer (persp-window-conf (persp-get-by-name persp-name)) nil)))
  72. (defun projectorg/remove-from-project-list ()
  73. (interactive)
  74. (let ((proj (projectile-project-name)))
  75. (projectile-remove-current-project-from-known-projects)
  76. (projectile-kill-buffers)
  77. (persp-kill-all-buffers proj)
  78. (persp-kill proj)))
  79. (defun projectorg/counsel-org-capture (&optional from-buffer)
  80. "Capture into the current project.
  81. This command is a replacement for `org-capture' (or
  82. `counsel-org-capture') offering project-specific capture
  83. templates, in addition to the regular templates available from
  84. `org-capture'. These project templates, which are \"expanded\"
  85. relatively to the current project, are determined by the
  86. variables `projectorg/counsel-org-capture-templates' and
  87. `projectorg/counsel-org-capture-templates-contexts'. See the
  88. former variable in particular for details.
  89. Optional argument FROM-BUFFER specifies the buffer from which to
  90. capture."
  91. (interactive)
  92. (require 'org-capture)
  93. (require 'counsel-projectile)
  94. (setq counsel-projectile--org-capture-templates-backup org-capture-templates)
  95. (let* ((ivy--actions-list (copy-sequence ivy--actions-list))
  96. (root (ignore-errors (projectile-project-root)))
  97. (name (projectorg/project-name))
  98. (org-capture-templates-contexts
  99. (append (when root
  100. projectorg/counsel-org-capture-templates-contexts)
  101. org-capture-templates-contexts))
  102. (org-capture-templates
  103. (append
  104. (unless counsel-projectile-org-capture-templates-first-p
  105. org-capture-templates)
  106. (when root
  107. (cl-loop
  108. with replace-fun = `(lambda (string)
  109. (replace-regexp-in-string
  110. "\\${[^}]+}"
  111. (lambda (s)
  112. (pcase s
  113. ("${root}" ,root)
  114. ("${name}" ,name)))
  115. string))
  116. for template in projectorg/counsel-org-capture-templates
  117. collect (cl-loop
  118. for item in template
  119. if (= (cl-position item template) 1) ;; template's name
  120. collect (funcall replace-fun item)
  121. else if (= (cl-position item template) 3) ;; template's target
  122. collect (cl-loop
  123. for x in item
  124. if (stringp x)
  125. collect (funcall replace-fun x)
  126. else
  127. collect x)
  128. else
  129. collect item)))
  130. (when counsel-projectile-org-capture-templates-first-p
  131. org-capture-templates))))
  132. (ivy-add-actions
  133. 'counsel-org-capture
  134. counsel-projectile-org-capture-extra-actions)
  135. (with-current-buffer (or from-buffer (current-buffer))
  136. (counsel-org-capture))))
  137. (advice-add 'find-file :before 'projectorg/add-to-project-list) ; For every file opened, check if belongs to a project and add that project to the list of projects
  138. (advice-add 'find-file-other-window :before 'projectorg/add-to-project-list)
  139. (provide 'projectorg)
  140. ;;; projectorg.el ends here