Showing 417 to 420 Newer

Taking ownership of org-id

#emacs

Let's say most of your Org files sit in a folder /home/kept/notes/ but some others are outside, scattered here and there, plus you'd like to try not depending on the handy org-roam-update-org-id-locations.

The challenges with org-id:

  1. The classic way to tell it where to look for IDs is adding the directories to org-agenda-files.
    • Unfortunately with thousands of files, this slows down the agenda something extreme. Not an option.
  2. An alternative way is to populate org-id-extra-files or org-agenda-text-search-extra-files.
    • See snippet A below, but unfortunately with thousands of files this slows down M-x customize-group for org-id or org-agenda something extreme.
    • Sounds like org-id could use a patch… I'll email someone about it…eventually
  3. To sidestep the small problem with #2, you could trust in org-id to keep itself updated, because it does that every time your Emacs creates or searches for an ID. You regenerate org-id-locations once (or well, once every time you wipe .emacs.d). See snippets B or C.
  4. org-id complains about duplicate IDs because it's also looking in e.g. the versioned backups generated by Logseq
    • So, you need some sort of exclusion ruleset.
      • For an elisp-only way, see snippets A or B.
      • A natural way is to obey .ignore or .gitignore, if you already keep such files. I've found no elisp gitignore parser, but see snippet C for a way to use ripgrep's builtin parser.
    • Why org-roam didn't give you this problem? It has actually been suppressing org-id errors!
  5. If your Emacs quits unexpectedly, it can forget many ID locations! To ensure it remembers, either use a hook like

    (add-hook 'after-save-hook
     (defun my-save-id-soon ()
       (run-with-idle-timer 10 t #'org-id-locations-save)))
    

    or enable eager-state-preempt-kill-emacs-hook-mode from eager-state.

Snippet A

;; Populate `org-id-extra-files'
(dolist (file (mapcan (lambda (dir)
                        (directory-files-recursively dir "\\.org$"))
                      '(;; Example values
                        "/home/kept/notes/"
                        "/home/kept/project1/"
                        "/home/kept/project2/")))
  (or (string-search "/logseq/bak/" file)
      (string-search "/logseq/version-files/" file)
      (push file org-id-extra-files)))

;; Then either run M-x org-id-update-id-locations or restart Emacs.

Snippet B

;; Populate org-id without setting `org-id-extra-files'.  Only do it if
;; `org-id-locations' is gone.
(when (or (and (not (file-exists-p org-id-locations-file))
               (null org-id-locations))
          (if (null org-id-locations)
              (org-id-locations-load)
            (if (listp org-id-locations)
                (null org-id-locations)
              (hash-table-empty-p org-id-locations))))
  (org-id-update-id-locations
   (seq-remove (lambda (file)
                 (or (string-search "/logseq/bak/" file)
                     (string-search "/logseq/version-files/" file)))
               (mapcan (lambda (dir)
                         (directory-files-recursively dir "\\.org$"))
                       '(;; Example values
                         "/home/kept/roam/"
                         "/home/kept/project1/"
                         "/home/kept/project2/"))))
  (org-id-locations-save))

Snippet C

;; Populate org-id without setting `org-id-extra-files'. Only do it if
;; `org-id-locations' is gone.
(when (or (and (not (file-exists-p org-id-locations-file))
               (null org-id-locations))
          (if (null org-id-locations)
                     (org-id-locations-load)
                   (if (listp org-id-locations)
                       (null org-id-locations)
                     (hash-table-empty-p org-id-locations))))
  (dolist (default-directory '(;; Example values
                               "/home/kept/notes/"
                               "/home/kept/project1/"
                               "/home/kept/project2/"))
    ;; Borrow ripgrep's ability to obey .ignore/.gitignore
    (org-id-update-id-locations
     (split-string (shell-command-to-string "rg -ilt org :ID:") "\n" t))
    (org-id-locations-save)))

Bonus snippet: full reset

;; FOR TESTING: wipe all records
;; You ONLY need to wipe if it won't shut up about duplicates!
(progn
  (delete-file org-id-locations-file)
  (setq org-id-locations nil)
  (setq org-id--locations-checksum nil)
  (setq org-agenda-text-search-extra-files nil)
  (setq org-id-files nil)
  (setq org-id-extra-files nil))
Created (3 weeks ago)

Nested notes enable folgezettel (note-sequences)

#org-roam

The packages zk, orgrr and denote mandate the rule of "one note per file". So how do they do note-sequences?

Well. In orgrr, you'd manually give special IDs to notes that should be parts of sequences, and then use commands like orgrr-show-sequence to work with them.

In org-roam, which permits nested notes, you'd just have a series of headings in a single file… you know, a conventional org-mode file.

I like that better. You can export it to PDF or HTML. There are ways to isolate and export a subtree from one file, but it's not as convenient (yet) to tell Org to concatenate multiple whole files and export them as one.

I suppose zk/orgrr/denote users could use org-transclusion to get the same effect.

Multiple sequences?

Hm… the concept of orgrr-show-sequence has merit. Nesting org-roam nodes only gives you one sequence. What if you want a node to be part of several?

For example, say you have a node about Isaac Newton, which could be part of both a Philosophy sequence and a Math sequence.

Well, you could have a "Math" note that transcludes all other math notes, and a "Philosophy" note that transcludes all the philosophy notes. And that works whether you use orgrr or org-roam. But yea, org-roam's ability to nest nodes suddenly became less important.

Created (2 months ago)
Updated (5 weeks ago)

Svelte: Redirect to new page ID

Suppose

  • that your website has page IDs in the sense of domain.com/PAGEID/slug. For example, you're probably seeing this very page at the address edstrom.dev/csbxq/redirect-to-new-page-ids-in-sveltekit, so the ID is csbxq.
  • that you update the ID to use new algorithm, so now it's going to be 6uXpfOI2c9fJdh2aa22. Time to set up a redirect!

I'll assume your page's loader (the file you've got on a path like src/routes/[id]/[[slug]]/+page.js) contains this:

import { error } from '$sveltejs/kit'
import posts from '$lib/posts.json'

export function load({ params }) {
    let post = posts[params.id]
    if (post) return post
    
    error(404, "Not found")
}

Edit that to something like this.

import { error, redirect } from '$sveltejs/kit'
import posts from '$lib/posts.json'
import v1_to_v2 from '$lib/v1_to_v2.json' // assumed a table of old and new ID

export function load({ params }) {
  let post = posts[params.id]
  if (post) return post

  // Redirect from v1 to v2 id
  let idv2 = v1_to_v2[params.id]
  if (idv2) redirect(308, `/${idv2}`)
  
  error(404, "Not found")
}

This assumes that navigating to just domain.com/IDV2 is satisfactory.

To also include the slug in the redirection (remember the slug in domain.com/PAGEID/slug?), I'll assume your post object contains metadata post.id and post.slug and then we can do the redirection like this:

// ...
export function load({ params }) {
  let post = posts[params.id]
  if (post) return post

  // Redirect from v1 to v2 id
  post = posts[v1_to_v2[params.id]]
  if (post) redirect(308, `/${post.id}/${post.slug}`)

  error(404, "Not found")
}
Created (2 months ago)

Smart tabs in 2024

There is an old little-used #emacs package for smart tabs. All the languages it supports:

(require 'smart-tabs-mode)
(smart-tabs-insinuate 'c 'c++ 'java 'javascript 'cperl 'python 'ruby 'nxml)
(add-hook 'c-mode-common-hook #'indent-tabs-mode)
(add-hook 'java-mode-hook #'indent-tabs-mode)
(add-hook 'js2-mode-hook #'indent-tabs-mode)
(add-hook 'cperl-mode-hook #'indent-tabs-mode)
(add-hook 'python-mode-hook #'indent-tabs-mode)
(add-hook 'ruby-mode-hook #'indent-tabs-mode)
(add-hook 'nxml-mode-hook #'indent-tabs-mode)

Not much, is it? But that's ok, this way to do it has always been somewhat doomed.

Let's say you share a codebase with people on different platforms. It's not as if you can tell a VSCode person to install the above Emacs-Lisp package.

Autoformatters like gofmt do smart tabs for you these days, and when you can't mandate an autoformatter, the standard way to prevent chaos has been a "spaces only" rule. Maybe when we get an .editorconfig rule for smart tabs (issue 323), this can change?

(Once that's finalized, then Emacs is gonna need a new way more general smart-tabs-mode! Probably based on tree-sitter.)

As far as I can figure, "spaces only" must have been the pragmatic option since the dawn of computing, but maybe the innovations of tree-sitter and editorconfig finally make tabs a real option.


Someone points out that editorconfig's indent_style = tabs rule actually makes no sense if it's not smart tabs anyway, so it shouldn't exist. Makes you think who was using that rule before…

I.e. the choices are:

  • Spaces only: OK
  • Smart tabs: OK
  • Tabs only, used as "wide spaces" assuming they will be rendered at a specific width: no
Created (2 months ago)
Showing 417 to 420 Newer