diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/blog_cli/README.md | 41 | ||||
-rw-r--r-- | tools/blog_cli/default.nix | 9 | ||||
-rw-r--r-- | tools/blog_cli/main.go | 209 | ||||
-rw-r--r-- | tools/cheddar/src/main.rs | 51 | ||||
-rw-r--r-- | tools/emacs-pkgs/dottime/dottime.el | 3 | ||||
-rw-r--r-- | tools/emacs-pkgs/nix-util/nix-util.el | 40 | ||||
-rw-r--r-- | tools/emacs/config/functions.el | 29 | ||||
-rw-r--r-- | tools/emacs/config/init.el | 19 | ||||
-rw-r--r-- | tools/emacs/config/look-and-feel.el | 9 | ||||
-rw-r--r-- | tools/emacs/default.nix | 20 |
10 files changed, 145 insertions, 285 deletions
diff --git a/tools/blog_cli/README.md b/tools/blog_cli/README.md deleted file mode 100644 index 7afa0fe9207a..000000000000 --- a/tools/blog_cli/README.md +++ /dev/null @@ -1,41 +0,0 @@ -tazblog CLI -=========== - -My blog stores its content in DNS, spread out over three types of `TXT` entries: - -* `TXT _posts.blog.tazj.in.`: A sorted list of posts, serialised as a JSON list of - strings (e.g. `["1486830338", "1476807384"]`) - -* `TXT _chunks.$postID.blog.tazj.in`: JSON chunks containing the blog post text - -* `TXT _meta.$postID.blog.tazj.in`: JSON blob with blog post metadata - -All JSON blobs are base64-encoded. - -This CLI tool helps to update those records. - -Each blog post data is a series of JSON-encoded structures which follow one of -these formats: - -``` -struct metadata { - chunks: int - title: string - date: date -} -``` - -Where `chunks` describes the number of chunks following this format: - -``` -struct chunk { - c: int - t: string -} -``` - -Writing a blog post to DNS means taking its text and metadata, chunking it up -and writing the chunks. - -Reading a blog post means retrieving all data, reading the metadata and then -assembling the chunks in order. diff --git a/tools/blog_cli/default.nix b/tools/blog_cli/default.nix deleted file mode 100644 index c22e4c949bc1..000000000000 --- a/tools/blog_cli/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ pkgs, ... }: - -pkgs.buildGo.program { - name = "blog_cli"; - srcs = [ ./main.go ]; - deps = with pkgs.third_party; [ - gopkgs."google.golang.org".api.dns.v1.gopkg - ]; -} // { meta.enableCI = true; } diff --git a/tools/blog_cli/main.go b/tools/blog_cli/main.go deleted file mode 100644 index db64f8378e40..000000000000 --- a/tools/blog_cli/main.go +++ /dev/null @@ -1,209 +0,0 @@ -// The tazblog CLI implements updating my blog records in DNS, see the -// README in this folder for details. -// -// The post input format is a file with the title on one line, -// followed by the date on a line, followed by an empty line, followed -// by the post text. -package main - -import ( - "context" - "encoding/base64" - "encoding/json" - "flag" - "fmt" - "io/ioutil" - "log" - "time" - - "google.golang.org/api/dns/v1" -) - -var ( - project = flag.String("project", "tazjins-infrastructure", "Target GCP project") - zone = flag.String("zone", "blog-tazj-in", "Target Cloud DNS zone") - title = flag.String("title", "", "Title of the blog post") - date = flag.String("date", "", "Date the post was written on") - infile = flag.String("text", "", "Text file containing the blog post") - id = flag.String("id", "", "Post ID - will be generated if unset") -) - -// Number of runes to include in a single chunk. If any chunks exceed -// the limit of what can be encoded, the chunk size is reduced and we -// try again. -var chunkSize = 200 - -type day time.Time - -func (d day) MarshalJSON() ([]byte, error) { - j := (time.Time(d)).Format(`"2006-01-02"`) - return []byte(j), nil -} - -type metadata struct { - Chunks int `json:"c"` - Title string `json:"t"` - Date day `json:"d"` -} - -type chunk struct { - Chunk int - Text string -} - -type post struct { - ID string - Meta metadata - Chunks []string -} - -func (p *post) writeToDNS() error { - var additions []*dns.ResourceRecordSet - additions = append(additions, &dns.ResourceRecordSet{ - Name: fmt.Sprintf("_meta.%s.blog.tazj.in.", p.ID), - Type: "TXT", - Ttl: 1200, - Rrdatas: []string{ - encodeJSON(p.Meta), - }, - }) - - for i, c := range p.Chunks { - additions = append(additions, &dns.ResourceRecordSet{ - Name: fmt.Sprintf("_%v.%s.blog.tazj.in.", i, p.ID), - Type: "TXT", - Ttl: 1200, - Rrdatas: []string{c}, - }) - } - - ctx := context.Background() - dnsSvc, err := dns.NewService(ctx) - if err != nil { - return err - } - - change := dns.Change{ - Additions: additions, - } - - _, err = dnsSvc.Changes.Create(*project, *zone, &change).Do() - if err != nil { - return err - } - - return nil -} - -// Encode given value as JSON and base64-encode it. -func encodeJSON(v interface{}) string { - outer, err := json.Marshal(v) - if err != nil { - log.Fatalln("Failed to encode JSON", err) - } - - return base64.RawStdEncoding.EncodeToString(outer) -} - -// Encode a chunk and check whether it is too large -func encodeChunk(c chunk) (string, bool) { - tooLarge := false - s := base64.RawStdEncoding.EncodeToString([]byte(c.Text)) - - if len(s) >= 255 { - tooLarge = true - } - - return s, tooLarge -} - -func createPost(id, title, text string, date day) post { - runes := []rune(text) - n := 0 - tooLarge := false - - var chunks []string - - for chunkSize < len(runes) { - c, l := encodeChunk(chunk{ - Chunk: n, - Text: string(runes[0:chunkSize:chunkSize]), - }) - - tooLarge = tooLarge || l - chunks = append(chunks, c) - runes = runes[chunkSize:] - n++ - } - - if len(runes) > 0 { - c, l := encodeChunk(chunk{ - Chunk: n, - Text: string(runes), - }) - - tooLarge = tooLarge || l - chunks = append(chunks, c) - n++ - } - - if tooLarge { - log.Println("Too large at chunk size", chunkSize) - chunkSize -= 5 - return createPost(id, title, text, date) - } - - return post{ - ID: id, - Meta: metadata{ - Chunks: n, - Title: title, - Date: date, - }, - Chunks: chunks, - } -} - -func main() { - flag.Parse() - - if *title == "" { - log.Fatalln("Post title must be set (-title)") - } - - if *infile == "" { - log.Fatalln("Post text file must be set (-text)") - } - - if *id == "" { - log.Fatalln("Post ID must be set (-id)") - } - - var postDate day - if *date != "" { - t, err := time.Parse("2006-01-02", *date) - if err != nil { - log.Fatalln("Invalid post date", err) - } - - postDate = day(t) - } else { - postDate = day(time.Now()) - } - - t, err := ioutil.ReadFile(*infile) - if err != nil { - log.Fatalln("Failed to read post:", err) - } - - post := createPost(*id, *title, string(t), postDate) - - log.Println("Writing post to DNS ...") - err = post.writeToDNS() - - if err != nil { - log.Fatalln("Failed to write post:", err) - } - - log.Println("Successfully wrote entries") -} diff --git a/tools/cheddar/src/main.rs b/tools/cheddar/src/main.rs index 0912e29ec06e..52e518cd8201 100644 --- a/tools/cheddar/src/main.rs +++ b/tools/cheddar/src/main.rs @@ -48,17 +48,41 @@ lazy_static! { // Emulates the GitHub style (subtle background hue and padding). const BLOCK_PRE: &str = "<pre style=\"background-color:#f6f8fa;padding:16px;\">\n"; -fn args_extension() -> Option<String> { - // The name of the file to be formatted is usually passed in as - // the first argument and can be used to determine a syntax set. - let args = env::args().collect::<Vec<String>>(); - if args.len() != 2 { - return None +struct Args { + /// Should Cheddar run as an about filter? (i.e. give special + /// rendering treatment to Markdown documents) + about_filter: bool, + + /// What file extension has been supplied (if any)? + extension: Option<String>, +} + +/// Parse the command-line flags passed to cheddar to determine +/// whether it is running in about-filter mode (`--about-filter`) and +/// what file extension has been supplied. +fn parse_args() -> Args { + let mut args = Args { + about_filter: false, + extension: None, + }; + + for (i, arg) in env::args().enumerate() { + if i == 0 { + continue; + } + + if arg == "--about-filter" { + args.about_filter = true; + continue; + } + + args.extension = Path::new(&arg) + .extension() + .and_then(OsStr::to_str) + .map(|s| s.to_string()); } - Path::new(&args[1]).extension() - .and_then(OsStr::to_str) - .map(|s| s.to_string()) + return args } fn should_continue(res: &io::Result<usize>) -> bool { @@ -268,9 +292,10 @@ fn format_code(extension: Option<&str>) { } fn main() { - let extension = args_extension(); - match extension.as_ref().map(String::as_str) { - Some("md") => format_markdown(), - extension => format_code(extension), + let args = parse_args(); + + match args.extension.as_ref().map(String::as_str) { + Some("md") if args.about_filter => format_markdown(), + extension => format_code(extension), } } diff --git a/tools/emacs-pkgs/dottime/dottime.el b/tools/emacs-pkgs/dottime/dottime.el index 3500b1c9f489..2446f6488f32 100644 --- a/tools/emacs-pkgs/dottime/dottime.el +++ b/tools/emacs-pkgs/dottime/dottime.el @@ -67,14 +67,13 @@ ;; This will never display offsets in the chat window, as those are ;; always visible in the modeline anyways. (when (featurep 'telega) - (require 'telega) (defun telega-ins--dottime-advice (orig timestamp) (let* ((dtime (decode-time timestamp t)) (current-ts (time-to-seconds (current-time))) (ctime (decode-time current-ts)) (today00 (telega--time-at00 current-ts ctime))) (if (> timestamp today00) - (telega-ins-fmt "%02d·%02d" (nth 2 dtime) (nth 1 dtime)) + (telega-ins (format "%02d·%02d" (nth 2 dtime) (nth 1 dtime))) (funcall orig timestamp)))) (advice-add 'telega-ins--date :around #'telega-ins--dottime-advice))) diff --git a/tools/emacs-pkgs/nix-util/nix-util.el b/tools/emacs-pkgs/nix-util/nix-util.el index 533e7e6f34c9..4b9dd31a022e 100644 --- a/tools/emacs-pkgs/nix-util/nix-util.el +++ b/tools/emacs-pkgs/nix-util/nix-util.el @@ -9,11 +9,13 @@ ;;; Commentary: ;; ;; This package adds some functionality that I find useful when -;; working in Nix buffers. +;; working in Nix buffers or programs installed from Nix. (require 'json) (require 'map) +(defvar nix-depot-path "/home/tazjin/depot") + (defun nix/prefetch-github (owner repo) ; TODO(tazjin): support different branches "Fetch the master branch of a GitHub repository and insert the call to `fetchFromGitHub' at point." @@ -38,7 +40,7 @@ ("finished\n" (let* ((json-string (with-current-buffer outbuf (buffer-string))) - (result (json-parse-string json-string))) + (result (json-read-from-string json-string))) (with-current-buffer buffer (goto-char point) (map-let (("rev" rev) ("sha256" sha256)) result @@ -64,4 +66,38 @@ :stderr errbuf :sentinel prefetch-handler))) +(defun nix/sly-from-depot (attribute) + "Start a Sly REPL configured with a Lisp matching a derivation + from my depot. + + The derivation invokes nix.buildLisp.sbclWith and is built + asynchronously. The build output is included in the error + thrown on build failures." + + (interactive "sAttribute: ") + (lexical-let* ((outbuf (get-buffer-create (format "*depot-out/%s*" attribute))) + (errbuf (get-buffer-create (format "*depot-errors/%s*" attribute))) + (expression (format "let depot = import <depot> {}; in depot.nix.buildLisp.sbclWith [ depot.%s ]" attribute)) + ;; TODO(tazjin): use <depot> + (command (list "nix-build" "--no-out-link" "-I" (format "depot=%s" nix-depot-path) "-E" expression))) + + (message "Acquiring Lisp for <depot>.%s" attribute) + (make-process :name (format "depot-nix-build/%s" attribute) + :buffer outbuf + :stderr errbuf + :command command + :sentinel + (lambda (process event) + (unwind-protect + (pcase event + ("finished\n" + (let* ((outpath (s-trim (with-current-buffer outbuf (buffer-string)))) + (lisp-path (s-concat outpath "/bin/sbcl"))) + (message "Acquired Lisp for <depot>.%s at %s" attribute lisp-path) + (sly lisp-path))) + (_ (with-current-buffer errbuf + (error "Failed to build '%s':\n%s" attribute (buffer-string))))) + (kill-buffer outbuf) + (kill-buffer errbuf)))))) + (provide 'nix-util) diff --git a/tools/emacs/config/functions.el b/tools/emacs/config/functions.el index 2b9713006837..1bec8ecd98a7 100644 --- a/tools/emacs/config/functions.el +++ b/tools/emacs/config/functions.el @@ -203,7 +203,7 @@ (save-excursion (move-end-of-line nil) (insert (format " %s TODO(%s): %s" - comment-start + (s-trim-right comment-start) (if prefix (read-string "Who needs to do this? ") (getenv "USER")) todo)))) @@ -238,4 +238,31 @@ (if prefix (text-scale-adjust 0) (set-face-attribute 'default nil :height (or to 120)))) +(defun notmuch-depot-apply-patch () + "Apply the currently opened notmuch message as a patch on the + depot." + + (interactive) + ;; The implementation works by letting notmuch render a raw message + ;; and capturing it by overriding the `view-buffer' function it + ;; calls after rendering. + ;; + ;; The buffer is then passed to `git-am'. + (cl-letf (((symbol-function 'view-buffer) + (lambda (buffer &optional exit-action) buffer))) + (if-let ((raw-buffer (notmuch-show-view-raw-message))) + (progn + (with-current-buffer raw-buffer + (call-shell-region (point-min) (point-max) "git am -C ~/depot") + (message "Patch applied!") + (kill-buffer)) + (magit-status "~/depot")) + (warn "notmuch failed to render the raw message buffer")))) + +(defun scrot-select () + "Take a screenshot based on a mouse-selection and save it to + ~/screenshots." + (interactive) + (shell-command "scrot '$a_%s.png' -s -e 'mv $f ~/screenshots/'")) + (provide 'functions) diff --git a/tools/emacs/config/init.el b/tools/emacs/config/init.el index eac109f985d0..d705274e9397 100644 --- a/tools/emacs/config/init.el +++ b/tools/emacs/config/init.el @@ -73,7 +73,9 @@ (use-package multiple-cursors) (use-package notmuch - :bind (:map global-map ("s-g m" . notmuch)) ;; g m -> gmail + :bind (:map global-map + ("s-g m" . notmuch) + ("s-g M" . counsel-notmuch)) ;; g m -> gmail :config (setq notmuch-search-oldest-first nil)) @@ -147,6 +149,7 @@ (local-set-key ">" 'self-insert-command))))) (use-package f) +(use-package geiser) (use-package go-mode :bind (:map go-mode-map ("C-c C-r" . recompile)) @@ -157,6 +160,12 @@ (use-package haskell-mode) +(use-package ielm + :hook ((inferior-emacs-lisp-mode . (lambda () + (paredit-mode) + (rainbow-delimiters-mode-enable) + (company-mode))))) + (use-package jq-mode :config (add-to-list 'auto-mode-alist '("\\.jq\\'" . jq-mode))) @@ -182,6 +191,14 @@ (use-package nginx-mode) (use-package rust-mode) +(use-package sly + :hook ((sly-mrepl-mode . (lambda () + (paredit-mode) + (rainbow-delimiters-mode-enable) + (company-mode)))) + :config + (setq common-lisp-hyperspec-root "file:///home/tazjin/docs/lisp/")) + (use-package telega :bind (:map global-map ("s-t" . telega)) :config (telega-mode-line-mode 1)) diff --git a/tools/emacs/config/look-and-feel.el b/tools/emacs/config/look-and-feel.el index 98716dde6465..5a4d874f6f0d 100644 --- a/tools/emacs/config/look-and-feel.el +++ b/tools/emacs/config/look-and-feel.el @@ -21,10 +21,9 @@ (mouse-wheel-mode t) (blink-cursor-mode -1)) -;; Configure editor fonts -(let ((font (format "Input Mono-%d" 12))) - (setq default-frame-alist `((font-backend . "xft") - (font . ,font))) +;; Configure Emacs fonts. +(let ((font (format "JetBrains Mono-%d" 12))) + (setq default-frame-alist `((font . ,font))) (set-frame-font font t t)) ;; Configure telephone-line @@ -37,7 +36,7 @@ load, battery levels on all buffers." (when (bottom-right-window-p) - (telephone-line-raw mode-line-misc-info t))) + (telephone-line-raw mode-line-misc-info t))) (defun telephone-line-setup () (telephone-line-defsegment telephone-line-last-window-segment () diff --git a/tools/emacs/default.nix b/tools/emacs/default.nix index ce515928a279..dda7788b4355 100644 --- a/tools/emacs/default.nix +++ b/tools/emacs/default.nix @@ -20,6 +20,19 @@ let emacsBinPath = lib.makeBinPath [ third_party.telega ]; identity = x: x; + + # EXWM straight from GitHub. As of 2020-02-07, XELB in nixpkgs is + # already at a recent enough version and does not need to be + # overridden. + exwmMaster = exwm.overrideAttrs(_: { + src = third_party.fetchFromGitHub { + owner = "ch11ng"; + repo = "exwm"; + rev = "48db94f48bea1137132345abfe8256cfc6219248"; + sha256 = "0jj12z6m5kvanq19gds3jpvid2mg8w28bbbq9iycl751y2sj4l1r"; + }; + }); + tazjinsEmacs = pkgfun: (emacsWithPackages(epkgs: pkgfun( # Actual ELPA packages (the enlightened!) (with epkgs.elpaPackages; [ @@ -32,6 +45,7 @@ let # MELPA packages: (with epkgs.melpaPackages; [ + ace-link browse-kill-ring cargo clojure-mode @@ -43,7 +57,7 @@ let elixir-mode elm-mode erlang - exwm + geiser go-mode gruber-darker-theme haskell-mode @@ -56,6 +70,7 @@ let ivy-prescient jq-mode kotlin-mode + lispy lsp-mode magit markdown-toc @@ -71,8 +86,8 @@ let racket-mode rainbow-delimiters refine - restclient request + restclient sly string-edit swiper @@ -102,6 +117,7 @@ let # Custom packages (with pkgs.tools.emacs-pkgs; [ carp-mode + exwmMaster dottime nix-util term-switcher |