├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── Cask ├── Makefile ├── README.org ├── org-project.el ├── test ├── org-project-test.el └── test_projects.org └── travis-evm-cask.sh /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | pull_request: 4 | workflow_dispatch: 5 | 6 | name: Build and run tests 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | emacs-version: 15 | - 27.1 16 | - snapshot 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: purcell/setup-emacs@master 20 | with: 21 | version: ${{ matrix.emacs-version }} 22 | - uses: actions/cache@v2 23 | id: cache-cask-packages 24 | with: 25 | path: .cask 26 | key: cache-cask-packages-000 27 | - uses: actions/cache@v2 28 | id: cache-cask-executable 29 | with: 30 | path: ~/.cask 31 | key: cache-cask-executable-000 32 | - uses: conao3/setup-cask@master 33 | if: steps.cache-cask-executable.outputs.cache-hit != 'true' 34 | with: 35 | version: snapshot 36 | - run: echo "$HOME/.cask/bin" >> $GITHUB_PATH 37 | - name: install 38 | run: make compile 39 | - name: run tests 40 | run: make test 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Elisp ### 4 | # Compiled 5 | *.elc 6 | 7 | # Packaging 8 | .cask 9 | 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v1.1.0](https://github.com/IvanMalison/org-projectile/tree/v1.1.0) 4 | 5 | [Full Changelog](https://github.com/IvanMalison/org-projectile/compare/v1.0.0...HEAD) 6 | 7 | **Closed issues:** 8 | 9 | - Creating a TODO fails with error 'Wrong type argument: number-or-marker-p, nil' [\#33](https://github.com/IvanMalison/org-projectile/issues/33) 10 | - org-projectile-location-for-project fails with wrong-number-of-arguments [\#32](https://github.com/IvanMalison/org-projectile/issues/32) 11 | 12 | ## [v1.0.0](https://github.com/IvanMalison/org-projectile/tree/v1.0.0) (2017-08-01) 13 | [Full Changelog](https://github.com/IvanMalison/org-projectile/compare/v0.2.6...v1.0.0) 14 | 15 | **Closed issues:** 16 | 17 | - `\(org-projectile-per-repo\)` produces error [\#31](https://github.com/IvanMalison/org-projectile/issues/31) 18 | - Getting error "org-projectile:insert-or-goto-heading: Wrong number of arguments: \#\[\(&optional arg invisible-ok\)" [\#29](https://github.com/IvanMalison/org-projectile/issues/29) 19 | - \(invalid-function helm-build-sync-source\) [\#14](https://github.com/IvanMalison/org-projectile/issues/14) 20 | 21 | **Merged pull requests:** 22 | 23 | - org-projectile:project-name-to-location-alist: use true names [\#30](https://github.com/IvanMalison/org-projectile/pull/30) ([braham-snyder](https://github.com/braham-snyder)) 24 | 25 | ## [v0.2.6](https://github.com/IvanMalison/org-projectile/tree/v0.2.6) (2016-12-05) 26 | [Full Changelog](https://github.com/IvanMalison/org-projectile/compare/v0.2.5...v0.2.6) 27 | 28 | **Closed issues:** 29 | 30 | - Failure in org-projectile:project-todo-completing-read [\#27](https://github.com/IvanMalison/org-projectile/issues/27) 31 | - Obsolete usage of pcache-repository [\#24](https://github.com/IvanMalison/org-projectile/issues/24) 32 | - Byte compiling error: void variable: occ-build-capture-template \(broken in emacs 24\) [\#22](https://github.com/IvanMalison/org-projectile/issues/22) 33 | - user-error: Not on a heading [\#20](https://github.com/IvanMalison/org-projectile/issues/20) 34 | 35 | **Merged pull requests:** 36 | 37 | - Remove pcache as a package dependency [\#26](https://github.com/IvanMalison/org-projectile/pull/26) ([yyadavalli](https://github.com/yyadavalli)) 38 | - Remove org-projectile:path-to-category [\#25](https://github.com/IvanMalison/org-projectile/pull/25) ([mdorman](https://github.com/mdorman)) 39 | - Load dash explicitly for using '--some' [\#23](https://github.com/IvanMalison/org-projectile/pull/23) ([syohex](https://github.com/syohex)) 40 | 41 | ## [v0.2.5](https://github.com/IvanMalison/org-projectile/tree/v0.2.5) (2016-11-04) 42 | [Full Changelog](https://github.com/IvanMalison/org-projectile/compare/v0.2.4...v0.2.5) 43 | 44 | ## [v0.2.4](https://github.com/IvanMalison/org-projectile/tree/v0.2.4) (2016-11-03) 45 | [Full Changelog](https://github.com/IvanMalison/org-projectile/compare/v0.2.3...v0.2.4) 46 | 47 | **Closed issues:** 48 | 49 | - org-insert-subheading: Buffer is read-only: [\#21](https://github.com/IvanMalison/org-projectile/issues/21) 50 | - org-projectile:known-projects shouldn't load remote projects [\#10](https://github.com/IvanMalison/org-projectile/issues/10) 51 | 52 | ## [v0.2.3](https://github.com/IvanMalison/org-projectile/tree/v0.2.3) (2016-06-17) 53 | [Full Changelog](https://github.com/IvanMalison/org-projectile/compare/v0.2.2...v0.2.3) 54 | 55 | ## [v0.2.2](https://github.com/IvanMalison/org-projectile/tree/v0.2.2) (2016-06-17) 56 | [Full Changelog](https://github.com/IvanMalison/org-projectile/compare/v0.2.1...v0.2.2) 57 | 58 | **Merged pull requests:** 59 | 60 | - Miscellaneous [\#19](https://github.com/IvanMalison/org-projectile/pull/19) ([TheBB](https://github.com/TheBB)) 61 | 62 | ## [v0.2.1](https://github.com/IvanMalison/org-projectile/tree/v0.2.1) (2016-06-15) 63 | [Full Changelog](https://github.com/IvanMalison/org-projectile/compare/v0.2.0...v0.2.1) 64 | 65 | **Closed issues:** 66 | 67 | - Customization of todo entry properties [\#18](https://github.com/IvanMalison/org-projectile/issues/18) 68 | - Per-repo mode gives nested headings [\#17](https://github.com/IvanMalison/org-projectile/issues/17) 69 | - Helm requirement [\#15](https://github.com/IvanMalison/org-projectile/issues/15) 70 | - file-truename\(nil\) [\#13](https://github.com/IvanMalison/org-projectile/issues/13) 71 | - Perhaps tag a release? [\#8](https://github.com/IvanMalison/org-projectile/issues/8) 72 | - Don't run file-truename on nil \(handle deleted projects gracefully\) [\#7](https://github.com/IvanMalison/org-projectile/issues/7) 73 | 74 | **Merged pull requests:** 75 | 76 | - Remove helm from requirments. Fixes issue \#15 [\#16](https://github.com/IvanMalison/org-projectile/pull/16) ([jtamagnan](https://github.com/jtamagnan)) 77 | - Fix org-projectile:capture-for-current-project to be a command [\#12](https://github.com/IvanMalison/org-projectile/pull/12) ([blallau](https://github.com/blallau)) 78 | - Cleanup compiler warnings [\#11](https://github.com/IvanMalison/org-projectile/pull/11) ([bookest](https://github.com/bookest)) 79 | - Add a Gitter chat badge to README.org [\#9](https://github.com/IvanMalison/org-projectile/pull/9) ([gitter-badger](https://github.com/gitter-badger)) 80 | 81 | ## [v0.2.0](https://github.com/IvanMalison/org-projectile/tree/v0.2.0) (2015-09-03) 82 | **Closed issues:** 83 | 84 | - Insertion into subheading in single-file mode may be broken [\#6](https://github.com/IvanMalison/org-projectile/issues/6) 85 | - Saving todos in project specific org-file [\#4](https://github.com/IvanMalison/org-projectile/issues/4) 86 | - org-projectile:project-root-of-filepath: Wrong type argument: arrayp, nil [\#2](https://github.com/IvanMalison/org-projectile/issues/2) 87 | 88 | **Merged pull requests:** 89 | 90 | - Added a capture-for-current-project function [\#3](https://github.com/IvanMalison/org-projectile/pull/3) ([luxbock](https://github.com/luxbock)) 91 | - Remove org dependency [\#1](https://github.com/IvanMalison/org-projectile/pull/1) ([bbigras](https://github.com/bbigras)) 92 | 93 | 94 | 95 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* 96 | -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source gnu) 2 | (source melpa) 3 | 4 | (package-file "org-project.el") 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CASK = cask 2 | 3 | SRCS = $(shell find . -maxdepth 1 -name '*.el') 4 | OBJECTS = $(SRCS:.el=.elc) 5 | EMACS = $(shell sh -c 'evm bin') 6 | 7 | .PHONY: test compile recompile clean 8 | 9 | .cask: 10 | $(CASK) install 11 | 12 | $(OBJECTS): .cask 13 | $(CASK) build --verbose 14 | 15 | compile: $(OBJECTS) 16 | 17 | clean-elc: 18 | rm -f $(OBJECTS) 19 | 20 | recompile: clean-elc compile 21 | 22 | clean: clean-elc 23 | rm -rf .cask/ 24 | 25 | test: recompile 26 | cask exec ert-runner -L $(PWD) 27 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | ~org-project~ provides functions for the creation of [[http://orgmode.org/][org-mode]] TODOs that are associated with [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Projects.html][Emacs projects]]. 2 | 3 | This package is originally forked from and now heavily based on [[https://github.com/IvanMalison/org-projectile][org-projectile]]. 4 | 5 | * Installation 6 | Installation from MELPA should be shortly available. 7 | 8 | * Setup 9 | Before using org-project, you must specify the file you would like to use for storing projects =TODOs=. You may also wish to bind keys to =org-project= function in ~project-prefix-map~. It is recommended that you start with the following configuration: 10 | 11 | #+BEGIN_SRC emacs-lisp 12 | (use-package org-project 13 | :straight (org-project :type git :host github :repo "delehef/org-project")) 14 | #+end_src 15 | 16 | ** Example Configuration 17 | #+begin_src emacs-lisp 18 | (use-package org-project 19 | :straight (org-project :type git :host github :repo "delehef/org-project") 20 | :custom 21 | ;; If invoked outside of a project, prompt for a valid project to capture for 22 | (org-project-prompt-for-project t) 23 | 24 | ;; Store all TODOs in a ORG_DIRECTORY/project.org 25 | (org-project-todos-per-project nil) 26 | (org-project-todos-file (concat org-directory "/projects.org")) 27 | 28 | ;; Or use a single file per project, PROJECT_ROOT/todos.org 29 | ;; (org-project-todos-per-project t) 30 | ;; (org-project-per-project-file "todos.org") 31 | 32 | ;; Use custom capture templates 33 | (org-project-capture-template "* TODO %?\n%t\n") ;; Ask for a TODO and a date 34 | (org-project-quick-capture-template "* TODO %? %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n") ;; Quick TODOs ae scheduled in two days 35 | 36 | ;; Add some binding for org-project in project.el map 37 | :bind (:map project-prefix-map 38 | ("t" . org-project-quick-capture) 39 | ("T" . org-project-capture) 40 | ("o" . org-project-open-todos))) 41 | #+end_src 42 | * Provided Functions 43 | ** ~org-project-capture~ 44 | Triggers ~org-capture~ using the template provided in ~org-project-capture-template~. 45 | 46 | ** ~org-project-quick-capture~ 47 | Prompts for a simple TODO, that is then captured using ~org-project-quick-capture-template~. 48 | 49 | ** ~org-project-open-todos~ 50 | Jump to the org-mode heading containing the TODOs for the current project. 51 | 52 | All of these functions will create the relevant top level heading in the [[http://orgmode.org/][org-mode]] file stored in ~org-project-projects-file~ if ~org-project-per-project-file~ is ~nil~, or in ~org-project-todos-per-project~ under the project root if it is ~t~. 53 | -------------------------------------------------------------------------------- /org-project.el: -------------------------------------------------------------------------------- 1 | ;;; org-project.el --- Repository todo management for org-mode -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 Franklin Delehelle 4 | 5 | ;; Author: Franklin Delehelle 6 | ;; Keywords: org-mode project todo tools 7 | ;; URL: https://github.com/delehef/org-project 8 | ;; Version: 1.0.0 9 | ;; Package-Requires: ((emacs "27.1")) 10 | 11 | ;; This program is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; This package aims to provide an easy interface to creating per 27 | ;; project org-mode TODO headings. 28 | 29 | ;;; Code: 30 | 31 | (require 'project) 32 | (require 'org) 33 | (require 'org-capture) 34 | 35 | (defgroup org-project () 36 | "Customizations for org-project." 37 | :group 'org 38 | :prefix "org-project-") 39 | 40 | (defcustom org-project-todos-file "~/projects.org" 41 | "The path to the file in which project TODOs will be stored." 42 | :type '(string) 43 | :group 'org-project) 44 | 45 | (defcustom org-project-prompt-for-project nil 46 | "Prompt for a project when none is active. 47 | 48 | If non nil, org-project functions will prompt for a valid project when called 49 | outside of a project. Otherwise, they will just abort." 50 | :type '(bool) 51 | :group 'org-project) 52 | 53 | (defcustom org-project-per-project-file "TODO.org" 54 | "A file relative to the project root where TODOs will be stored. 55 | 56 | This will only be used if `org-project-todos-per-project' is set; otherwise 57 | TODOS will be stored in `org-project-todos-file'" 58 | :type '(string) 59 | :group 'org-project) 60 | 61 | (defcustom org-project-todos-per-project nil 62 | "Whether TODOs are stored globally or per-project. 63 | 64 | If non nil, TODOs for each project are stored in their own 65 | `org-project-per-project-file'. Otherwise, all TODOs are stored in 66 | `org-project-todos-file', under their project heading." 67 | :type '(bool) 68 | :group 'org-project) 69 | 70 | (defcustom org-project-capture-template "* TODO %?\n" 71 | "The capture template to use for org-project TODOs." 72 | :type '(string) 73 | :group 'org-project) 74 | 75 | (defcustom org-project-quick-capture-template "* TODO TEXT\n" 76 | "The capture template to use for org-project quick TODOs. 77 | 78 | TEXT will be replaced with the string prompted for." 79 | :type '(string) 80 | :group 'org-project) 81 | 82 | (defcustom org-project-link-heading t 83 | "Whether to make project headings links to their projects." 84 | :type '(boolean) 85 | :group 'org-project) 86 | 87 | (defcustom org-project-allow-tramp-projects nil 88 | "Whether to use tramp/sudo requiring projects." 89 | :type '(boolean) 90 | :group 'org-project) 91 | 92 | 93 | (defun org-project--io-action-permitted (filepath) 94 | "Return whether org-project can work on FILEPATH." 95 | (or org-project-allow-tramp-projects 96 | (eq nil (find-file-name-handler filepath 'file-truename)))) 97 | 98 | (defun org-project--project-from-file (filepath) 99 | "Return the project root of a file given its FILEPATH." 100 | (when (org-project--io-action-permitted filepath) 101 | (project-current nil))) 102 | 103 | (defun org-project--name-from-project (project-root) 104 | "Return the display name of a project identified by PROJECT-ROOT." 105 | (file-name-nondirectory (directory-file-name project-root))) 106 | 107 | (defun org-project--get-capture-file (projectpath) 108 | "Return the file PROJECTPATH TODOs depending on org-project settings." 109 | (if org-project-todos-per-project 110 | (concat projectpath org-project-per-project-file) 111 | org-project-todos-file)) 112 | 113 | (defun org-project--linkize-heading (heading projectpath) 114 | "Create an org link to PROJECTPATH with name HEADING." 115 | (org-link-make-string (format "elisp:(project-switch-project \"%s\")" projectpath) heading)) 116 | 117 | (defun org-project--build-heading (projectpath) 118 | "Create an org heading for PROJECTPATH." 119 | (let* ((raw-heading (org-project--name-from-project projectpath)) 120 | (heading-linkized (if org-project-link-heading 121 | (org-project--linkize-heading raw-heading projectpath) 122 | raw-heading)) 123 | (heading-final heading-linkized)) 124 | heading-final)) 125 | 126 | (defun org-project--current-project () 127 | "Return the root of the current project if any, errors otherwise." 128 | (let ((project (project-current org-project-prompt-for-project))) 129 | (if project 130 | (project-root project) 131 | (error "%s is not in a project" (buffer-name))))) 132 | 133 | (defun org-project--completing-read (prompt choices &optional action) 134 | "Prompt for a string using `completing-read', featuring CHOICES. 135 | 136 | The PROMPT is prefixed by the current project name. If ACTION is specified, it 137 | is called with the entered value." 138 | (let* ((prompt (format "[%s] %s" 139 | (org-project--name-from-project (org-project--current-project)) 140 | prompt)) 141 | (res (completing-read prompt choices))) 142 | (if action 143 | (funcall action res) 144 | res))) 145 | 146 | (defun org-project--capture (projectpath &optional content goto) 147 | "Capture a TODO for the project located at PROJECTPATH. 148 | 149 | If CONTENT is provided, automatically use it as the new TODO. 150 | If GOTO is non-nil, jumpt to the capture target without capturing." 151 | (let* ((org-capture-templates `(("d" "default" entry 152 | (file+headline 153 | ,(org-project--get-capture-file projectpath) 154 | ,(org-project--build-heading projectpath)) 155 | ,(if content (string-replace 156 | "TEXT" content org-project-quick-capture-template) 157 | org-project-capture-template) 158 | :immediate-finish ,(not (null content)))))) 159 | (org-capture (if goto '(4) nil) "d"))) 160 | 161 | ;;;###autoload 162 | (cl-defun org-project-quick-capture () 163 | "Prompt for and directly record a TODO for the current project." 164 | (interactive) 165 | (org-project--capture 166 | (org-project--current-project) 167 | (org-project--completing-read "TODO: " nil))) 168 | 169 | ;;;###autoload 170 | (defun org-project-capture () 171 | "Use `org-capture' to record a TODO for the current project." 172 | (interactive) 173 | (org-project--capture (org-project--current-project))) 174 | 175 | ;;;###autoload 176 | (defun org-project-open-todos () 177 | "Jump to the TODOs for the current project." 178 | (interactive) 179 | (org-project--capture (org-project--current-project) " " t) 180 | (org-narrow-to-subtree) 181 | (outline-show-subtree)) 182 | 183 | (provide 'org-project) 184 | ;;; org-project.el ends here 185 | -------------------------------------------------------------------------------- /test/org-project-test.el: -------------------------------------------------------------------------------- 1 | ;;; org-project-test.el --- org-project test suite -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 Franklin Delehelle 4 | 5 | ;; This program is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation, either version 3 of the License, or 8 | ;; (at your option) any later version. 9 | 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 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see . 17 | 18 | ;;; Commentary: 19 | 20 | ;; The unit test suite of org-project 21 | 22 | ;;; Code: 23 | 24 | (require 'ert) 25 | 26 | (require 'org-project) 27 | (setq org-adapt-indentation 1) 28 | 29 | (defun equal-as-sets (seq1 seq2) 30 | (and 31 | (-all? (lambda (element) (member element seq2)) seq1) 32 | (-all? (lambda (element) (member element seq1)) seq2))) 33 | 34 | 35 | (provide 'org-project-test) 36 | ;;; org-project-test.el ends here 37 | -------------------------------------------------------------------------------- /test/test_projects.org: -------------------------------------------------------------------------------- 1 | * [[elisp:(org-projectile:open-project%20"github-search")][github-search]] [0/1] 2 | ** TODO do my thing 3 | ** TODO cool 4 | * proj1 5 | * proj3 [0/0] 6 | * emacs 7 | :PROPERTIES: 8 | :CATEGORY: proj4 9 | :END: 10 | * [[elisp:(org-projectile:open-project%20"ideas")][ideas]] [0/22] 11 | :PROPERTIES: 12 | :CATEGORY: ideas2 13 | :END: 14 | ** TODO 15 | * [[elisp:(org-projectile-open-project%20"test")][test]] 16 | ** TODO 17 | -------------------------------------------------------------------------------- /travis-evm-cask.sh: -------------------------------------------------------------------------------- 1 | export PATH="$HOME/.cask/bin:$HOME/.evm/bin:$PATH" 2 | 3 | git clone https://github.com/rejeep/evm.git "$HOME/.evm" 4 | evm config path /tmp 5 | evm install "$EVMVERSION" --use --skip 6 | export EMACS="$(evm bin)" 7 | 8 | curl -fsSkL https://raw.github.com/cask/cask/master/go | python 9 | --------------------------------------------------------------------------------