bastibe / org-journal

A simple org-mode based journaling mode
BSD 3-Clause "New" or "Revised" License
1.23k stars 122 forks source link

[[https://github.com/bastibe/org-journal/actions/workflows/test.yml][file:https://github.com/bastibe/org-journal/actions/workflows/test.yml/badge.svg?branch=master]] [[https://elpa.nongnu.org/nongnu/org-journal.html][https://elpa.nongnu.org/nongnu/org-journal.svg]] [[http://melpa.org/#/org-journal][file:http://melpa.org/packages/org-journal-badge.svg]] [[http://stable.melpa.org/#/org-journal][file:http://stable.melpa.org/packages/org-journal-badge.svg]] [[contributors][file:https://img.shields.io/github/contributors/bastibe/org-journal.svg]] [[license][file:https://img.shields.io/github/license/bastibe/org-journal.svg]]

+CAPTION: The org-journal logo

[[./org-journal.svg]]

[[open-issues][file:https://img.shields.io/github/issues/bastibe/org-journal.svg]] [[closed-issues][file:https://img.shields.io/github/issues-closed/bastibe/org-journal.svg]] [[open-pull-requests][file:https://img.shields.io/github/issues-pr/bastibe/org-journal.svg]] [[closed-pull-requests][file:https://img.shields.io/github/issues-pr-closed/bastibe/org-journal.svg]]

** Synopsis

=org-journal= maintains a set of files, depending on the value of =org-journal-file-type=, a file represents a day, week, month or year. When =org-journal-file-type= is set to ='daily=, each file represent a day. In case =org-journal-file-type= is set to ='weekly=, a file represents a week, etc. Convenient bindings allow the creation of journal records in the current daily, weekly, monthly or yearly file and search within all records or specified time intervals. All records can be browsed and searched from the Emacs Calendar for convenience. All entries in a specified TODO state will be carried over to the next day, see =org-journal-carryover-items=. Optionally, the journal entry can be encrypted, so can the file, see =org-journal-enable-encryption= and =org-journal-encrypt-journal=, respectively.

Every journal entry must have a CREATED property when using yearly, monthly and weekly journal files. This property is added by =org-journal-new-entry= automatically.

An example of a daily file (it will actually look a lot nicer, depending on your org-mode settings):

+BEGIN_EXAMPLE org

An example of a weekly/monthly/yearly journal file, see also =org-journal-file-type=.

+BEGIN_EXAMPLE org

** Installation

=org-journal= is available through [[https://elpa.nongnu.org/][NonGNU ELPA]], [[https://melpa.org/][MELPA]] and [[https://stable.melpa.org/][MELPA Stable]]. So installation should be trivial:

+BEGIN_EXAMPLE

M-x package-install org-journal

+END_EXAMPLE

Then add =(require 'org-journal)= to your =.emacs=.

Note! If you are using =org-mode= version 9.6 consider customizing the variable =org-element-use-cache= and setting it to =nil= to workaround [[https://github.com/bastibe/org-journal/issues/406][an issue where journal items are not properly carried over to the next day]]

** Quickstart

Doing =M-x org-journal-new-entry= will immediately create a journal directory in the default path (customized using the =org-journal-dir= variable), open or create a file in =org-journal-mode=, and insert a template for a new journal entry.

The same command with a prefix argument (=C-u M-x org-journal-new-entry=) will do everything mentioned while skipping entry creation, which is useful for looking at the current journal file.

** Basic Usage

Bindings available in =org-journal-mode=:

All journal entries are registered in the Emacs Calendar. To see available journal entries do =M-x calendar=. Bindings available in the =calendar-mode=:

** Setup and customization

The following variables can be customized through =M-x customize=, or configured programmatically in your =.init.el=.

See below for an example.

*** Journal Directory and Files

Customization options related to journal directory and files:

*** Journal File Content

Customization options related to the journal file contents:

*** =org-journal= behavior

Customization options related to =org-journal= itself:

*** An example setup

A very basic example of customization.

+BEGIN_EXAMPLE emacs-lisp

(setq org-journal-dir "~/org/journal/") (setq org-journal-date-format "%A, %d %B %Y") (require 'org-journal)

+END_EXAMPLE

For users of =use-package=, this setup could look like the following:

+BEGIN_EXAMPLE emacs-lisp

(use-package org-journal :ensure t :defer t :init ;; Change default prefix key; needs to be set before loading org-journal (setq org-journal-prefix-key "C-c j ") :config (setq org-journal-dir "~/org/journal/" org-journal-date-format "%A, %d %B %Y"))

+END_EXAMPLE

** Advanced Usage *** Searching the Journal

=org-journal= has two searching options: the usual =org-mode= agenda search and the built-in plain text search. The former can become slow with bigger journals, so the built-in search is a recommended option.

To use the agenda search, you can add all journal entries to your org-agenda by adding =org-journal-dir= to =org-agenda-files= and setting =org-agenda-file-regexp= to include files matching your =org-journal-file-pattern=.

+BEGIN_EXAMPLE emacs-lisp

;; When =org-journal-file-pattern= has the default value, this would be the regex. (setq org-agenda-file-regexp "\`\([^.].*\.org\|[0-9]\{8\}\(\.gpg\)?\)\'") (add-to-list 'org-agenda-files org-journal-dir)

+END_EXAMPLE

However, this can become /very/ slow if you have many journal entries. As a compromize, you can set =org-journal-enable-agenda-integration= to =t=, which automatically adds the current and all future journal entries to the agenda. This is enough to get an overview over current and future tasks.

The built-in search is available through the following function: =org-journal-search= (=C-c C-s= in =org-journal-mode=). By default, it will ask for the time interval to search within (accepting the =org-read-date= format such as "-1y" or "-1m") and the string to search for. Given a prefix argument (=C-u org-journal-search=), it will go through the whole journal.

The order of the search results (ascending or descending by date) can be customized using the =org-journal-search-results-order-by= variable.

Search is also available through the Emacs Calendar as described in "Basic Usage".

*** Carry Over

By default, =org-journal= will try to /carry over/ previous day TODO-marked items whenever a new journal file is created. The older journal entry will be inserted to the current day's file.

This feature is controlled through the =org-journal-carryover-items= variable. To disable this feature set =org-journal-carryover-items= to an empty string =""=. Any [[http://orgmode.org/manual/Matching-tags-and-properties.html][agenda tags view match string]], tags, properties, and todo states are allowed. By default this is ~TODO=”TODO”~. Which will match TODO items.

The old carryover items in the previous day's journal are processed by the function assigned to =org-journal-handle-old-carryover= variable. Default is to remove all of them. You can change this behavior by assigning a custom fuction to the variable. Your function has to take one argument, which is a list of old carryover entries. The list is in form of ((START_POINT (END_POINT . "TEXT")) ... (START_POINT (END_POINT . "TEXT"))); and in ascending order of START_POINT.

For example, you can choose putting a tag on the old carryover entries intead of removing them:

+begin_src elisp

(defun my-old-carryover (old_carryover) (save-excursion (let ((matcher (cdr (org-make-tags-matcher org-journal-carryover-items)))) (dolist (entry (reverse old_carryover)) (save-restriction (narrow-to-region (car entry) (cadr entry)) (goto-char (point-min)) (org-scan-tags '(lambda () (org-set-tags ":carried:")) matcher org--matcher-tags-todo-only))))))

(setq org-journal-handle-old-carryover 'my-old-carryover)

+end_src

You can also skip carry over of [[https://orgmode.org/manual/Drawers.html][Drawers]] through the =org-journal-skip-carryover-drawers= variable. This is specifically useful when you want to skip carry over of previous days clocked entries when it is under the drawer =LOGBOOK=. The variable accepts a list of drawers names which will be skipped on carry over. Sample configuration for skipping =LOGBOOK= drawer:

+begin_src elisp

(setq org-journal-skip-carryover-drawers (list "LOGBOOK"))

+end_src

*** Encryption

The journal entry can be encrypted using =org-crypt=, to enable it set ~org-journal-enable-encryption~ to =t=.

You can also encrypt the journal files itself by setting the variable ~org-journal-encrypt-journal~ to =t=. =org-journal= will always search for journal files with the =.gpg= extension, and highlights them in the calendar, etc., regardless of the value of ~org-journal-encrypt-journal~. See the info page =(info "(epa)Encrypting/decrypting gpg files")= for more information about gpg encryption in Emacs.

*** Agenda and Scheduling

An easy way of keeping track of appointments or future TODOs is to simply create a journal entry in the future. Such entries will automatically get a timestamp and show up in the current day's journal entry once you reach that day.

There are a few helper functions to deal with such scheduled entries:

**** iCalendar export

You can export your scheduled entries to an iCalendar file, and subscribe to that file in your calendar application. You need to enable the agenda integration for this to work. I also recommend you set the following values before exporting:

+begin_src elisp

(setq org-journal-enable-agenda-integration t org-icalendar-store-UID t org-icalendar-include-todo "all" org-icalendar-combined-agenda-file "~/path/to/org-journal.ics")

+end_src

With this done, you can export your agenda, including your scheduled entries, with =(org-icalendar-combine-agenda-files)=.

*** Journal Capture Template

You can configure a capture template in order to integrate =org-journal= with =org-capture=, as in the following example for a daily journal:

+BEGIN_EXAMPLE emacs-lisp

(defun org-journal-find-location () ;; Open today's journal, but specify a non-nil prefix argument in order to ;; inhibit inserting the heading; org-capture will insert the heading. (org-journal-new-entry t) (unless (eq org-journal-file-type 'daily) (org-narrow-to-subtree)) (goto-char (point-max)))

(setq org-capture-templates '(("j" "Journal entry" plain (function org-journal-find-location) "** %(format-time-string org-journal-time-format)%^{Title}\n%i%?" :jump-to-captured t :immediate-finish t)))

+END_EXAMPLE

If you want to do the same to schedule a task for a future date, you can use the following:

+BEGIN_EXAMPLE emacs-lisp

(defvar org-journal--date-location-scheduled-time nil)

(defun org-journal-date-location (&optional scheduled-time) (let ((scheduled-time (or scheduled-time (org-read-date nil nil nil "Date:")))) (setq org-journal--date-location-scheduled-time scheduled-time) (org-journal-new-entry t (org-time-string-to-time scheduled-time)) (unless (eq org-journal-file-type 'daily) (org-narrow-to-subtree)) (goto-char (point-max))))

(setq org-capture-templates '(("j" "Journal entry" plain (function org-journal-date-location) "** TODO %?\n <%(princ org-journal--date-location-scheduled-time)>\n" :jump-to-captured t))

+END_EXAMPLE

*** Caching of journal dates Since version 2.0.0 a cache has been added to speed up calendar operations. This should drastically improve the performance when using encrypted journal files, see =org-journal-encrypt-journal=.

The caching functionality can be enabled by settings =org-journal-enable-cache= to =t=. The cache can be reset by calling =org-journal-invalidate-cache=.

** FAQ

*** Can I use weekly/monthly/yearly journal entries instead of daily ones?

Yes, see =org-journal-file-type=.

*** Can I have multiple journals?

At the moment, this is not possible. But it should be possible to switch the value of =org-journal-directory= using a custom function or directory local variables.

*** Can I use org-journal with Spacemacs?

Yes you can!

*** Some key-bindings in org-journal conflict with org-mode key bindings

Minor modes are supposed to only use key bindings of the form =C-c C-?=, where =?= can be any letter, and to not overwrite major mode bindings. With org-mode already using most interesting keys, collisions are inevitable. This means that some org-journal key bindings will not work as expected in an org-mode buffer, and also that some org-mode key bindings will not work as expected in an org-journal buffer.

When working in an org-mode buffer the following org-journal key bindings are overwritten:

To workaround this, you can use user bindings of the form =C-c ?=, where =?= can be any letter, to call the org-journal functions. This allows you to have a set of keybindings that work the same in org-mode and org-journal buffers. However, this is Emacs, and if you don't like a key binding, change it!

*** Opening journal entries from the calendar are not editable

Old entries are opened in =view-mode=, which has convenient key bindings for browsing files. Most notably, you can quickly close =view-mode= buffers with =q=, scroll them with the =SPC= and =DEL=, or quit =view-mode= with =e=.

*** Can I insert some text on a newly created journal file?

Yes, you can write a custom function and assign it =org-journal-date-format=.

*** Can I do more powerful things on a newly created journal entry?

Yes, there are two hooks that are run when a journal entry is created. Each (=org-journal-new-entry=) will call =org-journal-after-entry-create-hook=, and =org-journal-after-header-create-hook= is called each time the date (the parent headline of each entry) is generated.

** Convenient =org-journal= Snippet Extensions

*** Kill journal buffer after saving buffer (By [[https://github.com/dhruvparamhans][@dhruvparamhans]])

+BEGIN_SRC emacs-lisp

(defun org-journal-save-entry-and-exit() "Simple convenience function. Saves the buffer of the current day's entry and kills the window Similar to org-capture like behavior" (interactive) (save-buffer) (kill-buffer-and-window)) (define-key org-journal-mode-map (kbd "C-x C-s") 'org-journal-save-entry-and-exit)

+END_SRC

** Contributors

See [[file:CONTRIBUTORS][CONTRIBUTORS]].

** Contributing to =org-journal= We format the code using =common-lisp-indent-function= rather than the default =lisp-indent-function=. Please set the variable =lisp-indent-function= to =common-lisp-indent-function=, and format the code before creating a PR.

+BEGIN_SRC emacs-lisp

(setq lisp-indent-function 'common-lisp-indent-function) ;; Markt the whole buffer: C-x h ;; Call indent-region: C-M-\

+END_SRC

** Changelog

See [[file:CHANGELOG][CHANGELOG]].