A First Post!
1. Introduction
Hello, world! Welcome to my new blog. I plan to use this blog to document technical tips, hacks, bugs, coding “best practises,” and the like. Like many programmers, I spend heaps of my day, not implementing features, but trying to get a constellation of libraries, services, tools, and code to play nicely together. Normally, once I fix something, I immediately get back to whatever I’m supposed to be doing; however, I think it would be much more interesting to collect these in a blog!
Aesthetically-speaking, this blog is a bit ugly, but I plan to keep it that way. The entire site is generated automatically (via org-mode) from my plain-text notes. My goal is to brain-dump as fast an effortlessly as possible, so I’m going to force myself to not waste time making things pretty.
2. Content
I plan to write two types of documents on this site:
- articles
- These are normal documents, about a specific topic. These files are sorted into a topic-based directory structure. I may or may not return to update individual articles as I learn more about their topics.
- blog posts
- These posts are journal/diary/log entries. They’re sorted by date, with the most recent coming first. I’ll probably use these to rant and rave about whatever is on my mind at the time.
3. Navigation
Every article and post (like this one) is divided into numbered sections. These sections can be navigated to from the Table of Contents, in the sidebar.
The sidebar also contains a site-map linking to every article and post on the site. There’s also a JavaScript widget to help quickly filter through the site-map. When this widget has focus, you can press enter to navigate to the top-most entry.
4. Example widgets
Here I’ll list some example widgets that can easily be created with org-mode. This is mostly for my benefit. Until that muscle-memory kicks in, I can use this as a reference! 😁
4.1. Quotations
For stealing other people’s words. It seems that org-mode doesn’t generate
<cite> elements when exporting to HTML. That will remain a TODO for now.
Update: It’s not built-in, but it’s easy enough to generate your own <cite>:
#+begin_quote */Magit/* is a complete text-based user interface to Git. #+begin_cite [[https://magit.vc/][Magit Homepage]] #+end_cite #+end_quote
Magit is a complete text-based user interface to Git.
4.2. Notes
I expect to use these for more-important tid-bits of information. Warnings perhaps.
- As of org-mode 9.2, “easy templates” (like <s) have been replaced by C-c C-,. org-mode provides org-tempo.el to reimplement this functinality, but for now I’m using YASnippets instead.
- Another note.
4.3. Images
4.4. Source code
Here are a few example code blocks. I expect to include a lot of code examples in my posts.
4.4.1. Emacs Lisp
1: (defun dired-xdg-open-file () 2: "In dired, open the file named on this line." 3: (interactive) 4: (let ((file (dired-get-filename nil t))) 5: (call-process "xdg-open" nil 0 nil file)))
4.4.2. JavaScript
/** * This component loads the generated /sitemap file and displays it inline. */ export default class Sitemap extends Component { constructor(props) { super(props) this.state = { error: null, isLoaded: false, items: JSON.parse(localStorage.getItem(storageKey) || '{}'), filter: '', filterItems: {}, } this.listRef = React.createRef() this.patternCache = this.createPatternCache() this.activePath = ['org.ertt.ca'].concat( document.location.pathname.split("/").filter(part => part.length) ) } }
4.4.3. C
char* APP_NAME; int main(int argc, char *argv[]) { APP_NAME = argv[0]; if (argc < 2) { fprintf(stderr, "No command specified\n"); fprint_usage(stderr, APP_NAME); return ERR_ARGS; } const char* CMD = argv[1]; argc -= 2; argv += 2; switch(parse_command(CMD)) { case CMD_BITMAP: return cmd_bmp(argc, argv); case CMD_FONT: return cmd_font(argc, argv); case CMD_FORMAT: return cmd_fmt(argc, argv); case CMD_HELP: fprint_usage(stdout, APP_NAME); return 0; default: fprintf(stderr, "Unknown command: %s\n", CMD); /* fprint_usage(stderr, APP_NAME); */ return ERR_CMD_UNKNOWN; } }
4.4.4. ruby
module Blog VERSION = '1.0.0'.freeze def self.version if (tag = ENV['TAG']) "#{VERSION}-#{tag}" else VERSION end end end
4.4.5. HTML
<!doctype html> <html lang="en"> <head> <title>HTML!</title> <link rel="stylesheet" href="css/styles.css"> </head> <body> <script src="js/scripts.js"></script> </body> </html>
4.4.6. CSS
mark { background: #ff0; color: #000; } audio:not([controls]) { display: none; height: 0; } q { quotes: "\201C" "\201D" "\2018" "\2019"; }
4.4.7. SQL
SELECT customerName, customercity, customermail, salestotal FROM onlinecustomers AS oc JOIN orders AS o ON oc.customerid = o.customerid JOIN sales AS s ON o.orderId = s.orderId
4.4.8. Nix
{ nixpkgs , default-system ? {} , ... }: let inherit (nixpkgs.lib) head tail isAttrs isList unique zipAttrsWith; bothAttrs = l: r: isAttrs r && isAttrs l; bothLists = l: r: isList r && isList l; mergeLists = l: r: unique (l ++ r); merge = lhs: rhs: let f = attrPath: zipAttrsWith (n: values: let here = attrPath ++ [n]; l = head values; r = head (tail values); in if tail values == [] then l else if bothAttrs l r then f here values else (if bothLists l r then mergeLists l r else l) ); in f [] [rhs lhs]; nixosSystem = args: nixpkgs.lib.nixosSystem (merge default-system args); in { inherit nixosSystem; }
4.4.9. Haskell
module Y2019.Day01 (parts) where parts = ( (part1, Just "3152919") , (part2, Just "4726527") , readMasses ) part1 :: [Int] -> String part1 masses = show . sum $ fuelRequirement <$> masses part2 :: [Int] -> String part2 masses = show . sum $ totalRequirement <$> masses readMasses :: String -> [Int] readMasses = fmap read . lines fuelRequirement :: Int -> Int fuelRequirement mass = (floor $ fromIntegral mass / 3.0) - 2 totalRequirement :: Int -> Int totalRequirement mass | fuel <= 0 = 0 | otherwise = fuel + totalRequirement fuel where fuel = fuelRequirement mass
4.4.10. rust
mod logging; use init_ai::game_loop; use stdweb::js; fn main() { if cfg!(feature = "debug") { logging::setup_logging(logging::Debug); } else { logging::setup_logging(logging::Info); } js! { const game_loop = @{game_loop}; module.exports.loop = () => { // Provide actual error traces. try { game_loop(); } catch (error) { // console_error function provided by 'screeps-game-api' console_error("caught exception:", error); if (error.stack) { console_error("stack trace:", error.stack); } console_error("resetting VM next tick."); // reset the VM since we don't know if everything was cleaned up // and don't want an inconsistent state. module.exports.loop = wasm_initialize; } }; } }
4.4.11. go
func main() { dbFilename := os.Getenv("BGNOISE_DB") if len(dbFilename) == 0 { dbFilename = database.DefaultFilename } db := database.MustConnect("sqlite3", dbFilename) defer db.Close() migrator := database.Migrator{DB: db} migrator.Execute() if len(os.Args) < 2 { commands.PrintHelpAndExit(os.Stderr, 2) } else { runCommand(db, os.Args[1], os.Args[2:]) } }
4.4.12. C#
public void Main(string arg, UpdateType updateType) { if ((updateType & ManualUpdate) != 0) { _closer.Toggle(arg); } else if ((updateType & FrequencyUpdate) != 0) { _closer.Update(); } }