dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

org-roam-migrate.el (6711B)


      1 ;;; org-roam-migrate.el --- Migration utilities from v1 to v2 -*- coding: utf-8; lexical-binding: t; -*-
      2 
      3 ;; Copyright © 2020-2022 Jethro Kuan <jethrokuan95@gmail.com>
      4 
      5 ;; Author: Jethro Kuan <jethrokuan95@gmail.com>
      6 ;; URL: https://github.com/org-roam/org-roam
      7 ;; Keywords: org-mode, roam, convenience
      8 ;; Version: 2.2.2
      9 ;; Package-Requires: ((emacs "26.1") (dash "2.13") (org "9.4") (emacsql "3.0.0") (emacsql-sqlite "1.0.0") (magit-section "3.0.0"))
     10 
     11 ;; This file is NOT part of GNU Emacs.
     12 
     13 ;; This program is free software; you can redistribute it and/or modify
     14 ;; it under the terms of the GNU General Public License as published by
     15 ;; the Free Software Foundation; either version 3, or (at your option)
     16 ;; any later version.
     17 ;;
     18 ;; This program is distributed in the hope that it will be useful,
     19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     21 ;; GNU General Public License for more details.
     22 ;;
     23 ;; You should have received a copy of the GNU General Public License
     24 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
     25 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     26 ;; Boston, MA 02110-1301, USA.
     27 
     28 ;;; Commentary:
     29 ;;
     30 ;; This is a special library provided for the v1 users of this package. It's
     31 ;; purpose is to ease the transition from v1 to v2, by providing migration
     32 ;; utilities to convert from v1 notes to v2 nodes.
     33 ;;
     34 ;;; Code:
     35 (require 'org-roam)
     36 
     37 ;;; Migration wizard (v1 -> v2)
     38 ;;;###autoload
     39 (defun org-roam-migrate-wizard ()
     40   "Migrate all notes from to be compatible with Org-roam v2.
     41 1. Convert all notes from v1 format to v2.
     42 2. Rebuild the cache.
     43 3. Replace all file links with ID links."
     44   (interactive)
     45   (when (yes-or-no-p "Org-roam will now convert all your notes from v1 to v2.
     46 This will take a while. Are you sure you want to do this?")
     47     ;; Back up notes
     48     (let ((backup-dir (expand-file-name "org-roam.bak"
     49                                         (file-name-directory (directory-file-name org-roam-directory)))))
     50       (message "Backing up files to %s" backup-dir)
     51       (copy-directory org-roam-directory backup-dir))
     52 
     53     ;; Upgrade database to v2
     54     (org-roam-db-sync 'force)
     55 
     56     ;; Convert v1 to v2
     57     (dolist (f (org-roam-list-files))
     58       (org-roam-with-file f nil
     59         (org-roam-migrate-v1-to-v2)))
     60 
     61     ;; Rebuild cache
     62     (org-roam-db-sync 'force)
     63 
     64     ;;Replace all file links with ID links
     65     (dolist (f (org-roam-list-files))
     66       (org-roam-with-file f nil
     67         (org-roam-migrate-replace-file-links-with-id)
     68         (save-buffer)))))
     69 
     70 (defun org-roam-migrate-v1-to-v2 ()
     71   "Convert the current buffer to v2 format."
     72   ;; Create file level ID
     73   (org-with-point-at 1
     74     (org-id-get-create))
     75   ;; Replace roam_key into properties drawer roam_ref
     76   (when-let* ((refs (mapcan #'split-string-and-unquote
     77                             (cdar (org-collect-keywords '("roam_key"))))))
     78     (let ((case-fold-search t))
     79       (org-with-point-at 1
     80         (dolist (ref refs)
     81           (org-roam-ref-add ref))
     82         (while (re-search-forward "^#\\+roam_key:" (point-max) t)
     83           (beginning-of-line)
     84           (kill-line 1)))))
     85 
     86   ;; Replace roam_alias into properties drawer roam_aliases
     87   (when-let* ((aliases (mapcan #'split-string-and-unquote
     88                                (cdar (org-collect-keywords '("roam_alias"))))))
     89     (dolist (alias aliases)
     90       (org-roam-alias-add alias)))
     91   (let ((case-fold-search t))
     92     (org-with-point-at 1
     93       (while (re-search-forward "^#\\+roam_alias:" (point-max) t)
     94         (beginning-of-line)
     95         (kill-line 1))))
     96 
     97   ;; Replace #+roam_tags into #+filetags
     98   (org-with-point-at 1
     99     (let* ((roam-tags (org-roam-migrate-get-prop-list "ROAM_TAGS"))
    100            (file-tags (cl-mapcan (lambda (value)
    101                                    (cl-mapcan
    102                                     (lambda (k) (org-split-string k ":"))
    103                                     (split-string value)))
    104                                  (org-roam-migrate-get-prop-list "FILETAGS")))
    105            (tags (append roam-tags file-tags))
    106            (tags (seq-map (lambda (tag)
    107                             (replace-regexp-in-string
    108                              "[^[:alnum:]_@#%]"
    109                              "_"
    110                              tag)) tags))
    111            (tags (seq-uniq tags)))
    112       (when tags
    113         (org-roam-migrate-prop-set "filetags" (org-make-tag-string tags))))
    114     (let ((case-fold-search t))
    115       (org-with-point-at 1
    116         (while (re-search-forward "^#\\+roam_tags:" (point-max) t)
    117           (beginning-of-line)
    118           (kill-line 1)))))
    119   (save-buffer))
    120 
    121 (defun org-roam-migrate-get-prop-list (keyword)
    122   "Return prop list for KEYWORD."
    123   (let ((re (format "^#\\+%s:[ \t]*\\([^\n]+\\)" (upcase keyword)))
    124         lst)
    125     (goto-char (point-min))
    126     (while (re-search-forward re 2048 t)
    127       (setq lst (append lst (split-string-and-unquote
    128                              (buffer-substring-no-properties
    129                               (match-beginning 1) (match-end 1))))))
    130     lst))
    131 
    132 (defun org-roam-migrate-prop-set (name value)
    133   "Set a file property called NAME to VALUE in buffer file.
    134 If the property is already set, replace its value."
    135   (setq name (downcase name))
    136   (org-with-point-at 1
    137     (let ((case-fold-search t))
    138       (if (re-search-forward (concat "^#\\+" name ":\\(.*\\)")
    139                              (point-max) t)
    140           (replace-match (concat "#+" name ": " value) 'fixedcase)
    141         (while (and (not (eobp))
    142                     (looking-at "^[#:]"))
    143           (if (save-excursion (end-of-line) (eobp))
    144               (progn
    145                 (end-of-line)
    146                 (insert "\n"))
    147             (forward-line)
    148             (beginning-of-line)))
    149         (insert "#+" name ": " value "\n")))))
    150 
    151 (defun org-roam-migrate-replace-file-links-with-id ()
    152   "Replace all file: links with ID links in current buffer."
    153   (org-with-point-at 1
    154     (while (re-search-forward org-link-bracket-re nil t)
    155       (let* ((mdata (match-data))
    156              (path (match-string 1))
    157              (desc (match-string 2)))
    158         (when (string-prefix-p "file:" path)
    159           (setq path (expand-file-name (substring path 5)))
    160           (when-let ((node-id (caar (org-roam-db-query [:select [id] :from nodes
    161                                                         :where (= file $s1)
    162                                                         :and (= level 0)] path))))
    163             (set-match-data mdata)
    164             (replace-match (org-link-make-string (concat "id:" node-id)
    165                                                  desc) nil t)))))))
    166 
    167 (provide 'org-roam-migrate)
    168 ;;; org-roam-migrate.el ends here