I use a cron job to sync my [[Obsidian]] notes to a private GitHub repository every day. It’s just a simple script to do a git commit && git push, and it works mostly fine. However, the .github directory synced to iCloud significantly slows down the Obsidian startup speed on mobile.
result.unwrap and result.or are both useful functions in Gleam for working with Result types, but they serve different purposes.
result.unwrap
result.unwrap is used to extract the value from a Result, providing a default value if the Result is an Error. It’s typically used when you want to proceed with a default value rather than propagating an error.
In this example, result.unwrap allows us to use a default value (“Guest”) when the user lookup fails, ensuring that we always have a name to greet.
result.or
result.or is used to provide an alternative Result when the first Result is an Error. It’s typically used when you have a fallback operation or value that you want to try if the primary operation fails.
Typical usage:
importgleam/resultpubfnget_config_from_file()->Result(Config,String){// Simulated file read
Error("File not found")}pubfnget_default_config()->Result(Config,String){// Return a default configuration
Ok(Config(..))}pubfnget_config()->Result(Config,String){get_config_from_file()|>result.or(get_default_config())}// Usage:
pubfnmain(){caseget_config(){Ok(config)->io.println("Config loaded")Error(err)->io.println("Failed to load config: "<>err)}}
In this example, result.or allows us to try loading the configuration from a file first, and if that fails, fall back to using a default configuration. The get_config function will only return an Error if both operations fail.
Key differences and when to use each:
Use result.unwrap when you want to extract a value from a Result and have a sensible default to use if it’s an Error. This effectively “throws away” the error information.
Use result.or when you want to try an alternative operation if the first one fails, while still preserving the Result type. This allows you to chain multiple fallback options.
result.unwrap returns the unwrapped value directly, while result.or returns another Result.
result.unwrap is often used at the “edges” of your program where you need to interface with code that doesn’t use Results, while result.or is more commonly used within the “core” logic where you’re still working with Results.
Both functions are valuable tools for error handling in Gleam, and understanding when to use each can lead to more robust and expressive code.
Rust
Same principle can be applied to Rust as the design is very alike.
Here is the most valuable part of my emacs
vTerm
config with fish shell.
add source vterm.fish to your fish shell configuration file
use edit <file> in vTerm buffer will open file at the new window above vTerm buffer, similar to using code <file> in VSCode embedded terminal.
vterm.fish
# https://github.com/akermu/emacs-libvtermfunction vterm_printf
if begin
[ -n "$TMUX"]; and string match -q -r "screen|tmux""$TERM" end
# tell tmux to pass the escape sequences throughprintf"\ePtmux;\e\e]%s\007\e\\""$argv"elseif string match -q -- "screen*""$TERM"# GNU screen (screen, screen-256color, screen-256color-bce)printf"\eP\e]%s\007\e\\""$argv"elseprintf"\e]%s\e\\""$argv" end
end
function vterm_cmd --description 'Run an Emacs command among the ones been defined in vterm-eval-cmds.'set -l vterm_elisp ()for arg in $argvset -a vterm_elisp (printf'"%s" '(string replace -a -r '([\\\\"])''\\\\\\\\$1'$arg)) end
vterm_printf '51;E'(string join ''$vterm_elisp)end
if["$INSIDE_EMACS"= vterm ]set -gx EDITOR code
function clear
vterm_printf "51;Evterm-clear-scrollback" tput clear
end
# used by vterm buffer namefunction fish_title
hostname
echo":" prompt_pwd
end
function vterm_prompt_end --on-variable PWD
vterm_printf '51;A'(whoami)'@'(hostname)':'(pwd) end
function edit
set -q argv[1]; or set argv[1]"." vterm_cmd vterm-edit-file (realpath "$argv") end
end
doom emacs config
config.el
(after!vterm(defunvterm-edit-file(file)"Open a file from vterm in another window, keeping only the vterm window and the new file window."(interactive)(let((current-vterm-window(catch'found(dolist(win(window-list))(when(and(window-live-pwin)(eq'vterm-mode(buffer-local-value'major-mode(window-bufferwin))))(throw'foundwin)))))new-file-window)(whencurrent-vterm-window;; Open file in a new window from current VTerm window(select-windowcurrent-vterm-window)(setqnew-file-window(split-window-below)); Adjust split direction to preference(set-window-buffernew-file-window(find-file-noselectfile));; Delete all other windows except for VTerm and the new file window(mapc(lambda(win)(unless(or(eqwincurrent-vterm-window)(eqwinnew-file-window))(delete-windowwin)))(window-list))(select-windownew-file-window))))(add-to-list'vterm-eval-cmds'("vterm-edit-file"vterm-edit-file)))
(use-package!gleam-ts-mode:config;; setup formatter to be used by `SPC c f`(after!apheleia(setf(alist-get'gleam-ts-modeapheleia-mode-alist)'gleam)(setf(alist-get'gleamapheleia-formatters)'("gleam""format""--stdin"))))(after!treesit(add-to-list'auto-mode-alist'("\\.gleam$".gleam-ts-mode)))(after!gleam-ts-mode(unless(treesit-language-available-p'gleam);; compile the treesit grammar file the first time(gleam-ts-install-grammar)))
hack
If you, like me, use Treesitter grammar files from Nix, the tree-sitter subdirectory within the directory specified by user-emacs-directory is linked to Nix’s read-only filesystem, meaning gleam-ts-install-grammar is unable to install grammar files there.
Here’s how you can adjust treesit-extra-load-path and install the grammar file.
(after!gleam-ts-mode(setqtreesit-extra-load-path(list(expand-file-name"~/.local/tree-sitter/")))(unless(treesit-language-available-p'gleam);; hack: change `out-dir' when install language-grammar'(let((orig-treesit--install-language-grammar-1(symbol-function'treesit--install-language-grammar-1)))(cl-letf(((symbol-function'treesit--install-language-grammar-1)(lambda(out-dirlangurl)(funcallorig-treesit--install-language-grammar-1"~/.local/tree-sitter/"langurl))))(gleam-ts-install-grammar)))))
Just learned that using [[Emacs]], press C-\ to set input method to TeX, then input Greek symbols will be very easy, for example type \theta will display θ
Here are some frequently used symbols when writing math equations.
In macOS, I’ve configured a variety of shortcuts to automate tasks, for example:
Appending selected text to a specified Obsidian note.
Opening a prompt menu and sharing the selected text with ChatGPT to obtain results.
Extracting text from an image to read in an editor.
And many others.
I frequently use these actions on both my iPhone and Mac. However, on macOS, I find myself having to manually navigate through the Services menu to activate these actions, which tends to slow down my workflow.
To streamline this process, I’ve developed a script that automatically triggers a Services menu item whenever I select text and press Command + Right Click. This enhancement significantly speeds up my interaction with macOS, making my workflow more efficient.
[[Hammerspoon]] script
-- Ensure the listener is global to avoid garbage collection issuesMouseListener=hs.eventtap.new({hs.eventtap.event.types.rightMouseDown},function(e)localbuttonPressed=e:getProperty(hs.eventtap.event.properties.mouseEventButtonNumber)localcmdPressed=e:getFlags().cmdifcmdPressed==truethen-- AppleScript to open the Services menu-- https://stackoverflow.com/a/59330902/22903883hs.osascript.applescript([[
tell application "System Events"
set appName to item 1 of (get name of processes whose frontmost is true)
tell (process 1 where frontmost is true)
tell menu bar 1
tell menu bar item appName
tell menu appName
tell menu item "Services"
tell menu "Services"
click menu item "GPT: Share"
end tell
end tell
end tell
end tell
end tell
end tell
end tell]])returntrue-- Consume the right-right-clickendreturnfalseend)-- Start the event listenerMouseListener:start()
Using lsp-bridge for Language Server Protocol in Emacs
Emacs 29 introduced built-in TRAMP Docker support for editing files within containers. However, enabling auto-completion inside a container remains challenging.
After discovering
lsp-bridge
, I realized the benefit of a Python process interacting with the language server, rather than relying solely on Emacs Lisp.
I added a feature for automatic reconnection to remote SSH servers and started supporting devcontainers. Using nix-darwin to configure my MacBook, I implemented the
devcontainer feature
to install lsp-bridge and the language server within the devcontainer. This was facilitated by the
Nix ecosystem
. To make it work, I needed to start with patching the official Nix devcontainer feature and create separate projects — a long journey, but worthwhile.
Container File Buffer Handling
When opening files in a container, lsp-bridge creates an lsp-bridge buffer, inserts the file content, and uses it for editing and rendering diagnostic information. The remote file content on the container is maintained by the lsp-bridge server running inside the container.
I completed the handling of the visited file modification time for the buffer and enabled auto-revert mode to reload file content automatically when the formatter updates the file.
Using the created lsp-bridge buffer, I used set-visited-file-name to associate a buffer with a file path. However, saving the buffer after the first save prompts a warning: “File has been changed since visited.” This occurs due to discrepancies in how file timestamps are handled by Emacs through TRAMP. To resolve this, Emacs’ record of the file’s modification time must be manually updated with something like this:
Emacs’ “auto-revert” mode, which automatically reverts buffers when the displayed files change externally, is also useful when files are updated by the formatter. Therefore, the file buffer must enable auto-revert-mode.
With these pieces in place, I now have a smooth editing experience with auto-completion inside the devcontainer.
When taking notes, friction is often a good thing. If it is too easy to create a new note, you don’t write something on your own, but rather just collect information. Consequently, your brain do not process the information, so you learn less from just collecting and reading it. Physical notes have a physical constraint and a limitation on how much we can write on them, but digital notes have no such limitation and rely on self-discipline. So, when taking notes, it is too easy to write too many words. Keep it short and simple.
I use
vertico-postframe
to place my completion window at the center of screen, however when doing incremental search like doom-emacs SPC s s, it will block the man window.
Here is how to temporarily disable vertico-posframe-mode in Emacs before executing a function, like an incremental search, and then re-enable it afterward.
(defunmy-search-without-posframe()"Perform a search without `vertico-posframe-mode' temporarily."(interactive);; Disable vertico-posframe-mode if it's enabled(when(bound-and-true-pvertico-posframe-mode)(vertico-posframe-mode-1)(unwind-protect;; Perform the search(call-interactively'+default/search-buffer);; Re-enable vertico-posframe-mode(vertico-posframe-mode1))))
in config.el
(map!:leader:desc"Search without posframe""s s"#'my-search-without-posframe)