Emacs Archiver

Thursday, September 19, 2024

Table of Contents

Org Archiver

A major paint point in using org mode's archive functionality in Emacs is that the archive doesn't mirror the tree structure of the source files, which means that every archived heading is at the top level of the org tree. So I created this package to archive org headings with the ancestors of the heading, and merge them with the existing archive tree structure.

emacs-archiver.gif

Figure 1: Demo of Archiver Package

This package allows you to archive headings into your agenda file in such a way that the ancestors will also be archived. And if some of the ancestors exist in the archive already, the newly archived headings will be merged with the tree structure of the archive.

Installation

Clone the repository in your preferred destination.

cd ~/path/to
git clone https://github.com/Duncan-Britt/emacs-archiver.git

Add the following to your config, specifying the path to the cloned repository and the path to your agenda archive file.

(use-package archiver
  :load-path "~/path/to/emacs-archiver/"
  :after org
  :init
  (setq *archiver-agenda-archive-location*
        (expand-file-name "~/path/to/your/agenda_archive.org"))
  :bind
  (:map org-mode-map
        ("C-c C-x C-a" . archiver-archive-heading)))

File Archiver

Like the home, the home directory tends to become cluttered. I was accumulating files and folders that I no longer used, but wasn't ready to get delete permanently. So I created a shell script which allows me to move files into an ~/archive directory where the file path mirrors that of the location from whence it came. Importantly, archiving sibling files and folders won't create duplicates or clobber existing directories in the archive.

#!/bin/bash

# Archive directory
ARCHIVE_DIR="$HOME/archive"

# Check if a file or directory is specified
if [ -z "$1" ]; then
  echo "Usage: archive <file-or-directory>"
  exit 1
fi

# File or directory to archive
TARGET="$1"

# Get the current directory relative to home
if [ "$(pwd)" == "$HOME" ]; then
  CURRENT_DIR=""
else
  CURRENT_DIR=$(pwd | sed "s|^$HOME/||")
fi

# Construct the full path of the target
if [ -z "$CURRENT_DIR" ]; then
  RELATIVE_TARGET_PATH="$TARGET"
else
  RELATIVE_TARGET_PATH="$CURRENT_DIR/$TARGET"
fi

# Construct the destination directory in the archive
DEST_DIR="$ARCHIVE_DIR/$(dirname "$RELATIVE_TARGET_PATH")"

# Create the destination directory if it doesn't exist
mkdir -p "$DEST_DIR"

# Check if the target is a directory
if [ -d "$TARGET" ]; then
  # Use rsync to merge directories if the target is a directory
  rsync -a "$TARGET/" "$DEST_DIR/$(basename "$TARGET")/"
  # Remove the original directory after merging
  rm -rf "$TARGET"
else
  # Move the target to the archive directory if it's a file
  mv "$TARGET" "$DEST_DIR/"
fi

echo "Archived '$TARGET' to '$DEST_DIR/'."

I also created a little emacs lisp script to archive the open file.

(defvar my-archive-dir "~/archive"
  "Directory where files will be archived.")

(defun archiver-archive-open-file ()
  "Move the current file to the archive directory."
  (interactive)
  (if (buffer-file-name)
      (let* ((file-path (buffer-file-name))
             (relative-path (file-relative-name file-path (getenv "HOME")))
             (archive-path (expand-file-name relative-path my-archive-dir)))
        (if (file-exists-p file-path)
            (progn
              (save-buffer)
              (make-directory (file-name-directory archive-path) t)
              (rename-file file-path archive-path t)
              (kill-buffer)
              (message "Archived: %s" archive-path))
          (message "File does not exist: %s" file-path)))
    (message "No file is associated with this buffer.")))

Date: 2024-09-19 Thu 00:00