From 126eae927fab0cdd93ca9fe85cc005e89b12aa69 Mon Sep 17 00:00:00 2001 From: Chris Barrett Date: Tue, 16 Aug 2022 18:59:12 +1200 Subject: [PATCH] Add org-roam-gc --- Readme.org | 12 +++- lisp/org-roam-gc.el | 136 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 lisp/org-roam-gc.el diff --git a/Readme.org b/Readme.org index 60e8fe4..b2cea14 100644 --- a/Readme.org +++ b/Readme.org @@ -1,9 +1,19 @@ #+title: nursery #+author: Chris Barrett +#+todo: SPIKE(s) INCUBATING(i) | STABLE(t) PUBLISHED(p) This is a repository for Emacs Lisp packages that I think could be useful for friends and coworkers. It's an experimental, low-pressure space for me just to -hack on Lisp without the pressure of supporting issues from the wider Internet. :) +hack on Lisp with the garage door open. + +If something reaches a stable state I may publish it if there's interest. + +* Objects of interest +This repository contains a mix of supporting libraries and things that are +actually useful. + +** SPIKE [[file:lisp/org-roam-gc.el][org-roam-gc]] +Automatically delete empty dailies files so they don't build up forever. ** INCUBATING [[file:lisp/org-roam-dblocks.el][org-roam-dblocks]] Add org dynamic blocks that implement "canned searches" for org-roam. You can diff --git a/lisp/org-roam-gc.el b/lisp/org-roam-gc.el new file mode 100644 index 0000000..cbc5f63 --- /dev/null +++ b/lisp/org-roam-gc.el @@ -0,0 +1,136 @@ +;;; org-roam-gc.el --- Clean up empty roam files -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Chris Barrett + +;; Author: Chris Barrett + +;; Homepage: https://github.com/chrisbarrett/nursery + +;; Version: 0.0.1-pre + +;; Package-Requires: ((emacs "27.1") (org "9.5.3") (org-roam "2.2.2")) + +;; 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 . + +;;; Commentary: + +;; Simple package that deletes dailies files that don't have anything in them. + +;; Example configuration: +;; +;; (use-package org-roam-gc +;; :after org-roam +;; :demand t +;; :hook (org-mode . org-roam-gc-automatically)) + +;;; Code: + +(require 'org-roam) +(require 'org-roam-dailies) + +(defconst org-roam-gc-prompt-before-deleting-p nil + "Whether to prompt before removing files when run interactively.") + +(defconst org-roam-gc-debug nil + "Whether to output extra messages for debugging purposes.") + +(defun org-roam-gc--empty-content-p (buf) + (with-current-buffer buf + (save-restriction + (widen) + (save-excursion + (goto-char (point-min)) + (while (equal 'property-drawer (org-element-type (org-element-at-point))) + (org-forward-element)) + (while (and (equal 'keyword (org-element-type (org-element-at-point))) + (ignore-errors + (org-forward-element) + t))) + (or (eobp) + (string-blank-p (buffer-substring (1+ (point)) (point-max)))))))) + +(defun org-roam-gc--empty-file-content-p (file) + (with-temp-buffer + (insert-file-contents file) + (org-roam-gc--empty-content-p (current-buffer)))) + +(defun org-roam-gc-dailies-files () + (require 'org-roam) + (require 'org-roam-dailies) + (let ((path (expand-file-name org-roam-dailies-directory org-roam-directory))) + (seq-filter #'file-regular-p (directory-files path t)))) + +(defmacro org-roam-gc--log (msg &rest args) + `(when org-roam-gc-debug + (message (concat "org-roam-gc: " ,msg) ,@args) + nil)) + +(defun org-roam-gc--file-editing-p (file) + (when-let* ((buf (find-buffer-visiting file))) + (or (buffer-modified-p buf) + (get-buffer-window-list buf)))) + +(defun org-roam-gc--remove-file (file confirm-p) + (let ((file (expand-file-name file))) + (cond + ((org-roam-gc--file-editing-p file) + (org-roam-gc--log "Skipping open file: %s" file)) + + (t + (org-roam-gc--log "Removing file: %s" file) + (with-current-buffer (find-file file) + (when (or (not confirm-p) + (y-or-n-p (format "Delete file `%s'? " (abbreviate-file-name file)))) + (kill-buffer) + (delete-file file))) + t)))) + +(defun org-roam-gc (&optional interactive) + "Delete empty org-roam dailies. + +Optional arg INTERACTIVE determines whether to query before +removing files." + (interactive "p") + (let ((count + (thread-last (org-roam-gc-dailies-files) + (seq-filter #'org-roam-gc--empty-file-content-p) + (seq-filter (lambda (file) + (org-roam-gc--remove-file file (and interactive + org-roam-gc-prompt-before-deleting-p)))) + (length)))) + (cond + (interactive + (message "Deleted %s file%s" count (if (eq 1 count) "" "s"))) + ((< 0 count) + (message "org-roam-gc deleted %s file%s" count (if (eq 1 count) "" "s")))))) + +(defun org-roam-gc--maybe-remove-this-file () + (when-let* ((file (buffer-file-name))) + (cond + ((not (derived-mode-p 'org-mode)) + (org-roam-gc--log "Skipping non-org file: %s" file)) + ((and (org-roam-gc--empty-content-p (current-buffer)) + (org-roam-dailies--daily-note-p)) + (org-roam-gc--log "Removing file: %s" file) + (delete-file (buffer-file-name))) + (t + (org-roam-gc--log "Skipping file: %s" file))))) + +;;;###autoload +(defun org-roam-gc-automatically () + (add-hook 'kill-buffer-hook #'org-roam-gc--maybe-remove-this-file nil t)) + +(provide 'org-roam-gc) + +;;; org-roam-gc.el ends here