remove notdeft

This commit is contained in:
KemoNine 2022-08-26 12:46:14 -04:00
parent 9af503897a
commit 3790c91768
26 changed files with 910 additions and 5979 deletions

View File

@ -0,0 +1,69 @@
;;; diredfl-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "diredfl" "diredfl.el" (0 0 0 0))
;;; Generated autoloads from diredfl.el
(autoload 'diredfl-mode "diredfl" "\
Enable additional font locking in `dired-mode'.
This is a minor mode. If called interactively, toggle the
`Diredfl mode' mode. If the prefix argument is positive, enable
the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `diredfl-mode'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
\(fn &optional ARG)" t nil)
(put 'diredfl-global-mode 'globalized-minor-mode t)
(defvar diredfl-global-mode nil "\
Non-nil if Diredfl-Global mode is enabled.
See the `diredfl-global-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `diredfl-global-mode'.")
(custom-autoload 'diredfl-global-mode "diredfl" nil)
(autoload 'diredfl-global-mode "diredfl" "\
Toggle Diredfl mode in all buffers.
With prefix ARG, enable Diredfl-Global mode if ARG is positive;
otherwise, disable it.
If called from Lisp, toggle the mode if ARG is `toggle'.
Enable the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
Diredfl mode is enabled in all buffers where `(lambda nil (when
\(derived-mode-p 'dired-mode) (diredfl-mode)))' would do it.
See `diredfl-mode' for more information on Diredfl mode.
\(fn &optional ARG)" t nil)
(register-definition-prefixes "diredfl" '("diredfl-"))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; diredfl-autoloads.el ends here

View File

@ -0,0 +1,2 @@
;;; Generated package description from diredfl.el -*- no-byte-compile: t -*-
(define-package "diredfl" "20220508.805" "Extra font lock rules for a more colourful dired" '((emacs "24")) :commit "62b559e1d6b69834a56a57eb1832ac6ad4d2e5d0" :authors '(("Steve Purcell" . "steve@sanityinc.com")) :maintainer '("Steve Purcell" . "steve@sanityinc.com") :keywords '("faces") :url "https://github.com/purcell/diredfl")

View File

@ -0,0 +1,382 @@
;;; diredfl.el --- Extra font lock rules for a more colourful dired -*- lexical-binding: t; -*-
;; Copyright (C) 2017 Steve Purcell
;; Author: Steve Purcell <steve@sanityinc.com>
;; Author: Drew Adams
;; Keywords: faces
;; Package-Commit: 62b559e1d6b69834a56a57eb1832ac6ad4d2e5d0
;; URL: https://github.com/purcell/diredfl
;; Package-Requires: ((emacs "24"))
;; Package-Version: 20220508.805
;; Package-X-Original-Version: 0
;; 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 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This is adapted from the extra font lock rules provided by Drew
;; Adams' `dired+' package, but published via a modern means, and with
;; support for older Emacsen removed.
;; Enable in all Dired buffers by calling or customising `diredfl-global-mode'.
;; Alternatively:
;; (add-hook 'dired-mode-hook 'diredfl-mode)
;;; Code:
(require 'dired)
(defgroup diredfl ()
"Extra font lock rules for a more colourful Dired."
:group 'dired)
(defcustom diredfl-compressed-extensions '(".tar" ".taz" ".tgz" ".arj" ".lzh"
".lzma" ".xz" ".zip" ".z" ".Z" ".gz" ".bz2")
"*List of compressed-file extensions, for highlighting."
:type '(repeat string) :group 'diredfl)
(defcustom diredfl-ignore-compressed-flag t
"*Non-nil means to font-lock names of compressed files as ignored files.
This applies to filenames whose extensions are in
`diredfl-compressed-extensions'. If nil they are highlighted using
face `diredfl-compressed-file-name'."
:type 'boolean :group 'diredfl)
(defface diredfl-autofile-name
'((((background dark)) (:background "#111313F03181")) ; Very dark blue
(t (:background "#EEECEC0FCE7E"))) ; Very pale goldenrod
"*Face used in Dired for names of files that are autofile bookmarks."
:group 'diredfl)
(defvar diredfl-autofile-name 'diredfl-autofile-name)
(defface diredfl-compressed-file-name
'((((background dark)) (:foreground "Blue"))
(t (:foreground "Brown")))
"*Face used for compressed file names."
:group 'diredfl)
(defvar diredfl-compressed-file-name 'diredfl-compressed-file-name)
(defface diredfl-compressed-file-suffix
'((((background dark)) (:foreground "Blue"))
(t (:foreground "Yellow")))
"*Face used for compressed file suffixes in Dired buffers.
This means the `.' plus the file extension. Example: `.zip'."
:group 'diredfl)
(defvar diredfl-compressed-file-suffix 'diredfl-compressed-file-suffix)
(defface diredfl-date-time
'((((background dark)) (:foreground "#74749A9AF7F7")) ; ~ med blue
(t (:foreground "DarkGoldenrod4")))
"*Face used for date and time in Dired buffers."
:group 'diredfl)
(defvar diredfl-date-time 'diredfl-date-time)
(defface diredfl-deletion
'((t (:foreground "Yellow" :background "Red")))
"*Face used for deletion flags (D) in Dired buffers."
:group 'diredfl)
(defvar diredfl-deletion 'diredfl-deletion)
(defface diredfl-deletion-file-name
'((t (:foreground "Red")))
"*Face used for names of deleted files in Dired buffers."
:group 'diredfl)
(defvar diredfl-deletion-file-name 'diredfl-deletion-file-name)
(defface diredfl-dir-heading
'((((background dark)) (:foreground "Yellow" :background "#00003F3F3434")) ; ~ dark green
(t (:foreground "Blue" :background "Pink")))
"*Face used for directory headings in Dired buffers."
:group 'diredfl)
(defvar diredfl-dir-heading 'diredfl-dir-heading)
(defface diredfl-dir-name
'((((background dark))
(:foreground "#7474FFFFFFFF" :background "#2C2C2C2C2C2C")) ; ~ cyan, dark gray
(t (:foreground "DarkRed" :background "LightGray")))
"*Face used for directory names."
:group 'diredfl)
(defvar diredfl-dir-name 'diredfl-dir-name)
(defface diredfl-dir-priv
'((((background dark))
(:foreground "#7474FFFFFFFF" :background "#2C2C2C2C2C2C")) ; ~ cyan, dark gray
(t (:foreground "DarkRed" :background "LightGray")))
"*Face used for directory privilege indicator (d) in Dired buffers."
:group 'diredfl)
(defvar diredfl-dir-priv 'diredfl-dir-priv)
(defface diredfl-exec-priv
'((((background dark)) (:background "#4F4F3B3B2121")) ; ~ dark brown
(t (:background "LightSteelBlue")))
"*Face used for execute privilege indicator (x) in Dired buffers."
:group 'diredfl)
(defvar diredfl-exec-priv 'diredfl-exec-priv)
;; For this to show up, you need `F' among the options in `dired-listing-switches'.
;; For example, I use "-alF" for `dired-listing-switches'.
(defface diredfl-executable-tag
'((t (:foreground "Red")))
"*Face used for executable tag (*) on file names in Dired buffers."
:group 'diredfl)
(defvar diredfl-executable-tag 'diredfl-executable-tag)
(defface diredfl-file-name
'((((background dark)) (:foreground "Yellow"))
(t (:foreground "Blue")))
"*Face used for file names (without suffixes) in Dired buffers.
This means the base name. It does not include the `.'."
:group 'diredfl)
(defvar diredfl-file-name 'diredfl-file-name)
(defface diredfl-file-suffix
'((((background dark)) (:foreground "#7474FFFF7474")) ; ~ light green
(t (:foreground "DarkMagenta")))
"*Face used for file suffixes in Dired buffers.
This means the `.' plus the file extension. Example: `.elc'."
:group 'diredfl)
(defvar diredfl-file-suffix 'diredfl-file-suffix)
(defface diredfl-flag-mark
'((((background dark)) (:foreground "Blue" :background "#7575D4D41D1D")) ; ~ olive green
(t (:foreground "Yellow" :background "Blueviolet")))
"*Face used for flags and marks (except D) in Dired buffers."
:group 'diredfl)
(defvar diredfl-flag-mark 'diredfl-flag-mark)
(defface diredfl-flag-mark-line
'((((background dark)) (:background "#787831311414")) ; ~ dark red brown
(t (:background "Skyblue")))
"*Face used for flagged and marked lines in Dired buffers."
:group 'diredfl)
(defvar diredfl-flag-mark-line 'diredfl-flag-mark-line)
(defface diredfl-ignored-file-name
'(;; (((background dark)) (:foreground "#FFFF921F921F")) ; ~ salmon
;; (((background dark)) (:foreground "#A71F5F645F64")) ; ~ dark salmon
(((background dark)) (:foreground "#C29D6F156F15")) ; ~ salmon
(t (:foreground "#00006DE06DE0"))) ; ~ dark cyan
"*Face used for ignored file names in Dired buffers."
:group 'diredfl)
(defvar diredfl-ignored-file-name 'diredfl-ignored-file-name)
(defface diredfl-link-priv
'((((background dark)) (:foreground "#00007373FFFF")) ; ~ blue
(t (:foreground "DarkOrange")))
"*Face used for link privilege indicator (l) in Dired buffers."
:group 'diredfl)
(defvar diredfl-link-priv 'diredfl-link-priv)
(defface diredfl-no-priv
'((((background dark)) (:background "#2C2C2C2C2C2C")) ; ~ dark gray
(t (:background "LightGray")))
"*Face used for no privilege indicator (-) in Dired buffers."
:group 'diredfl)
(defvar diredfl-no-priv 'diredfl-no-priv)
(defface diredfl-number
'((((background dark)) (:foreground "#FFFFFFFF7474")) ; ~ light yellow
(t (:foreground "DarkBlue")))
"*Face used for numerical fields in Dired buffers.
In particular, inode number, number of hard links, and file size."
:group 'diredfl)
(defvar diredfl-number 'diredfl-number)
(defface diredfl-other-priv
'((((background dark)) (:background "#111117175555")) ; ~ dark blue
(t (:background "PaleGoldenrod")))
"*Face used for l,s,S,t,T privilege indicators in Dired buffers."
:group 'diredfl)
(defvar diredfl-other-priv 'diredfl-other-priv)
(defface diredfl-rare-priv
'((((background dark)) (:foreground "Green" :background "#FFFF00008080")) ; ~ hot pink
(t (:foreground "Magenta" :background "SpringGreen")))
"*Face used for rare privilege indicators (b,c,s,m,p,S) in Dired buffers."
:group 'diredfl)
(defvar diredfl-rare-priv 'diredfl-rare-priv)
(defface diredfl-read-priv
'((((background dark)) (:background "#999932325555")) ; ~ burgundy / dark magenta
(t (:background "MediumAquamarine")))
"*Face used for read privilege indicator (w) in Dired buffers."
:group 'diredfl)
(defvar diredfl-read-priv 'diredfl-read-priv)
(defface diredfl-symlink
'((((background dark)) (:foreground "#00007373FFFF")) ; ~ blue
(t (:foreground "DarkOrange")))
"*Face used for symbolic links in Dired buffers."
:group 'diredfl)
(defvar diredfl-symlink 'diredfl-symlink)
(defface diredfl-tagged-autofile-name
'((((background dark)) (:background "#328C0411328C")) ; Very dark magenta
(t (:background "#CD73FBEECD73"))) ; Very pale green
"*Face used in Dired for names of files that are autofile bookmarks."
:group 'diredfl)
(defvar diredfl-tagged-autofile-name 'diredfl-tagged-autofile-name)
(defface diredfl-write-priv
'((((background dark)) (:background "#25258F8F2929")) ; ~ dark green
(t (:background "Orchid")))
"*Face used for write privilege indicator (w) in Dired buffers."
:group 'diredfl)
(defvar diredfl-write-priv 'diredfl-write-priv)
(defun diredfl-match-ignored-extensions (limit)
"A matcher of ignored filename extensions for use in `font-lock-keywords'.
LIMIT is the extent of the search."
(let ((ignored-extensions
(append (if (boundp 'dired-omit-extensions)
dired-omit-extensions
completion-ignored-extensions)
(when diredfl-ignore-compressed-flag
diredfl-compressed-extensions))))
(when ignored-extensions
(re-search-forward
(concat "^ \\(.*"
(regexp-opt ignored-extensions)
;; Optional executable flag
"[*]?\\)$")
limit
t))))
;;; Define second level of fontifying.
(defconst diredfl-font-lock-keywords-1
(list
'("^ \\(.+:\\)$" 1 diredfl-dir-heading) ; Directory headers
'("^ wildcard.*$" 0 'default) ; Override others, e.g. `l' for `diredfl-other-priv'.
'("^ (No match).*$" 0 'default) ; Override others, e.g. `t' for `diredfl-other-priv'.
'("[^ .]\\(\\.[^. /]+\\)$" 1 diredfl-file-suffix) ; Suffix, including `.'.
'("\\([^ ]+\\) -> .+$" 1 diredfl-symlink) ; Symbolic links
;; 1) Date/time and 2) filename w/o suffix.
;; This is a bear, and it is fragile - Emacs can change `dired-move-to-filename-regexp'.
`(,dired-move-to-filename-regexp
(7 diredfl-date-time t t) ; Date/time, locale (western or eastern)
(2 diredfl-date-time t t) ; Date/time, ISO
(,(concat "\\(.+\\)\\(" (concat (funcall #'regexp-opt diredfl-compressed-extensions)
"\\)[*]?$"))
nil nil (0 diredfl-compressed-file-name keep t))) ; Compressed-file suffix
`(,dired-move-to-filename-regexp
(7 diredfl-date-time t t) ; Date/time, locale (western or eastern)
(2 diredfl-date-time t t) ; Date/time, ISO
("\\(.+\\)$" nil nil (0 diredfl-file-name keep t))) ; Filename (not a compressed file)
;; Files to ignore
'(diredfl-match-ignored-extensions 1 diredfl-ignored-file-name t)
;; Compressed-file (suffix)
(list (concat "\\(" (concat (funcall #'regexp-opt diredfl-compressed-extensions) "\\)[*]?$"))
1 diredfl-compressed-file-suffix t)
'("\\([*]\\)$" 1 diredfl-executable-tag t) ; Executable (*)
;; Inode, hard-links, & file size (. and , are for the decimal point, depending on locale)
;; See comment for `directory-listing-before-filename-regexp' in `files.el' or `files+.el'.
'("\\_<\\(\\([0-9]+\\([.,][0-9]+\\)?\\)[BkKMGTPEZY]?[ /]?\\)" 1 'diredfl-number)
;; Directory names - exclude d:/..., Windows drive letter in a dir heading.
(list (concat dired-re-maybe-mark dired-re-inode-size "\\(d\\)[^:]")
'(1 diredfl-dir-priv t) '(".+" (dired-move-to-filename) nil (0 diredfl-dir-name t)))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]........\\(x\\)") ; o x
'(1 diredfl-exec-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]........\\([lsStT]\\)") ; o misc
'(1 diredfl-other-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].......\\(w\\).") ; o w
'(1 diredfl-write-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]......\\(r\\)..") ; o r
'(1 diredfl-read-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].....\\(x\\)...") ; g x
'(1 diredfl-exec-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].....\\([lsStT]\\)...") ; g misc
'(1 diredfl-other-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]....\\(w\\)....") ; g w
'(1 diredfl-write-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]...\\(r\\).....") ; g r
'(1 diredfl-read-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]..\\(x\\)...") ; u x
'(1 diredfl-exec-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]..\\([lsStT]\\)...") ; u misc
'(1 diredfl-other-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].\\(w\\)....") ; u w
'(1 diredfl-write-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]\\(r\\).....") ; u r
'(1 diredfl-read-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]........\\([-rwxlsStT]\\)") ; o -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].......\\([-rwxlsStT]\\).") ; g -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]......\\([-rwxlsStT]\\)..") ; u -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].....\\([-rwxlsStT]\\)...") ; o -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]....\\([-rwxlsStT]\\)....") ; g -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]...\\([-rwxlsStT]\\).....") ; u -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]..\\([-rwxlsStT]\\)......") ; o -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].\\([-rwxlsStT]\\).......") ; g -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]\\([-rwxlsStT]\\)........") ; u -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "\\(-\\)")
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "\\([bcsmpS]\\)") ; (rare)
'(1 diredfl-rare-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "\\(l\\)[-rwxlsStT]") ; l
'(1 diredfl-rare-priv keep))
(list (concat "^\\([^\n " (char-to-string dired-del-marker) "].*$\\)")
'(1 diredfl-flag-mark-line prepend)) ; Flag/mark lines
(list (concat "^\\([^\n " (char-to-string dired-del-marker) "]\\)") ; Flags, marks (except D)
'(1 diredfl-flag-mark prepend))
(list (concat "^\\([" (char-to-string dired-del-marker) "].*$\\)") ; Deletion-flagged lines
'(1 diredfl-deletion-file-name prepend))
(list (concat "^\\([" (char-to-string dired-del-marker) "]\\)") ; Deletion flags (D)
'(1 diredfl-deletion prepend)))
"2nd level of Dired highlighting. See `font-lock-maximum-decoration'.")
;;;###autoload
(define-minor-mode diredfl-mode
"Enable additional font locking in `dired-mode'."
:global nil
(setq font-lock-defaults
(if diredfl-mode
'((dired-font-lock-keywords
dired-font-lock-keywords
diredfl-font-lock-keywords-1)
t nil nil beginning-of-line)
'(dired-font-lock-keywords t nil nil beginning-of-line)))
(font-lock-refresh-defaults))
;;;###autoload
(define-globalized-minor-mode diredfl-global-mode diredfl-mode
(lambda ()
(when (derived-mode-p 'dired-mode)
(diredfl-mode)))
:require 'diredfl)
(provide 'diredfl)
;;; diredfl.el ends here

View File

@ -56,75 +56,6 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; dired adjustments
(use-package dired-rainbow
:defer 4
:config
(dired-rainbow-define-chmod directory "#0074d9" "d.*")
(dired-rainbow-define html "#eb5286"
("css" "less" "sass" "scss" "htm" "html" "jhtm" "mht"
"eml" "mustache" "xhtml"))
(dired-rainbow-define xml "#f2d024"
("xml" "xsd" "xsl" "xslt" "wsdl" "bib" "json" "msg"
"pgn" "rss" "yaml" "yml" "rdata" "sln" "csproj"
"meta" "unity" "tres" "tscn" "import" "godot"))
(dired-rainbow-define document "#9561e2"
("docm" "doc" "docx" "odb" "odt" "pdb" "pdf" "ps"
"rtf" "djvu" "epub" "odp" "ppt" "pptx" "xls" "xlsx"
"vsd" "vsdx" "plantuml"))
(dired-rainbow-define markdown "#4dc0b5"
("org" "org_archive" "etx" "info" "markdown" "md"
"mkd" "nfo" "pod" "rst" "tex" "texi" "textfile" "txt"))
(dired-rainbow-define database "#6574cd"
("xlsx" "xls" "csv" "accdb" "db" "mdb" "sqlite" "nc"))
(dired-rainbow-define media "#de751f"
("mp3" "mp4" "MP3" "MP4" "avi" "mpeg" "mpg" "flv"
"ogg" "mov" "mid" "midi" "wav" "aiff" "flac" "mkv"))
(dired-rainbow-define image "#f66d9b"
("tiff" "tif" "cdr" "gif" "ico" "jpeg" "jpg" "png"
"psd" "eps" "svg"))
(dired-rainbow-define log "#c17d11"
("log" "log.1" "log.2" "log.3" "log.4" "log.5" "log.6"
"log.7" "log.8" "log.9"))
(dired-rainbow-define shell "#f6993f"
("awk" "bash" "bat" "fish" "sed" "sh" "zsh" "vim"))
(dired-rainbow-define interpreted "#38c172"
("py" "ipynb" "hy" "rb" "pl" "t" "msql" "mysql"
"pgsql" "sql" "r" "clj" "cljs" "cljc" "cljx" "edn"
"scala" "js" "jsx" "lua" "fnl" "gd"))
(dired-rainbow-define compiled "#6cb2eb"
("asm" "cl" "lisp" "el" "c" "h" "c++" "h++" "hpp"
"hxx" "m" "cc" "cs" "cp" "cpp" "go" "f" "for" "ftn"
"f90" "f95" "f03" "f08" "s" "rs" "active" "hs"
"pyc" "java"))
(dired-rainbow-define executable "#8cc4ff"
("com" "exe" "msi"))
(dired-rainbow-define compressed "#51d88a"
("7z" "zip" "bz2" "tgz" "txz" "gz" "xz" "z" "Z" "jar"
"war" "ear" "rar" "sar" "xpi" "apk" "xz" "tar" "rar"))
(dired-rainbow-define packaged "#faad63"
("deb" "rpm" "apk" "jad" "jar" "cab" "pak" "pk3" "vdf"
"vpk" "bsp"))
(dired-rainbow-define encrypted "#f2d024"
("gpg" "pgp" "asc" "bfe" "enc" "signature" "sig" "p12"
"pem"))
(dired-rainbow-define fonts "#f6993f"
("afm" "fon" "fnt" "pfb" "pfm" "ttf" "otf" "woff"
"woff2" "eot"))
(dired-rainbow-define partition "#e3342f"
("dmg" "iso" "bin" "nrg" "qcow" "toast" "vcd" "vmdk"
"bak"))
(dired-rainbow-define vc "#6cb2eb"
("git" "gitignore" "gitattributes" "gitmodules"))
(dired-rainbow-define config "#5040e2"
("cfg" "conf"))
(dired-rainbow-define certificate "#6cb2eb"
("cer" "crt" "pfx" "p7b" "csr" "req" "key"))
(dired-rainbow-define junk "#7F7D7D"
("DS_Store" "projectile"))
(dired-rainbow-define icloud "#e3342f" ("icloud"))
(dired-rainbow-define-chmod executable-unix "#38c172" "-.*x.*")
)
; use single buffer for nav damnit
(require 'dired-single)
(defun my-dired-init ()
@ -151,6 +82,10 @@
(interactive)
(all-the-icons-dired-mode 1)
(hl-line-mode 1)))
(use-package diredfl
:commands diredfl-global-mode
:init
(diredfl-global-mode))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; origami config

View File

@ -0,0 +1,69 @@
;;; diredfl-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "diredfl" "diredfl.el" (0 0 0 0))
;;; Generated autoloads from diredfl.el
(autoload 'diredfl-mode "diredfl" "\
Enable additional font locking in `dired-mode'.
This is a minor mode. If called interactively, toggle the
`Diredfl mode' mode. If the prefix argument is positive, enable
the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `diredfl-mode'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
\(fn &optional ARG)" t nil)
(put 'diredfl-global-mode 'globalized-minor-mode t)
(defvar diredfl-global-mode nil "\
Non-nil if Diredfl-Global mode is enabled.
See the `diredfl-global-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `diredfl-global-mode'.")
(custom-autoload 'diredfl-global-mode "diredfl" nil)
(autoload 'diredfl-global-mode "diredfl" "\
Toggle Diredfl mode in all buffers.
With prefix ARG, enable Diredfl-Global mode if ARG is positive;
otherwise, disable it.
If called from Lisp, toggle the mode if ARG is `toggle'.
Enable the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
Diredfl mode is enabled in all buffers where `(lambda nil (when
\(derived-mode-p 'dired-mode) (diredfl-mode)))' would do it.
See `diredfl-mode' for more information on Diredfl mode.
\(fn &optional ARG)" t nil)
(register-definition-prefixes "diredfl" '("diredfl-"))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; diredfl-autoloads.el ends here

View File

@ -0,0 +1,2 @@
;;; Generated package description from diredfl.el -*- no-byte-compile: t -*-
(define-package "diredfl" "20220508.805" "Extra font lock rules for a more colourful dired" '((emacs "24")) :commit "62b559e1d6b69834a56a57eb1832ac6ad4d2e5d0" :authors '(("Steve Purcell" . "steve@sanityinc.com")) :maintainer '("Steve Purcell" . "steve@sanityinc.com") :keywords '("faces") :url "https://github.com/purcell/diredfl")

View File

@ -0,0 +1,382 @@
;;; diredfl.el --- Extra font lock rules for a more colourful dired -*- lexical-binding: t; -*-
;; Copyright (C) 2017 Steve Purcell
;; Author: Steve Purcell <steve@sanityinc.com>
;; Author: Drew Adams
;; Keywords: faces
;; Package-Commit: 62b559e1d6b69834a56a57eb1832ac6ad4d2e5d0
;; URL: https://github.com/purcell/diredfl
;; Package-Requires: ((emacs "24"))
;; Package-Version: 20220508.805
;; Package-X-Original-Version: 0
;; 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 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This is adapted from the extra font lock rules provided by Drew
;; Adams' `dired+' package, but published via a modern means, and with
;; support for older Emacsen removed.
;; Enable in all Dired buffers by calling or customising `diredfl-global-mode'.
;; Alternatively:
;; (add-hook 'dired-mode-hook 'diredfl-mode)
;;; Code:
(require 'dired)
(defgroup diredfl ()
"Extra font lock rules for a more colourful Dired."
:group 'dired)
(defcustom diredfl-compressed-extensions '(".tar" ".taz" ".tgz" ".arj" ".lzh"
".lzma" ".xz" ".zip" ".z" ".Z" ".gz" ".bz2")
"*List of compressed-file extensions, for highlighting."
:type '(repeat string) :group 'diredfl)
(defcustom diredfl-ignore-compressed-flag t
"*Non-nil means to font-lock names of compressed files as ignored files.
This applies to filenames whose extensions are in
`diredfl-compressed-extensions'. If nil they are highlighted using
face `diredfl-compressed-file-name'."
:type 'boolean :group 'diredfl)
(defface diredfl-autofile-name
'((((background dark)) (:background "#111313F03181")) ; Very dark blue
(t (:background "#EEECEC0FCE7E"))) ; Very pale goldenrod
"*Face used in Dired for names of files that are autofile bookmarks."
:group 'diredfl)
(defvar diredfl-autofile-name 'diredfl-autofile-name)
(defface diredfl-compressed-file-name
'((((background dark)) (:foreground "Blue"))
(t (:foreground "Brown")))
"*Face used for compressed file names."
:group 'diredfl)
(defvar diredfl-compressed-file-name 'diredfl-compressed-file-name)
(defface diredfl-compressed-file-suffix
'((((background dark)) (:foreground "Blue"))
(t (:foreground "Yellow")))
"*Face used for compressed file suffixes in Dired buffers.
This means the `.' plus the file extension. Example: `.zip'."
:group 'diredfl)
(defvar diredfl-compressed-file-suffix 'diredfl-compressed-file-suffix)
(defface diredfl-date-time
'((((background dark)) (:foreground "#74749A9AF7F7")) ; ~ med blue
(t (:foreground "DarkGoldenrod4")))
"*Face used for date and time in Dired buffers."
:group 'diredfl)
(defvar diredfl-date-time 'diredfl-date-time)
(defface diredfl-deletion
'((t (:foreground "Yellow" :background "Red")))
"*Face used for deletion flags (D) in Dired buffers."
:group 'diredfl)
(defvar diredfl-deletion 'diredfl-deletion)
(defface diredfl-deletion-file-name
'((t (:foreground "Red")))
"*Face used for names of deleted files in Dired buffers."
:group 'diredfl)
(defvar diredfl-deletion-file-name 'diredfl-deletion-file-name)
(defface diredfl-dir-heading
'((((background dark)) (:foreground "Yellow" :background "#00003F3F3434")) ; ~ dark green
(t (:foreground "Blue" :background "Pink")))
"*Face used for directory headings in Dired buffers."
:group 'diredfl)
(defvar diredfl-dir-heading 'diredfl-dir-heading)
(defface diredfl-dir-name
'((((background dark))
(:foreground "#7474FFFFFFFF" :background "#2C2C2C2C2C2C")) ; ~ cyan, dark gray
(t (:foreground "DarkRed" :background "LightGray")))
"*Face used for directory names."
:group 'diredfl)
(defvar diredfl-dir-name 'diredfl-dir-name)
(defface diredfl-dir-priv
'((((background dark))
(:foreground "#7474FFFFFFFF" :background "#2C2C2C2C2C2C")) ; ~ cyan, dark gray
(t (:foreground "DarkRed" :background "LightGray")))
"*Face used for directory privilege indicator (d) in Dired buffers."
:group 'diredfl)
(defvar diredfl-dir-priv 'diredfl-dir-priv)
(defface diredfl-exec-priv
'((((background dark)) (:background "#4F4F3B3B2121")) ; ~ dark brown
(t (:background "LightSteelBlue")))
"*Face used for execute privilege indicator (x) in Dired buffers."
:group 'diredfl)
(defvar diredfl-exec-priv 'diredfl-exec-priv)
;; For this to show up, you need `F' among the options in `dired-listing-switches'.
;; For example, I use "-alF" for `dired-listing-switches'.
(defface diredfl-executable-tag
'((t (:foreground "Red")))
"*Face used for executable tag (*) on file names in Dired buffers."
:group 'diredfl)
(defvar diredfl-executable-tag 'diredfl-executable-tag)
(defface diredfl-file-name
'((((background dark)) (:foreground "Yellow"))
(t (:foreground "Blue")))
"*Face used for file names (without suffixes) in Dired buffers.
This means the base name. It does not include the `.'."
:group 'diredfl)
(defvar diredfl-file-name 'diredfl-file-name)
(defface diredfl-file-suffix
'((((background dark)) (:foreground "#7474FFFF7474")) ; ~ light green
(t (:foreground "DarkMagenta")))
"*Face used for file suffixes in Dired buffers.
This means the `.' plus the file extension. Example: `.elc'."
:group 'diredfl)
(defvar diredfl-file-suffix 'diredfl-file-suffix)
(defface diredfl-flag-mark
'((((background dark)) (:foreground "Blue" :background "#7575D4D41D1D")) ; ~ olive green
(t (:foreground "Yellow" :background "Blueviolet")))
"*Face used for flags and marks (except D) in Dired buffers."
:group 'diredfl)
(defvar diredfl-flag-mark 'diredfl-flag-mark)
(defface diredfl-flag-mark-line
'((((background dark)) (:background "#787831311414")) ; ~ dark red brown
(t (:background "Skyblue")))
"*Face used for flagged and marked lines in Dired buffers."
:group 'diredfl)
(defvar diredfl-flag-mark-line 'diredfl-flag-mark-line)
(defface diredfl-ignored-file-name
'(;; (((background dark)) (:foreground "#FFFF921F921F")) ; ~ salmon
;; (((background dark)) (:foreground "#A71F5F645F64")) ; ~ dark salmon
(((background dark)) (:foreground "#C29D6F156F15")) ; ~ salmon
(t (:foreground "#00006DE06DE0"))) ; ~ dark cyan
"*Face used for ignored file names in Dired buffers."
:group 'diredfl)
(defvar diredfl-ignored-file-name 'diredfl-ignored-file-name)
(defface diredfl-link-priv
'((((background dark)) (:foreground "#00007373FFFF")) ; ~ blue
(t (:foreground "DarkOrange")))
"*Face used for link privilege indicator (l) in Dired buffers."
:group 'diredfl)
(defvar diredfl-link-priv 'diredfl-link-priv)
(defface diredfl-no-priv
'((((background dark)) (:background "#2C2C2C2C2C2C")) ; ~ dark gray
(t (:background "LightGray")))
"*Face used for no privilege indicator (-) in Dired buffers."
:group 'diredfl)
(defvar diredfl-no-priv 'diredfl-no-priv)
(defface diredfl-number
'((((background dark)) (:foreground "#FFFFFFFF7474")) ; ~ light yellow
(t (:foreground "DarkBlue")))
"*Face used for numerical fields in Dired buffers.
In particular, inode number, number of hard links, and file size."
:group 'diredfl)
(defvar diredfl-number 'diredfl-number)
(defface diredfl-other-priv
'((((background dark)) (:background "#111117175555")) ; ~ dark blue
(t (:background "PaleGoldenrod")))
"*Face used for l,s,S,t,T privilege indicators in Dired buffers."
:group 'diredfl)
(defvar diredfl-other-priv 'diredfl-other-priv)
(defface diredfl-rare-priv
'((((background dark)) (:foreground "Green" :background "#FFFF00008080")) ; ~ hot pink
(t (:foreground "Magenta" :background "SpringGreen")))
"*Face used for rare privilege indicators (b,c,s,m,p,S) in Dired buffers."
:group 'diredfl)
(defvar diredfl-rare-priv 'diredfl-rare-priv)
(defface diredfl-read-priv
'((((background dark)) (:background "#999932325555")) ; ~ burgundy / dark magenta
(t (:background "MediumAquamarine")))
"*Face used for read privilege indicator (w) in Dired buffers."
:group 'diredfl)
(defvar diredfl-read-priv 'diredfl-read-priv)
(defface diredfl-symlink
'((((background dark)) (:foreground "#00007373FFFF")) ; ~ blue
(t (:foreground "DarkOrange")))
"*Face used for symbolic links in Dired buffers."
:group 'diredfl)
(defvar diredfl-symlink 'diredfl-symlink)
(defface diredfl-tagged-autofile-name
'((((background dark)) (:background "#328C0411328C")) ; Very dark magenta
(t (:background "#CD73FBEECD73"))) ; Very pale green
"*Face used in Dired for names of files that are autofile bookmarks."
:group 'diredfl)
(defvar diredfl-tagged-autofile-name 'diredfl-tagged-autofile-name)
(defface diredfl-write-priv
'((((background dark)) (:background "#25258F8F2929")) ; ~ dark green
(t (:background "Orchid")))
"*Face used for write privilege indicator (w) in Dired buffers."
:group 'diredfl)
(defvar diredfl-write-priv 'diredfl-write-priv)
(defun diredfl-match-ignored-extensions (limit)
"A matcher of ignored filename extensions for use in `font-lock-keywords'.
LIMIT is the extent of the search."
(let ((ignored-extensions
(append (if (boundp 'dired-omit-extensions)
dired-omit-extensions
completion-ignored-extensions)
(when diredfl-ignore-compressed-flag
diredfl-compressed-extensions))))
(when ignored-extensions
(re-search-forward
(concat "^ \\(.*"
(regexp-opt ignored-extensions)
;; Optional executable flag
"[*]?\\)$")
limit
t))))
;;; Define second level of fontifying.
(defconst diredfl-font-lock-keywords-1
(list
'("^ \\(.+:\\)$" 1 diredfl-dir-heading) ; Directory headers
'("^ wildcard.*$" 0 'default) ; Override others, e.g. `l' for `diredfl-other-priv'.
'("^ (No match).*$" 0 'default) ; Override others, e.g. `t' for `diredfl-other-priv'.
'("[^ .]\\(\\.[^. /]+\\)$" 1 diredfl-file-suffix) ; Suffix, including `.'.
'("\\([^ ]+\\) -> .+$" 1 diredfl-symlink) ; Symbolic links
;; 1) Date/time and 2) filename w/o suffix.
;; This is a bear, and it is fragile - Emacs can change `dired-move-to-filename-regexp'.
`(,dired-move-to-filename-regexp
(7 diredfl-date-time t t) ; Date/time, locale (western or eastern)
(2 diredfl-date-time t t) ; Date/time, ISO
(,(concat "\\(.+\\)\\(" (concat (funcall #'regexp-opt diredfl-compressed-extensions)
"\\)[*]?$"))
nil nil (0 diredfl-compressed-file-name keep t))) ; Compressed-file suffix
`(,dired-move-to-filename-regexp
(7 diredfl-date-time t t) ; Date/time, locale (western or eastern)
(2 diredfl-date-time t t) ; Date/time, ISO
("\\(.+\\)$" nil nil (0 diredfl-file-name keep t))) ; Filename (not a compressed file)
;; Files to ignore
'(diredfl-match-ignored-extensions 1 diredfl-ignored-file-name t)
;; Compressed-file (suffix)
(list (concat "\\(" (concat (funcall #'regexp-opt diredfl-compressed-extensions) "\\)[*]?$"))
1 diredfl-compressed-file-suffix t)
'("\\([*]\\)$" 1 diredfl-executable-tag t) ; Executable (*)
;; Inode, hard-links, & file size (. and , are for the decimal point, depending on locale)
;; See comment for `directory-listing-before-filename-regexp' in `files.el' or `files+.el'.
'("\\_<\\(\\([0-9]+\\([.,][0-9]+\\)?\\)[BkKMGTPEZY]?[ /]?\\)" 1 'diredfl-number)
;; Directory names - exclude d:/..., Windows drive letter in a dir heading.
(list (concat dired-re-maybe-mark dired-re-inode-size "\\(d\\)[^:]")
'(1 diredfl-dir-priv t) '(".+" (dired-move-to-filename) nil (0 diredfl-dir-name t)))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]........\\(x\\)") ; o x
'(1 diredfl-exec-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]........\\([lsStT]\\)") ; o misc
'(1 diredfl-other-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].......\\(w\\).") ; o w
'(1 diredfl-write-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]......\\(r\\)..") ; o r
'(1 diredfl-read-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].....\\(x\\)...") ; g x
'(1 diredfl-exec-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].....\\([lsStT]\\)...") ; g misc
'(1 diredfl-other-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]....\\(w\\)....") ; g w
'(1 diredfl-write-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]...\\(r\\).....") ; g r
'(1 diredfl-read-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]..\\(x\\)...") ; u x
'(1 diredfl-exec-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]..\\([lsStT]\\)...") ; u misc
'(1 diredfl-other-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].\\(w\\)....") ; u w
'(1 diredfl-write-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]\\(r\\).....") ; u r
'(1 diredfl-read-priv))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]........\\([-rwxlsStT]\\)") ; o -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].......\\([-rwxlsStT]\\).") ; g -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]......\\([-rwxlsStT]\\)..") ; u -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].....\\([-rwxlsStT]\\)...") ; o -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]....\\([-rwxlsStT]\\)....") ; g -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]...\\([-rwxlsStT]\\).....") ; u -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]..\\([-rwxlsStT]\\)......") ; o -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl].\\([-rwxlsStT]\\).......") ; g -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "[-dl]\\([-rwxlsStT]\\)........") ; u -
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "\\(-\\)")
'(1 diredfl-no-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "\\([bcsmpS]\\)") ; (rare)
'(1 diredfl-rare-priv keep))
(list (concat dired-re-maybe-mark dired-re-inode-size "\\(l\\)[-rwxlsStT]") ; l
'(1 diredfl-rare-priv keep))
(list (concat "^\\([^\n " (char-to-string dired-del-marker) "].*$\\)")
'(1 diredfl-flag-mark-line prepend)) ; Flag/mark lines
(list (concat "^\\([^\n " (char-to-string dired-del-marker) "]\\)") ; Flags, marks (except D)
'(1 diredfl-flag-mark prepend))
(list (concat "^\\([" (char-to-string dired-del-marker) "].*$\\)") ; Deletion-flagged lines
'(1 diredfl-deletion-file-name prepend))
(list (concat "^\\([" (char-to-string dired-del-marker) "]\\)") ; Deletion flags (D)
'(1 diredfl-deletion prepend)))
"2nd level of Dired highlighting. See `font-lock-maximum-decoration'.")
;;;###autoload
(define-minor-mode diredfl-mode
"Enable additional font locking in `dired-mode'."
:global nil
(setq font-lock-defaults
(if diredfl-mode
'((dired-font-lock-keywords
dired-font-lock-keywords
diredfl-font-lock-keywords-1)
t nil nil beginning-of-line)
'(dired-font-lock-keywords t nil nil beginning-of-line)))
(font-lock-refresh-defaults))
;;;###autoload
(define-globalized-minor-mode diredfl-global-mode diredfl-mode
(lambda ()
(when (derived-mode-p 'dired-mode)
(diredfl-mode)))
:require 'diredfl)
(provide 'diredfl)
;;; diredfl.el ends here

View File

@ -114,24 +114,6 @@
(setq org-habit-following-days 7)
(setq org-agenda-span 1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; notdeft search
; (add-to-list 'load-path "~/.emacs.d.profiles/org/notdeft")
; (add-to-list 'load-path "~/.emacs.d.profiles/org/notdeft/extras")
; (require 'notdeft-autoloads)
; (require 'notdeft-org9)
; (setq notdeft-allow-org-property-drawers t)
; (add-hook 'org-mode-hook 'notdeft-note-mode)
; (setq notdeft-extension "org")
; (setq notdeft-secondary-extensions '("md" "txt"))
; (setq notdeft-directories '("~/org"
; "~/org/blog"
; "~/org/culinary"
; "~/org/health"
; "~/org/photography"
; "~/org/reading"
; ))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ui tuning
(diminish 'projectile-mode)

View File

@ -1,14 +0,0 @@
*~
\#*
.\#*
*.swp
.DS_Store
/xapian/notdeft-xapian
*.elc
notdeft-autoloads.el
local.mk
/README.html
.dir-locals.el
/download/
/PKGNAMEVER
.notdeft-db/

View File

@ -1,30 +0,0 @@
default : clean install
# Override this to add custom load paths to the required libraries, or perhaps to load the init file with "emacs --batch -l ~/.emacs".
EMACS_BATCH := emacs --batch
-include local.mk
install :
$(EMACS_BATCH) -L . -l notdeft-install -f notdeft-install-autoloads -f notdeft-install-bytecode
exe :
$(MAKE) -C xapian
clean :
-rm notdeft-autoloads.el *.elc extras/*.elc
PKGVER := $(shell date --utc +%Y%m%d.%H%M)
PKGNAMEVER := notdeft-$(PKGVER)
PKGTMPDIR := /tmp/$(PKGNAMEVER)
PKGMANIFEST := $(PKGTMPDIR)/notdeft-pkg.el
package :
mkdir -p download
-rm -r $(PKGTMPDIR)
mkdir -p $(PKGTMPDIR)
cp -ri ./ $(PKGTMPDIR)/
( cd $(PKGTMPDIR) && git clean -dxffq && rm -rf .git && rm .gitignore Makefile )
echo '(define-package "notdeft" "'$(PKGVER)'"' > $(PKGMANIFEST)
echo ' "Note manager and search engine")' >> $(PKGMANIFEST)
( tar --create --file download/$(PKGNAMEVER).tar -C /tmp $(PKGNAMEVER) )

View File

@ -1,54 +0,0 @@
#+TITLE: NotDeft
#+AUTHOR: Tero Hasu
#+OPTIONS: toc:nil
/NotDeft/ is a spin-off of [[https://jblevins.org/projects/deft/][Deft]], an “Emacs mode for quickly browsing, filtering, and editing directories of plain text notes.” NotDeft retains functionality similar to Deft's, albeit with less configurability. In addition, NotDeft features efficient, local, [[https://xapian.org/][Xapian]]-engine-based free-text search over potentially very large numbers of note files; in that respect it is like the [[https://notmuchmail.org/][Notmuch]] Emacs mode for managing email.
NotDeft is not as deft in note management as Deft. One reason for this is that NotDeft is designed to support managing of multiple directories of notes. Another complication is that locating the desired notes is a two-stage process, as it entails both searching for a set of notes by entering a query, and then further narrowing down the result set through interactive filtering.
[[file:images/notdeft-screenshot-query-and-filter.png]]
NotDeft does not aim for the user interaction simplicity of applications like Deft and Notational Velocity---instead, it intends to provide global note search and manipulation functionality, accessible from various Emacs buffers and Emacs-based applications.
* Quick Start
Open a terminal, and =cd= into your home directory. Download the source code into some directory with
: git clone https://github.com/hasu/notdeft.git
Then prepare NotDeft's Emacs Lisp files for use with the commands
: cd notdeft
: make
where =make= is assumed to invoke GNU Make.
Then build the Xapian backend by doing
: cd xapian
: make
If the =make= command fails, then you will need to ensure that you have the required libraries installed, and find the right C++ compiler incantation for building the =notdeft-xapian= program on your system. A notable library requirement for compiling the program is [[http://tclap.sourceforge.net/][TCLAP]].
To make NotDeft loadable in Emacs, add the following code to your Emacs startup file (e.g., “~/.emacs”):
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path "~/notdeft")
(add-to-list 'load-path "~/notdeft/extras")
(load "notdeft-example")
#+END_SRC
The [[./extras/notdeft-example.el][notdeft-example.el]] file is a sample configuration for NotDeft, which sets up NotDeft for use with Org mode based note files, also enabling some optional components of NotDeft, and setting up some keybindings. It may be a useful starting point for a personal configuration.
Create a “~/.deft” directory, and copy some “.org” files there.
Launch Emacs with
: emacs -f notdeft
Press =TAB= to enter a search query, and type characters to do further filtering of the results. Press =RET= to select a file to open. Use =C-c f1= to see other available commands.
To configure =notdeft-note-mode= minor mode for use automatically in note buffers, add the required directory local variable to “~/.deft” by entering the command =f6 a d l v=, and by saving the resulting file.
For other ways to install, configure, and use NotDeft, see the [[https://tero.hasu.is/notdeft/][documentation]].
* See Also
- https://tero.hasu.is/notdeft/ :: documentation
- https://tero.hasu.is/notdeft/download/ :: installable Emacs packages
- https://tero.hasu.is/tags/notdeft/ :: related blog posts
- https://github.com/hasu/notdeft :: source code repository
- [[./notdeft.el][“notdeft.el”]] :: some more documentation (in comments)
- [[https://xapian.org/][Xapian]] and [[https://jblevins.org/projects/deft/][Deft]] :: related software

View File

@ -1,112 +0,0 @@
;; Autoloads for NotDeft commands.
(require 'notdeft-autoloads)
;; Full path of "notdeft-xapian" executable.
(let ((x
(let ((default-directory
(file-name-directory
(file-truename (locate-library "notdeft")))))
(file-truename "xapian/notdeft-xapian"))))
(setq notdeft-xapian-program
(and (file-executable-p x) x)))
;; As an alternative to the above, you can try building and
;; configuring "notdeft-xapian" on demand, but on most systems this
;; will not succeed out of the box.
;(add-hook 'notdeft-load-hook 'notdeft-xapian-make-program-when-uncurrent)
(defun run-local-variables-mode-hooks ()
"Run hooks for `major-mode' with locals set.
Like `run-mode-hooks', but run later, with any buffer and
directory local variables set."
(run-hooks (intern (concat (symbol-name major-mode)
"-local-variables-hook"))))
(add-hook 'hack-local-variables-hook 'run-local-variables-mode-hooks)
;; A variable determining whether to enable minor mode.
(defcustom notdeft-note-mode-auto-enable nil
"Whether to enable NotDeft Note minor mode for a buffer."
:type 'boolean
:safe 'booleanp)
(make-variable-buffer-local 'notdeft-note-mode-auto-enable)
;; Define a hook for conditionally enabling the NotDeft minor mode.
(defun default-notdeft-hook ()
"Conditionally enable `notdeft-note-mode'.
Enable when the buffer local variable
`notdeft-note-mode-auto-enable' is set to a non-nil value."
(when notdeft-note-mode-auto-enable
(notdeft-note-mode 1)))
;; Have Org mode files respect the flag. A hook like this should be
;; set for all NotDeft note file types, and no others.
(add-hook 'org-mode-local-variables-hook 'default-notdeft-hook)
(defun my-notdeft-add-directory-local-variables ()
"Add `notdeft-note-mode-auto-enable' flag.
Add it for all `notdeft-directories'."
(interactive)
(require 'notdeft) ;; for `notdeft-directories'
(dolist (dir notdeft-directories)
(make-directory dir t)
(let ((default-directory dir))
(add-dir-local-variable nil 'notdeft-note-mode-auto-enable t))))
;; Org mode "deft:" and "notdeft:" link support.
(eval-after-load 'org
(lambda ()
(let ((ver (ignore-errors
(car (version-to-list org-version)))))
(require (if (and ver (< ver 9))
'notdeft-org8
'notdeft-org9)))))
;; Add global bindings for NotDeft. To do that, bind a custom keymap
;; that inherits from NotDeft's, one that we can use to override and
;; add to the predefined set of bindings.
(require 'notdeft-global)
(defvar my-notdeft-global-map
(let ((map (make-sparse-keymap)))
(define-key map [(a) (d) (l) (v)]
#'my-notdeft-add-directory-local-variables)
(define-key map [(l)] #'notdeft-org-link-existing-note) ;; l for link
(define-key map [(n)] #'notdeft-org-link-new-file) ;; n for new
(define-key map [(s)] #'org-store-link) ;; s for store
(define-key map [(S)] #'notdeft-org-store-deft-link) ;; s for store
(define-key map [(*)] #'notdeft-org-open-heading-as-query)
(set-keymap-parent map 'notdeft-global-map)
map)
"Custom keymap for accessing NotDeft functionality.
\\{my-notdeft-global-map}")
(fset 'my-notdeft-global-map my-notdeft-global-map)
(global-set-key [f6] 'my-notdeft-global-map)
;; Add Org-specific bindings that are also usable in a NotDeft buffer.
(add-hook 'notdeft-load-hook
(lambda ()
(define-key notdeft-mode-map (kbd "C-c S")
#'notdeft-org-store-deft-link)))
(require 'hydra nil t)
(when (featurep 'hydra)
;; Augment `notdeft-mode' bindings with a hydra.
(autoload 'notdeft-mode-hydra/body "notdeft-mode-hydra" nil t)
(add-hook 'notdeft-load-hook
(lambda ()
(define-key notdeft-mode-map (kbd "C-c h")
#'notdeft-mode-hydra/body)))
;; Augment the global NotDeft keymap with a hydra also.
(autoload 'notdeft-global-hydra/body "notdeft-global-hydra" nil t)
(define-key my-notdeft-global-map [(h)] #'notdeft-global-hydra/body))
(require 'ivy nil t)
(when (featurep 'ivy)
;; Do minibuffer note selection by search and then Ivy choice list.
(require 'notdeft-ivy)
(add-to-list 'ivy-re-builders-alist
'(notdeft-ivy-completing-read . ivy--regex-ignore-order))
(setq notdeft-completing-read-function 'notdeft-ivy-completing-read)
(setq notdeft-select-note-file-by-search t)
(setq notdeft-select-note-file-all t))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,52 +0,0 @@
;;; notdeft-global.el --- Global NotDeft keymap -*- lexical-binding: t; -*-
;; Copyright (C) 2017 by the author.
;; All rights reserved.
;; Author: Tero Hasu <tero@hasu.is>
;; See "notdeft.el" for licensing information.
;;; Commentary:
;; A keymap of NotDeft commands usable from outside `notdeft-mode'. It is
;; bound both as a variable and a function, to the name
;; `notdeft-global-map'.
;;; Code:
(defvar notdeft-global-map
(let ((map (make-sparse-keymap)))
;; file management
(define-key map (kbd "C-n") #'notdeft-new-file)
(define-key map (kbd "C-m") #'notdeft-new-file-named)
(define-key map (kbd "C-x C-f") #'notdeft-find-file)
(define-key map (kbd "C-x C-w") #'notdeft-save-buffer)
(define-key map (kbd "C-d") #'notdeft-delete-file)
(define-key map (kbd "C-r") #'notdeft-rename-file)
(define-key map (kbd "C-v") #'notdeft-move-file)
(define-key map (kbd "C-x s") #'notdeft-move-into-subdir)
(define-key map (kbd "C-x e") #'notdeft-change-file-extension)
(define-key map (kbd "C-a") #'notdeft-archive-file)
(define-key map (kbd "C-i") #'notdeft-show-file-directory)
(define-key map (kbd "C-x d") #'notdeft-open-in-deft)
;; state
(define-key map (kbd "C-j") #'notdeft-chdir)
(define-key map (kbd "C-x g") #'notdeft-refresh)
(define-key map (kbd "C-x c") #'notdeft-gc)
(define-key map (kbd "C-x r") #'notdeft-reindex)
;; search
(define-key map (kbd "C-o") #'notdeft-open-query)
(define-key map (kbd "C-f") #'notdeft-query-select-find-file)
(define-key map (kbd "C-x o") #'notdeft-lucky-find-file)
;; movement
(define-key map (kbd "C-x b") #'notdeft-switch-to-note-buffer)
(define-key map (kbd "C-x B") #'notdeft-switch-to-buffer)
;; other
(define-key map (kbd "C-c") #'notdeft)
map)
"Global keymap for NotDeft.
\\{notdeft-global-map}")
(fset 'notdeft-global-map notdeft-global-map)
(provide 'notdeft-global)
;;; notdeft-global.el ends here

View File

@ -1,68 +0,0 @@
;;; notdeft-install.el --- NotDeft installer -*- lexical-binding: t; -*-
;; Copyright (C) 2020 by the authors.
;; All rights reserved.
;; Author: Tero Hasu <tero@hasu.is>
;; See "notdeft.el" for licensing information.
;;; Commentary:
;; Functionality for setting up NotDeft from its source distribution,
;; without using a package manager.
;;
;; Suggested use:
;; (require 'notdeft-install)
;; (notdeft-install)
;;; Code:
(require 'autoload)
(require 'bytecomp)
(declare-function notdeft-xapian-make-program "notdeft-xapian-make")
(defun notdeft-install-autoloads ()
"Generate NotDeft autoloads and load them."
(let ((home (file-name-directory
(locate-library "notdeft-install"))))
(let ((generated-autoload-file
(expand-file-name "notdeft-autoloads.el" home)))
(update-directory-autoloads home))
(load "notdeft-autoloads.el" nil nil t)))
(defun notdeft-install-bytecode (&optional force)
"Generate NotDeft Emacs Lisp \".elc\" files.
Optionally FORCE byte-compilation even when existing bytecode
files appear to be up-to-date."
(let ((dir (file-name-directory
(locate-library "notdeft-install"))))
(notdeft-install--byte-compile dir "./" t force)
(notdeft-install--byte-compile dir "extras/" nil force)))
(defun notdeft-install--byte-compile (dir subdir must force)
"Byte-compile NotDeft sources in DIR SUBDIR.
If so indicated, the directory MUST exist. Optionally FORCE the
compilation."
(let ((home (expand-file-name subdir dir)))
(when (or must (file-exists-p home))
(let ((files (directory-files home nil "^notdeft.*\\.el$")))
(dolist (file files)
(unless (member file '("notdeft-autoloads.el"
"notdeft-example.el"))
(let ((file (concat home file)))
(byte-recompile-file file force 0))))))))
;;;###autoload
(defun notdeft-install (&optional force)
"Generate NotDeft autoloads and binaries.
Optionally FORCE byte-compilation even when existing bytecode
files appear to be up-to-date."
(interactive "P")
(notdeft-install-autoloads)
(require 'notdeft-autoloads)
(notdeft-install-bytecode force)
(require 'notdeft-xapian-make)
(notdeft-xapian-make-program force))
(provide 'notdeft-install)
;;; notdeft-install.el ends here

View File

@ -1,227 +0,0 @@
;;; notdeft-org.el --- some support for Org format NotDeft notes -*- lexical-binding: t; -*-
;; Copyright (C) 2017-2020 by the author.
;; All rights reserved.
;; Author: Tero Hasu <tero@hasu.is>
;; See "notdeft.el" for licensing information.
;;; Commentary:
;; Some NotDeft-specific support for `org-mode'. For Org mode version
;; 8 and higher.
;;
;; This feature requires no specific setup, as the public commands and
;; functions of this feature are autoloadable. However, see also
;; `notdeft-org8' and `notdeft-org9', which are optional extensions to
;; this feature, and do require setting up for use.
;;; Code:
(require 'org)
(require 'notdeft)
;;;###autoload
(defun notdeft-org-open-deft-link (link)
"Visit the NotDeft note specified by LINK.
The argument is a non-directory filename, possibly followed by
search options (see the fourth argument of `org-open-file'). This
function defines the opening of Org \"deft:\" links."
(let ((name link) search)
(save-match-data
(when (string-match "::\\(.+\\)\\'" link)
(setq search (match-string 1 link)
name (substring link 0 (match-beginning 0)))))
(let ((path (notdeft-file-by-basename name)))
(if (not path)
(message "No NotDeft note %S" name)
(org-open-file path t nil search)))))
(defun notdeft-org-read-deft-link-name ()
"Query for a \"deft:\" link name.
Do so interactively. Return the name component of a link, without
the \"deft:\" prefix."
(let ((name-lst (notdeft-make-basename-list)))
;; `ido` has been a part of Emacs since version 22
(when name-lst
(ido-completing-read "NotDeft note: " name-lst))))
;;;###autoload
(defun notdeft-org-complete-deft-link (&optional prefix)
"Define completion for Org \"deft:\" links.
The optional PREFIX argument is ignored."
(ignore prefix)
(let* ((file (notdeft-select-note-file))
(name (when file
(file-name-nondirectory file))))
(concat "deft:" (or name ""))))
(defvar notdeft-describe-link 'notdeft-title-from-file-content
"Function to determine NotDeft note file link description.
The function is given the file name as its sole argument.
Used by `notdeft-select-make-org-link'.")
(defun notdeft-org-read-link-description (&optional desc)
"Read a link description, interactively.
If DESC is provided, it is used as the initial input. Returns a
string, or nil if no non-whitespace description was provided."
(notdeft-chomp-nullify
(read-string "Description: " desc nil nil t)))
(defun notdeft-make-deft-link (name &optional desc)
"Turn NAME and DESC into a \"deft:\" link.
NAME should be a non-directory file name with extension."
(org-make-link-string (concat "deft:" name) desc))
;;;###autoload
(defun notdeft-org-store-deft-link ()
"Store a \"deft:\" link for the current note.
Like `org-store-link', store the link into `org-stored-links'."
(interactive)
(let ((old-file (notdeft-current-filename t t)))
(when old-file
(let* ((name (file-name-nondirectory old-file))
(link (concat "deft:" name))
(desc (notdeft-title-from-file-content old-file)))
(push (list link desc) org-stored-links)
(message "Stored: %s" (or desc link))))))
;;;###autoload
(defun notdeft-org-link-existing-note (notename &optional desc region)
"Create a \"deft:\" link to an existing note.
Link to a note by NOTENAME, inserting a link description if DESC
is non-nil. Insert the created link at point, unless REGION in
specified \(as a list of two positions), in which case replace
that region. When called interactively: offer a list of notes
from which to choose the link target; query for a note
description, offering to use the text of any active region as the
title, or the result of calling `notdeft-describe-link'
otherwise; use any active region as REGION; if one
\\[universal-argument] is given, then insert a link without DESC;
and if two \\[universal-argument]s are given, use the title of
any note as the description. If multiple notes have the same
NOTENAME, pick any one of them for deriving a description."
(interactive
(progn
(barf-if-buffer-read-only)
(let* ((pfx (prefix-numeric-value current-prefix-arg))
(region (when mark-active
(list (region-beginning) (region-end))))
(desc (and region (= pfx 1)
(apply #'buffer-substring-no-properties region)))
(file
;; Select note before prompting for any description.
;; Provide any region text as a selection hint.
(let ((notdeft-select-note-file-query desc)
(notdeft-xapian-order-by-time nil))
(notdeft-select-note-file)))
(desc
(when (and file (/= pfx 4))
(notdeft-org-read-link-description
(or desc
(pcase pfx
(1 (notdeft-chomp-nullify
(funcall notdeft-describe-link file)))
(16 (notdeft-title-from-file-content file)))))))
(notename (when file
(file-name-nondirectory file))))
(list notename desc region))))
(when notename
(when region
(apply #'delete-region region))
(insert (notdeft-make-deft-link notename desc))))
(defalias 'notdeft-insert-org-link
#'notdeft-org-link-existing-note
"Deprecated. Use `notdeft-org-link-existing-note'.")
;;;###autoload
(defun notdeft-org-link-new-file (&optional dir notename ext data desc region)
"Create a \"deft:\" link to a new note.
Return the filename of the created file. The arguments DIR,
NOTENAME, EXT, and DATA are as for `notdeft-create-file'. Use
DESC, if any, as the link description. Insert an Org \"deft:\"
link to the newly created note at point, except if REGION is
non-nil, in which case replace that buffer region \(specified as
a list of two position values) with the link. When called
interactively: query for a note title, offering to use the text
of any active region as the title; use any active region as
REGION; derive a NOTENAME based on the title, as usual; use the
default filename extension as EXT; if one \\[universal-argument]
is given, then insert a link without DESC; if two
\\[universal-argument]s are given, the query for a target DIR for
the new note."
(interactive
(progn
(barf-if-buffer-read-only)
(let* ((pfx (prefix-numeric-value current-prefix-arg))
(region (when mark-active
(list (region-beginning) (region-end))))
(title
(notdeft-chomp-nullify
(read-string "Title: "
(when region
(notdeft-chomp
(apply #'buffer-substring-no-properties region)))
nil nil t)))
(desc (unless (= pfx 4)
(notdeft-org-read-link-description title))))
(list (and (= pfx 16) 'dir) ;; dir
(and title `(title, title)) ;; notename
nil ;; ext
title ;; data
desc
region))))
(let* ((buf (current-buffer))
(name (file-name-nondirectory
(notdeft-create-file dir notename ext data))))
(switch-to-buffer buf)
(when region
(apply #'delete-region region))
(insert (notdeft-make-deft-link name desc))))
(defalias 'notdeft-link-new-file
#'notdeft-org-link-new-file
"Deprecated. Use `notdeft-org-link-new-file'.")
(eval-when-compile
(defvar notdeft-xapian-query))
;;;###autoload
(defun notdeft-org-open-notdeft-link (query)
"Open the NotDeft search specified by QUERY.
This defines the opening of Org \"notdeft:\" links."
(notdeft-open-query query))
;;;###autoload
(defun notdeft-org-store-notdeft-link ()
"Store the current NotDeft search as an Org link.
Use `org-store-link' to invoke this function in a `notdeft-mode'
buffer. Return nil if not in `notdeft-mode', or if there is no
current query."
(when (and (eq major-mode 'notdeft-mode)
notdeft-xapian-query)
(org-store-link-props
:type "notdeft"
:link (concat "notdeft:" notdeft-xapian-query))))
;;;###autoload
(defun notdeft-org-open-heading-as-query (&optional rank negate)
"Query for current Org heading text.
The RANK and NEGATE arguments are as for `notdeft-open-query'.
When called interactively, any prefix arguments are also
interpreted in the `notdeft-open-query' sense."
(interactive
(let ((prefix current-prefix-arg))
(list (equal prefix 1)
(equal prefix '(4)))))
(let ((title
(save-excursion
(org-back-to-heading t)
(nth 4 (org-heading-components)))))
(when title
(let ((title (notdeft-chomp title)))
(unless (string-equal title "")
(notdeft-open-phrase-as-query title rank negate))))))
(provide 'notdeft-org)
;;; notdeft-org.el ends here

View File

@ -1,36 +0,0 @@
;;; notdeft-org8.el --- Org link support for NotDeft notes -*- lexical-binding: t; -*-
;; Copyright (C) 2019 by the author.
;; All rights reserved.
;; Author: Tero Hasu <tero@hasu.is>
;; See "notdeft.el" for licensing information.
;;; Commentary:
;; Support for "deft:" and "notdeft:" links for `org-mode' version 8.
;; The `org-add-link-type' API is obsolete since Org version 9.
;;
;; Suggested use:
;; (eval-after-load 'org (lambda () (require 'notdeft-org8)))
;;; Code:
(require 'org)
(org-add-link-type
"deft"
#'notdeft-org-open-deft-link) ;; follow
(defun org-deft-complete-link ()
"Complete a \"deft:\" link.
Just call `notdeft-org-complete-deft-link'. Defined for
`org-link-try-special-completion', which expects a specific name
for the link-type-specific completion function."
(notdeft-org-complete-deft-link))
(org-add-link-type
"notdeft"
#'notdeft-org-open-notdeft-link) ;; follow
(provide 'notdeft-org8)
;;; notdeft-org8.el ends here

View File

@ -1,32 +0,0 @@
;;; notdeft-org9.el --- Org link support for NotDeft notes -*- lexical-binding: t; -*-
;; Copyright (C) 2017 by the author.
;; All rights reserved.
;; Author: Tero Hasu <tero@hasu.is>
;; See "notdeft.el" for licensing information.
;;; Commentary:
;; Support for "deft:" and "notdeft:" links for `org-mode' version 9.
;; The `org-link-set-parameters' API is available since Org version 9,
;; in the `org' feature.
;;
;; Suggested use:
;; (eval-after-load 'org (lambda () (require 'notdeft-org9)))
;;; Code:
(require 'org)
(org-link-set-parameters
"deft"
:follow #'notdeft-org-open-deft-link
:complete #'notdeft-org-complete-deft-link)
(org-link-set-parameters
"notdeft"
:follow #'notdeft-org-open-notdeft-link
:store #'notdeft-org-store-notdeft-link)
(provide 'notdeft-org9)
;;; notdeft-org9.el ends here

View File

@ -1,79 +0,0 @@
;;; notdeft-path.el --- NotDeft directory dynamic resolution -*- lexical-binding: t; -*-
;; Copyright (C) 2018 by the author.
;; All rights reserved.
;; Author: Tero Hasu <tero@hasu.is>
;; See "notdeft.el" for licensing information.
;;; Commentary:
;; A system for resolving `notdeft-directories' dynamically, based on
;; a configurable `notdeft-path' specification. Might be useful when
;; storing some NotDeft directories on removable filesystems, allowing
;; the command `notdeft-refresh' to be used to update the available
;; `notdeft-directories' list. The function
;; `notdeft-refresh-directories' should be called where necessary to
;; ensure that the list is kept up to date.
;;
;; Suggested use:
;; (require 'notdeft-path)
;; (notdeft-refresh-directories)
;; (add-hook 'notdeft-pre-refresh-hook 'notdeft-refresh-directories)
;;; Code:
(require 'cl-lib)
(defcustom notdeft-path '("~/.deft/")
"NotDeft directory search path.
A list of strings, or a function returning a list of strings. The
strings should name directories, which may or may not exist."
:type '(choice
(repeat (string :tag "Directory"))
(function :tag "Function"))
:safe (lambda (lst) (cl-every #'stringp lst))
:group 'notdeft)
(defvar notdeft-directories-changed-hook nil
"Hook run after each refresh of `notdeft-directories'.
It is called by `notdeft-refresh-directories'.")
(defun notdeft-existing-directories (dirs)
"Return a list of existing directories DIRS."
(mapcar #'file-name-as-directory
(cl-remove-if-not #'file-directory-p dirs)))
(defun notdeft-resolve-directories ()
"Resolve directories from `notdeft-path'.
Return the result as a list of strings that are syntactically
directory names, and name existing directories."
(let ((lst (if (functionp notdeft-path)
(funcall notdeft-path)
notdeft-path)))
(unless (listp lst)
(error "Expected a list: %S" lst))
(dolist (elem lst)
(unless (stringp elem)
(error "Expected a string: %S" elem)))
(notdeft-existing-directories lst)))
(eval-when-compile
(defvar notdeft-directories)
(defvar notdeft-directory))
(defun notdeft-refresh-directories ()
"Update `notdeft-directories' based on `notdeft-path'.
Only include existing directories. Also clear `notdeft-directory'
if it is no longer one of the `notdeft-directories'."
(setq notdeft-directories (notdeft-resolve-directories))
(when (and (boundp 'notdeft-directory) notdeft-directory)
(unless (and (file-directory-p notdeft-directory)
(cl-some (lambda (dir)
(file-equal-p notdeft-directory dir))
notdeft-directories))
(setq notdeft-directory nil)))
(run-hooks 'notdeft-directories-changed-hook)
notdeft-directories)
(provide 'notdeft-path)
;;; notdeft-path.el ends here

View File

@ -1,150 +0,0 @@
;;; notdeft-xapian-make.el --- Xapian backend auto-installer -*- lexical-binding: t; -*-
;; Copyright (C) 2020 by the authors.
;; All rights reserved.
;; Author: MaxSt <max@stoerchle.at>
;; Author: Tero Hasu <tero@hasu.is>
;; See "notdeft.el" for licensing information.
;;; Commentary:
;; Functionality for compiling the C++ code for the NotDeft Xapian
;; backend, automatically or otherwise.
;;
;; Instead of setting the `notdeft-xapian-program' variable yourself,
;; you may instead load this `notdeft-xapian-make' feature to have the
;; program built and configured automatically. You may additionally
;; need to set the `notdeft-xapian-program-compile-command-format' to
;; something that produces a suitable compiler invocation for your
;; platform.
;;
;; Suggested use:
;; (add-hook 'notdeft-load-hook 'notdeft-xapian-make-program-when-uncurrent)
;;; Code:
(defcustom notdeft-xapian-program-compile-command-format
"c++ -o %s %s -std=c++11 -Wall `pkg-config --cflags --libs tclap` `xapian-config --cxxflags --libs`"
"Compilation shell command.
Can be a `format' string with two \"%s\" directives, or a
function of two arguments, with the first directive or argument
specifying the executable path for the program, and the second
specifying the C++ source file for the notdeft-xapian program. If
it is a function, it should do any necessary shell escaping of
the arguments."
:type '(choice
(string :tag "Format string")
(function :tag "Function"))
:group 'notdeft)
(defcustom notdeft-xapian-program-install-path
"notdeft-xapian"
"Path for the notdeft-xapian executable to build.
If the path is not absolute, it is considered relative to
`notdeft-xapian-home'."
:type 'string
:safe #'stringp
:group 'notdeft)
(defvar notdeft-xapian-home
(expand-file-name "xapian/"
(file-name-directory
(file-truename (locate-library "notdeft"))))
"Directory path for notdeft-xapian sources.
Must specify an absolute path.")
(defvar notdeft-xapian-compile-buffer-name "*Compile notdeft-xapian*"
"Name of the buffer used for compiling notdeft-xapian.")
(defun notdeft-xapian-program-current-p (&optional program)
"Whether the notdeft-xapian PROGRAM is current.
It is uncurrent if it does not exist as an executable, or if its
source file is newer. PROGRAM defaults to
`notdeft-xapian-program-install-path'."
(let ((exe-file (expand-file-name
(or program notdeft-xapian-program-install-path)
notdeft-xapian-home)))
(when (file-executable-p exe-file)
(let ((cxx-file (expand-file-name
"notdeft-xapian.cc"
notdeft-xapian-home)))
(when (file-exists-p cxx-file)
(not (time-less-p
(nth 5 (file-attributes exe-file))
(nth 5 (file-attributes cxx-file)))))))))
;;;###autoload
(defun notdeft-xapian-compile-program (&optional program)
"Compile the notdeft-xapian program.
Use notdeft-xapian sources in `notdeft-xapian-home', and build
the PROGRAM, which defaults to `notdeft-xapian-program-install-path'.
On success, return the path of the built executable."
(interactive)
(unless (file-directory-p notdeft-xapian-home)
(error "Cannot locate notdeft-xapian sources"))
(let* ((exe-file (expand-file-name
(or program notdeft-xapian-program-install-path)
notdeft-xapian-home))
(cxx-file (expand-file-name
"notdeft-xapian.cc"
notdeft-xapian-home))
(compile-command
(if (functionp notdeft-xapian-program-compile-command-format)
(funcall notdeft-xapian-program-compile-command-format
exe-file cxx-file)
(format notdeft-xapian-program-compile-command-format
(shell-quote-argument exe-file)
(shell-quote-argument cxx-file))))
(buffer (get-buffer-create notdeft-xapian-compile-buffer-name)))
(pop-to-buffer notdeft-xapian-compile-buffer-name)
(let ((exit-code
(call-process
"sh" nil buffer t "-c" compile-command)))
(unless (zerop exit-code)
(error "Compilation of notdeft-xapian failed: %s (%d)"
compile-command exit-code))
(unless (file-executable-p exe-file)
(error (concat "Compilation of notdeft-xapian failed: "
"Executable %S not created")
exe-file))
(message "Compilation of notdeft-xapian succeeded: %S" exe-file)
exe-file)))
(defun notdeft-xapian-make-program (&optional force)
"Compile notdeft-xapian program.
Only do that if the source directory `notdeft-xapian-home'
exists, and the target path `notdeft-xapian-program-install-path'
is non-nil. In that case generate the executable with the target
path, but only if any existing executable appears to be
uncurrent, or if the FORCE flag is non-nil. Return the absolute
target path if it is known, even if the program could not be
compiled."
(when notdeft-xapian-program-install-path
(when (and notdeft-xapian-home
(file-directory-p notdeft-xapian-home))
(let ((exe-file (expand-file-name
notdeft-xapian-program-install-path
notdeft-xapian-home)))
(when (or force (not (notdeft-xapian-program-current-p exe-file)))
(notdeft-xapian-compile-program exe-file))
exe-file))))
(eval-when-compile
(defvar notdeft-xapian-program))
;;;###autoload
(defun notdeft-xapian-make-program-when-uncurrent ()
"Compile notdeft-xapian program when it is uncurrent.
Do that as for `notdeft-xapian-make-program', but fail silently
if compilation fails. Set `notdeft-xapian-program' to the
program's absolute path, or to nil if the program does not exist
even after any compilation attempt."
(setq notdeft-xapian-program
(let ((exe-file
(ignore-errors
(notdeft-xapian-make-program nil))))
(when (and exe-file (file-executable-p exe-file))
exe-file))))
(provide 'notdeft-xapian-make)
;;; notdeft-xapian-make.el ends here

View File

@ -1,196 +0,0 @@
;;; notdeft-xapian.el --- Xapian backend for NotDeft -*- lexical-binding: t; -*-
;; Copyright (C) 2017 by the author.
;; All rights reserved.
;; Author: Tero Hasu <tero@hasu.is>
;; See "notdeft.el" for licensing information.
;;; Commentary:
;; Xapian-specific functionality for NotDeft.
;;; Code:
(defcustom notdeft-xapian-program nil
"Xapian backend's executable program path.
Specified as an absolute path. When nil, the Xapian backend is
disabled, and filtering does not concern search results, but all
notes in `notdeft-directories'."
:type '(choice (const :tag "None" nil)
(file :tag "Path"))
:safe #'string-or-null-p
:group 'notdeft)
(defcustom notdeft-xapian-max-results 100
"Maximum number of Xapian query results.
\(I.e., '--max-count' for `notdeft-xapian-program'.)
No limit if 0."
:type 'integer
:safe #'integerp
:group 'notdeft)
(defcustom notdeft-xapian-language "en"
"Stemming language to use in Xapian indexing and searching.
See Xapian documentation for a list of supported language names
and abbreviations. May be specified as \"none\" for no stemming.
The language identifier may be followed by the string \":cjk\" to
enable generation of n-grams from CJK text. The CJK option is
ignored unless a recent enough version of Xapian is used."
:type 'string
:safe #'stringp
:group 'notdeft)
(defcustom notdeft-xapian-order-by-time t
"Whether to order file list by decreasing modification time.
Otherwise order by decreasing relevance, unless overridden by
a query modifier."
:type 'boolean
:safe #'booleanp
:group 'notdeft)
(defcustom notdeft-xapian-boolean-any-case t
"Whether to allow query operators in any case.
That is, whether the operator syntax also allows
lowercase characters (e.g., \"and\" and \"or\")."
:type 'boolean
:safe #'booleanp
:group 'notdeft)
(defcustom notdeft-xapian-pure-not t
"Whether to allow \"NOT\" in queries.
Using such queries is costly on performance."
:type 'boolean
:safe #'booleanp
:group 'notdeft)
(defface notdeft-xapian-query-face
'((t :inherit font-lock-string-face :bold t))
"Face for NotDeft Xapian queries."
:group 'notdeft-faces)
(defvar notdeft-xapian-query-history nil
"Xapian query string history.
Not cleared between invocations of `notdeft-mode'.")
(defun notdeft-xapian-read-query (&optional initial)
"Read a Xapian query string, interactively.
Use and update `notdeft-xapian-query-history' in querying.
Optionally fill in the specified INITIAL input. Return the read
string, or nil if no query is given."
(let* ((hist (if (not initial)
'notdeft-xapian-query-history
(setq notdeft-xapian-query-history
(cons initial
notdeft-xapian-query-history))
'(notdeft-xapian-query-history . 1)))
(s (read-from-minibuffer
"Query: " ;; PROMPT
initial nil nil ;; INITIAL-CONTENTS KEYMAP READ
hist ;; HIST
nil ;; DEFAULT-VALUE
t ;; INHERIT-INPUT-METHOD
)))
(when (and s (not (string= s "")))
s)))
(eval-when-compile
(defvar notdeft-extension)
(defvar notdeft-secondary-extensions)
(defvar notdeft-allow-org-property-drawers))
(defun notdeft-xapian-index-dirs (dirs &optional recreate)
"Create or update a Xapian index for DIRS.
Each element of DIRS must be either a directory path string, or a
list of the form (directory-path . relative-file-path-list). With
RECREATE, truncate any existing index files."
(with-temp-buffer
(dolist (dir dirs)
(if (stringp dir)
(insert ":idir\n" (file-relative-name dir "~") "\n")
(let ((dir (car dir))
(files (cdr dir)))
(insert ":ifiles\n" (file-relative-name dir "~") "\n")
(insert (format "%d\n" (length files)))
(dolist (file files)
(insert file "\n")))))
(let ((ret
(apply
#'call-process-region
(point-min) ;; START
(point-max) ;; END
notdeft-xapian-program ;; PROGRAM
t ;; DELETE (delete input)
t ;; BUFFER (output to current buffer)
nil ;; DISPLAY (do not refresh)
`("index"
"--chdir" ,(expand-file-name "." "~")
,@(when recreate '("--recreate"))
,@(apply #'append
(mapcar
(lambda (ext)
`("--extension" ,(concat "." ext)))
(cons notdeft-extension
notdeft-secondary-extensions)))
"--lang" ,(or notdeft-xapian-language "none")
,@(when notdeft-allow-org-property-drawers
'("--allow-org-property-drawers"))
"--input"))))
(when (/= 0 ret)
(error "Index generation failed: %s (%d): %s"
notdeft-xapian-program ret (buffer-string))))))
(defun notdeft-xapian-search (dirs &optional query)
"On the Xapian indexes in DIRS, perform the search QUERY.
I.e., perform the query in terms of the Xapian indexes in the
specified DIRS. Where a query is not specified, use a query that
matches any file, and in that case consider
`notdeft-xapian-order-by-time' to be true. Return at most
`notdeft-xapian-max-results' results, as pathnames of the
matching files. Sort by relevance, modification time, or
non-directory filename, all descending, based on the
`notdeft-xapian-order-by-time' setting and any query modifiers."
(let ((time-sort (if query notdeft-xapian-order-by-time t))
(max-results notdeft-xapian-max-results)
name-sort)
(when query
(save-match-data
(while (string-match "^ *!\\([[:alpha:]]+\\)\\>" query)
(let ((opt (match-string 1 query)))
(setq query (substring query (match-end 0)))
(pcase (downcase opt)
("time" (setq time-sort t))
("rank" (setq time-sort nil))
("all" (setq max-results 0))
("file" (setq name-sort t)))))))
(let* ((query (notdeft-chomp-nullify query))
(s (shell-command-to-string
(concat
(shell-quote-argument notdeft-xapian-program) " search"
(if name-sort " --name-sort" "")
(if time-sort " --time-sort" "")
" --lang " (shell-quote-argument
(or notdeft-xapian-language "none"))
(if notdeft-xapian-boolean-any-case
" --boolean-any-case" "")
(if notdeft-xapian-pure-not
" --pure-not" "")
(if (> max-results 0)
(format " --max-count %d" max-results)
"")
(if query
(concat " --query " (shell-quote-argument query))
"")
" " (mapconcat
(lambda (dir)
(shell-quote-argument
(expand-file-name dir "~")))
dirs " "))))
(files
(mapcar
(lambda (file)
(expand-file-name file "~"))
(split-string s "\n" t))))
files)))
(provide 'notdeft-xapian)
;;; notdeft-xapian.el ends here

File diff suppressed because it is too large Load Diff

View File

@ -1,792 +0,0 @@
# -*- mode:org; mode:notdeft-note -*-
#+TITLE: NotDeft (homepage)
#+KEYWORDS: website terohasu
#+VIEW_ACTION: export_hugo_page
#+CTIME: Wed, 23 Aug 2017 01:00:08 +0300
#+MTIME: Sun, 09 May 2021 23:13:34 +0200
#+PAGE_META: title = "NotDeft"
#+PAGE_META: url = "/notdeft/"
#+PAGE_META: aliases = ["/deft/"]
#+PAGE_META: tags = ["Emacs", "Lisp", "NotDeft", "Org", "software"]
#+OPTIONS: toc:nil
/NotDeft/ is an [[https://www.gnu.org/software/emacs/][Emacs]]-based manager and local search engine for directories of plain text notes. NotDeft features a [[https://xapian.org/][Xapian]] backend for efficient free-text search over potentially very large numbers of note files; in that respect it is like the [[https://notmuchmail.org/][Notmuch]] Emacs mode for managing email. NotDeft is a spin-off of the [[https://jblevins.org/projects/deft/][Deft]] note manager, and retains similar functionality for browsing, filtering, and managing note collections. While NotDeft inherits its user interface from Deft, that interface is used for managing search result sets of notes, rather than directory contents. When used together with [[https://orgmode.org/][Org mode]] and its support for document linking, NotDeft can also function as a “desktop wiki,” such that documents can also be found by following links, and not just by searching and filtering.
The NotDeft source code repository can be found at\\
https://github.com/hasu/notdeft
#+BEGIN_EXPORT html
<p class="text-align-center">
<img src="/notdeft/notdeft-query-filter.gif" />
</p>
#+END_EXPORT
#+TOC: headlines 2
* Quick Start
:PROPERTIES:
:CUSTOM_ID: quick-start
:END:
For the impatient, this section outlines one way of downloading, installing and setting up NotDeft.
Open a terminal, and =cd= into your home directory. Download the source code into some directory with
: git clone https://github.com/hasu/notdeft.git
Then prepare and byte-compile Emacs Lisp files with the commands
: cd notdeft
: make
where =make= is assumed to invoke GNU Make.
Then build the Xapian backend by doing
: cd xapian
: make
If the =make= command fails, then you will need to ensure that you have the required libraries installed, and find the right C++ compiler incantation for building the =notdeft-xapian= program on your system. A notable library requirement for compiling the program is [[http://tclap.sourceforge.net/][TCLAP]].
To make NotDeft loadable in Emacs, add the following code to your Emacs startup file (e.g., “~/.emacs”):
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path "~/notdeft")
(add-to-list 'load-path "~/notdeft/extras")
(load "notdeft-example")
#+END_SRC
The “notdeft-example.el” file is a sample configuration for NotDeft, which sets up NotDeft for use with Org mode based note files, also enabling some optional components of NotDeft, and setting up some keybindings. It may be a useful starting point for a personal configuration.
Create a “~/.deft” directory, and copy some “.org” files there.
Launch Emacs with
: emacs -f notdeft
*Press =TAB= to enter a search query*, and type characters to do further filtering of the results. Press =RET= to select a file to open. Use =C-c f1= to see other available commands.
To configure =notdeft-note-mode= minor mode for use automatically in note buffers, add the required directory local variable to “~/.deft” by entering the command =f6 a d l v=, and by saving the resulting file.
For other ways to install, configure, and use NotDeft, see the rest of this page.
* NotDeft's Deft Origins
NotDeft is derived from Deft version 0.3, but differs in several notable ways:
1. Rather than supporting a single, customizable =deft-directory= (tree) of note files, NotDeft supports a customizable =notdeft-directories= search path of directory trees.
- If the search path includes multiple directories, then many file creation and management operations involve choosing a directory, making NotDeft not so deft.
2. NotDeft supports (optional) invocation of a =notdeft-xapian-program=, which uses the Xapian library to implement free-text search across note files, with convenient query syntax. The search is performed across all =notdeft-directories=, and further narrowing down of the result set can then be done incrementally by typing in a search string for purposes of filtering (as in Deft).
- That is, when =notdeft-xapian-program= is set, the file browser of NotDeft lists Xapian search results instead of listing directory contents.
3. NotDeft includes multiple functions and commands intended to be usable from outside =notdeft-mode=, leading to more complex modalities of use than with Deft's list view centric operation.
4. NotDeft supports the existence of multiple =notdeft-mode= buffers within a single Emacs instance, so that one can view and operate on multiple search result sets of notes simultaneously.
NotDeft is not Deft---it aims for wide utility rather than the user experience of a Notational Velocity type application. The added complication of having two stages (query, then filter) to the process of looking for interesting files makes NotDeft less simple and intuitive than Deft, as does having multiple directories, modalities, and buffers.
* NotDeft Installation
NotDeft can be installed either manually, or as a package.
** Manual Installation from Source
First download the source code with the command
: git clone https://github.com/hasu/notdeft.git
Go to the “notdeft” directory, and prepare the Emacs Lisp code for use with the command
: make
Add the directory containing those files to the Emacs search path by adding
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path "/path/to/repo/of/notdeft")
#+END_SRC
to your Emacs startup file (e.g., “~/.emacs”). Also add
#+BEGIN_SRC emacs-lisp
(require 'notdeft-autoloads)
#+END_SRC
to the Emacs startup file, to make NotDeft available for on-demand loading.
With the above setup work done, NotDeft is available for launching from within Emacs with the command
: M-x notdeft
While the above commands acquire, build, and set up NotDeft's Emacs Lisp code, they do not build and configure the C++-based Xapian backend; see [[*Building the Xapian Backend][Building the Xapian Backend]] and [[*Configuring the Xapian Backend][Configuring the Xapian Backend]].
** Installation as a Package with straight.el
The [[https://github.com/raxod502/straight.el][straight.el]] package manager is able to install NotDeft as a package directly from its source repository. If you have that manager correctly installed, then you can install NotDeft with the command
#+BEGIN_SRC emacs-lisp
(straight-use-package
'(notdeft
:type git :host github :repo "hasu/notdeft"
:files ("*.el" "xapian")))
#+END_SRC
which should download NotDeft, generate its autoloads, and handle Emacs Lisp file byte-compilation.
While that command downloads and unpacks the Xapian backend source code, it does not build it or configure it; see [[*Building the Xapian Backend][Building the Xapian Backend]] and [[*Configuring the Xapian Backend][Configuring the Xapian Backend]].
** Installation from a Package File
Installing from Git is recommended where you wish to be sure that you are installing the most recent available version. Still, installation from a downloadable package file is also an option.
To install NotDeft as a package, first [[./download/][download]] the (chosen version's) package, and then install the downloaded TAR file with
: M-x package-install-file
You can check whether the package has been installed by evaluating
: (package-installed-p 'notdeft)
If so, information about the installation can be shown with
: (describe-package 'notdeft)
No documentation is shown by that command, but it does show the location of the package's files, allowing navigation to the documentation.
One might also implement a command for opening something in the package. For example, the readme file can be opened with
#+BEGIN_SRC emacs-lisp
(defun notdeft-open-readme ()
(interactive)
(find-file
(expand-file-name
"README.org"
(package-desc-dir
(cadr (assq 'notdeft package-alist))))))
#+END_SRC
While installing the package does unpack the Xapian backend source code, it does not build it or configure it; see [[*Building the Xapian Backend][Building the Xapian Backend]] and [[*Configuring the Xapian Backend][Configuring the Xapian Backend]].
** Building the Xapian Backend
To enable Xapian search queries, you should build the =notdeft-xapian= C++ program in the “xapian” directory. On some systems simply going into that directory and typing
: make
should do the trick, provided that the required tools and libraries have already been installed. Other systems may require more work not only on satisfying the dependencies, but also in finding the right C++ compiler incantation for building the program. (On some systems building it may not be feasible at all, and NotDeft's functionality will be more limited.)
Once a working compiler invocation command has been found, and the necessary C++ libraries have been installed, it is also possible to build the C++ program from within Emacs by using the included =notdeft-xapian-make= Emacs Lisp feature. To use it, set the variable =notdeft-xapian-program-compile-command-format= with the appropriate format string for the compilation command. In that format string the path of the executable comes first, and the path of the source file comes second. For example:
#+BEGIN_SRC emacs-lisp
(setq notdeft-xapian-program-compile-command-format "g++ -o %s %s -std=c++11 -Wall `pkg-config --cflags --libs tclap` `xapian-config --cxxflags --libs`")
#+END_SRC
With the feature appropriately configured you can then try issuing the command
: M-x notdeft-xapian-compile-program
which should display any build errors if the executable cannot be built.
*** Building the Backend Automatically
The =notdeft-xapian-make= Emacs Lisp feature also includes a mechanism for building the Xapian backend program whenever it is out of date with respect to its sources, or does not exist at all. This can be particularly useful if you =git pull= a new version of “notdeft-xapian.cc”, and do not wish to worry about manually rebuilding the latest version.
For example, we might try to build =notdeft-xapian= on NotDeft startup as necessary:
#+BEGIN_SRC emacs-lisp
(add-hook 'notdeft-load-hook 'notdeft-xapian-make-program-when-uncurrent)
#+END_SRC
The =notdeft-xapian-make-program-when-uncurrent= function automatically sets the =notdeft-xapian-program= to the path of a successfully built program, so that it no longer needs to be specified otherwise.
* NotDeft Configuration
Once the =notdeft= feature has been loaded, you can see and edit all of its configuration options and their documentation with
: (customize-group "notdeft")
That command is also callable interactively as
: M-x customize-group RET notdeft RET
The most essential settings are
- =notdeft-directories= :: to specify the location(s) of your notes
- =notdeft-xapian-program= :: to specify the path of the Xapian search tool
** Specifying Note File Locations
In a simple case you would have a single directory (tree) of note file, specified by the =notdeft-directories= configuration variable, which you can configure with the command
: M-x customize-variable RET notdeft-directories RET
For example:
: (setq notdeft-directories '("~/all-my-notes"))
You can have multiple directories, which makes NotDeft use a bit harder, as you may at times get asked for a target directory for some file operations.
: (setq notdeft-directories '("~/some-notes" "~/some-more"))
If your notes are not in a fixed directory, but you'd rather discover the directories programmatically, it may be convenient to set =notdeft-directories= in your startup file. For example:
: (setq notdeft-directories (cons "~/notes" (file-expand-wildcards "~/*/notes")))
*** Sparse Directories
If you wish to include some additional text files into your searches, you may also explicitly specify files that reside outside any of the =notdeft-directories=. You must still specify a directory for a search index covering those files. In effect, you specify a /sparse directory/, since it is not scanned, but rather only explicitly specified files are considered to be NotDeft notes, if they exist.
To specify the index directories and any files within them, use the =notdeft-sparse-directories= configuration variable to specify directories and their file lists. For example:
#+BEGIN_SRC emacs-lisp
(setq notdeft-sparse-directories
'(("~" .
("projects/magnolisp/web/magnolisp-homepage.org"
"projects/notdeft/web/notdeft-homepage.org"))))
#+END_SRC
where all note file paths are specified relative to the search index containing directory, which should be a parent directory of all the specified notes.
The usual note manipulation operations (renaming, deleting, etc.) are not available for notes in sparse directories, which are not managed by NotDeft as such. The facility exists merely to support cases where you have important note files spread around project-specific directories, ones that you want to make accessible from within NotDeft. If you have a standard naming convention for such files, you can certainly resolve the list value programmatically:
#+BEGIN_SRC emacs-lisp
(setq notdeft-sparse-directories
`(("~" . ,(mapcar
(lambda (file) (file-relative-name file "~"))
(file-expand-wildcards "~/projects/*/web/*-homepage.org")))))
#+END_SRC
** Choosing the Note File Format
The default is to have the note filename =notdeft-extension= set to "org" to indicate the Org format. If you prefer some other note format, you should change that setting, which can be done with
: M-x customize-variable RET notdeft-extension RET
The configured =notdeft-extension= is used by default when creating new notes, but a note collection can also use other extensions. There are none by default, but you can define such secondary extensions with
: M-x customize-variable RET notdeft-secondary-extensions RET
For example, one might set these as
: (setq notdeft-extension "txt")
: (setq notdeft-secondary-extensions '("md" "org" "scrbl"))
** Configuring the File Naming Convention
When creating a note file with the =notdeft-new-file-named= command, NotDeft automatically derives a name for the file based on the title that is provided for the note. The configuration option =notdeft-notename-function= determines how the name is derived.
The default setting is to use the =notdeft-default-title-to-notename= function to translate the title to a file basename. For example, the title “Rust (programming language)” translates into
: rust-programming-language
The default implementation is suitable for titles with ASCII letters, and you probably want to pick a different implementation if your titles do not tend to use the English alphabet.
Where necessary, the configuration option =notdeft-new-file-data-function= provides even more control over the naming (and content) of new notes. Such a function could, for example, add some metadata to every new note's file name and/or content.
** Configuring the Xapian Backend
To have NotDeft use the =notdeft-xapian= program you've built, you will have to specify its full path in the =notdeft-xapian-program= variable. You could use =M-x customize-variable= to set it, or simply
: (setq notdeft-xapian-program "/path/to/notdeft-xapian")
Set the variable to the program's full absolute path, without any shorthands, as no shell expansion is performed on the path name---you may explicitly expand it using Emacs' =expand-file-name= function instead.
If you installed as a package, and built the =notdeft-xapian= executable in that location, then the appropriate setting may be
#+BEGIN_SRC emacs-lisp
(setq notdeft-xapian-program
(expand-file-name
"xapian/notdeft-xapian"
(package-desc-dir
(cadr (assq 'notdeft package-alist)))))
#+END_SRC
Such code must appear after
: (package-initialize)
See the other =notdeft-xapian-*= customization variables for configuring the Xapian indexing and searching behavior. Most notably:
- The configuration variable =notdeft-xapian-max-results= controls the maximum number of files to list in a =notdeft-mode= buffer. You may set it to 0 to always have all results displayed.
- The default is to order the results so that most recently edited files are listed first, but you may change this behavior by setting =notdeft-xapian-order-by-time= to =nil=, in which case Xapian's ranking mechanism is used instead.
* NotDeft Mode
Running the =notdeft= command switches to a =*NotDeft*= buffer, creating one as necessary. Such a buffer's major mode is =notdeft-mode=. Buffers with that mode are read only, and cannot be edited directly, although most keys without modifiers do cause editing of the filter string.
Roughly, there are three kinds of things one can do in a =*NotDeft*= buffer:
1. set a query string to define a result set of notes
2. filter the result set by interactively editing a filter string
3. manipulate note files though NotDeft's commands
That is, finding an interesting set of notes is a two-step process: (1) enter a query to define a “topic area” of interest, using the /Xapian query syntax/; and then (2) narrow down that set interactively by typing in a /list of substrings/ (in any order) that should match. It is possible to edit the query without modifying the filter string, and vice versa.
#+CAPTION: Querying and filtering in a =*NotDeft*= buffer.
[[./notdeft-screenshot-query-and-filter.png]]
The NotDeft Mode interface is optimized for editing the filter string. You can append characters to the filter by pressing regular symbol keys without modifiers. Other available commands include =DEL=, =M-DEL=, =C-y=, with familiar Emacs style behavior.
To enter a query, press =TAB= (or =C-c C-o=) to open a prompt for typing in the query. The query is then executed when you press =RET=.
To clear a query, you can
1. press =TAB= and enter the empty string, or
2. press =S-TAB=, or
3. =C-u C-c C-c= also works for clearing the query in addition to any filter string.
To manage the notes that are listed in the NotDeft Mode buffer, you can use mode-specific command, which are bound to the mode's =C-c= keymap. There are commands for renaming, deleting, and moving notes, for example. Press =C-c f1= to see a full list of the commands bound to =C-c=.
To open a =*NotDeft*= buffer directly with a particular search query, use the command =notdeft-open-query= from any buffer.
** Displaying Individual Filter String Matches
The filter string “emacs org mode” narrows a =*NotDeft*= buffer file list down only to the files that contain all of the substrings “emacs”, “org”, and “mode”. To see each of the matching positions within those files, consider entering the command =C-c g= (or =M-x notdeft-grep-for-filter=) to display the matching strings with highlighting. That command invokes the shell command =grep= (through the Emacs command =grep=), and displays the results in a separate buffer. This may fail to work if you system does not have a compatible =grep= executable on the search path.
** Using Multiple NotDeft Mode Buffers
NotDeft allows multiple =notdeft-mode= buffers to exist at once, which may be useful if one wants to explore multiple sets of search results at once. Each NotDeft buffer has its own state, including a search query, filter string, default directory for creating new notes, etc.
Normally, executing the =notdeft= command only creates a new =*NotDeft*= buffer if one does not already exist---otherwise the command merely switches to an existing =*NotDeft*= buffer. It is possible to have the command always create a new =notdeft-mode= buffer by invoking it with a prefix argument, i.e., =C-u M-x notdeft=.
The =notdeft-open-query= command also accepts a prefix argument, to arrange for the search results to be listed in a new buffer. This behavior can also be made the default for that command by setting the configuration parameter =notdeft-open-query-in-new-buffer= to =t=. With that parameter set, the prefix argument's meaning is inverted, so that =C-u M-x notdeft-open-query= does /not/ create an additional buffer.
The question of whether to create a new buffer does not apply to other search commands. Within a NotDeft buffer, the commands =notdeft-query-edit= and =notdeft-query-clear= merely replace the buffer's search result set, whereas the commands =notdeft-lucky-find-file= and =notdeft-query-select-find-file= do not use a NotDeft buffer for displaying their results.
For dealing with existing =notdeft-mode= buffers, there is a =notdeft-switch-to-buffer= command for interactively selecting a buffer and switching to it. It presents a choice list of buffer names in the minibuffer, and shows any query and filter strings associated with those buffers for better informed selection.
As for closing NotDeft buffers, the =notdeft-quit= command is bound to =C-c C-q=, and it can be invoked in three ways:
1. Without a prefix argument, it buries the current buffer.
2. With one prefix argument, it kills the current buffer.
3. With two prefix arguments, it kills /all/ =notdeft-mode= buffers.
#+CAPTION: Four Emacs “windows” with different NotDeft buffers.
[[./multiple-buffers.png]]
** Displaying File Path Information
By default, NotDeft does not show any note directory or file names in its list view, but this behavior can be controlled by specifying a =notdeft-file-display-function=.
For example, we can display the name of each note's containing NotDeft (root) directory, with abbreviations for long directory names:
#+BEGIN_SRC emacs-lisp
(setq notdeft-file-display-function
(lambda (file w)
(when (> w 30)
(let* ((s (file-name-nondirectory
(directory-file-name
(notdeft-dir-of-file file))))
(s (pcase s
("bibliography-notes" "bib")
("homepage-notes" "hp")
(_ s)))
(s (if (> (string-width s) 12)
(truncate-string-to-width s 12)
s)))
(concat " " s)))))
#+END_SRC
We refrain from displaying any directory information in cases where the Emacs window is very narrow (as indicated by the =w= argument), as otherwise there will be little space left for the note titles.
#+CAPTION: NotDeft mode with directory indicators.
[[./directory-indicator.png]]
* NotDeft Note Mode
:PROPERTIES:
:CUSTOM_ID: notdeft-note-mode
:END:
Invoking the =notdeft= command opens an Emacs buffer whose major mode is =notdeft-mode=. That mode displays a list of notes, and if you want the list to be automatically updated when a note file gets saved, you may want to enable the =notdeft-note-mode= minor mode for those files' buffers.
The sole purpose of =notdeft-note-mode= is to take care of keeping NotDeft's knowledge of the note collection up to date. Whenever a note file is saved, =notdeft-note-mode= sees to it that the search index is updated with the new file contents. NotDeft does not itself do anything to enable that mode, but rather the user should arrange for that to happen in some suitable way (see below for some suggestions). The benefit of this approach is that even if a note file then is open using a regular Emacs command (e.g., =find-file=), the editing buffer will notify NotDeft of any changes.
** Enabling NotDeft Note Mode based on Major Mode
The simple approach is to always enable =notdeft-note-mode= for the major mode(s) that you use for editing notes. For example:
#+BEGIN_SRC emacs-lisp
(add-hook 'org-mode-hook 'notdeft-note-mode)
#+END_SRC
This approach should be safe in that changes to files not residing in =notdeft-directories= get ignored by NotDeft. Still, the approach has the disadvantage that the minor mode indicator “¬D” does not tell you whether a note is actually a NotDeft note.
** Enabling NotDeft Note Mode Locally to a Directory
Another solution is to try enabling =notdeft-note-mode= for every NotDeft /directory/ in terms of [[https://www.gnu.org/software/emacs/manual/html%255Fnode/emacs/Directory-Variables.html][per-directory local variables]]. For example, have your “.dir-locals.el” file state
#+BEGIN_SRC emacs-lisp
((org-mode . ((mode . org)
(mode . notdeft-note))))
#+END_SRC
This way of declaring both a major and minor =mode= appears to work at least in some versions of Emacs, although it may rely on undefined behavior.
** Enabling NotDeft Note Mode based on a Directory-Local Variable
If enabling =notdeft-note-mode= directly in “.dir-locals.el” does not work or appeal to you, then it's possible to do the same thing indirectly, by using an actual per-directory local variable to indicate if the minor mode should be enabled. That is, you can have the “.dir-locals.el” file contain
#+BEGIN_SRC emacs-lisp
((nil . ((notdeft-note-mode-auto-enable . t))))
#+END_SRC
The variable can be declared as
#+BEGIN_SRC emacs-lisp
(defcustom notdeft-note-mode-auto-enable nil
"Whether to enable NotDeft note mode for a buffer."
:type 'boolean
:safe 'booleanp)
(make-variable-buffer-local 'notdeft-note-mode-auto-enable)
#+END_SRC
To set that variable for a note directory, we can use the Emacs command
: M-x add-dir-local-variable RET nil RET notdeft-note-mode-auto-enable RET t RET
Or, if we want to programmatically set the variable for all our =notdeft-directories=, we can use the code
#+BEGIN_SRC emacs-lisp
(dolist (dir notdeft-directories)
(let ((default-directory dir))
(add-dir-local-variable nil 'notdeft-note-mode-auto-enable t)))
#+END_SRC
Defining and setting the variable alone does not enable the mode, which we want to do only for specific file types, reflecting our =notdeft-extension= and =notdeft-secondary-extensions= configuration. If we only supported =org-mode= files, we would like to say something like
#+BEGIN_SRC emacs-lisp
(add-hook
'org-mode-local-variables-hook
(lambda ()
(when notdeft-note-mode-auto-enable
(notdeft-note-mode 1))))
#+END_SRC
We cannot just use =org-mode-hook=, as directory locals are not yet set at the time when the mode is enabled. What is needed is a later hook, which in the above is called =org-mode-local-variables-hook=.
We also have to get such hooks to run. Borrowing code from “phils” at Stack Overflow, we can get our =org-mode-local-variables-hook= run by defining and registering a new kind of hook as
#+BEGIN_SRC emacs-lisp
(defun run-local-variables-mode-hooks ()
"Run hooks for `major-mode' with locals set.
Like `run-mode-hooks', but run later, with any buffer and
directory local variables set."
(run-hooks (intern (concat (symbol-name major-mode)
"-local-variables-hook"))))
(add-hook 'hack-local-variables-hook 'run-local-variables-mode-hooks)
#+END_SRC
The above solution gives us a “proper” way to enable the NotDeft note minor mode, and to do it only within directories that have a persistent NotDeft “signature” (in a “.dir-locals.el” file), and only for our chosen note-editing major modes.
* Using NotDeft from Non-NotDeft Modes
:PROPERTIES:
:CUSTOM_ID: outside-notdeft-commands
:END:
Several of NotDeft's commands are autoloadable, and may be invoked from outside a =*NotDeft*= buffer. For example, to quickly find relevant notes when in another buffer, you might use
: M-x notdeft-open-query
which then interactively asks for a search query for opening up in a NotDeft buffer. That command can of course be bound to a key.
A command similar to =notdeft-open-query= is
: M-x notdeft-lucky-find-file
which also asks for a search query, but then proceeds to open up the most highly ranked result file directly, without going via a =*NotDeft*= buffer. This command is similar to =find-file= in Emacs, but avoids having to specify the path of the file you're interested in; instead, this approach to “file finding” relies on sufficiently unique titling or tagging of the notes involved.
NotDeft commands that are usable from outside =notdeft-mode= might be bound to key combinations for convenient access. To facilitate this, NotDeft provides a =notdeft-global= feature, which exports a keymap for such commands. That keymap can be bound to a prefix key. For example:
#+BEGIN_SRC emacs-lisp
(require 'notdeft-global)
(global-set-key [f6] 'notdeft-global-map)
#+END_SRC
after which the command =[f6] o= should invoke the =notdeft-open-query= command in any mode that does not override the binding for F6 with something else.
** Access from NotDeft Note Buffers
Some of NotDeft's commands have specific support for use from within NotDeft note buffers. For example, the =notdeft-rename-file= command can be useful for renaming a note file that was perhaps created without a proper name (e.g., by using =C-c C-n=). Having written a note in a current buffer, issue the command
: M-x notdeft-rename-file
to enter a new basename for the file of that buffer. Any =C-u= prefix causes the default value to be derived from the title of the note, as extracted from the buffer contents. (The same command also works in a =*NotDeft*= buffer, affecting the currently selected file.)
** Programmatic NotDeft Access
You might also implement additional commands in terms of the globally accessible commands and Emacs Lisp functions, for example for quickly listing documents tagged in a certain way:
#+BEGIN_SRC emacs-lisp
(defun my-open-todo-notes ()
(interactive)
(notdeft-open-query "tag:todo"))
#+END_SRC
An intended use case for NotDeft is to support other applications that wish to locate files in terms of search queries instead of path names. For example, suppose we are using an =org-contacts= command to look for contacts by =name=, and that command expects the =org-contacts-files= list to be set. In that scenario we might set that variable for it based on a suitable NotDeft search query:
#+BEGIN_SRC emacs-lisp
(setq org-contacts-files
(notdeft-list-files-by-query
"!all ext:Org AND Email"))
(org-contacts name)
#+END_SRC
Similarly, we might use =org-agenda='s =org-todo-list= command to list to-do entries, but resolving the =org-agenda-files= list on demand by looking for the “TODO” and “DONE” keywords in any Org files in our collection:
#+BEGIN_SRC emacs-lisp
(setq org-agenda-files
(notdeft-list-files-by-query
"!all ext:Org AND (Todo OR Done)"))
(org-todo-list)
#+END_SRC
* NotDeft Note Syntax
NotDeft does not have much of a note syntax, although a subset of Org's syntax is supported in the form of [[https://orgmode.org/manual/In_002dbuffer-settings.html][in-buffer settings]]. The supported Org keywords are
- =#+TITLE=
- =#+FILETAGS=
A NotDeft-specific keyword is
- =#+KEYWORDS=
which is intended for tagging notes with keywords, in a way that does not set any tags for Org.
As for Org, the keyword names are case insensitive, so that one can write =#+title= instead of =#+TITLE=.
You can have in-buffer settings even if you do not use Org for your notes---the syntax for in-buffer settings is the same regardless of the markup language used in notes. Even in a plain “.txt” file, you can still specify =#+KEYWORDS=, for example.
** Example Notes
No special markup is necessarily required:
#+BEGIN_SRC org
this is a title
This is body text.
#+END_SRC
Comments can be included, and they are ignored when searching:
#+BEGIN_SRC org
# this is a comment
this is a title
This is body text.
#+END_SRC
Org mode's =#+TITLE= syntax is supported:
#+BEGIN_SRC org
# this is a comment
,#+TITLE: this is a title
# this is a comment
This is body text.
#+END_SRC
A note can be tagged, e.g., with the tags “some” and “tags”:
#+BEGIN_SRC org
,#+TITLE: this is a title
,#+KEYWORDS: some tags
This is body text.
#+END_SRC
Instead of the =#+KEYWORDS= syntax, we can use the Org standard =#+FILETAGS= syntax:
#+BEGIN_SRC org
,#+FILETAGS: :some:tags:
this is a title
This is body text.
#+END_SRC
Stemming is used also on tags, and so the query “tag:tag” will find these two notes (assuming English stemming---see =notdeft-xapian-language=).
Whitespace is considered as a separator for tags, as are the delimiters “:”, “;”, and “,”. This means that the keyword declaration
#+BEGIN_SRC org
,#+KEYWORDS: helsinki-vantaa places
#+END_SRC
is not matched by the search phrase “tag:"vantaa places"”. However, a hyphen still separates words, so that “tag:helsinki” and “tag:vantaa” and “tag:helsinki-vantaa” all match the first tag, which is semantically appropriate at least in this case.
* Search Query Syntax
The usual Xapian search [[https://xapian.org/docs/queryparser.html][query syntax]] is available for NotDeft queries, with some additional /query modifiers/ (see below). Operators such as =AND=, =OR=, and =XOR= are available, and they may also be written in lowercase (or mixed case) if =notdeft-xapian-boolean-any-case= is set to =t=. The =NOT= operator is also available if =notdeft-xapian-pure-not= is =t=. It is possible to query for a phrase by quoting the phrase (e.g., "Deft for Emacs"). To look for a search term without stemming, give it capitalized (e.g., "Abstract" will not match “abstraction”). Wildcards in search terms are not supported (trailing wildcards /are/ supported by Xapian, but not enabled in NotDeft).
** Prefixes
The following prefixes are supported by NotDeft:
- =file:= :: Indicates that the search term must appear in the (non-directory, non-extension) filename.
- =ext:= :: Indicates the string that must be the filename extension of the file (without the ".").
- =path:= :: Indicates that the search term must appear in the non-directory part of the file pathname, where the pathname is relative to the user's home directory.
- =title:= :: Indicates that the search term must appear in the title.
- Title is specified either as the first non-empty non-comment line, or as the file property (or Org mode “in-buffer setting”) =#+TITLE=. (Multiple =#+TITLE= lines are not supported.)
- =tag:= :: Indicates that the search term must appear among the tags given to the document.
- The tags for a note are specified either with the standard Org file property =#+FILETAGS=, or the custom file property =#+KEYWORDS=. (Org headline tags do not qualify.)
** Query Modifiers
The following custom query syntax is supported:
- =!time= :: Prefix a query with =!time= to have the results sorted by decreasing file modification time, even if the =notdeft-xapian-order-by-time= configuration option is disabled.
- =!rank= :: Prefix a query with =!rank= to have the results sorted by decreasing relevance, regardless of the =notdeft-xapian-order-by-time= setting.
- =!file= :: Prefix a query with =!file= to have results sorted by (non-directory) file name, alphabetically, in decreasing order. Overrides all of the other sorting settings and modifiers.
- =!all= :: Prefix a query with =!all= to show /all/ matching results. Note that unless you specify this modifier, the contents of a query result list may differ depending on how the results are sorted, since less highly ranked notes may get excluded.
A space character must be used to separate the above keywords from the rest of the query string.
The =!file= modifier might be useful for instance when you have file names such as “2017-01-01-0001.txt” and “2017-09-19-0123.txt”, and you would like to see them in chronological order by “creation time”, even if some of the files have been edited, and consequently have had their modification times changed.
** Example Search Queries
It is simple to find all notes containing both the words Emacs and Org:
: Emacs AND Org
If you have a lot of notes about Org mode, and few about other Emacs matters, it may be interesting to use
: Emacs AND NOT Org
which works if the =notdeft-xapian-pure-not= option is set.
While you're often likely to be more interested in recent (or best maintained) notes, sorting by relevance can be useful particularly when there are multiple search terms: you may be more interested in seeing notes that contain /all/ the terms instead of just /one/ of them. You may use “!rank” to enable relevance-based ranking for a specific query:
: !rank Emacs Org Deft
If, on the other hand, you use a single, common search term, and have a lot of documents, you may run into your =notdeft-xapian-max-results= limit, and miss out on some documents. In this case, you might use
: !all Emacs
to list /all/ documents mentioning Emacs.
If, unlike in the above case, you just want to see all documents that are about Emacs specifically, you may get more useful results with the query
: title:Emacs
to only find documents whose title indicates that they concern Emacs. Or, to be more thorough, you might want to make sure you also find notes with the word Emacs in the filename:
: title:Emacs OR file:Emacs
You can combine prefixes and “bracketed subexpressions”:
: title:(Ayn AND Rand)
which will match both “Ayn Rand” and “Rand, Ayn” in a title.
Phrase searches are allowed for tags, and
: tag:helsinki-vantaa
: tag:"helsinki vantaa"
: tag:(helsinki AND vantaa)
all match the tag “helsinki-vantaa”.
Filename extensions can be capitalized to avoid any stemming. For example, to find all “.org” documents that may contain open to-do entries, we might query with
: !all ext:Org AND TODO
* Command Popup Buffers
If it seems hard to remember the various NotDeft commands, one may wish to have a command selection dialog, similar to the one in [[https://magit.vc/][Magit]]. For implementing such “helpful key bindings,” one can use [[https://magit.vc/manual/magit-popup.html][Magit-Popup]] or [[https://github.com/abo-abo/hydra][Hydra]], for instance. As an example, the “extras” directory of NotDeft's source repository contains a predefined hydra for NotDeft's mode-agnostic commands, provided by the =notdeft-global-hydra= feature. To bind =[f6]= to the hydra (instead of the =notdeft-global-map= keymap directly), one can use the configuration code
#+BEGIN_SRC emacs-lisp
(autoload 'notdeft-global-hydra/body "notdeft-global-hydra" nil t)
(global-set-key [f6] 'notdeft-global-hydra/body)
#+END_SRC
There is also an optional hydra for =notdeft-mode=, which can be made available with code such as
#+BEGIN_SRC emacs-lisp
(autoload 'notdeft-mode-hydra/body "notdeft-mode-hydra")
(eval-after-load "notdeft"
'(define-key notdeft-mode-map (kbd "C-c h") 'notdeft-mode-hydra/body))
#+END_SRC
#+CAPTION: A NotDeft command “hydra” invoked from Org mode.
[[./global-hydra.png]]
* Org Mode Integration
NotDeft is somewhat specialized for managing notes in the Org format. If you do use Org mode for editing your notes, there are some Org-specific NotDeft commands available (for autoloading) in the =notdeft-org= feature.
Additionally, depending on your Org version, you may want to
: (require 'notdeft-org8)
or
: (require 'notdeft-org9)
in your Org startup code, to set up support for “deft:” and “notdeft:” links in =org-mode=. A “deft:” link names a note by its non-directory filename, whereas a “notdeft:” link contains a NotDeft Xapian search expression.
Org mode's =org-store-link= command may be used to capture any Xapian search in a NotDeft buffer, to be later inserted with =org-insert-link=. The =notdeft-org= feature also defines NotDeft-specific =notdeft-org-link-existing-note= and =notdeft-org-link-new-file= commands for inserting “deft:” links, either to an existing note or a new one.
The =notdeft-org= feature also defines a =notdeft-org-store-deft-link= command, which functions similarly to =org-store-link=, but stores a "deft:" link to the current note. In a NotDeft buffer, it stores a link to any selected note; and in a NotDeft note buffer, it stores a link to that buffer's note.
NotDeft allows a "deft:" link to also include a search option, which follows the filename, separated by =::=. Search options are specified in the same way as for "file:" links. For example:
: [[deft:notdeft-homepage.org::*Note Archival]]
: [[deft:notdeft-homepage.org::#capture-protocol]]
NotDeft has some optional support for the Org [[https://orgmode.org/manual/Property-Syntax.html][property syntax]], which can be enabled by setting the variable =notdeft-allow-org-property-drawers= to a non-nil value. Enabling that option makes it so that any top-level =PROPERTIES= [[https://orgmode.org/manual/Drawers.html][drawer]] appearing at the beginning of a note is treated as a comment. The title of the note
#+BEGIN_SRC org
:PROPERTIES:
:CUSTOM_ID: my-custom-id
:END:
Note title
Note body.
#+END_SRC
can be either ":PROPERTIES:" or "Note title", depending on whether NotDeft is configured to recognize =PROPERTIES= drawers.
** Using NotDeft and Org Mode as a Desktop Wiki Engine
It is “deft:” links in particular that allow NotDeft to be used as a desktop wiki, linking documents by topic, where a topic is named by the non-directory name of a note file. For “deft:” links to consistently resolve to the same note, you should name your note files uniquely.
For example, when following the link
: [[deft:notdeft.org]]
NotDeft will look for a “notdeft.org” file anywhere in the note collection, and open the first match.
A benefit of that “deft:” link semantics is that using the command
: M-x notdeft-move-file
to move a note file into a different directory does not cause any “deft:” link to break, whereas regular “file:” links may break.
To conveniently create a dedicated note for a given topic in an Org-mode buffer, and also link to that note at the same time, highlight the title (and link description) of that topic so that it becomes the active region, and then issue the command
: M-x notdeft-link-new-file
For example, if you've highlighted the text “desktop wikis”, the command will offer to create a note of the same title, derive a filename for it based on the title, and replace the region with a “deft:” link to it. (The command is defined by the =notdeft-org= feature.)
* Quick Note Capture
To quickly create a new note file from any buffer, you can use
: M-x notdeft-new-file
That command is also bound to =C-n= in =notdeft-global-map=, and if that keymap is bound to the prefix =[f6]=, for example, then you can create a new note with the key combination =[f6] C-n=.
Org mode has its own “capture” mechanism, and you can certainly configure capturing into a file that resides in a NotDeft directory. For example:
#+BEGIN_SRC emacs-lisp
(setq org-directory "~/notes") ;; default Org files location
(setq notdeft-directories (list org-directory)) ;; NotDeft search path
(setq org-default-notes-file (concat org-directory "/notes.org"))
(global-set-key [f7] 'org-capture)
#+END_SRC
which defines "~/notes" as the sole NotDeft directory, and has the key F7 initiate an =org-capture=, by default into the file "~/notes/notes.org". After completing capture, you can go back to the previously captured item with
: C-u C-u M-x org-capture
The capture facility supports the definition and use of =org-capture-templates= for different purposes.
A caveat with Org capturing is that unless you have already opened the capture file under NotDeft, any newly captured items may not immediately get noticed by NotDeft. To ensure that NotDeft is aware of any changes, one might arrange for the capture file to include file variables for enabling the =notdeft-note-mode= minor mode for any buffers opened for that file. Setting directory local variables are another option.
A more involved option is to write custom commands which enable the minor mode for the capture file, for example with
: (notdeft-register-file org-default-notes-file)
Note that different =org-capture-templates= may define different capture locations. Consequently, it may be appropriate for the templates themselves to embed code for performing the registration (e.g., as shown in the [[*=capture= from Firefox][=capture= from Firefox]] section).
* Adding Attachments to Notes
NotDeft has a simple mechanism to support “attaching” files to notes, one that is agnostic to the note file format. If you have a note file
: ~/notes/deft-for-emacs.txt
you can use the command =C-c S= to move the file into a subdirectory of the same name, so that the file's pathname becomes
: ~/notes/deft-for-emacs/deft-for-emacs.txt
Now you can copy/move/link any attachments for the note into that subdirectory, and it is convenient to move the note together with its attachments using a regular file manager.
To move a note from within =*NotDeft*=, the command =C-u C-c m= can be used to move it under another NotDeft root directory, where the prefix =C-u= assures NotDeft that the file really is to be moved together with its subdirectory.
When the attachments reside in the same directory as the note itself, in Org mode it is then easy to add a “file:” link to any attachment with the command =C-u C-c C-l=. For example, if the attachment directory contains a file named “2017-01-01-0001.JPG”, then a “file:” link to it would be simply
: [[file:2017-01-01-0001.JPG]]
and the command =C-c C-x C-v= can be used to toggle inline display of images.
Org itself has its own attachment management mechanism, whose action menu is bound to =C-c C-a=. This mechanism allows an attachment directory to be associated with an Org heading (as identified by information stored within the heading's properties), and thus the NotDeft note file itself can reside directly within a NotDeft root directory. Org has no command for moving an Org file together with its attachments, however.
To make the Org mechanism compatible with the NotDeft mechanism, one can store the attachments in the same (sub)directory as the note file itself, by specifying that directory with the =ATTACH_DIR= property. For example:
#+BEGIN_SRC org
,* Bergen, Norway :ATTACH:
:PROPERTIES:
:ATTACH_DIR: ./
:Attachments: 2017-01-01-0001.JPG 2017-09-19-0123.JPG
:END:
#+END_SRC
This way it is still convenient to move a note together with its attachments, and Org commands such as =C-c C-a o= (for opening the attachments) can still be used.
* Note Archival
To archive away a note so that its contents will no longer be included in a search, one can press =C-c C-a= from within =*NotDeft*=. This is a note format agnostic archival method, as the entire note file gets moved into a =notdeft-archive-directory=, with the default name of
: "_archive"
meaning that a note file whose original path is
: ~/notes/deft-for-emacs.txt
would get moved to
: ~/notes/_archive/deft-for-emacs.txt
Any directories whose names begin with an underscore will be excluded from Xapian searches, and thus such an archived note will no longer clutter search results.
In Org mode one can use Org's own [[http://orgmode.org/manual/Archiving.html][archival mechanism]] to archive just a part of a note document subtree, and the archival file will also be excluded from Xapian searches, provided that its filename extension is not =notdeft-extension= or one of the =notdeft-secondary-extensions=. Org's default extension is
: org_archive
which by default is not an extension recognized by NotDeft.
* Capturing Data from External Applications
The =org-protocol= feature of Org mode provides a way for external applications to interface with Emacs and Org, and that mechanism can also be adopted for capturing data into NotDeft. For example, data can be sent from Firefox to NotDeft using the predefined =store-link= and =capture= protocols.
The mechanism works by the external application invoking =emacsclient=, and for this to work you should have an Emacs server running in the Emacs instance you want to use to receive data into NotDeft. A server can be started by evaluating
: (server-start)
** =org-protocol= Content Type in Firefox
To configure Firefox to support the =org-protocol:= scheme, first open =about:config=, and add a =boolean= property
: network.protocol-handler.expose.org-protocol false
Then craft an HTML file such as
#+BEGIN_SRC html
<html>
<body>
<a href="org-protocol://store-link?url=URL&title=TITLE">link</a>
</body>
</html>
#+END_SRC
and open that file in Firefox, and click the link, after which a “Launch Application” dialog is presented. “Choose other Application”, tick the box “Remember my choice for org-protocol links”, and specify =emacsclient= as the executable.
That application selection can later be modified from Firefox “Preferences” / “Applications”. If required, the “Content Type” should be removable at least by editing the “mimeTypes.rdf” file in the Firefox profile.
** =store-link= from Firefox
There is nothing NotDeft specific about the =store-link= Org protocol, as it merely stores a link to the Emacs =kill-ring= for yanking. To configure Firefox to support the protocol, just add a suitable bookmarklet (e.g., to the “Bookmarks Toolbar”). The bookmark “Location” can be specified as
#+BEGIN_SRC javascript
javascript:location.href='org-protocol://store-link?url='+encodeURIComponent(document.location)+'&title='+encodeURIComponent(document.title);void(0);
#+END_SRC
By selecting that bookmark a link to the current page can be sent to Emacs. Its URL can then be inserted in Emacs with =C-y=. A full Org link in turn can be inserted with
: M-x org-insert-link
which is bound to =C-c C-l= in Org.
** =capture= from Firefox
:PROPERTIES:
:CUSTOM_ID: capture-protocol
:END:
The =capture= protocol, in turn, allows for web page content and metadata to be captured from Firefox into Emacs. Configuring the =capture= protocol for use with NotDeft is slightly more involved than in the case of =store-link=, as we must choose what page data to store, and where in our NotDeft note collection to store it.
Suppose we wish to store any currently selected text, along with the URL of the containing page, and a capture timestamp. Suppose also that we wish to store it into a file whose name is derived from the page title, so that if we capture multiple times from the same page, then all of the captured text snippets will end up in the same note file.
In that case the Firefox bookmarklet for sending over the required information can for example be
#+BEGIN_SRC javascript
javascript:location.href='org-protocol://capture?template=w&url='+encodeURIComponent(document.location)+'&title='+encodeURIComponent(document.title)+'&body='+encodeURIComponent(window.getSelection());void(0);
#+END_SRC
where we have given the name “w” for the Org capture template.
We must also define that template as one of our =org-capture-templates=, and the definition can be
#+BEGIN_SRC emacs-lisp
(require 'org-protocol)
(setq org-capture-templates
'(("w" "capture selection into NotDeft" plain
(file (lambda ()
(notdeft-switch-to-file-named
(plist-get org-store-link-plist :description))))
"%l\non %u\n\n%i"
:empty-lines-before 1)))
#+END_SRC
This definition assumes that the link =:description= is available from =org-store-link-plist=, and that it corresponds to the =document.title=; this may be undocumented functionality, but works in Org mode 9.1.1. The =notdeft-switch-to-file-named= derives a filename from the description, creates that file if it doesn't yet exist, and returns the complete =file= name.
* Troubleshooting
** When Search Queries Are Not Yielding Expected Results
Try doing the following in a =*NotDeft*= buffer:
1. Press =TAB= (or =M-x notdeft-query-edit=) to be prompted for a Xapian query.
2. If nothing happens when you press =TAB=, then you have probably not configured a value for =notdeft-xapian-program=. Assign a value to that variable.
3. Having pressed =TAB=, enter a query string at the prompt, one that should match some notes, and press =RET=.
4. If that reports "Found no notes", or an unexpected set of notes, then your search index may not be up-to-date, perhaps due to filesystem changes outside of NotDeft. Invoke the command =M-x notdeft-refresh= (i.e., =C-c C-x g=) to refresh the search index.
5. If you suspect that your search index may be corrupt or incompatible in some way, you may invoke the command =M-x notdeft-reindex= (i.e., =C-c C-x r=) to fully rebuild the search index, instead of just refreshing it.
6. If you see unexpected behavior after setting a search query, ensure that the =notdeft-xapian-program= variable names the complete and fully expanded path of a working executable. It may be worth trying to run the program directly, and seeing what it says. For example:
: /path/to/notdeft-xapian search -q 'Emacs OR Vi' ~/.deft
7. If your search query includes prefix terms such as “title:Emacs”, and you do not get all the expected matches, then make sure that any lines before any =#+TITLE= (or, =#+KEYWORDS=, etc.) are either whitespace only or begin with “#”. While the Org markup language allows in-buffer settings to appear anywhere in a file, NotDeft only scans the beginning of each file for such settings.
8. If all else fails, a tool such as =xapian-delve= may be used to inspect the contents of the search index to see which terms it actually contains.
* See Also
#+BEGIN_EXPORT html
{{< taggedpagelistexceptself "notdeft" >}}
#+END_EXPORT

View File

@ -1,339 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 2 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -1,16 +0,0 @@
PROGRAM := notdeft-xapian
default : build
-include local.mk
build : $(PROGRAM)
$(PROGRAM) : notdeft-xapian.cc
c++ -o $@ $< -std=c++11 -Wall `pkg-config --cflags --libs tclap` `xapian-config --cxxflags --libs`
clean :
-rm $(PROGRAM)
install-deps :
sudo aptitude install pkg-config libtclap-dev libxapian-dev

View File

@ -1,805 +0,0 @@
#include <algorithm>
#include <ctype.h>
#include <dirent.h>
#include <fstream>
#include <iostream>
#include <string.h>
#include <sys/stat.h>
#include <tclap/CmdLine.h>
#include <unistd.h>
#include <xapian.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#if !defined(XAPIAN_AT_LEAST)
#define XAPIAN_AT_LEAST(x,y,z) 0
#endif
#if XAPIAN_AT_LEAST(1,3,4) || XAPIAN_AT_LEAST(1,2,2) && !XAPIAN_AT_LEAST(1,3,0)
#define TG_CJK (Xapian::TermGenerator::FLAG_CJK_NGRAM)
#define QP_CJK (Xapian::QueryParser::FLAG_CJK_NGRAM)
#else
#define TG_CJK ((Xapian::TermGenerator::flags)0)
#define QP_CJK (0)
#endif
using namespace std;
namespace NotDeft {
struct ReadError {};
}
/** Serializes in a sorting friendly way, similarly to
`Xapian::sortable_serialise`. Should be quite portable when the
argument is coerced from `time_t`, although C does not actually
even guarantee an integer type in that case. */
static string time_serialize(const int64_t v) {
char buf[16+1];
// format in hexadecimal, zero padded, 64/4 digits
if (snprintf(buf, sizeof buf, "%016" PRIx64, v) != 16) {
// POSIX requires `errno` to be set, but C does not
throw Xapian::AssertionError("unexpected snprintf failure", errno);
}
return string(buf);
}
/** The inverse of `time_serialize`. */
static int64_t time_deserialize(const string& s) {
int64_t v;
if (sscanf(s.c_str(), "%" SCNx64, &v) != 1) {
throw Xapian::InvalidArgumentError("bad time_deserialize arg", errno);
}
return v;
}
/** Returns the length of any note header marker such as "#" or "%#"
* or "@;#". If the string is not a header string, returns 0. */
static size_t string_header_marker_len(const string& s) {
const size_t len = s.length();
if (len >= 1) {
if (s[0] == '#')
return 1;
if (len >= 2) {
if ((s[1] == '#') && (s[0] == '%'))
return 2;
if (len >= 3) {
if ((s[2] == '#') && (s[0] == '@') && (s[1] == ';'))
return 3;
if (len >= 5) {
if ((s[4] == '#') && (s[0] == '<') && (s[1] == '!') &&
(s[2] == '-') && (s[3] == '-'))
return 5;
}
}
}
}
return 0;
}
static bool line_skip_marker(const string& s, size_t& pos) {
const size_t len = string_header_marker_len(s);
if (len == 0)
return false;
pos = len;
return true;
}
/** Whether the lowercased string 's' matches 'pfx' starting at
* position 'pos'. If so, increment 'pos' to index the position after
* 'pfx'. */
static bool string_lc_skip_keyword(const string& s,
size_t& pos,
const string& pfx) {
auto pfx_len = pfx.length();
auto epos = pos + pfx_len;
if (s.length() < epos)
return false;
for (size_t i = 0; i < pfx_len; ++i) {
if (tolower(s[pos + i]) != pfx[i])
return false;
}
pos += pfx_len;
return true;
}
static bool string_ends_with(const string& s, const string& sfx) {
const int pos = s.length() - sfx.length();
return (pos >= 0) && (s.compare(pos, sfx.length(), sfx) == 0);
}
static bool string_ends_with_one_of(const string& s,
const vector<string>& sfxs) {
for (const string& sfx : sfxs) {
if (string_ends_with(s, sfx)) {
return true;
}
}
return false;
}
static bool drop_substring(string& s, const string& sub) {
auto found = s.rfind(sub);
if (found == string::npos)
return false;
s.replace(found, sub.length(), "");
return true;
}
static bool whitespace_p(const string& s) {
for (auto p = s.c_str(); *p; p++)
if (!isspace(*p))
return false;
return true;
}
static bool org_drawer_line_p(const string& s,
const char* kw = nullptr,
bool req_ws = false) {
auto p = s.c_str();
while (isblank(*p)) p++;
if (*p++ != ':') return false;
if (kw) {
/* Skip specified keyword, e.g., "END". */
auto len = strlen(kw);
if (strncmp(p, kw, len) != 0)
return false;
p += len;
} else {
/* Require a property name of at least one non-whitespace. */
if (!(*p && *p != ':' && !isspace(*p)))
return false;
p++;
while (*p && *p != ':' && !isspace(*p))
p++;
}
if (*p != ':') return false;
if (req_ws) {
while (*++p)
if (!isspace(*p))
return false;
}
return true;
}
static string downcase(const string& s) {
string data;
data.resize(s.length());
std::transform(s.begin(), s.end(), data.begin(), ::tolower);
return data;
}
static bool file_directory_p(const string& file) {
struct stat sb;
return (stat(file.c_str(), &sb) == 0) && S_ISDIR(sb.st_mode);
}
/** Returns an empty list on failure. */
static vector<string> ls(const string& file) {
vector<string> lst;
DIR* dir = opendir(file.c_str());
if (dir == NULL)
return lst;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
string name(entry->d_name);
if (name.length() > 0
&& name[0] != '.'
&& name[0] != '_'
&& name[0] != '#'
&& name.find('/') == string::npos) {
lst.push_back(name);
}
}
closedir(dir);
return lst;
}
static string file_join(const string& x, const string& y) {
if (x == ".")
return y;
if (string_ends_with(x, "/"))
return x + y;
return x + "/" + y;
}
/** Return the pathname of the parent directory of `s`, or return ""
if `s` has no directory components, or if `s` is "/". */
static string file_directory_path(const string& s) {
auto found = s.find_last_of('/');
if ((found == string::npos) || (found == 0))
return "";
return string(s.substr(0, found));
}
/** Return the non-directory component of pathname `s`, or return `s`
itself if `s` has no directory components. */
static string file_non_directory(const string& s) {
auto found = s.find_last_of('/');
if (found == string::npos)
return s;
return string(s.substr(found + 1));
}
/** Return the non-directory component of `s`, with its last extension
(if any) removed. A filename that is "all extension" has no
extension. */
static string file_basename(const string& s) {
auto basename = file_non_directory(s);
size_t found = basename.find_last_of('.');
if ((found == 0) || (found == string::npos))
return basename;
return string(basename.substr(0, found));
}
/** Return the last filename extension of `s`, with its leading ".",
or return "" if `s` has no extension. A filename that is "all
extension" has no extension. */
static string file_extension(const string& s) {
auto basename = file_non_directory(s);
size_t found = basename.find_last_of('.');
if ((found == 0) || (found == string::npos))
return "";
return string(basename.substr(found));
}
static void ls_org(vector<string>& res, const string& root,
const string& dir, const vector<string>& exts) {
auto absDir = file_join(root, dir);
for (const string& file : ls(absDir)) {
auto relFile = file_join(dir, file);
auto absFile = file_join(absDir, file);
bool isDir = file_directory_p(absFile);
if (string_ends_with_one_of(file, exts)) {
if (!isDir)
res.push_back(relFile);
} else if (isDir) {
ls_org(res, root, relFile, exts);
}
}
}
static bool uni_keyword_separator_p(const unsigned ch) {
return (ch == ':') || (ch == ';') || (ch == ',')
|| Xapian::Unicode::is_whitespace(ch);
}
/** Expects an UTF-8 encoded line as the argument `s`,
but reverts to octets for the remaining input if
non-UTF-8 encoding is detected. */
static void uni_index_keywords(Xapian::TermGenerator& indexer,
const string& s) {
Xapian::Utf8Iterator q(s);
for (;;) {
while (q.left() && uni_keyword_separator_p(*q)) q++;
if (!q.left()) break;
const char* const p = q.raw();
while (q.left() && !uni_keyword_separator_p(*q)) q++;
const string kw(p, q.raw());
indexer.index_text(kw, 0, "K");
indexer.increase_termpos();
if (!q.left()) break;
}
}
struct Op {
bool whole_dir;
string dir;
vector<string> files;
Op() {}
explicit Op(const string& d) : whole_dir(true), dir(d) {}
};
static bool parse_ops(istream& in, vector<Op>& lst) {
string opcode;
while (getline(in, opcode)) {
if (opcode == ":idir") {
string dir;
if (getline(in, dir)) {
lst.push_back(Op(dir));
} else {
return false; // expected directory name
}
} else if (opcode == ":ifiles") {
string dir;
if (!getline(in, dir))
return false; // expected directory name
string count_s;
if (!getline(in, count_s))
return false; // expected file count
int count = std::stoi(count_s);
if (count < 0)
return false; // expected non-negative integer
Op op;
op.whole_dir = false;
op.dir = dir;
string file;
for ( ; count > 0; count--) {
if (!getline(in, file))
return false; // expected count filenames
op.files.push_back(file);
}
lst.push_back(op);
} else {
return false; // unknown command
}
}
return true;
}
static void usage()
{
cerr << "notdeft-xapian" << endl;
cerr << "USAGE:" << endl;
cerr << "To build/refresh search indices" << endl;
cerr << "(for specified directories):" << endl;
cerr << " notdeft-xapian index [options] directory..." << endl;
cerr << "To find text documents" << endl;
cerr << "(matching the specified query):" << endl;
cerr << " notdeft-xapian search [options] directory..." << endl;
}
static constexpr Xapian::valueno DOC_MTIME = 0;
static constexpr Xapian::valueno DOC_FILENAME = 1;
static int doIndex(vector<string> subArgs) {
TCLAP::CmdLine cmdLine
("Specify any indexing commands via STDIN."
" For each command, specify its database index directory."
" All paths are used and stored as given."
" Search results are reported with the stored paths,"
" regardless of the search-time working directory.");
TCLAP::ValueArg<string>
langArg("l", "lang", "stemming language (e.g., 'en' or 'fi')",
false, "en", "language");
cmdLine.add(langArg);
TCLAP::MultiArg<string>
extArg("x", "extension", "filename extension (default: '.org')",
false, "extension");
cmdLine.add(extArg);
TCLAP::ValueArg<string>
chdirArg("c", "chdir", "change working directory first",
false, ".", "directory");
cmdLine.add(chdirArg);
TCLAP::SwitchArg
resetArg("r", "recreate", "recreate database", false);
cmdLine.add(resetArg);
TCLAP::ValueArg<int>
titleArg("t", "title-wdf", "title importance (default: 10)",
false, 10, "wdf_inc");
cmdLine.add(titleArg);
TCLAP::SwitchArg
verboseArg("v", "verbose", "be verbose", false);
cmdLine.add(verboseArg);
TCLAP::SwitchArg
inputArg("i", "input", "read instructions from STDIN", false);
cmdLine.add(inputArg);
TCLAP::SwitchArg
skipDrawersArg("", "allow-org-property-drawers",
"allow Org :PROPERTIES: drawers in header", false);
cmdLine.add(skipDrawersArg);
TCLAP::UnlabeledMultiArg<string>
dirsArg("directory...", "index specified dirs", false, "directory");
cmdLine.add(dirsArg);
cmdLine.parse(subArgs);
if (chdirArg.getValue() != ".") {
if (chdir(chdirArg.getValue().c_str()) == -1) {
auto e = errno;
cerr << "could not change into directory " <<
chdirArg.getValue() << " (errno: " << e << ")" << endl;
return 1;
}
}
vector<string> exts = extArg.getValue();
if (exts.empty())
exts.push_back(".org");
auto verbose = verboseArg.getValue();
string lang(langArg.getValue());
bool cjk = drop_substring(lang, ":cjk");
vector<Op> opList;
{
auto dirs = dirsArg.getValue();
for (auto dir : dirs) {
opList.push_back(Op(dir));
}
}
if (inputArg.getValue()) {
if (!parse_ops(cin, opList)) {
cerr << "option -i / --input given, "
"but failed to parse instructions from STDIN" << endl;
if (verbose) { // print out parsed instructions
cerr << "successfully parsed:" << endl;
ostream& out(cerr);
for (auto op : opList) {
out << op.dir;
if (op.whole_dir) {
out << endl << " (ALL)" << endl;
} else {
for (auto file : op.files) {
out << endl << " " << file;
}
out << endl;
}
}
}
return 1;
}
}
try {
Xapian::TermGenerator indexer;
Xapian::Stem stemmer(lang);
indexer.set_stemmer(stemmer);
indexer.set_stemming_strategy(Xapian::TermGenerator::STEM_SOME);
if (cjk)
indexer.set_flags(TG_CJK);
for (auto op : opList) {
auto dir = op.dir;
struct stat sb;
// Whether a readable and writable directory.
if ((stat(dir.c_str(), &sb) == 0) && S_ISDIR(sb.st_mode) &&
(access(dir.c_str(), R_OK|W_OK) != -1)) {
if (verbose) {
cerr << "indexing directory " << dir << endl;
}
string dbFile(file_join(dir, ".notdeft-db"));
Xapian::WritableDatabase db(dbFile,
resetArg.getValue() ?
Xapian::DB_CREATE_OR_OVERWRITE :
Xapian::DB_CREATE_OR_OPEN);
map<string, int64_t> fsFiles; // mtimes for files in file system
map<string, int64_t> dbFiles; // mtimes for files in database
map<string, Xapian::docid> dbIds;
vector<string> orgFiles;
if (op.whole_dir) {
ls_org(orgFiles, dir, ".", exts);
} else {
// Sparse directory paths must be specified relative to
// their database root.
orgFiles = op.files;
}
for (const string& file : orgFiles) { // `dir` relative `file`
auto filePath = file_join(dir, file);
struct stat sb;
if (stat(filePath.c_str(), &sb) == 0) {
fsFiles[filePath] = sb.st_mtime;
}
}
db.begin_transaction(false);
for (Xapian::PostingIterator it = db.postlist_begin("");
it != db.postlist_end(""); ++it) {
auto docId = *it;
auto doc = db.get_document(docId);
auto filePath = doc.get_data();
auto t = time_deserialize(doc.get_value(DOC_MTIME));
// Overwrites any existing value of the same key, and thus
// there will be no dupes in `dbFiles`, even if the database
// should have some.
dbFiles[filePath] = t;
dbIds[filePath] = docId;
}
{
auto makeDoc = [&] (const pair<string, int64_t>& x) {
const string& filePath = x.first;
ifstream infile(filePath);
Xapian::Document doc;
doc.set_data(filePath);
doc.add_value(DOC_MTIME, time_serialize(x.second));
const string fileNonDir = file_non_directory(filePath);
doc.add_value(DOC_FILENAME, fileNonDir);
indexer.set_document(doc);
{
const string fileDir = file_directory_path(filePath);
indexer.index_text(fileDir, 1, "P");
}
{
const string fileBase = file_basename(fileNonDir);
indexer.index_text(fileBase, 1, "F");
}
{
/* As for Omega, lowercase, without dot, and just "E"
for the no extension case. */
string fileExt = file_extension(fileNonDir);
if (!fileExt.empty()) {
fileExt = downcase(fileExt.substr(1));
}
//doc.add_boolean_term("E" + fileExt);
indexer.index_text_without_positions(fileExt, 0, "E");
//cerr << "ext: '" << fileExt << "'" << endl;
}
{
string line;
bool titleDone = false;
size_t pos = 0;
while (getline(infile, line)) {
if (whitespace_p(line)) {
// skip blank line
} else if (skipDrawersArg.getValue() &&
org_drawer_line_p(line, "PROPERTIES", true)) {
while (getline(infile, line)) {
// skip Org drawer
if (org_drawer_line_p(line, "END"))
break;
if (!org_drawer_line_p(line))
break; // unclosed drawer
}
} else if (!line_skip_marker(line, pos)) {
// non Org header mode
if (!titleDone) {
indexer.index_text(line, 1, "S");
indexer.index_text(line, titleArg.getValue());
indexer.increase_termpos();
} else {
indexer.index_text(line);
}
while (getline(infile, line)) {
//cerr << "body line: '" << line << "'" << endl;
indexer.index_text(line);
}
break;
} else if (string_lc_skip_keyword(line, pos, "+title:")) {
const string s = line.substr(pos);
indexer.index_text(s, 1, "S");
indexer.index_text(s, titleArg.getValue());
indexer.increase_termpos();
titleDone = true;
} else if (string_lc_skip_keyword(line, pos, "+keywords:") ||
string_lc_skip_keyword(line, pos, "+filetags:")) {
const string s = line.substr(pos);
uni_index_keywords(indexer, s);
indexer.index_text(s);
indexer.increase_termpos();
} else {
// skip comment (or unknown property) line
}
}
}
if (!infile.eof())
throw NotDeft::ReadError();
return doc;
}; // end makeDoc
auto addFile = [&] (const pair<string, int64_t>& x) {
if (verbose)
cerr << "indexing file " << x.first << endl;
try {
Xapian::Document doc = makeDoc(x);
db.add_document(doc);
} catch (const NotDeft::ReadError& e) {
// File not (fully) readable, so don't index.
}
};
auto updateFile = [&] (const pair<string, int64_t>& x,
Xapian::docid docId) {
if (verbose)
cerr << "re-indexing file " << x.first << endl;
try {
Xapian::Document doc = makeDoc(x);
db.replace_document(docId, doc);
} catch (const NotDeft::ReadError& e) {
// File no longer (fully) readable, so remove from index.
db.delete_document(docId);
}
};
auto rmFile = [&] (const pair<string, int64_t>& x) {
if (verbose)
cerr << "de-indexing file " << x.first << endl;
auto docId = dbIds[x.first];
db.delete_document(docId);
};
auto fi = fsFiles.cbegin();
auto di = dbFiles.cbegin();
for (;;) {
if (fi == fsFiles.cend()) {
// The remaining files have been deleted.
for ( ; di != dbFiles.cend(); ++di) {
rmFile(*di);
}
break;
} else if (di == dbFiles.cend()) {
// The remaining files are new.
for ( ; fi != fsFiles.cend(); ++fi) {
addFile(*fi);
}
break;
} else if ((*fi).first == (*di).first) {
if ((*fi).second != (*di).second) {
// The file has been modified.
updateFile(*fi, dbIds[(*di).first]);
}
fi++;
di++;
} else if ((*fi).first < (*di).first) {
// The file has been added.
addFile(*fi);
fi++;
} else if ((*fi).first > (*di).first) {
// The file has been deleted.
rmFile(*di);
di++;
} else {
throw Xapian::AssertionError("unexpected condition");
}
} // end `for`
}
db.commit_transaction();
}
}
} catch (const Xapian::Error &e) {
cerr << e.get_description() << endl;
return 1;
}
return 0;
}
static int doSearch(vector<string> subArgs) {
TCLAP::CmdLine cmdLine("Specify a query expression as a string.");
TCLAP::ValueArg<string>
langArg("l", "lang", "stemming language (e.g., 'en' or 'fi')",
false, "en", "language");
cmdLine.add(langArg);
TCLAP::ValueArg<string>
queryArg("q", "query", "specifies a query string", false, "", "string");
cmdLine.add(queryArg);
TCLAP::ValueArg<int>
countArg("c", "max-count", "maximum number of results", false, 0, "number");
cmdLine.add(countArg);
TCLAP::SwitchArg
timeArg("t", "time-sort", "sort by modification time", false);
cmdLine.add(timeArg);
TCLAP::SwitchArg
nameArg("f", "name-sort", "sort by file name (overrides '-t')", false);
cmdLine.add(nameArg);
TCLAP::SwitchArg
verboseArg("v", "verbose", "be verbose", false);
cmdLine.add(verboseArg);
TCLAP::SwitchArg
flag_pure_not("n", "pure-not", "allow NOT", false);
cmdLine.add(flag_pure_not);
TCLAP::SwitchArg
flag_boolean_any_case("a", "boolean-any-case",
"allow lowercase operators", false);
cmdLine.add(flag_boolean_any_case);
TCLAP::UnlabeledMultiArg<string>
dirsArg("dir...", "specifies directories to search", false, "directory");
cmdLine.add(dirsArg);
cmdLine.parse(subArgs);
auto maxDocCount = countArg.getValue();
bool nameSort = nameArg.getValue();
bool timeSort = timeArg.getValue();
auto verbose = verboseArg.getValue();
string lang(langArg.getValue());
bool cjk = drop_substring(lang, ":cjk");
try {
Xapian::Database db;
auto dirs = dirsArg.getValue();
int numDbFiles = 0;
for (auto dir : dirs) {
string dbFile(file_join(dir, ".notdeft-db"));
if (access(dbFile.c_str(), R_OK) != -1) {
Xapian::Database dirDb(dbFile);
db.add_database(dirDb);
numDbFiles++;
//cout << "Added database: " << db.get_description() << endl;
}
}
if (numDbFiles == 0)
return 0;
Xapian::Enquire enquire(db);
if (nameSort) // by filename, descending
enquire.set_sort_by_value(DOC_FILENAME, true);
else if (timeSort) // by modification time, descending
enquire.set_sort_by_value(DOC_MTIME, true);
Xapian::QueryParser qp;
qp.add_prefix("path", "P");
qp.add_prefix("file", "F");
qp.add_prefix("ext", "E");
qp.add_prefix("title", "S");
qp.add_prefix("tag", "K");
Xapian::Stem stemmer(lang);
Xapian::Query query;
if (queryArg.getValue() == "") {
query = Xapian::Query::MatchAll;
} else {
qp.set_stemmer(stemmer);
qp.set_database(db);
qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
unsigned flags =
Xapian::QueryParser::FLAG_DEFAULT |
(flag_pure_not.getValue() ?
Xapian::QueryParser::FLAG_PURE_NOT : 0) |
(flag_boolean_any_case.getValue() ?
Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE : 0) |
(cjk ? QP_CJK : 0);
query = qp.parse_query(queryArg.getValue(), flags);
if (verbose)
cerr << "parsed query is: " << query.get_description() << endl;
}
enquire.set_query(query);
int maxItems = (maxDocCount ? maxDocCount : db.get_doccount());
Xapian::MSet matches = enquire.get_mset(0, maxItems);
for (Xapian::MSetIterator i = matches.begin(); i != matches.end(); ++i) {
cout << i.get_document().get_data() << endl;
}
} catch (const Xapian::Error &e) {
cerr << e.get_description() << endl;
return 1;
}
return 0;
}
int main(int argc, const char* argv[])
{
if (argc <= 1) {
usage();
return 1;
}
string cmd(argv[1]);
vector<string> args({ string(argv[0]) + " " + cmd });
for (int i = 2; i < argc; i++)
args.emplace_back(argv[i]);
// for (auto s : args) cout << s << endl;
if (cmd == "index") {
return doIndex(args);
} else if (cmd == "search") {
return doSearch(args);
} else if (cmd == "-h" || cmd == "--help") {
usage();
return 0;
} else {
usage();
return 1;
}
}
/*
Copyright (C) 2017 Tero Hasu
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 2 of the
License, 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
See the file GPL-2 for the full text of the GNU GPL.
*/