Daily Notes for 2023-06-15

ยท 804 words ยท 4 minute read

Golden ratio ๐Ÿ”—

This is kind of cool. The golden-ratio package dynamically resizes Emacs windows within frames as they become the active window. It works okay on a desktop machine, and I really like it on my laptop. Opening up a window for LSP output, for instance, kept the code buffer at a better size while still being able to track the LSP to the side.

It doesn’t work out of the box with Doom Emacs — it needs an incantation:

(use-package! golden-ratio
  :after-call pre-command-hook
  :config
  (golden-ratio-mode +1)
  ;; Using this hook for resizing windows is less precise than
  ;; `doom-switch-window-hook'.
  (remove-hook 'window-configuration-change-hook #'golden-ratio)
  (add-hook 'doom-switch-window-hook #'golden-ratio))

Demo/tutorial video:

I understand that Golden Ratio is no longer maintained. Some people say Zoom is as good, so maybe I’ll try it if I hit any of those bugs people talk about.

“When you feel the heat coming around the corner …” ๐Ÿ”—

I think I lost an hour to figuring out how to take a batch of existing Denote notes in org format and move them into Markdown, so I got very, very patient with ChatGPT and together we came up with this:

import os
import re
import argparse
from datetime import datetime
import pypandoc
import shutil

org_dir = '/Users/mph/org/notes'  # Replace with your directory path
md_dir = '/Users/mph/org/notes-md'  # Replace with the desired directory path for markdown files

def convert_org_to_md(org_file, org_path, md_dir, with_hashtags):
    # Recreate the directory structure in the markdown directory
    org_relative_path = os.path.relpath(org_path, org_dir)
    md_relative_dir = os.path.dirname(org_relative_path)
    md_output_dir = os.path.join(md_dir, md_relative_dir)
    os.makedirs(md_output_dir, exist_ok=True)

    md_file = os.path.splitext(org_file)[0] + '.md'
    md_path = os.path.join(md_output_dir, md_file)

    # Copy org file to markdown directory
    shutil.copy2(org_path, md_path)

    with open(md_path, 'r') as file:
        org_content = file.read()

    # Extract frontmatter variables from org file
    title_match = re.search(r'#\+title:\s+(.+)', org_content)
    identifier_match = re.search(r'#\+identifier:\s+(.+)', org_content)
    tags_match = re.search(r'#\+filetags:\s+(.+)', org_content)

    frontmatter = {}
    if title_match:
        frontmatter['title'] = title_match.group(1)
    if identifier_match:
        frontmatter['identifier'] = identifier_match.group(1)
    if tags_match:
        tags_string = tags_match.group(1)
        tags_list = [tag.strip(':') for tag in tags_string.split(':') if tag.strip()]
        frontmatter['tags'] = ' '.join(tags_list)

    # Update date stamp format
    org_content = re.sub(r'\[(\d{4}-\d{2}-\d{2}) (\w{3} \d{2}:\d{2})\]', r'\1T\2:00-07:00', org_content)

    # Convert org to markdown using Pandoc
    md_content = pypandoc.convert_text(org_content, 'gfm', format='org')

    # Generate new frontmatter content
    updated_frontmatter = {
        'title': frontmatter.get('title', ''),
        'date': datetime.now().strftime('%Y-%m-%dT%H:%M:%S-07:00'),
        'identifier': frontmatter.get('identifier', ''),
        'tags': frontmatter.get('tags', ''),
    }

    # Generate the new frontmatter string
    frontmatter_string = '---\n'
    for key, value in updated_frontmatter.items():
        frontmatter_string += f'{key}: {value}\n'
    frontmatter_string += '---\n'

    # Add updated frontmatter to the markdown content
    md_content = frontmatter_string + md_content

    # Insert tags as hashtags on the last line if enabled
    if with_hashtags:
        tags_line = ' '.join([f'#{tag}' for tag in frontmatter.get('tags', '').split()])
        md_content += '\n' + tags_line

    # Save the markdown file
    with open(md_path, 'w') as file:
        file.write(md_content)

def convert_directory(org_dir, md_dir, with_hashtags):
    org_files = []
    for root, _, files in os.walk(org_dir):
        for file in files:
            if file.endswith('.org'):
                org_files.append(os.path.join(root, file))

    for org_path in org_files:
        org_file = os.path.basename(org_path)
        convert_org_to_md(org_file, org_path, md_dir, with_hashtags)

# Parse command-line arguments
parser = argparse.ArgumentParser(description='Convert org files to markdown.')
parser.add_argument('--with-hashtags', action='store_true', help='Insert tags as hashtags on the last line')
args = parser.parse_args()

# Convert org files to markdown
convert_directory(org_dir, md_dir, args.with_hashtags)

I thiiiink that’s according to the Denote spec for Markdown, and I think that makes it good enough for Hugo, too, excepting links in the [[denote:12345678]] format.

So, what is it good for? Mostly just getting from an org-mode-based Denote corpus to a Markdown-based one. At least, it seems to “just work” to do that.

If you’re not heavily cross-linked and don’t mind cleaning up denote:-style links I suppose you could drop the whole thing into Obsidian. In fact, I did. Works well minus, again, denote: links. I was also personally curious about whether the whole mess would work well with Marksman — an LSP server for Markdown that has some interesting “make a wiki out of simple Markdown” features — but I’m missing something about Marksman. It doesn’t work well with the stock LSP under Doom, and while it doesn’t crash using Eglot, I’m still not sure of its utility.

I also tossed in a command line switch that adds the tags as hashtags at the bottom of the file, which is where I tend to put them, and also what I thought I needed to do until I realized that Obsidian actually understands the tags: ["foo","bar","baz"] notation in YAML frontmatter if you do a tag:#foo search in its search tool. So — if you’re a frontmatter person, just run it plain. If you’re not then --with-hashtags is your friend.

Obsidian.el ๐Ÿ”—

Another option, I guess, is obsidian.el, which is meant to provide a way to get around an Obsidian vault within Emacs. You point it at your vault directory, designate an inbox folder, and it provides ways to search by tag, etc.

I dunno. At this point it’s all just messing around and seeing how all this stuff hangs together (or doesn’t.) Fun.