chore(users): grfn -> aspen

Change-Id: I6c6847fac56f0a9a1a2209792e00a3aec5e672b9
Reviewed-on: https://cl.tvl.fyi/c/depot/+/10809
Autosubmit: aspen <root@gws.fyi>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
Reviewed-by: lukegb <lukegb@tvl.fyi>
This commit is contained in:
Aspen Smith 2024-02-11 22:00:40 -05:00 committed by clbot
parent 0ba476a426
commit 82ecd61f5c
478 changed files with 75 additions and 77 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,149 @@
;; -*- lexical-binding: t; -*-
(defalias 'ex! 'evil-ex-define-cmd)
(defun delete-file-and-buffer ()
"Kill the current buffer and deletes the file it is visiting."
(interactive)
(let ((filename (buffer-file-name)))
(when filename
(if (vc-backend filename)
(vc-delete-file filename)
(progn
(delete-file filename)
(message "Deleted file %s" filename)
(kill-buffer))))))
;;; Commands defined elsewhere
;;(ex! "al[ign]" #'+evil:align)
;;(ex! "g[lobal]" #'+evil:global)
;;; Custom commands
;; Editing
(ex! "@" #'+evil:macro-on-all-lines) ; TODO Test me
(ex! "al[ign]" #'+evil:align)
(ex! "enhtml" #'+web:encode-html-entities)
(ex! "dehtml" #'+web:decode-html-entities)
(ex! "mc" #'+evil:mc)
(ex! "iedit" #'evil-multiedit-ex-match)
(ex! "na[rrow]" #'+evil:narrow-buffer)
(ex! "retab" #'+evil:retab)
(ex! "glog" #'magit-log-buffer-file)
;; External resources
;; TODO (ex! "db" #'doom:db)
;; TODO (ex! "dbu[se]" #'doom:db-select)
;; TODO (ex! "go[ogle]" #'doom:google-search)
(ex! "lo[okup]" #'+jump:online)
(ex! "dash" #'+lookup:dash)
(ex! "dd" #'+lookup:devdocs)
(ex! "http" #'httpd-start) ; start http server
(ex! "repl" #'+eval:repl) ; invoke or send to repl
;; TODO (ex! "rx" 'doom:regex) ; open re-builder
(ex! "sh[ell]" #'+eshell:run)
(ex! "t[mux]" #'+tmux:run) ; send to tmux
(ex! "tcd" #'+tmux:cd-here) ; cd to default-directory in tmux
(ex! "x" #'doom/open-project-scratch-buffer)
;; GIT
(ex! "gist" #'+gist:send) ; send current buffer/region to gist
(ex! "gistl" #'+gist:list) ; list gists by user
(ex! "gbrowse" #'+vcs/git-browse) ; show file in github/gitlab
(ex! "gissues" #'+vcs/git-browse-issues) ; show github issues
(ex! "git" #'magit-status) ; open magit status window
(ex! "gstage" #'magit-stage)
(ex! "gunstage" #'magit-unstage)
(ex! "gblame" #'magit-blame)
(ex! "grevert" #'git-gutter:revert-hunk)
;; Dealing with buffers
(ex! "clean[up]" #'doom/cleanup-buffers)
(ex! "k[ill]" #'doom/kill-this-buffer)
(ex! "k[ill]all" #'+hlissner:kill-all-buffers)
(ex! "k[ill]m" #'+hlissner:kill-matching-buffers)
(ex! "k[ill]o" #'doom/kill-other-buffers)
(ex! "l[ast]" #'doom/popup-restore)
(ex! "m[sg]" #'view-echo-area-messages)
(ex! "pop[up]" #'doom/popup-this-buffer)
;; Project navigation
(ex! "a" #'projectile-toggle-between-implementation-and-test)
(ex! "as" #'projectile-find-implementation-or-test-other-window)
(ex! "av" #'projectile-find-implementation-or-test-other-window)
(ex! "cd" #'+hlissner:cd)
(cond ((featurep! :completion ivy)
(ex! "ag" #'+ivy:ag)
(ex! "agc[wd]" #'+ivy:ag-cwd)
(ex! "rg" #'+ivy:rg)
(ex! "rgc[wd]" #'+ivy:rg-cwd)
(ex! "sw[iper]" #'+ivy:swiper)
(ex! "todo" #'+ivy:todo))
((featurep! :completion helm)
(ex! "ag" #'+helm:ag)
(ex! "agc[wd]" #'+helm:ag-cwd)
(ex! "rg" #'+helm:rg)
(ex! "rgc[wd]" #'+helm:rg-cwd)
(ex! "sw[oop]" #'+helm:swoop)
(ex! "todo" #'+helm:todo)))
;; Project tools
(ex! "build" #'+eval/build)
(ex! "debug" #'+debug/run)
(ex! "er[rors]" #'flycheck-list-errors)
;; File operations
(ex! "cp" #'+evil:copy-this-file)
(ex! "mv" #'+evil:move-this-file)
(ex! "rm" #'+evil:delete-this-file)
;; Sessions/tabs
(ex! "sclear" #'+workspace/kill-session)
(ex! "sl[oad]" #'+workspace:load-session)
(ex! "ss[ave]" #'+workspace:save-session)
(ex! "tabcl[ose]" #'+workspace:delete)
(ex! "tabclear" #'doom/kill-all-buffers)
(ex! "tabl[ast]" #'+workspace/switch-to-last)
(ex! "tabload" #'+workspace:load)
(ex! "tabn[ew]" #'+workspace:new)
(ex! "tabn[ext]" #'+workspace:switch-next)
(ex! "tabp[rev]" #'+workspace:switch-previous)
(ex! "tabr[ename]" #'+workspace:rename)
(ex! "tabs" #'+workspace/display)
(ex! "tabsave" #'+workspace:save)
(ex! "scr[atch]" #'cider-scratch)
;; Org-mode
(ex! "cap" #'+org-capture/dwim)
(evil-define-command evil-alembic-revision (args)
(interactive "<a>")
(apply
#'generate-alembic-migration
(read-string "Message: ")
(s-split "\\s+" (or args ""))))
(ex! "arev[ision]" #'evil-alembic-revision)
(evil-define-command evil-alembic-upgrade (&optional revision)
(interactive "<a>")
(alembic-upgrade (or revision "head")))
(ex! "aup[grade]" #'evil-alembic-upgrade)
(evil-define-command evil-alembic-downgrade (&optional revision)
(interactive "<a>")
(alembic-downgrade revision))
(ex! "adown[grade]" #'evil-alembic-downgrade)
(evil-define-command evil-alembic (args)
(interactive "<a>")
(run-alembic args))
(ex! "alemb[ic]" #'evil-alembic)
;; Elixir
(add-hook! elixir-mode
(ex! "AV" #'alchemist-project-toggle-file-and-tests-other-window)
(ex! "A" #'alchemist-project-toggle-file-and-tests))

Binary file not shown.

2
users/aspen/emacs.d/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.authinfo.gpg
+private.el

View file

@ -0,0 +1,37 @@
;;; /autoload/evil.el -*- lexical-binding: t; -*-
;;;###if (featurep! :feature evil)
;;;###autoload (autoload '+hlissner:multi-next-line "/autoload/evil" nil t)
(evil-define-motion +hlissner:multi-next-line (count)
"Move down 6 lines."
:type line
(let ((line-move-visual (or visual-line-mode (derived-mode-p 'text-mode))))
(evil-line-move (* 6 (or count 1)))))
;;;###autoload (autoload '+hlissner:multi-previous-line "/autoload/evil" nil t)
(evil-define-motion +hlissner:multi-previous-line (count)
"Move up 6 lines."
:type line
(let ((line-move-visual (or visual-line-mode (derived-mode-p 'text-mode))))
(evil-line-move (- (* 6 (or count 1))))))
;;;###autoload (autoload '+hlissner:cd "/autoload/evil" nil t)
(evil-define-command +hlissner:cd ()
"Change `default-directory' with `cd'."
(interactive "<f>")
(cd input))
;;;###autoload (autoload '+hlissner:kill-all-buffers "/autoload/evil" nil t)
(evil-define-command +hlissner:kill-all-buffers (&optional bang)
"Kill all buffers. If BANG, kill current session too."
(interactive "<!>")
(if bang
(+workspace/kill-session)
(doom/kill-all-buffers)))
;;;###autoload (autoload '+hlissner:kill-matching-buffers "/autoload/evil" nil t)
(evil-define-command +hlissner:kill-matching-buffers (&optional bang pattern)
"Kill all buffers matching PATTERN regexp. If BANG, only match project
buffers."
(interactive "<a>")
(doom/kill-matching-buffers pattern bang))

View file

@ -0,0 +1,53 @@
;;; autoload/hlissner.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +hlissner/install-snippets ()
"Install my snippets from https://github.com/hlissner/emacs-snippets into
private/hlissner/snippets."
(interactive)
(doom-fetch :github "hlissner/emacs-snippets"
(expand-file-name "snippets" (doom-module-path :private 'hlissner))))
;;;###autoload
(defun +hlissner/yank-buffer-filename ()
"Copy the current buffer's path to the kill ring."
(interactive)
(if-let* ((filename (or buffer-file-name (bound-and-true-p list-buffers-directory))))
(message (kill-new (abbreviate-file-name filename)))
(error "Couldn't find filename in current buffer")))
(defmacro +hlissner-def-finder! (name dir)
"Define a pair of find-file and browse functions."
`(progn
(defun ,(intern (format "+hlissner/find-in-%s" name)) ()
(interactive)
(let ((default-directory ,dir)
projectile-project-name
projectile-require-project-root
projectile-cached-buffer-file-name
projectile-cached-project-root)
(call-interactively (command-remapping #'projectile-find-file))))
(defun ,(intern (format "+hlissner/browse-%s" name)) ()
(interactive)
(let ((default-directory ,dir))
(call-interactively (command-remapping #'find-file))))))
;;;###autoload (autoload '+hlissner/find-in-templates "autoload/hlissner" nil t)
;;;###autoload (autoload '+hlissner/browse-templates "autoload/hlissner" nil t)
(+hlissner-def-finder! templates +file-templates-dir)
;;;###autoload (autoload '+hlissner/find-in-snippets "autoload/hlissner" nil t)
;;;###autoload (autoload '+hlissner/browse-snippets "autoload/hlissner" nil t)
(+hlissner-def-finder! snippets +hlissner-snippets-dir)
;;;###autoload (autoload '+hlissner/find-in-dotfiles "autoload/hlissner" nil t)
;;;###autoload (autoload '+hlissner/browse-dotfiles "autoload/hlissner" nil t)
(+hlissner-def-finder! dotfiles (expand-file-name ".dotfiles" "~"))
;;;###autoload (autoload '+hlissner/find-in-emacsd "autoload/hlissner" nil t)
;;;###autoload (autoload '+hlissner/browse-emacsd "autoload/hlissner" nil t)
(+hlissner-def-finder! emacsd doom-emacs-dir)
;;;###autoload (autoload '+hlissner/find-in-notes "autoload/hlissner" nil t)
;;;###autoload (autoload '+hlissner/browse-notes "autoload/hlissner" nil t)
(+hlissner-def-finder! notes +org-dir)

View file

@ -0,0 +1,17 @@
;;; -*- lexical-binding: t; -*-
(load (expand-file-name "init" (or (getenv "EMACSDIR")
(expand-file-name
"../.emacs.d"
(file-name-directory (file-truename load-file-name))))))
(require 'org-clock)
(require 'org-element)
(let ((item (or org-clock-marker
(car org-clock-history))))
(when item
(with-current-buffer (marker-buffer item)
(goto-char (marker-position item))
(let ((element (org-element-at-point)))
(when (eq 'headline (car element))
(message "%s" (plist-get (cadr element) :raw-value)))))))

View file

@ -0,0 +1,53 @@
;;; -*- lexical-binding: t; -*-
(defun clojure-thing-at-point-setup ()
(interactive)
;; Used by cider-find-dwim to parse the symbol at point
(setq-local
thing-at-point-file-name-chars
(concat thing-at-point-file-name-chars
"><!?")))
(defun +grfn/clojure-setup ()
;; (flycheck-select-checker 'clj-kondo)
(require 'flycheck)
(push 'clojure-cider-kibit flycheck-disabled-checkers)
(push 'clojure-cider-eastwood flycheck-disabled-checkers)
(push 'clojure-cider-typed flycheck-disabled-checkers)
)
(after! clojure-mode
(define-clojure-indent
(PUT 2)
(POST 2)
(GET 2)
(PATCH 2)
(DELETE 2)
(context 2)
(checking 3)
(match 1)
(domonad 0)
(describe 1)
(before 1)
(it 2))
(add-hook 'clojure-mode-hook #'clojure-thing-at-point-setup)
(add-hook 'clojure-mode-hook #'+grfn/clojure-setup))
(use-package! flycheck-clojure
;; :disabled t
:after (flycheck cider)
:config
(flycheck-clojure-setup))
(after! clj-refactor
(setq cljr-magic-requires :prompt
cljr-clojure-test-declaration "[clojure.test :refer :all]"
cljr-cljc-clojure-test-declaration"#?(:clj [clojure.test :refer :all]
:cljs [cljs.test :refer-macros [deftest is testing]])"
)
(add-to-list
'cljr-magic-require-namespaces
'("s" . "clojure.spec.alpha")))
(set-popup-rule! "^\\*cider-test-report" :size 0.4)

View file

@ -0,0 +1,299 @@
;;; Commentary:
;;; TODO
;;; Code:
(require 'emacsql)
(require 'emacsql-psql)
(require 'dash)
(require 's)
(require 'cl-lib)
;;; Config
(defvar-local company-sql-db-host "localhost"
"Host of the postgresql database to query for autocomplete information")
(defvar-local company-sql-db-port 5432
"Port of the postgresql database to query for autocomplete information")
(defvar-local company-sql-db-user "postgres"
"Username of the postgresql database to query for autocomplete information")
(defvar-local company-sql-db-name nil
"PostgreSQL database name to query for autocomplete information")
;;; DB Connection
(defvar-local company-sql/connection nil)
(defun company-sql/connect ()
(unless company-sql/connection
(setq-local company-sql/connection
(emacsql-psql company-sql-db-name
:hostname company-sql-db-host
:username company-sql-db-user
:port (number-to-string company-sql-db-port))))
company-sql/connection)
;;; Utils
(defmacro comment (&rest _))
(defun ->string (x)
(cond
((stringp x) x)
((symbolp x) (symbol-name x))))
(defun alist-get-equal (key alist)
"Like `alist-get', but uses `equal' instead of `eq' for comparing keys"
(->> alist
(-find (lambda (pair) (equal key (car pair))))
(cdr)))
;;; Listing relations
(cl-defun company-sql/list-tables (conn)
(with-timeout (3)
(-map (-compose 'symbol-name 'car)
(emacsql conn
[:select [tablename]
:from pg_catalog:pg_tables
:where (and (!= schemaname '"information_schema")
(!= schemaname '"pg_catalog"))]))))
(cl-defun company-sql/list-columns (conn)
(with-timeout (3)
(-map
(lambda (row)
(propertize (symbol-name (nth 0 row))
'table-name (nth 1 row)
'data-type (nth 2 row)))
(emacsql conn
[:select [column_name
table_name
data_type]
:from information_schema:columns]))))
;;; Keywords
(defvar company-postgresql/keywords
(list
"a" "abort" "abs" "absent" "absolute" "access" "according" "action" "ada" "add"
"admin" "after" "aggregate" "all" "allocate" "also" "alter" "always" "analyse"
"analyze" "and" "any" "are" "array" "array_agg" "array_max_cardinality" "as"
"asc" "asensitive" "assertion" "assignment" "asymmetric" "at" "atomic" "attach"
"attribute" "attributes" "authorization" "avg" "backward" "base64" "before"
"begin" "begin_frame" "begin_partition" "bernoulli" "between" "bigint" "binary"
"bit" "bit_length" "blob" "blocked" "bom" "boolean" "both" "breadth" "by" "c"
"cache" "call" "called" "cardinality" "cascade" "cascaded" "case" "cast"
"catalog" "catalog_name" "ceil" "ceiling" "chain" "char" "character"
"characteristics" "characters" "character_length" "character_set_catalog"
"character_set_name" "character_set_schema" "char_length" "check" "checkpoint"
"class" "class_origin" "clob" "close" "cluster" "coalesce" "cobol" "collate"
"collation" "collation_catalog" "collation_name" "collation_schema" "collect"
"column" "columns" "column_name" "command_function" "command_function_code"
"comment" "comments" "commit" "committed" "concurrently" "condition"
"condition_number" "configuration" "conflict" "connect" "connection"
"connection_name" "constraint" "constraints" "constraint_catalog"
"constraint_name" "constraint_schema" "constructor" "contains" "content"
"continue" "control" "conversion" "convert" "copy" "corr" "corresponding" "cost"
"count" "covar_pop" "covar_samp" "create" "cross" "csv" "cube" "cume_dist"
"current" "current_catalog" "current_date" "current_default_transform_group"
"current_path" "current_role" "current_row" "current_schema" "current_time"
"current_timestamp" "current_transform_group_for_type" "current_user" "cursor"
"cursor_name" "cycle" "data" "database" "datalink" "date"
"datetime_interval_code" "datetime_interval_precision" "day" "db" "deallocate"
"dec" "decimal" "declare" "default" "defaults" "deferrable" "deferred" "defined"
"definer" "degree" "delete" "delimiter" "delimiters" "dense_rank" "depends"
"depth" "deref" "derived" "desc" "describe" "descriptor" "detach"
"deterministic" "diagnostics" "dictionary" "disable" "discard" "disconnect"
"dispatch" "distinct" "dlnewcopy" "dlpreviouscopy" "dlurlcomplete"
"dlurlcompleteonly" "dlurlcompletewrite" "dlurlpath" "dlurlpathonly"
"dlurlpathwrite" "dlurlscheme" "dlurlserver" "dlvalue" "do" "document" "domain"
"double" "drop" "dynamic" "dynamic_function" "dynamic_function_code" "each"
"element" "else" "empty" "enable" "encoding" "encrypted" "end" "end-exec"
"end_frame" "end_partition" "enforced" "enum" "equals" "escape" "event" "every"
"except" "exception" "exclude" "excluding" "exclusive" "exec" "execute" "exists"
"exp" "explain" "expression" "extension" "external" "extract" "false" "family"
"fetch" "file" "filter" "final" "first" "first_value" "flag" "float" "floor"
"following" "for" "force" "foreign" "fortran" "forward" "found" "frame_row"
"free" "freeze" "from" "fs" "full" "function" "functions" "fusion" "g" "general"
"generated" "get" "global" "go" "goto" "grant" "granted" "greatest" "group"
"grouping" "groups" "handler" "having" "header" "hex" "hierarchy" "hold" "hour"
"id" "identity" "if" "ignore" "ilike" "immediate" "immediately" "immutable"
"implementation" "implicit" "import" "in" "include" "including" "increment"
"indent" "index" "indexes" "indicator" "inherit" "inherits" "initially" "inline"
"inner" "inout" "input" "insensitive" "insert" "instance" "instantiable"
"instead" "int" "integer" "integrity" "intersect" "intersection" "interval"
"into" "invoker" "is" "isnull" "isolation" "join" "k" "key" "key_member"
"key_type" "label" "lag" "language" "large" "last" "last_value" "lateral" "lead"
"leading" "leakproof" "least" "left" "length" "level" "library" "like"
"like_regex" "limit" "link" "listen" "ln" "load" "local" "localtime"
"localtimestamp" "location" "locator" "lock" "locked" "logged" "lower" "m" "map"
"mapping" "match" "matched" "materialized" "max" "maxvalue" "max_cardinality"
"member" "merge" "message_length" "message_octet_length" "message_text" "method"
"min" "minute" "minvalue" "mod" "mode" "modifies" "module" "month" "more" "move"
"multiset" "mumps" "name" "names" "namespace" "national" "natural" "nchar"
"nclob" "nesting" "new" "next" "nfc" "nfd" "nfkc" "nfkd" "nil" "no" "none"
"normalize" "normalized" "not" "nothing" "notify" "notnull" "nowait" "nth_value"
"ntile" "null" "nullable" "nullif" "nulls" "number" "numeric" "object"
"occurrences_regex" "octets" "octet_length" "of" "off" "offset" "oids" "old"
"on" "only" "open" "operator" "option" "options" "or" "order" "ordering"
"ordinality" "others" "out" "outer" "output" "over" "overlaps" "overlay"
"overriding" "owned" "owner" "p" "pad" "parallel" "parameter" "parameter_mode"
"parameter_name" "parameter_ordinal_position" "parameter_specific_catalog"
"parameter_specific_name" "parameter_specific_schema" "parser" "partial"
"partition" "pascal" "passing" "passthrough" "password" "path" "percent"
"percentile_cont" "percentile_disc" "percent_rank" "period" "permission"
"placing" "plans" "pli" "policy" "portion" "position" "position_regex" "power"
"precedes" "preceding" "precision" "prepare" "prepared" "preserve" "primary"
"prior" "privileges" "procedural" "procedure" "procedures" "program" "public"
"publication" "quote" "range" "rank" "read" "reads" "real" "reassign" "recheck"
"recovery" "recursive" "ref" "references" "referencing" "refresh" "regr_avgx"
"regr_avgy" "regr_count" "regr_intercept" "regr_r2" "regr_slope" "regr_sxx"
"regr_sxy" "regr_syy" "reindex" "relative" "release" "rename" "repeatable"
"replace" "replica" "requiring" "reset" "respect" "restart" "restore" "restrict"
"result" "return" "returned_cardinality" "returned_length"
"returned_octet_length" "returned_sqlstate" "returning" "returns" "revoke"
"right" "role" "rollback" "rollup" "routine" "routines" "routine_catalog"
"routine_name" "routine_schema" "row" "rows" "row_count" "row_number" "rule"
"savepoint" "scale" "schema" "schemas" "schema_name" "scope" "scope_catalog"
"scope_name" "scope_schema" "scroll" "search" "second" "section" "security"
"select" "selective" "self" "sensitive" "sequence" "sequences" "serializable"
"server" "server_name" "session" "session_user" "set" "setof" "sets" "share"
"show" "similar" "simple" "size" "skip" "smallint" "snapshot" "some" "source"
"space" "specific" "specifictype" "specific_name" "sql" "sqlcode" "sqlerror"
"sqlexception" "sqlstate" "sqlwarning" "sqrt" "stable" "standalone" "start"
"state" "statement" "static" "statistics" "stddev_pop" "stddev_samp" "stdin"
"stdout" "storage" "strict" "strip" "structure" "style" "subclass_origin"
"submultiset" "subscription" "substring" "substring_regex" "succeeds" "sum"
"symmetric" "sysid" "system" "system_time" "system_user" "t" "table" "tables"
"tablesample" "tablespace" "table_name" "temp" "template" "temporary" "text"
"then" "ties" "time" "timestamp" "timezone_hour" "timezone_minute" "to" "token"
"top_level_count" "trailing" "transaction" "transactions_committed"
"transactions_rolled_back" "transaction_active" "transform" "transforms"
"translate" "translate_regex" "translation" "treat" "trigger" "trigger_catalog"
"trigger_name" "trigger_schema" "trim" "trim_array" "true" "truncate" "trusted"
"type" "types" "uescape" "unbounded" "uncommitted" "under" "unencrypted" "union"
"unique" "unknown" "unlink" "unlisten" "unlogged" "unnamed" "unnest" "until"
"untyped" "update" "upper" "uri" "usage" "user" "user_defined_type_catalog"
"user_defined_type_code" "user_defined_type_name" "user_defined_type_schema"
"using" "vacuum" "valid" "validate" "validator" "value" "values" "value_of"
"varbinary" "varchar" "variadic" "varying" "var_pop" "var_samp" "verbose"
"version" "versioning" "view" "views" "volatile" "when" "whenever" "where"
"whitespace" "width_bucket" "window" "with" "within" "without" "work" "wrapper"
"write" "xml" "xmlagg" "xmlattributes" "xmlbinary" "xmlcast" "xmlcomment"
"xmlconcat" "xmldeclaration" "xmldocument" "xmlelement" "xmlexists" "xmlforest"
"xmliterate" "xmlnamespaces" "xmlparse" "xmlpi" "xmlquery" "xmlroot" "xmlschema"
"xmlserialize" "xmltable" "xmltext" "xmlvalidate" "year" "yes" "zone"))
;;; Company backend
(cl-defun company-postgresql/candidates (prefix conn)
(-filter
(apply-partially #'s-starts-with? prefix)
(append (-map (lambda (s)
(propertize s 'company-postgresql-annotation "table"))
(-map (lambda (s)
(propertize s 'company-postgresql-annotation
(format "%s.%s %s"
(get-text-property 0 'table-name s)
s
(->
(get-text-property 0 'data-type s)
(->string)
(upcase)))))
(company-sql/list-columns conn))
(-map (lambda (s)
(propertize s 'company-postgresql-annotation "keyword"))
company-postgresql/keywords)))))
(defun company-postgresql (command &optional arg &rest _)
(interactive (list 'interactive))
(cl-case command
(interactive (company-begin-backend 'company-postgresql))
(init (company-sql/connect))
(prefix (company-grab-symbol))
(annotation
(get-text-property 0 'company-postgresql-annotation arg))
(candidates (company-postgresql/candidates
arg
(company-sql/connect)))
(duplicates t)
(ignore-case t)))
;;; org-babel company sql
(defvar-local org-company-sql/connections
())
(defun org-company-sql/connect (conn-params)
(or (alist-get-equal conn-params org-company-sql/connections)
(let ((conn (apply 'emacsql-psql conn-params)))
(add-to-list 'org-company-sql/connections (cons conn-params conn))
conn)))
(defun org-company-sql/in-sql-source-block-p ()
(let ((org-elt (org-element-at-point)))
(and (eq 'src-block (car org-elt))
(equal "sql" (plist-get (cadr org-elt)
:language)))))
(defun org-company-sql/parse-cmdline (cmdline)
(let* ((lexed (s-split (rx (one-or-more blank)) cmdline))
(go (lambda (state tokens)
(if (null tokens) ()
(let ((token (car tokens))
(tokens (cdr tokens)))
(if (null state)
(if (s-starts-with? "-" token)
(funcall go token tokens)
(cons token (funcall go state tokens)))
(cons (cons state token) ; ("-h" . "localhost")
(funcall go nil tokens)))))))
(opts (funcall go nil lexed)))
opts))
(defun org-company-sql/source-block-conn-params ()
(let* ((block-info (org-babel-get-src-block-info))
(params (caddr block-info))
(cmdline (alist-get :cmdline params))
(parsed (org-company-sql/parse-cmdline cmdline))
(opts (-filter #'listp parsed))
(positional (-filter #'stringp parsed))
(host (alist-get-equal "-h" opts))
(port (or (alist-get-equal "-p" opts)
"5432"))
(dbname (or (alist-get-equal "-d" opts)
(car positional)))
(username (or (alist-get-equal "-U" opts)
(cadr positional))))
(list dbname
:hostname host
:username username
:port port)))
(defun org-company-sql/connection-for-source-block ()
(org-company-sql/connect
(org-company-sql/source-block-conn-params)))
(defun company-ob-postgresql (command &optional arg &rest _)
(interactive (list 'interactive))
(cl-case command
(interactive (company-begin-backend 'company-ob-postgresql))
(prefix (and (org-company-sql/in-sql-source-block-p)
(company-grab-symbol)))
(annotation (get-text-property 0 'company-postgresql-annotation arg))
(candidates
(company-postgresql/candidates
arg
(org-company-sql/connection-for-source-block)))
(duplicates t)
(ignore-case t)))
;;;
(provide 'company-sql)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
;;; -*- lexical-binding: t; -*-
(load! "google-c-style")
(after! flycheck
(add-to-list 'flycheck-disabled-checkers 'c/c++-gcc)
(add-to-list 'flycheck-disabled-checkers 'c/c++-clang))
(defun +aspen/cpp-setup ()
(when (s-starts-with?
"/home/aspen/code/depot/third_party/nix"
(buffer-file-name))
(setq lsp-clients-clangd-executable "/home/aspen/code/depot/users/aspen/emacs.d/nix-clangd.sh"
lsp-clients-clangd-args nil)
(google-set-c-style)
(lsp)
(add-to-list 'flycheck-disabled-checkers 'c/c++-gcc)
(add-to-list 'flycheck-disabled-checkers 'c/c++-clang)))
(add-hook 'c++-mode-hook #'+aspen/cpp-setup)
(use-package! protobuf-mode)
(use-package! clang-format+
:config
(add-hook 'c-mode-common-hook #'clang-format+-mode))
(map!
(:map c++-mode-map
:leader
(:n "/ i" #'counsel-semantic-or-imenu)))
(comment
(setq
lsp-clients-clangd-executable
"/home/aspen/code/depot/third_party/nix/clangd.sh"
lsp-clients-clangd-args nil)
)

View file

@ -0,0 +1,53 @@
;;; -*- lexical-binding: t; -*-
(after! notmuch
(setq notmuch-saved-searches
'((:name "inbox" :query "tag:inbox tag:important not tag:trash" :key "i")
(:name "flagged" :query "tag:flagged" :key "f")
(:name "sent" :query "tag:sent" :key "s")
(:name "drafts" :query "tag:draft" :key "d")
(:name "work" :query "tag:inbox and tag:important and path:work/**"
:key "w")
(:name "personal" :query "tag:inbox and tag:important and path:personal/**"
:key "p"))
message-send-mail-function 'message-send-mail-with-sendmail
message-sendmail-f-is-evil 't
message-sendmail-envelope-from 'header
message-sendmail-extra-arguments '("--read-envelope-from"))
(add-hook! notmuch-message-mode-hook #'notmuch-company-setup))
(setq notmuch-saved-searches
'((:name "inbox" :query "tag:inbox tag:important not tag:trash" :key "i")
(:name "flagged" :query "tag:flagged" :key "f")
(:name "sent" :query "tag:sent" :key "s")
(:name "drafts" :query "tag:draft" :key "d")
(:name "work" :query "tag:inbox and tag:important and path:work/**"
:key "w")
(:name "personal" :query "tag:inbox and tag:important and path:personal/**"
:key "p"))
message-send-mail-function 'message-send-mail-with-sendmail
message-sendmail-f-is-evil 't
message-sendmail-envelope-from 'header
message-sendmail-extra-arguments '("--read-envelope-from"))
(set-popup-rule! "^\\*notmuch-saved-search-"
:ignore t)
(set-popup-rule! (lambda (_ action)
(eq (car action)
'display-buffer-same-window))
:ignore t)
(defun apply-thread-patchset (repo branch)
(interactive "Dgit repo: \nsnew branch name: ")
(let ((tid notmuch-show-thread-id)
(tmp "/tmp/notmuch-patchset"))
(shell-command (format "notmuch-extract-patch %s > %s && ( cd %s && git checkout -b %s && git am %s )"
(shell-quote-argument tid)
(shell-quote-argument tmp)
(shell-quote-argument (expand-file-name repo))
(shell-quote-argument branch)
(shell-quote-argument tmp)))))

View file

@ -0,0 +1,99 @@
;;; -*- lexical-binding: t; -*-
(require 'ghub)
(defun grfn/alist->plist (alist)
(->> alist
(-mapcat (lambda (pair)
(list (intern (concat ":" (symbol-name (car pair))))
(cdr pair))))))
;;;
(cl-defstruct pull-request url number title author repository)
(defun grfn/query-pulls (query)
(let ((resp (ghub-graphql "query reviewRequests($query: String!) {
reviewRequests: search(
type:ISSUE,
query: $query,
first: 100
) {
issueCount
nodes {
... on PullRequest {
url
number
title
author {
login
... on User { name }
}
repository {
name
owner { login }
}
}
}
}
}" `((query . ,query)))))
(->> resp
(alist-get 'data)
(alist-get 'reviewRequests)
(alist-get 'nodes)
(-map
(lambda (pr)
(apply
#'make-pull-request
(grfn/alist->plist pr)))))))
(defun grfn/requested-changes ())
(defun grfn/pull-request->org-headline (format-string level pr)
(check-type format-string string)
(check-type level integer)
(check-type pr pull-request)
(s-format (concat (make-string level ?*) " " format-string)
'aget
`((author . ,(or (->> pr (pull-request-author) (alist-get 'name))
"no author"))
(owner . ,(->> pr (pull-request-repository)
(alist-get 'owner)
(alist-get 'login)))
(repo . ,(->> pr (pull-request-repository) (alist-get 'name)))
(pr-link . ,(org-make-link-string
(pull-request-url pr)
(pull-request-title pr)))
(today . ,(format-time-string "%Y-%m-%d %a")))))
(defun grfn/org-headlines-from-review-requests (level)
"Create org-mode headlines at LEVEL from all review-requested PRs on Github"
(interactive "*nLevel: ")
(let* ((prs (grfn/query-pulls
"is:open is:pr review-requested:glittershark archived:false"))
(text (mapconcat
(apply-partially
#'grfn/pull-request->org-headline
"TODO Review ${author}'s PR on ${owner}/${repo}: ${pr-link} :pr:
SCHEDULED: <${today}>"
level) prs "\n")))
(save-mark-and-excursion
(insert text))
(org-align-tags 't)))
(defun grfn/org-headlines-from-requested-changes (level)
"Create org-mode headlines at LEVEL from all PRs with changes requested
on Github"
(interactive "*nLevel: ")
(let* ((prs (grfn/query-pulls
(concat "is:pr is:open author:glittershark archived:false "
"sort:updated-desc review:changes-requested")))
(text (mapconcat
(apply-partially
#'grfn/pull-request->org-headline
"TODO Address review comments on ${pr-link} :pr:
SCHEDULED: <${today}>"
level) prs "\n")))
(save-mark-and-excursion
(insert text))
(org-align-tags 't)))

View file

@ -0,0 +1,151 @@
;;; google-c-style.el --- Google's C/C++ style for c-mode
;; Keywords: c, tools
;; google-c-style.el is Copyright (C) 2008 Google Inc. All Rights Reserved.
;;
;; It is free software; you can redistribute it and/or modify it under the
;; terms of either:
;;
;; a) the GNU General Public License as published by the Free Software
;; Foundation; either version 1, or (at your option) any later version, or
;;
;; b) the "Artistic License".
;;; Commentary:
;; Provides the google C/C++ coding style. You may wish to add
;; `google-set-c-style' to your `c-mode-common-hook' after requiring this
;; file. For example:
;;
;; (add-hook 'c-mode-common-hook 'google-set-c-style)
;;
;; If you want the RETURN key to go to the next line and space over
;; to the right place, add this to your .emacs right after the load-file:
;;
;; (add-hook 'c-mode-common-hook 'google-make-newline-indent)
;;; Code:
;; For some reason 1) c-backward-syntactic-ws is a macro and 2) under Emacs 22
;; bytecode cannot call (unexpanded) macros at run time:
(eval-when-compile (require 'cc-defs))
;; Wrapper function needed for Emacs 21 and XEmacs (Emacs 22 offers the more
;; elegant solution of composing a list of lineup functions or quantities with
;; operators such as "add")
(defun google-c-lineup-expression-plus-4 (langelem)
"Indents to the beginning of the current C expression plus 4 spaces.
This implements title \"Function Declarations and Definitions\"
of the Google C++ Style Guide for the case where the previous
line ends with an open parenthese.
\"Current C expression\", as per the Google Style Guide and as
clarified by subsequent discussions, means the whole expression
regardless of the number of nested parentheses, but excluding
non-expression material such as \"if(\" and \"for(\" control
structures.
Suitable for inclusion in `c-offsets-alist'."
(save-excursion
(back-to-indentation)
;; Go to beginning of *previous* line:
(c-backward-syntactic-ws)
(back-to-indentation)
(cond
;; We are making a reasonable assumption that if there is a control
;; structure to indent past, it has to be at the beginning of the line.
((looking-at "\\(\\(if\\|for\\|while\\)\\s *(\\)")
(goto-char (match-end 1)))
;; For constructor initializer lists, the reference point for line-up is
;; the token after the initial colon.
((looking-at ":\\s *")
(goto-char (match-end 0))))
(vector (+ 4 (current-column)))))
;;;###autoload
(defconst google-c-style
`((c-recognize-knr-p . nil)
(c-enable-xemacs-performance-kludge-p . t) ; speed up indentation in XEmacs
(c-basic-offset . 2)
(indent-tabs-mode . nil)
(c-comment-only-line-offset . 0)
(c-hanging-braces-alist . ((defun-open after)
(defun-close before after)
(class-open after)
(class-close before after)
(inexpr-class-open after)
(inexpr-class-close before)
(namespace-open after)
(inline-open after)
(inline-close before after)
(block-open after)
(block-close . c-snug-do-while)
(extern-lang-open after)
(extern-lang-close after)
(statement-case-open after)
(substatement-open after)))
(c-hanging-colons-alist . ((case-label)
(label after)
(access-label after)
(member-init-intro before)
(inher-intro)))
(c-hanging-semi&comma-criteria
. (c-semi&comma-no-newlines-for-oneline-inliners
c-semi&comma-inside-parenlist
c-semi&comma-no-newlines-before-nonblanks))
(c-indent-comments-syntactically-p . t)
(comment-column . 40)
(c-indent-comment-alist . ((other . (space . 2))))
(c-cleanup-list . (brace-else-brace
brace-elseif-brace
brace-catch-brace
empty-defun-braces
defun-close-semi
list-close-comma
scope-operator))
(c-offsets-alist . ((arglist-intro google-c-lineup-expression-plus-4)
(func-decl-cont . ++)
(member-init-intro . ++)
(inher-intro . ++)
(comment-intro . 0)
(arglist-close . c-lineup-arglist)
(topmost-intro . 0)
(block-open . 0)
(inline-open . 0)
(substatement-open . 0)
(statement-cont
.
(,(when (fboundp 'c-no-indent-after-java-annotations)
'c-no-indent-after-java-annotations)
,(when (fboundp 'c-lineup-assignments)
'c-lineup-assignments)
++))
(label . /)
(case-label . +)
(statement-case-open . +)
(statement-case-intro . +) ; case w/o {
(access-label . /)
(innamespace . 0))))
"Google C/C++ Programming Style.")
;;;###autoload
(defun google-set-c-style ()
"Set the current buffer's c-style to Google C/C++ Programming
Style. Meant to be added to `c-mode-common-hook'."
(interactive)
(make-local-variable 'c-tab-always-indent)
(setq c-tab-always-indent t)
(c-add-style "Google" google-c-style t))
;;;###autoload
(defun google-make-newline-indent ()
"Sets up preferred newline behavior. Not set by default. Meant
to be added to `c-mode-common-hook'."
(interactive)
(define-key c-mode-base-map "\C-m" 'newline-and-indent)
(define-key c-mode-base-map [ret] 'newline-and-indent))
(provide 'google-c-style)
;;; google-c-style.el ends here

128
users/aspen/emacs.d/grid.el Normal file
View file

@ -0,0 +1,128 @@
;;; -*- lexical-binding: t; -*-
(require 's)
(defun grfn/all-match-groups (s)
(loop for n from 1
for x = (match-string n s)
while x
collect x))
(defun projectile-grid-ff (path &optional ask)
"Call `find-file' function on PATH when it is not nil and the file exists.
If file does not exist and ASK in not nil it will ask user to proceed."
(if (or (and path (file-exists-p path))
(and ask (yes-or-no-p
(s-lex-format
"File does not exists. Create a new buffer ${path} ?"))))
(find-file path)))
(defun projectile-grid-goto-file (filepath &optional ask)
"Find FILEPATH after expanding root. ASK is passed straight to `projectile-grid-ff'."
(projectile-grid-ff (projectile-expand-root filepath) ask))
(defun projectile-grid-choices (ds)
"Uses `projectile-dir-files' function to find files in directories.
The DIRS is list of lists consisting of a directory path and regexp to filter files from that directory.
Optional third element can be present in the DS list. The third element will be a prefix to be placed before
the filename in the resulting choice.
Returns a hash table with keys being short names (choices) and values being relative paths to the files."
(loop with hash = (make-hash-table :test 'equal)
for (dir re prefix) in ds do
(loop for file in (projectile-dir-files (projectile-expand-root dir)) do
(when (string-match re file)
(puthash
(concat (or prefix "")
(s-join "/" (grfn/all-match-groups file)))
(concat dir file)
hash)))
finally return hash))
(defmacro projectile-grid-find-resource (prompt dirs &optional newfile-template)
"Presents files from DIRS with PROMPT to the user using `projectile-completing-read'.
If users chooses a non existant file and NEWFILE-TEMPLATE is not nil
it will use that variable to interpolate the name for the new file.
NEWFILE-TEMPLATE will be the argument for `s-lex-format'.
The bound variable is \"filename\"."
`(lexical-let ((choices (projectile-grid-choices ,dirs)))
(projectile-completing-read
,prompt
(hash-table-keys choices)
:action
(lambda (c)
(let* ((filepath (gethash c choices))
(filename c)) ;; so `s-lex-format' can interpolate FILENAME
(if filepath
(projectile-grid-goto-file filepath)
(when-let ((newfile-template ,newfile-template))
(projectile-grid-goto-file
(funcall newfile-template filepath)
;; (cond
;; ((functionp newfile-template) (funcall newfile-template filepath))
;; ((stringp newfile-template) (s-lex-format newfile-template)))
t))))))))
(defun projectile-grid-find-model ()
"Find a model."
(interactive)
(projectile-grid-find-resource
"model: "
'(("python/urbint_lib/models/"
"\\(.+\\)\\.py$")
("python/urbint_lib/"
"\\(.+\\)/models/\\(.+\\).py$"))
(lambda (filename)
(pcase (s-split "/" filename)
(`(,model)
(s-lex-format "python/urbint_lib/models/${model}.py"))
(`(,app ,model)
(s-lex-format "python/urbint_lib/${app}/models/${model}.py"))))))
(defun projectile-grid-find-repository ()
"Find a repository."
(interactive)
(projectile-grid-find-resource
"repository: "
'(("python/urbint_lib/repositories/"
"\\(.+\\)\\.py$")
("python/urbint_lib/"
"\\(.+\\)/repositories/\\(.+\\).py$"))
(lambda (filename)
(pcase (s-split "/" filename)
(`(,repository)
(s-lex-format "python/urbint_lib/repositories/${repository}.py"))
(`(,app ,repository)
(s-lex-format "python/urbint_lib/${app}/repositories/${repository}.py"))))))
(defun projectile-grid-find-controller ()
"Find a controller."
(interactive)
(projectile-grid-find-resource
"controller: "
'(("backend/src/grid/api/controllers/"
"\\(.+\\)\\.py$")
("backend/src/grid/api/apps/"
"\\(.+\\)/controllers/\\(.+\\).py$"))
(lambda (filename)
(pcase (s-split "/" filename)
(`(,controller)
(s-lex-format "backend/src/grid/api/controllers/${controller}.py"))
(`(,app ,controller)
(s-lex-format "backend/src/grid/api/apps/${app}/controllers/${controller}.py"))))))
(setq projectile-grid-mode-map
(let ((map (make-keymap)))
(map!
(:map map
(:leader
(:desc "Edit..." :prefix "e"
:desc "Model" :n "m" #'projectile-grid-find-model
:desc "Controller" :n "c" #'projectile-grid-find-controller
:desc "Repository" :n "r" #'projectile-grid-find-repository))))
map))
(define-minor-mode projectile-grid-mode
"Minor mode for finding files in GRID"
:init-value nil
:lighter " GRID"
:keymap projectile-grid-mode-map)

175
users/aspen/emacs.d/init.el Normal file
View file

@ -0,0 +1,175 @@
;;; -*- lexical-binding: t; -*-
(defvar native-comp-deferred-compilation-deny-list nil)
(doom! :completion
company ; the ultimate code completion backend
(ivy +fuzzy
+prescient) ; a search engine for love and life
:ui
;;deft ; notational velocity for Emacs
doom ; what makes DOOM look the way it does
;doom-dashboard ; a nifty splash screen for Emacs
doom-quit ; DOOM quit-message prompts when you quit Emacs
;fill-column ; a `fill-column' indicator
hl-todo ; highlight TODO/FIXME/NOTE tags
;;indent-guides ; highlighted indent columns
modeline ; snazzy, Atom-inspired modeline, plus API
nav-flash ; blink the current line after jumping
;;neotree ; a project drawer, like NERDTree for vim
ophints ; highlight the region an operation acts on
(popup ; tame sudden yet inevitable temporary windows
+all ; catch all popups that start with an asterix
+defaults) ; default popup rules
;; ligatures ; replace bits of code with pretty symbols
;; tabbar ; FIXME an (incomplete) tab bar for Emacs
;; treemacs ; a project drawer, like neotree but cooler
unicode ; extended unicode support for various languages
vc-gutter ; vcs diff in the fringe
vi-tilde-fringe ; fringe tildes to mark beyond EOB
;;window-select ; visually switch windows
workspaces ; tab emulation, persistence & separate workspaces
:editor
(evil +everywhere); come to the dark side, we have cookies
file-templates ; auto-snippets for empty files
fold ; (nigh) universal code folding
;;(format +onsave) ; automated prettiness
;;god ; run Emacs commands without modifier keys
;;lispy ; vim for lisp, for people who dont like vim
;;multiple-cursors ; editing in many places at once
;;objed ; text object editing for the innocent
;;parinfer ; turn lisp into python, sort of
;;rotate-text ; cycle region at point between text candidates
snippets ; my elves. They type so I don't have to
word-wrap
:emacs
dired ; making dired pretty [functional]
electric ; smarter, keyword-based electric-indent
;;eshell ; a consistent, cross-platform shell (WIP)
;;term ; terminals in Emacs
(undo +tree)
vc ; version-control and Emacs, sitting in a tree
:tools
;;ansible
;;debugger ; FIXME stepping through code, to help you add bugs
;;direnv
docker
;;editorconfig ; let someone else argue about tabs vs spaces
;; ein ; tame Jupyter notebooks with emacs
(eval +overlay) ; run code, run (also, repls)
gist ; interacting with github gists
(lookup ; helps you navigate your code and documentation
+docsets) ; ...or in Dash docsets locally
lsp
;;macos ; MacOS-specific commands
magit ; a git porcelain for Emacs
make ; run make tasks from Emacs
pass ; password manager for nerds
pdf ; pdf enhancements
;;prodigy ; FIXME managing external services & code builders
;;rgb ; creating color strings
;;terraform ; infrastructure as code
;;tmux ; an API for interacting with tmux
tree-sitter ; syntax and parsing, sitting in a tree...
;;upload ; map local to remote projects via ssh/ftp
;;wakatime
;;vterm ; another terminals in Emacs
:checkers
syntax ; tasing you for every semicolon you forget
; spell ; tasing you for misspelling mispelling
:lang
agda ; types of types of types of types...
;;assembly ; assembly for fun or debugging
cc ; C/C++/Obj-C madness
clojure ; java with a lisp
common-lisp ; if you've seen one lisp, you've seen them all
; coq ; proofs-as-programs
;;crystal ; ruby at the speed of c
;;csharp ; unity, .NET, and mono shenanigans
data ; config/data formats
erlang ; an elegant language for a more civilized age
elixir ; erlang done right
;;elm ; care for a cup of TEA?
emacs-lisp ; drown in parentheses
;;ess ; emacs speaks statistics
;;go ; the hipster dialect
;; (haskell +intero) ; a language that's lazier than I am
haskell ; a language that's lazier than I am
;;hy ; readability of scheme w/ speed of python
;; idris ;
;;(java +meghanada) ; the poster child for carpal tunnel syndrome
javascript ; all(hope(abandon(ye(who(enter(here))))))
julia ; a better, faster MATLAB
;;kotlin ; a better, slicker Java(Script)
latex ; writing papers in Emacs has never been so fun
;;ledger ; an accounting system in Emacs
lua ; one-based indices? one-based indices
markdown ; writing docs for people to ignore
;;nim ; python + lisp at the speed of c
nix ; I hereby declare "nix geht mehr!"
ocaml ; an objective camel
(org ; organize your plain life in plain text
+dragndrop ; drag & drop files/images into org buffers
+attach ; custom attachment system
+babel ; running code in org
+capture ; org-capture in and outside of Emacs
+export ; Exporting org to whatever you want
;; +habit ; Keep track of your habits
+present ; Emacs for presentations
+pretty
+brain
+protocol) ; Support for org-protocol:// links
;;perl ; write code no one else can comprehend
;;php ; perl's insecure younger brother
;;plantuml ; diagrams for confusing people more
purescript ; javascript, but functional
(python +lsp) ; beautiful is better than ugly
;;qt ; the 'cutest' gui framework ever
racket ; a DSL for DSLs
rest ; Emacs as a REST client
;;ruby ; 1.step do {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
(rust +tree-sitter) ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
;;scala ; java, but good
(sh +fish) ; she sells (ba|z|fi)sh shells on the C xor
;;solidity ; do you need a blockchain? No.
;;swift ; who asked for emoji variables?
;;terra ; Earth and Moon in alignment for performance.
;;web ; the tubes
;;vala ; GObjective-C
zig
;; Applications are complex and opinionated modules that transform Emacs
;; toward a specific purpose. They may have additional dependencies and
;; should be loaded late.
:app
;;(email +gmail) ; emacs as an email client
irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader
twitter ; twitter client https://twitter.com/vnought
;;(write ; emacs as a word processor (latex + org + markdown)
;; +wordnut ; wordnet (wn) search
;; +langtool) ; a proofreader (grammar/style check) for Emacs
:email
;; (mu4e +gmail)
notmuch
:collab
;;floobits ; peer programming for a price
;;impatient-mode ; show off code over HTTP
:config
;; For literate config users. This will tangle+compile a config.org
;; literate config in your `doom-private-dir' whenever it changes.
;;literate
;; The default module sets reasonable defaults for Emacs. It also
;; provides a Spacemacs-inspired keybinding scheme and a smartparens
;; config. Use it as a reference for your own modules.
(default +bindings +smartparens))

131
users/aspen/emacs.d/irc.el Normal file
View file

@ -0,0 +1,131 @@
;;; -*- lexical-binding: t; -*-
(require 'erc)
(require 'alert)
(defvar irc-servers
'("hackint"
"libera"))
(defun irc-connect (server)
(interactive
(list (ivy-read "Server: " irc-servers)))
(let ((pw (s-trim (shell-command-to-string
(format "pass irccloud/%s" server))))
(gnutls-verify-error nil))
(erc-tls :server "bnc.irccloud.com"
:port 6697
:nick "grfn"
:password (concat "bnc@"
(s-trim (shell-command-to-string "hostname"))
":"
pw))))
(defgroup erc-alert nil
"Alert me using alert.el for important ERC messages"
:group 'erc)
(defcustom erc-noise-regexp
"\\(Logging in:\\|Signing off\\|You're now away\\|Welcome back\\)"
"This regexp matches unwanted noise."
:type 'regexp
:group 'erc)
(setq tvl-enabled? t)
(defun disable-tvl-notifications ()
(interactive)
(setq tvl-enabled? nil))
(defun enable-tvl-notifications ()
(interactive)
(setq tvl-enabled? t))
(defun erc-alert-important-p (info)
(let ((message (plist-get info :message))
(erc-message (-> info (plist-get :data) (plist-get :message)))
(erc-channel (-> info (plist-get :data) (plist-get :channel))))
(and erc-message
(not (or (string-match "^\\** *Users on #" message)
(string-match erc-noise-regexp
message)))
(or (and tvl-enabled?
(string-equal erc-channel "#tvl"))
(string-match "grfn" message)))))
(comment
last-info
erc-noise-regexp
(setq tvl-enabled? nil)
)
(defun my-erc-hook (&optional match-type nick message)
"Shows a notification, when user's nick was mentioned.
If the buffer is currently not visible, makes it sticky."
(setq last-message message)
(if (or (null match-type) (not (eq match-type 'fool)))
(let (alert-log-messages)
(alert (or message (buffer-string))
:severity (if (string-match "grfn" (or message ""))
'high 'low)
:title (or nick (buffer-name))
:data `(:message ,(or message (buffer-string))
:channel ,(or nick (buffer-name)))))))
(add-hook 'erc-text-matched-hook 'my-erc-hook)
(add-hook 'erc-insert-modify-hook 'my-erc-hook)
(defun my-erc-define-alerts (&rest ignore)
;; Unless the user has recently typed in the ERC buffer, highlight the fringe
(alert-add-rule
:status '(buried visible idle)
:severity '(moderate high urgent)
:mode 'erc-mode
:predicate
#'(lambda (info)
(and (not (eq (current-buffer) (plist-get info :buffer)))
(string-match "grfn:" (plist-get info :message))))
:persistent
#'(lambda (info)
;; If the buffer is buried, or the user has been idle for
;; `alert-reveal-idle-time' seconds, make this alert
;; persistent. Normally, alerts become persistent after
;; `alert-persist-idle-time' seconds.
(memq (plist-get info :status) '(buried idle)))
:style 'message
:continue t)
(alert-add-rule
:status 'buried
:mode 'erc-mode
:predicate #'erc-alert-important-p
:style 'libnotify
:append t)
(alert-add-rule
:status 'buried
:mode 'erc-mode
:predicate #'erc-alert-important-p
:style 'message
:append t)
(alert-add-rule
:mode 'erc-mode
:predicate #'erc-alert-important-p
:style 'log
:append t)
(alert-add-rule :mode 'erc-mode :style 'ignore :append t))
(add-hook 'erc-connect-pre-hook 'my-erc-define-alerts)
(defun fix-irc-message (msg)
(let ((msg (s-trim msg)))
(if (string-equal msg ":q") "" msg)))
(advice-add #'erc-user-input :filter-return #'fix-irc-message)
(comment
(my-erc-define-alerts)
)

View file

@ -0,0 +1,38 @@
;;; -*- lexical-binding: t; -*-
(defun grfn/sly-panettone ()
(interactive)
(sly
(concat
(s-trim
(shell-command-to-string
"nix-build -o sbcl -E 'with import ~/code/depot {}; nix.buildLisp.sbclWith [web.panettone]'"))
"/bin/sbcl")))
(defun grfn/setup-lisp ()
(interactive)
(unless paxedit-mode (paxedit-mode 1))
(rainbow-delimiters-mode)
(flycheck-mode -1))
(add-hook 'common-lisp-lisp-mode-hook #'grfn/setup-lisp)
(defun sly-run-tests ()
(interactive)
;; TODO: handle other test frameworks
(let ((orig-window (get-buffer-window)))
(sly-eval '(fiveam:run!))
(funcall-interactively #'sly-mrepl-sync)
(select-window orig-window)))
(map!
(:map sly-mode-map
:n "g \\" #'sly-mrepl-sync
:n "g d" #'sly-edit-definition
:n "K" #'sly-documentation
:n "g SPC" #'sly-compile-and-load-file
:n "g RET" #'sly-run-tests)
(:map sly-mrepl-mode-map
"C-k" #'sly-mrepl-previous-prompt
"C-r" #'isearch-backward))

View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
CLANGD_FLAGS=--compile-commands-dir=/home/grfn/builds/tvix \
nix-shell /home/grfn/code/depot \
-A third_party.nix \
--run nix-clangd

View file

@ -0,0 +1,30 @@
;;; -*- lexical-binding: t; -*-
(defun nix-buffer-type ()
"Returns:
'home-manager, if the current buffer is a home-manager module
'nixos, if the current buffer is a nixos module
nil, if none of the above are the case"
(when buffer-file-name
(pcase buffer-file-name
((rx (0+ nonl) "system/home" (0+ nonl) ".nix" eos)
'home-manager)
((rx (0+ nonl) "system/system" (0+ nonl) ".nix" eos)
'nixos))))
(defun set-nix-compile-command ()
"Set the compile command for the current buffer based on the type of nix
buffer it is, per `nix-buffer-type'"
(interactive)
(when-let ((btype (nix-buffer-type)))
(setq-local
compile-command
(case btype
('home-manager "home-manager switch")
('nixos "sudo nixos-rebuild switch")))))
(add-hook 'nix-mode-hook #'set-nix-compile-command)
(map! (:map nix-mode-map
(:n "g SPC" #'compile)))

View file

@ -0,0 +1,188 @@
;;; -*- lexical-binding: t; -*-
;;; Commentary:
;;; Code:
(require 's)
(require 'dash)
(require 'alert)
(require 'org-agenda)
(defvar grfn/org-alert-interval 300
"Interval in seconds to recheck and display deadlines.")
(defvar grfn/org-alert-notification-title "*org*"
"Title to be sent with notify-send.")
(defvar grfn/org-alert-headline-regexp "\\(Sched.+:.+\\|Deadline:.+\\)"
"Regexp for headlines to search in agenda buffer.")
(defun grfn/org-alert--strip-prefix (headline)
"Remove the scheduled/deadline prefix from HEADLINE."
(replace-regexp-in-string ".*:\s+" "" headline))
(defun grfn/org-alert--unique-headlines (regexp agenda)
"Return unique headlines from the results of REGEXP in AGENDA."
(let ((matches (-distinct (-flatten (s-match-strings-all regexp agenda)))))
(--map (grfn/org-alert--strip-prefix it) matches)))
(defun grfn/org-alert--get-headlines ()
"Return the current org agenda as text only."
(with-temp-buffer
(let ((org-agenda-sticky nil)
(org-agenda-buffer-tmp-name (buffer-name)))
(ignore-errors (org-agenda-list nil "TODAY" 1))
(grfn/org-alert--unique-headlines
grfn/org-alert-headline-regexp
(buffer-substring-no-properties (point-min) (point-max))))))
(defun grfn/parse-range-string (str)
(when
(string-match (rx (group (repeat 2 (any digit))
":"
(repeat 2 (any digit)))
(optional
(and
"-"
(group (repeat 2 (any digit))
":"
(repeat 2 (any digit))))))
str)
(list
(org-read-date nil t
(match-string 1 str))
(when-let ((et (match-string 2 str))) (org-read-date nil t et)))))
(defun grfn/start-time-from-range-string (str)
(pcase-let ((`(,start-time . _) (grfn/parse-range-string str)))
start-time))
(comment
(org-agenda-list nil "TODAY" 1)
(grfn/org-alert--get-headlines)
(setq --src
(with-temp-buffer
(let ((org-agenda-sticky nil)
(org-agenda-buffer-tmp-name (buffer-name)))
(ignore-errors (org-agenda-list nil "TODAY" 1))
(buffer-substring-no-properties (point-min) (point-max)))))
(setq --entries
(with-temp-buffer
(let ((inhibit-redisplay t)
(org-agenda-sticky nil)
(org-agenda-buffer-tmp-name (buffer-name))
(org-agenda-buffer-name (buffer-name))
(org-agenda-buffer (current-buffer)))
(org-agenda-get-day-entries
(cadr (org-agenda-files nil 'ifmode))
(calendar-gregorian-from-absolute
(time-to-days (org-read-date nil t "TODAY")))))))
(loop for k in (text-properties-at 0 (car --entries))
by #'cddr
collect k)
(--map (substring-no-properties (get-text-property 0 'txt it)) --entries)
(--map (get-text-property 0 'time it) --entries)
(current-time)
(format-time-string "%R" (org-read-date nil t "10:00-11:00"))
(grfn/start-time-from-range-string "10:00")
(current-time-string (org-read-date nil t "10:00-11:00"))
(todo-state
org-habit-p
priority
warntime
ts-date
date
type
org-hd-marker
org-marker
face
undone-face
help-echo
mouse-face
done-face
org-complex-heading-regexp
org-todo-regexp
org-not-done-regexp
dotime
format
extra
time
level
txt
breadcrumbs
duration
time-of-day
org-lowest-priority
org-highest-priority
tags
org-category)
(propertize)
--src
)
(defun grfn/org-alert--headline-complete? (headline)
"Return whether HEADLINE has been completed."
(--any? (s-starts-with? it headline) org-done-keywords-for-agenda))
(defun grfn/org-alert--filter-active (deadlines)
"Remove any completed headings from the provided DEADLINES."
(-remove 'grfn/org-alert--headline-complete? deadlines))
(defun grfn/org-alert--strip-states (deadlines)
"Remove the todo states from DEADLINES."
(--map (s-trim (s-chop-prefixes org-todo-keywords-for-agenda it)) deadlines))
(defun grfn/org-alert-check ()
"Check for active, due deadlines and initiate notifications."
(interactive)
;; avoid interrupting current command.
(unless (minibufferp)
(save-window-excursion
(save-excursion
(save-restriction
(let ((active (grfn/org-alert--filter-active (grfn/org-alert--get-headlines))))
(dolist (dl (grfn/org-alert--strip-states active))
(alert dl :title grfn/org-alert-notification-title))))))
(when (get-buffer org-agenda-buffer-name)
(ignore-errors
(with-current-buffer org-agenda-buffer-name
(org-agenda-redo t))))))
(defun grfn/org-alert-enable ()
"Enable the notification timer. Cancels existing timer if running."
(interactive)
(grfn/org-alert-disable)
(run-at-time 0 grfn/org-alert-interval 'grfn/org-alert-check))
(defun grfn/org-alert-disable ()
"Cancel the running notification timer."
(interactive)
(dolist (timer timer-list)
(if (eq (elt timer 5) 'grfn/org-alert-check)
(cancel-timer timer))))
(provide 'grfn/org-alert)
;;; grfn/org-alert.el ends here

View file

@ -0,0 +1,191 @@
;;; -*- lexical-binding: t; -*-
(defun +aspen/org-setup ()
(setq-local truncate-lines -1)
(display-line-numbers-mode -1)
(line-number-mode -1))
(add-hook 'org-mode-hook #'+aspen/org-setup)
(defun notes-file (f)
(concat org-directory (if (string-prefix-p "/" f) "" "/") f))
(defun aspen/org-project-tag->key (tag)
(s-replace-regexp "^project__" "" tag))
(defun aspen/org-project-tag->name (tag)
(s-titleized-words
(s-join " " (s-split "_" (aspen/org-project-tag->key tag)))))
(defun aspen/org-project-tag->keys (tag)
(s-join "" (cons "p"
(-map (lambda (s) (substring-no-properties s 0 1))
(s-split "_" (aspen/org-project-tag->key tag))))))
(defun aspen/org-projects->agenda-commands (project-tags)
(loop for tag in project-tags
collect `(,(aspen/org-project-tag->keys tag)
,(aspen/org-project-tag->name tag)
tags-todo
,tag)))
(defun aspen/org-projects ()
(loop for (tag) in
(org-global-tags-completion-table
(directory-files-recursively "~/notes" "\\.org$"))
when (s-starts-with-p "project__" tag)
collect tag))
(comment
(aspen/org-projects->agenda-commands (aspen/org-projects))
)
(setq
org-directory (expand-file-name "~/notes")
+org-dir (expand-file-name "~/notes")
org-default-notes-file (concat org-directory "/inbox.org")
+org-default-todo-file (concat org-directory "/inbox.org")
org-agenda-files (directory-files-recursively
"~/notes" "\\.org$")
org-refile-targets '((org-agenda-files :maxlevel . 3))
org-outline-path-complete-in-steps nil
org-refile-use-outline-path t
org-file-apps `((auto-mode . emacs)
(,(rx (or (and "." (optional "x") (optional "htm") (optional "l") buffer-end)
(and buffer-start "http" (optional "s") "://")))
. "firefox %s")
(,(rx ".pdf" buffer-end) . "apvlv %s")
(,(rx "." (or "png"
"jpg"
"jpeg"
"gif"
"tif"
"tiff")
buffer-end)
. "feh %s"))
org-log-done 'time
org-archive-location "~/notes/trash::* From %s"
org-cycle-separator-lines 2
org-hidden-keywords '(title)
org-tags-column -130
org-ellipsis ""
org-imenu-depth 9
org-capture-templates
`(("t" "Todo" entry
(file +org-default-todo-file)
"* TODO %?\n%i"
:kill-buffer t)
("m" "Email" entry
(file +org-default-todo-file)
"* TODO [[%L][%:subject]] :email:\n%i")
("n" "Notes" entry
(file +org-default-todo-file)
"* %U %?\n%i"
:prepend t
:kill-buffer t)
("c" "Task note" entry
(clock)
"* %U %?\n%i[%l[Context]]\n"
:kill-buffer t
:unnarrowed t)
("p" "Projects")
("px" "Xanthous" entry
(file+headline ,(notes-file "xanthous.org") "Backlog")
"* TODO %?\nContext %a\nIn task: %K")
("pt" "Tvix" entry
(file+headline ,(notes-file "tvix.org") "Tvix TODO")
"* TODO %?\nContext %a\nIn task: %K")
("pw" "Windtunnel" entry
(file+headline ,(notes-file "windtunnel.org") "Inbox")
"* TODO %i%?\nContext: %a\nIn task: %K")
)
org-capture-templates-contexts
`(("px" ((in-file . "/home/aspen/code/depot/users/aspen/xanthous/.*")))
("e" ((in-mode . "notmuch-show-mode"))))
org-deadline-warning-days 1
org-agenda-skip-scheduled-if-deadline-is-shown 'todo
org-todo-keywords '((sequence "TODO(t)" "ACTIVE(a)" "|" "DONE(d)" "RUNNING(r)")
(sequence "NEXT(n)" "WAITING(w)" "LATER(l)" "|" "CANCELLED(c)"))
org-agenda-custom-commands
`(("i" "Inbox" tags "inbox")
("r" "Running jobs" todo "RUNNING")
("w" "@Work" tags-todo "@work")
("n" . "Next...")
("nw" "Next @Work" tags-todo "@work&next")
("nt" "Next tooling" tags-todo "tooling")
("p" . "Project...")
,@(aspen/org-projects->agenda-commands (aspen/org-projects)))
org-agenda-dim-blocked-tasks nil
org-enforce-todo-dependencies nil
org-babel-clojure-backend 'cider)
(defun +aspen/insert-work-template ()
(interactive)
(goto-char (point-min))
(forward-line)
(insert "#+TODO: TODO(t) NEXT(n) ACTIVE(a) | DONE(d) PR(p) RUNNING(r) TESTING(D)
#+TODO: BLOCKED(b) BACKLOG(l) PROPOSED(o) | CANCELLED(c)
#+FILETAGS: @work
#+FILETAGS: @work
#+PROPERTY: Effort_ALL 0 4:00 8:00 12:00 20:00 32:00
#+PROPERTY: ESTIMATE_ALL 0 1 2 3 5 8
#+PROPERTY: STORY-TYPE_ALL Feature Bug Chore
#+PROPERTY: NOBLOCKING t
#+COLUMNS: %TODO %40ITEM(Task) %17EFFORT(Estimated){:} %CLOCKSUM(Time Spent) %17STORY-TYPE(Type) %TAGS"))
(defun +aspen/insert-org-template ()
(interactive)
(pcase (buffer-file-name)
((s-contains "/work/") (+aspen/insert-work-template))))
;;; TODO: this doesn't work?
(define-auto-insert "\\.org?$" #'aspen/insert-org-template t)
(defun forge--post-submit-around---link-pr-to-org-item
(orig)
(let ((cb (funcall orig)))
(lambda (value headers status req)
(prog1 (funcall cb value headers status req)
(aspen/at-org-clocked-in-item
(let ((url (alist-get 'html_url value))
(number (alist-get 'number value)))
(org-set-property
"pull-request"
(org-make-link-string
url
(format "%s/%s/%d"
(->> value
(alist-get 'base)
(alist-get 'repo)
(alist-get 'name))
(->> value
(alist-get 'base)
(alist-get 'repo)
(alist-get 'owner)
(alist-get 'login))
number)))))))))
(advice-add
#'forge--post-submit-callback
:around #'forge--post-submit-around---link-pr-to-org-item)
(set-face-foreground 'org-block +solarized-s-base00)
(setq whitespace-global-modes '(not org-mode magit-mode vterm-mode))
(setf (alist-get 'file org-link-frame-setup) 'find-file-other-window)
(set-face-foreground 'org-block +solarized-s-base00)
;; (add-hook! org-mode
;; (set-company-backend! 'org-mode
;; '(:separate company-ob-postgresql
;; company-dabbrev
;; company-yasnippet
;; company-ispell)))

View file

@ -0,0 +1,181 @@
;;; -*- lexical-binding: t; -*-
(require 'aio)
(require 'parse-time)
(setq-local lexical-binding t)
(setq plstore-cache-passphrase-for-symmetric-encryption t)
(defvar gcal-client-id)
(defvar gcal-client-secret)
(defvar google-calendar-readonly-scope
"https://www.googleapis.com/auth/calendar.readonly")
(defvar events-file "/home/grfn/notes/events.org")
(defun google--get-token (scope client-id client-secret)
(oauth2-auth-and-store
"https://accounts.google.com/o/oauth2/v2/auth"
"https://oauth2.googleapis.com/token"
scope
client-id
client-secret))
(cl-defun google--request (url &key method params scope)
(let ((p (aio-promise))
(auth-token (google--get-token scope gcal-client-id gcal-client-secret)))
(oauth2-refresh-access auth-token)
(oauth2-url-retrieve
auth-token
url
(lambda (&rest _)
(goto-char (point-min))
(re-search-forward "^$")
(let ((resp (json-parse-buffer :object-type 'alist)))
(aio-resolve p (lambda () resp))))
nil
(or method "GET")
params)
p))
(cl-defun list-events (&key min-time max-time)
(google--request
(concat
"https://www.googleapis.com/calendar/v3/calendars/griffin@urbint.com/events"
"?timeMin=" (format-time-string "%Y-%m-%dT%T%z" min-time)
"&timeMax=" (format-time-string "%Y-%m-%dT%T%z" max-time))
:scope google-calendar-readonly-scope))
(defun last-week-events ()
(list-events :min-time (time-subtract
(current-time)
(seconds-to-time
(* 60 60 24 7)))
:max-time (current-time)))
(defun next-week-events ()
(list-events :min-time (current-time)
:max-time (time-add
(current-time)
(seconds-to-time
(* 60 60 24 7)))))
(defun attending-event? (event)
(let* ((attendees (append (alist-get 'attendees event) nil))
(self (--find (alist-get 'self it) attendees)))
(equal "accepted" (alist-get 'responseStatus self))))
(defun event->org-headline (event level)
(cl-flet ((make-time
(key)
(when-let ((raw-time (->> event (alist-get key) (alist-get 'dateTime))))
(format-time-string
(org-time-stamp-format t)
(parse-iso8601-time-string raw-time)))))
(if-let ((start-time (make-time 'start))
(end-time (make-time 'end)))
(s-format
"${headline} [[${htmlLink}][${summary}]] :event:
${startTime}--${endTime}
:PROPERTIES:
${location-prop}
:EVENT: ${htmlLink}
:END:
${description}"
(function
(lambda (k m)
(or (alist-get (intern k) m)
(format "key not found: %s" k))))
(append
event
`((headline . ,(make-string level ?*))
(startTime . ,start-time)
(endTime . ,end-time)
(location-prop
. ,(if-let ((location (alist-get 'location event)))
(s-lex-format ":LOCATION: ${location}")
"")))))
"")))
(comment
(alist-get 'foo nil)
)
(defun write-events (events)
(with-current-buffer (find-file-noselect events-file)
(save-mark-and-excursion
(save-restriction
(widen)
(erase-buffer)
(goto-char (point-min))
(insert "#+TITLE: Events")
(newline) (newline)
(prog1
(loop for event in (append events nil)
when (attending-event? event)
do
(insert (event->org-headline event 1))
(newline)
sum 1)
(org-align-tags t))))))
(defun +grfn/sync-events ()
(interactive)
(let* ((events (alist-get 'items (aio-wait-for (next-week-events))))
(num-written (write-events events)))
(message "Successfully wrote %d events" num-written)))
(comment
((kind . "calendar#event")
(etag . "\"3174776941020000\"")
(id . "SNIP")
(status . "confirmed")
(htmlLink . "https://www.google.com/calendar/event?eid=SNIP")
(created . "2020-04-01T13:30:09.000Z")
(updated . "2020-04-20T13:14:30.510Z")
(summary . "SNIP")
(description . "SNIP")
(location . "SNIP")
(creator
(email . "griffin@urbint.com")
(self . t))
(organizer
(email . "griffin@urbint.com")
(self . t))
(start
(dateTime . "2020-04-01T12:00:00-04:00")
(timeZone . "America/New_York"))
(end
(dateTime . "2020-04-01T12:30:00-04:00")
(timeZone . "America/New_York"))
(recurrence .
["RRULE:FREQ=WEEKLY;UNTIL=20200408T035959Z;BYDAY=WE"])
(iCalUID . "SNIP")
(sequence . 0)
(attendees .
[((email . "griffin@urbint.com")
(organizer . t)
(self . t)
(responseStatus . "accepted"))
((email . "SNIP")
(displayName . "SNIP")
(responseStatus . "needsAction"))])
(extendedProperties
(private
(origRecurringId . "309q48kc1dihsvbi13pnlimb5a"))
(shared
(origRecurringId . "309q48kc1dihsvbi13pnlimb5a")))
(reminders
(useDefault . t)))
(require 'icalendar)
(icalendar--convert-recurring-to-diary
nil
"RRULE:FREQ=WEEKLY;UNTIL=20200408T035959Z;BYDAY=WE"
)
)

View file

@ -0,0 +1,143 @@
;;; -*- lexical-binding: t; -*-
(require 'org)
(require 'org-agenda)
(require 'inflections)
(defun grfn/org-text-element->string (elt)
(cond
((stringp elt) elt)
((and (consp elt)
(symbolp (car elt)))
(-> elt (caddr) (grfn/org-text-element->string) (s-trim) (concat " ")))))
(defun grfn/org-element-title (elt)
(let ((title (org-element-property :title elt)))
(cond
((stringp title) title)
((listp title)
(->> title
(mapcar #'grfn/org-text-element->string)
(s-join "")
(s-trim))))))
(defun grfn/org-agenda-entry->element (agenda-entry)
;; ???
())
(defun org-elements-agenda-match (match &optional todo-only)
(setq match
(propertize match 'inherited t))
(with-temp-buffer
(let ((inhibit-redisplay (not debug-on-error))
(org-agenda-sticky nil)
(org-agenda-buffer-tmp-name (buffer-name))
(org-agenda-buffer-name (buffer-name))
(org-agenda-buffer (current-buffer))
(matcher (org-make-tags-matcher match))
result)
(org-agenda-prepare (concat "TAGS " match))
(setq match (car matcher)
matcher (cdr matcher))
(dolist (file (org-agenda-files nil 'ifmode)
result)
(catch 'nextfile
(org-check-agenda-file file)
(when-let ((buffer (if (file-exists-p file)
(org-get-agenda-file-buffer file)
(error "No such file %s" file))))
(with-current-buffer buffer
(unless (derived-mode-p 'org-mode)
(error "Agenda file %s is not in Org mode" file))
(save-excursion
(save-restriction
(if (eq buffer org-agenda-restrict)
(narrow-to-region org-agenda-restrict-begin
org-agenda-restrict-end)
(widen))
(setq result
(append result (org-scan-tags
'agenda
matcher
todo-only))))))))))))
(defun grfn/num-inbox-items ()
(length (org-elements-agenda-match "inbox" t)))
(defun grfn/num-inbox-items-message ()
(let ((n (grfn/num-inbox-items)))
(if (zerop n) ""
(format "%d %s"
n
(if (= 1 n) "item" "items")))))
(defmacro grfn/at-org-clocked-in-item (&rest body)
`(when (org-clocking-p)
(let ((m org-clock-marker))
(with-current-buffer (marker-buffer m)
(save-mark-and-excursion
(goto-char m)
(org-back-to-heading t)
,@body)))))
(defun grfn/org-element-clocked-in-task ()
(grfn/at-org-clocked-in-item
(org-element-at-point)))
(comment
(grfn/org-element-clocked-in-task)
(org-element-property :title (grfn/org-element-clocked-in-task))
)
(defun grfn/minutes->hours:minutes (minutes)
(format "%d:%02d"
(floor (/ minutes 60))
(mod minutes 60)))
(comment
(grfn/minutes->hours:minutes 1) ; => "0:01"
(grfn/minutes->hours:minutes 15) ; => "0:15"
(grfn/minutes->hours:minutes 130) ; => "2:10"
)
(defun grfn/org-current-clocked-in-task-message ()
(if (org-clocking-p)
(format "(%s) [%s]"
(->> (grfn/org-element-clocked-in-task)
(grfn/org-element-title)
(substring-no-properties)
(s-trim))
(grfn/minutes->hours:minutes
(org-clock-get-clocked-time)))
""))
(comment
(grfn/org-current-clocked-in-task-message)
)
(cl-defgeneric grfn/org-tracker-ticket-id-label (backend elt)
(org-tracker-backend/extract-issue-id backend elt))
(cl-defmethod grfn/org-tracker-ticket-id-label
((backend org-tracker-linear-backend) elt)
(when-let* ((link (plist-get elt :LINEAR-KEY)))
(string-match
(rx "[[" (one-or-more anything) "]"
"[" (group (one-or-more anything)) "]]")
link)
(match-string 1 link)))
(defun grfn/org-clocked-in-ticket-id ()
(grfn/at-org-clocked-in-item
(when-let* ((backend (org-tracker-current-backend t)))
(grfn/org-tracker-ticket-id-label
backend
(cadr (org-element-at-point))))))
(comment
(grfn/at-org-clocked-in-item
(org-tracker-backend/extract-issue-id
(org-tracker-current-backend)
(cadr (org-element-at-point))))
(grfn/org-clocked-in-ticket-id)
)

View file

@ -0,0 +1,154 @@
;; -*- no-byte-compile: t; -*-
;;; private/grfn/packages.el
(package! moody)
;; Editor
(package! solarized-theme)
(package! fill-column-indicator)
(package! flx)
(package! general
:recipe (:host github :repo "noctuid/general.el"))
(package! fill-column-indicator)
(package! writeroom-mode)
(package! dash)
(package! w3m)
(package! rainbow-mode)
(package! string-inflection)
;;; Org
(package! org-tracker
:recipe (:host file
:local-repo "~/code/org-tracker"))
(package! jiralib2)
(package! org-alert)
(package! ob-http)
(package! ob-ipython)
(package! ob-async)
(package! org-recent-headings)
(package! org-sticky-header)
(package! gnuplot)
(package! gnuplot-mode)
(package! org-d20)
;; Presentation
(package! epresent)
(package! org-tree-slide)
(package! ox-reveal)
;; Slack etc
(package! slack)
(package! alert)
;; Git
(package! evil-magit)
(package! marshal)
(package! forge)
(package!
github-review
:recipe
(:host github
:repo "charignon/github-review"
:files ("github-review.el")))
;; Elisp
(package! dash)
(package! dash-functional)
(package! s)
(package! request)
(package! predd
:recipe (:host github :repo "skeeto/predd"))
(package! aio)
;; Haskell
(package! lsp-haskell)
(package! counsel-etags)
;;; LSP
(package! lsp-mode)
(package! lsp-ui :recipe (:host github :repo "emacs-lsp/lsp-ui"))
(package! company-lsp)
(package! lsp-treemacs)
;; Rust
;; (package! rustic :disable t)
;; (package! racer :disable t)
(package! cargo)
;; Lisp
(package! paxedit)
;; Javascript
(package! flow-minor-mode)
(package! flycheck-flow)
(package! company-flow)
(package! prettier-js)
;; GraphQL
(package! graphql-mode)
;; Haskell
(package! lsp-mode)
(package! lsp-ui)
(package! lsp-haskell)
(package! company-lsp)
;; (package! lsp-imenu)
;; Clojure
(package! flycheck-clojure)
;; SQL
(package! sqlup-mode)
(package! emacsql)
(package! emacsql-psql)
;;; Python
(package! pyimport)
;; (package! yapfify)
(package! blacken)
;;; Desktop interaction
(package! counsel-spotify)
;;; Dhall
(package! dhall-mode)
;;; Kubernetes
(package! kubernetes)
(package! kubernetes-evil)
(package! k8s-mode)
;;; Stack Exchange
(package! sx)
;;; Nix
(package! nix-update
:recipe (:host github
:repo "glittershark/nix-update-el"))
(package! direnv)
;;; Sequence diagrams
(package! wsd-mode
:recipe (:host github
:repo "josteink/wsd-mode"))
;;; logic?
(package! metal-mercury-mode
:recipe (:host github
:repo "ahungry/metal-mercury-mode"))
(package! flycheck-mercury)
(package! terraform-mode)
(package! company-terraform)
(package! jsonnet-mode)
;;;
(package! znc
:recipe (:host github
:repo "sshirokov/ZNC.el"))
;;; cpp
(package! protobuf-mode)
(package! clang-format+)

View file

@ -0,0 +1,42 @@
;;; -*- lexical-binding: t; -*-
(add-to-list 'auto-mode-alist '("\\.rs$" . rust-mode))
(defun grfn/rust-setup ()
(interactive)
(direnv--maybe-update-environment)
(+evil-embrace-angle-bracket-modes-hook-h)
;; (setq lsp-rust-server 'rust-analyzer)
(setq-local whitespace-line-column 100
fill-column 100)
;; (setq rust-format-show-buffer nil)
(setq lsp-rust-analyzer-import-merge-behaviour "last"
lsp-rust-analyzer-cargo-watch-command "clippy"
lsp-rust-analyzer-cargo-watch-args ["--target-dir" "/home/grfn/code/readyset/readyset/target/rust-analyzer"]
rustic-format-trigger 'on-save
lsp-ui-doc-enable t)
;; (rust-enable-format-on-save)
(lsp))
(add-hook 'rust-mode-hook #'grfn/rust-setup)
(map!
(:map rust-mode-map
:n "g RET" #'lsp-rust-analyzer-run
:n "g R" #'lsp-find-references
:n "g d" #'lsp-find-definition
:n "g Y" #'lsp-goto-type-definition
(:localleader
"m" #'lsp-rust-analyzer-expand-macro)))
(comment
(flycheck-get-next-checkers 'lsp)
(flycheck-add-next-checker)
(flycheck-get-next-checkers 'lsp)
)
(set-company-backend! 'rust-mode
'(:separate company-capf company-yasnippet))

View file

@ -0,0 +1,61 @@
;;; -*- lexical-binding: t; -*-
;;; https://with-emacs.com/posts/ui-hacks/show-matching-lines-when-parentheses-go-off-screen/
;; we will call `blink-matching-open` ourselves...
(remove-hook 'post-self-insert-hook
#'blink-paren-post-self-insert-function)
;; this still needs to be set for `blink-matching-open` to work
(setq blink-matching-paren 'show)
(let ((ov nil)) ; keep track of the overlay
(advice-add
#'show-paren-function
:after
(defun show-paren--off-screen+ (&rest _args)
"Display matching line for off-screen paren."
(when (overlayp ov)
(delete-overlay ov))
;; check if it's appropriate to show match info,
;; see `blink-paren-post-self-insert-function'
(when (and (overlay-buffer show-paren--overlay)
(not (or cursor-in-echo-area
executing-kbd-macro
noninteractive
(minibufferp)
this-command))
(and (not (bobp))
(memq (char-syntax (char-before)) '(?\) ?\$)))
(= 1 (logand 1 (- (point)
(save-excursion
(forward-char -1)
(skip-syntax-backward "/\\")
(point))))))
;; rebind `minibuffer-message' called by
;; `blink-matching-open' to handle the overlay display
(cl-letf (((symbol-function #'minibuffer-message)
(lambda (msg &rest args)
(let ((msg (apply #'format-message msg args)))
(setq ov (display-line-overlay+
(window-start) msg ))))))
(blink-matching-open))))))
(defun display-line-overlay+ (pos str &optional face)
"Display line at POS as STR with FACE.
FACE defaults to inheriting from default and highlight."
(let ((ol (save-excursion
(goto-char pos)
(make-overlay (line-beginning-position)
(line-end-position)))))
(overlay-put ol 'display str)
(overlay-put ol 'face
(or face '(:inherit default :inherit highlight)))
ol))
(setq show-paren-style 'paren
show-paren-delay 0.03
show-paren-highlight-openparen t
show-paren-when-point-inside-paren nil
show-paren-when-point-in-periphery t)
(show-paren-mode 1)

View file

@ -0,0 +1,227 @@
;;; -*- lexical-binding: t; -*-
(require 'dash)
(require 'dash-functional)
(require 'request)
;;;
;;; Configuration
;;;
(defvar slack/token nil
"Legacy (https://api.slack.com/custom-integrations/legacy-tokens) access token")
(defvar slack/include-public-channels 't
"Whether or not to inclue public channels in the list of conversations")
(defvar slack/include-private-channels 't
"Whether or not to inclue public channels in the list of conversations")
(defvar slack/include-im 't
"Whether or not to inclue IMs (private messages) in the list of conversations")
(defvar slack/include-mpim nil
"Whether or not to inclue multi-person IMs (multi-person private messages) in
the list of conversations")
;;;
;;; Utilities
;;;
(defmacro comment (&rest _body)
"Comment out one or more s-expressions"
nil)
(defun ->list (vec) (append vec nil))
(defun json-truthy? (x) (and x (not (equal :json-false x))))
;;;
;;; Generic API integration
;;;
(defvar slack/base-url "https://slack.com/api")
(defun slack/get (path params &optional callback)
"params is an alist of query parameters"
(let* ((params-callback (if (functionp params) `(() . ,params) (cons params callback)))
(params (car params-callback)) (callback (cdr params-callback))
(params (append `(("token" . ,slack/token)) params))
(url (concat (file-name-as-directory slack/base-url) path)))
(request url
:type "GET"
:params params
:parser 'json-read
:success (cl-function
(lambda (&key data &allow-other-keys)
(funcall callback data))))))
(defun slack/post (path params &optional callback)
(let* ((params-callback (if (functionp params) `(() . ,params) (cons params callback)))
(params (car params-callback)) (callback (cdr params-callback))
(url (concat (file-name-as-directory slack/base-url) path)))
(request url
:type "POST"
:data (json-encode params)
:headers `(("Content-Type" . "application/json")
("Authorization" . ,(format "Bearer %s" slack/token)))
:success (cl-function
(lambda (&key data &allow-other-keys)
(funcall callback data))))))
;;;
;;; Specific API endpoints
;;;
;; Users
(defun slack/users (cb)
"Returns users as (id . name) pairs"
(slack/get
"users.list"
(lambda (data)
(->> data
(assoc-default 'members)
->list
(-map (lambda (user)
(cons (assoc-default 'id user)
(assoc-default 'real_name user))))
(-filter #'cdr)
(funcall cb)))))
(comment
(slack/get
"users.list"
(lambda (data) (setq response-data data)))
(slack/users (lambda (data) (setq --users data)))
)
;; Conversations
(defun slack/conversation-types ()
(->>
(list (when slack/include-public-channels "public_channel")
(when slack/include-private-channels "private_channel")
(when slack/include-im "im")
(when slack/include-mpim "mpim"))
(-filter #'identity)
(s-join ",")))
(defun channel-label (chan users-alist)
(cond
((json-truthy? (assoc-default 'is_channel chan))
(format "#%s" (assoc-default 'name chan)))
((json-truthy? (assoc-default 'is_im chan))
(let ((user-id (assoc-default 'user chan)))
(format "Private message with %s" (assoc-default user-id users-alist))))
((json-truthy? (assoc-default 'is_mpim chan))
(->> chan
(assoc-default 'purpose)
(assoc-default 'value)))))
(defun slack/conversations (cb)
"Calls `cb' with (id . '((label . \"label\") '(topic . \"topic\") '(purpose . \"purpose\"))) pairs"
(slack/get
"conversations.list"
`(("types" . ,(slack/conversation-types))
("exclude-archived" . "true"))
(lambda (data)
(setq --data data)
(slack/users
(lambda (users)
(->> data
(assoc-default 'channels)
->list
(-map
(lambda (chan)
(cons (assoc-default 'id chan)
`((label . ,(channel-label chan users))
(topic . ,(->> chan
(assoc-default 'topic)
(assoc-default 'value)))
(purpose . ,(->> chan
(assoc-default 'purpose)
(assoc-default 'value)))))))
(funcall cb)))))))
(comment
(slack/get
"conversations.list"
'(("types" . "public_channel,private_channel,im,mpim"))
(lambda (data) (setq response-data data)))
(slack/get
"conversations.list"
'(("types" . "im"))
(lambda (data) (setq response-data data)))
(slack/conversations
(lambda (convos) (setq --conversations convos)))
)
;; Messages
(cl-defun slack/post-message
(&key text channel-id (on-success #'identity))
(slack/post "chat.postMessage"
`((text . ,text)
(channel . ,channel-id)
(as_user . t))
on-success))
(comment
(slack/post-message
:text "hi slackbot"
:channel-id slackbot-channel-id
:on-success (lambda (data) (setq resp data)))
)
;;;
;;; Posting code snippets to slack
;;;
(defun prompt-for-channel (cb)
(slack/conversations
(lambda (conversations)
(ivy-read
"Select channel: "
;; TODO want to potentially use purpose / topic stuff here
(->> conversations
(-filter (lambda (c) (assoc-default 'label (cdr c))))
(-map (lambda (chan) (let ((label (assoc-default 'label (cdr chan)))
(id (car chan)))
(propertize label 'channel-id id)))))
:history 'slack/channel-history
:action (lambda (selected)
(let ((channel-id (get-text-property 0 'channel-id selected)))
(funcall cb channel-id)
(message "Sent message to %s" selected))))))
nil)
(comment
(prompt-for-channel #'message)
(->> --convos
(-filter (lambda (c) (assoc-default 'label (cdr c))))
(-map (lambda (chan) (let ((label (assoc-default 'label (cdr chan)))
(id (car chan)))
(propertize label 'channel-id id)))))
(->> --convos (car) (cdr) (assoc-default 'label))
)
(defun slack-send-code-snippet (&optional snippet-text)
(interactive
(list (buffer-substring-no-properties (mark) (point))))
(prompt-for-channel
(lambda (channel-id)
(slack/post-message
:text (format "```\n%s```" snippet-text)
:channel-id channel-id))))
(provide 'slack-snippets)

View file

@ -0,0 +1,24 @@
;;; -*- lexical-binding: t; -*-
(after! slack
(set-face-foreground 'slack-message-output-header +solarized-s-base01)
(set-face-attribute 'slack-message-output-header nil :underline nil)
(set-face-attribute 'slack-message-output-text nil :height 1.0))
(require 'slack)
(setq slack-buffer-emojify 't
slack-prefer-current-team 't
slack-thread-also-send-to-room nil)
(set-popup-rule! "^\\*Slack"
:quit nil
:select t
:side 'bottom
:ttl nil
:size 0.5)
(add-hook #'slack-message-buffer-mode-hook
(lambda () (toggle-truncate-lines -1)))
(map! (:map slack-message-buffer-mode-map
:n "q" #'delete-window))

View file

@ -0,0 +1,5 @@
# key: ann
# name: annotation
# expand-env: ((yas-indent-line 'fixed))
# --
{-# ANN ${1:module} ("${2:HLint: ignore ${3:Reduce duplication}}" :: String) #-}

View file

@ -0,0 +1,26 @@
# key: bench
# name: benchmark-module
# expand-env: ((yas-indent-line (quote fixed)))
# --
--------------------------------------------------------------------------------
module ${1:`(if (not buffer-file-name) "Module"
(let ((name (file-name-sans-extension (buffer-file-name)))
(case-fold-search nil))
(if (cl-search "bench/" name)
(replace-regexp-in-string "/" "."
(replace-regexp-in-string "^\/[^A-Z]*" ""
(car (last (split-string name "src")))))
(file-name-nondirectory name))))`} ( benchmark, main ) where
--------------------------------------------------------------------------------
import Bench.Prelude
--------------------------------------------------------------------------------
import ${1:$(s-chop-suffix "Bench" yas-text)}
--------------------------------------------------------------------------------
main :: IO ()
main = defaultMain [benchmark]
--------------------------------------------------------------------------------
benchmark :: Benchmark
benchmark = bgroup "${1:$(->> yas-text (s-chop-suffix "Bench") (s-split ".") -last-item)}" [bench "something dumb" $ nf (1 +) (1 :: Int)]

View file

@ -0,0 +1,5 @@
# key: hh
# name: header
# expand-env: ((yas-indent-line 'fixed))
# --
--------------------------------------------------------------------------------$2

View file

@ -0,0 +1,8 @@
# key: gen
# name: Hedgehog Generator
# expand-env: ((yas-indent-line (quote fixed)))
# --
gen${1:Foo} :: Gen $1
gen$1 = do
$2
pure $1{..}

View file

@ -0,0 +1,9 @@
# -*- mode: snippet -*-
# name: Hedgehog Property
# key: hprop
# expand-env: ((yas-indent-line 'fixed))
# --
hprop_${1:somethingIsAlwaysTrue} :: Property
hprop_$1 = property $ do
${2:x} <- forAll ${3:Gen.int $ Range.linear 1 100}
${4:x === x}

View file

@ -0,0 +1,8 @@
# -*- mode: snippet -*-
# name: hlint
# uuid: hlint
# expand-env: ((yas-indent-line 'fixed))
# key: hlint
# condition: t
# --
{-# ANN module ("Hlint: ignore $1" :: String) #- }

View file

@ -0,0 +1,4 @@
# key: i
# name: import-i
# --
import ${1:Prelude}

View file

@ -0,0 +1,6 @@
# -*- mode: snippet -*-
# name: inl
# key: inl
# expand-env: ((yas-indent-line 'fixed))
# --
{-# INLINE $1 #-}

View file

@ -0,0 +1,5 @@
# key: inline
# name: inline
# expand-env: ((yas-indent-line 'fixed))
# --
{-# INLINE $1 #-}

View file

@ -0,0 +1,6 @@
# -*- mode: snippet -*-
# name: language pragma
# key: lang
# expand-env: ((yas-indent-line 'fixed))
# --
{-# LANGUAGE $1 #-}

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: lens.field
# key: lens
# expand-env: ((yas-indent-line 'fixed))
# --
${1:field} :: Lens' ${2:Source} ${3:Target}
$1 = lens _${4:sourceField} $ \\${2:$(-> yas-text s-word-initials s-downcase)} ${4:$(-> yas-text s-word-initials s-downcase)} -> ${2:$(-> yas-text s-word-initials s-downcase)} { _$4 = ${4:$(-> yas-text s-word-initials s-downcase)} }

View file

@ -0,0 +1,32 @@
# -*- mode: snippet -*-
# key: module
# name: module
# condition: (= (length "module") (current-column))
# expand-env: ((yas-indent-line 'fixed))
# contributor: Luke Hoersten <luke@hoersten.org>
# --
--------------------------------------------------------------------------------
-- |
-- Module : $1
-- Description : $2
-- Maintainer : Griffin Smith <grfn@urbint.com>
-- Maturity : ${3:Draft, Usable, Maintained, OR MatureAF}
--
-- $4
--------------------------------------------------------------------------------
module ${1:`(if (not buffer-file-name) "Module"
(let ((name (file-name-sans-extension (buffer-file-name)))
(case-fold-search nil))
(if (or (cl-search "src/" name)
(cl-search "test/" name))
(replace-regexp-in-string "/" "."
(replace-regexp-in-string "^\/[^A-Z]*" ""
(car (last (split-string name "src")))))
(file-name-nondirectory name))))`}
(
) where
--------------------------------------------------------------------------------
import Prelude
--------------------------------------------------------------------------------
$0

View file

@ -0,0 +1,6 @@
# -*- mode: snippet -*-
# name: shut up, hlint
# key: dupl
# expand-env: ((yas-indent-line 'fixed))
# --
{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}

View file

@ -0,0 +1,9 @@
# -*- mode: snippet -*-
# name: test-group
# uuid: test-group
# key: testGroup
# condition: t
# --
testGroup "${1:name}"
[ $0
]

View file

@ -0,0 +1,27 @@
# -*- mode: snippet -*-
# name: test-module
# key: test
# expand-env: ((yas-indent-line 'fixed))
# --
--------------------------------------------------------------------------------
module ${1:`(if (not buffer-file-name) "Module"
(let ((name (file-name-sans-extension (buffer-file-name)))
(case-fold-search nil))
(if (cl-search "test/" name)
(replace-regexp-in-string "/" "."
(replace-regexp-in-string "^\/[^A-Z]*" ""
(car (last (split-string name "src")))))
(file-name-nondirectory name))))`} (main, test) where
--------------------------------------------------------------------------------
import Test.Prelude
--------------------------------------------------------------------------------
import ${1:$(s-chop-suffix "Spec" yas-text)}
--------------------------------------------------------------------------------
main :: IO ()
main = defaultMain test
test :: TestTree
test = testGroup "$1"
[ $0
]

View file

@ -0,0 +1,6 @@
# -*- mode: snippet -*-
# name: undefined
# key: u
# expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil))
# --
undefined$1

View file

@ -0,0 +1,4 @@
# key: at
# name: action-type
# --
export const ${1:FOO_BAR$(->> yas-text s-upcase (s-replace-all '(("-" . "_") (" " . "_"))))}: '${3:ns}/${1:$(-> yas-text s-dashed-words)}' = '$3/${1:$(-> yas-text s-dashed-words)}'$5

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: before
# key: bef
# --
before(function() {
$1
})

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: context
# key: context
# --
context('$1', function() {
$2
})

View file

@ -0,0 +1,6 @@
# key: desc
# name: describe
# --
describe('$1', () => {
$2
})

View file

@ -0,0 +1,5 @@
# -*- mode: snippet -*-
# name: expect
# key: ex
# --
expect($1).$2

View file

@ -0,0 +1,6 @@
# key: f
# name: function
# --
function $1($2) {
$3
}

View file

@ -0,0 +1,6 @@
# -*- mode: snippet -*-
# name: header
# key: hh
# expand-env: ((yas-indent-line 'fixed))
# --
////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: it
# key: it
# --
it('$1', () => {
$2
})

View file

@ -0,0 +1,5 @@
# -*- mode: snippet -*-
# name: it-pending
# key: xi
# --
it('$1')$0

View file

@ -0,0 +1,12 @@
# key: module
# name: module
# expand-env: ((yas-indent-line (quote fixed)))
# condition: (= (length "module") (current-column))
# --
/**
* @fileOverview $1
* @name ${2:`(file-name-nondirectory (buffer-file-name))`}
* @author Griffin Smith
* @license Proprietary
*/
$3

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: record
# key: rec
# --
export default class $1 extends Record({
$2
}) {}

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: test
# key: test
# --
test('$1', () => {
$2
})

View file

@ -0,0 +1,12 @@
# -*- mode: snippet -*-
# name: fetchFromGitHub
# uuid: fetchFromGitHub
# key: fetchFromGitHub
# condition: t
# --
fetchFromGitHub {
owner = "$1";
repo = "$2";
rev = "$3";
sha256 = "0000000000000000000000000000000000000000000000000000";
}

View file

@ -0,0 +1,16 @@
# key: pypkg
# name: pythonPackage
# condition: t
# --
${1:pname} = buildPythonPackage rec {
name = "\${pname}-\${version}";
pname = "$1";
version = "${2:1.0.0}";
src = fetchPypi {
inherit pname version;
sha256 = "0000000000000000000000000000000000000000000000000000";
};
propagatedBuildInputs = with pythonSelf; [
$3
];
};

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: sha256
# uuid: sha256
# key: sha256
# condition: t
# --
sha256 = "0000000000000000000000000000000000000000000000000000";

View file

@ -0,0 +1,6 @@
# key: sql
# name: SQL source block
# --
#+BEGIN_SRC sql ${1::async}
$2
#+END_SRC

View file

@ -0,0 +1,13 @@
# -*- mode: snippet -*-
# name: combat
# uuid: combat
# key: combat
# condition: t
# --
| | initiative | max hp | current hp | status | |
|-------------+------------+--------+------------+--------+------|
| Barty Barty | | | | | <--- |
| Hectoroth | | | | | |
| Xanadu | | | | | |
| Aurora | | | | | |
| EFB | | | | | |

View file

@ -0,0 +1,5 @@
# -*- mode: snippet -*-
# key: date
# name: date.org
# --
[`(format-time-string "%Y-%m-%d")`]$0

View file

@ -0,0 +1,5 @@
# -*- mode: snippet -*-
# name: date-time
# key: dt
# --
[`(format-time-string "%Y-%m-%d %H:%m:%S")`]

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: description
# key: desc
# --
:DESCRIPTION:
$1
:END:

View file

@ -0,0 +1,5 @@
# -*- mode: snippet -*-
# name: nologdone
# key: nologdone
# --
#+STARTUP: nologdone$0

View file

@ -0,0 +1,6 @@
# key: py
# name: Python source block
# --
#+BEGIN_SRC python
$0
#+END_SRC

View file

@ -0,0 +1,6 @@
# key: reveal
# name: reveal
# condition: t
# --
#+ATTR_REVEAL: :frag ${1:roll-in}
$0

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: transaction
# key: begin
# --
BEGIN;
$0
ROLLBACK;

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: use-module
# uuid: use-module
# key: use
# condition: t
# --
:- use_module(${1:library($2)}${3:, [$4]}).

View file

@ -0,0 +1,5 @@
# -*- mode: snippet -*-
# name: add_column
# key: op.add_column
# --
op.add_column('${1:table}', sa.Column('${2:name}', sa.${3:String()}))$0

View file

@ -0,0 +1,15 @@
# -*- mode: snippet -*-
# name: decorate
# uuid: decorate
# key: decorate
# condition: t
# --
def wrap(inner):
@wraps(inner)
def wrapped(*args, **kwargs):
ret = inner(*args, **kwargs)
return ret
return wrapped
return wrap

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: dunder
# uuid: dunder
# key: du
# condition: t
# --
__$1__$0

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: name
# uuid: name
# key: name
# condition: t
# --
__name__

View file

@ -0,0 +1,7 @@
# key: exec
# name: op.get_bind.execute
# --
op.get_bind().execute(
"""
`(progn (sqlup-mode) "")`$1
""")

View file

@ -0,0 +1,7 @@
# -*- mode: snippet -*-
# name: pdb
# uuid: pdb
# key: pdb
# condition: t
# --
import pdb; pdb.set_trace()

View file

@ -0,0 +1,5 @@
# key: macro_use
# name: #[macro_use]
# --
#[macro_use]
${1:extern crate} ${2:something};$0

View file

@ -0,0 +1,10 @@
# -*- mode: snippet -*-
# name: async test
# uuid: atest
# key: atest
# condition: t
# --
#[tokio::test${1:(flavor = "multi_thread")}]
async fn ${2:test_name}() {
`%`$0
}

View file

@ -0,0 +1,10 @@
# -*- mode: snippet -*-
# name: benchmark
# uuid: benchmark
# key: bench
# condition: t
# --
#[bench]
fn ${1:benchmark_name}(b: &mut Bencher) {
`%`b.iter(|| $0);
}

View file

@ -0,0 +1,10 @@
# -*- mode: snippet -*-
# name: proptest
# uuid: proptest
# key: proptest
# condition: t
# --
#[proptest]
fn ${1:test_name}($2) {
`%`$0
}

View file

@ -0,0 +1,11 @@
# -*- mode: snippet -*-
# name: test-module
# uuid: test-module
# key: tmod
# condition: t
# --
mod $1 {
use super::*;
$0
}

View file

@ -0,0 +1,9 @@
# key: tests
# name: test module
# --
#[cfg(test)]
mod ${1:tests} {
use super::*;
$0
}

View file

@ -0,0 +1,5 @@
# -*- mode: snippet -*-
# name: indent
# key: indent
# --
# expand-env: ((yas-indent-line 'fixed))

View file

@ -0,0 +1,5 @@
# -*- mode: snippet -*-
# name: count(*) group by
# key: countby
# --
SELECT count(*), ${1:column} FROM ${2:table} GROUP BY $1;

View file

@ -0,0 +1,11 @@
# -*- mode: snippet -*-
# name: variable
# uuid: variable
# key: var
# condition: t
# --
variable "${1:name}" {
type = ${2:string}
${3:default = ${4:default}}
}
$0

View file

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# name: date
# key: date
# --
`(format-time-string "%Y-%m-%d")`$0

View file

@ -0,0 +1,192 @@
;;; -*- lexical-binding: t; -*-
(require 'dash)
(load! "utils")
;;;
;;; Vars
;;;
(defvar +splitjoin/split-callbacks '()
"Alist mapping major mode symbol names to lists of split callbacks")
(defvar +splitjoin/join-callbacks '()
"Alist mapping major mode symbol names to lists of join callbacks")
;;;
;;; Definition macros
;;;
(defmacro +splitjoin/defsplit (mode name &rest body)
`(setf
(alist-get ',name (alist-get ,mode +splitjoin/split-callbacks))
(λ! () ,@body)))
(defmacro +splitjoin/defjoin (mode name &rest body)
`(setf
(alist-get ',name (alist-get ,mode +splitjoin/join-callbacks))
(λ! () ,@body)))
;;;
;;; Commands
;;;
(defun +splitjoin/split ()
(interactive)
(when-let (callbacks (->> +splitjoin/split-callbacks
(alist-get major-mode)
(-map #'cdr)))
(find-if #'funcall callbacks)))
(defun +splitjoin/join ()
(interactive)
(when-let (callbacks (->> +splitjoin/join-callbacks
(alist-get major-mode)
(-map #'cdr)))
(find-if #'funcall callbacks)))
;;;
;;; Splits and joins
;;; TODO: this should probably go in a file-per-language
;;;
(+splitjoin/defjoin
'elixir-mode
join-do
(let* ((function-pattern (rx (and (zero-or-more whitespace)
"do"
(zero-or-more whitespace)
(optional (and "#" (zero-or-more anything)))
eol)))
(end-pattern (rx bol
(zero-or-more whitespace)
"end"
(zero-or-more whitespace)
eol))
(else-pattern (rx bol
(zero-or-more whitespace)
"else"
(zero-or-more whitespace)
eol))
(lineno (line-number-at-pos))
(line (thing-at-point 'line t)))
(when-let ((do-start-pos (string-match function-pattern line)))
(cond
((string-match-p end-pattern (get-line (inc lineno)))
(modify-then-indent
(goto-line-char do-start-pos)
(insert ",")
(goto-char (line-end-position))
(insert ": nil")
(line-move 1)
(delete-line))
t)
((string-match-p end-pattern (get-line (+ 2 lineno)))
(modify-then-indent
(goto-line-char do-start-pos)
(insert ",")
(goto-char (line-end-position))
(insert ":")
(join-line t)
(line-move 1)
(delete-line))
t)
((and (string-match-p else-pattern (get-line (+ 2 lineno)))
(string-match-p end-pattern (get-line (+ 4 lineno))))
(modify-then-indent
(goto-line-char do-start-pos)
(insert ",")
(goto-char (line-end-position))
(insert ":")
(join-line t)
(goto-eol)
(insert ",")
(join-line t)
(goto-eol)
(insert ":")
(join-line t)
(line-move 1)
(delete-line))
t)))))
(comment
(string-match (rx (and bol
"if "
(one-or-more anything)
","
(zero-or-more whitespace)
"do:"
(one-or-more anything)
","
(zero-or-more whitespace)
"else:"
(one-or-more anything)))
"if 1, do: nil, else: nil")
)
(+splitjoin/defsplit
'elixir-mode
split-do-with-optional-else
(let* ((if-with-else-pattern (rx (and bol
(one-or-more anything)
","
(zero-or-more whitespace)
"do:"
(one-or-more anything)
(optional
","
(zero-or-more whitespace)
"else:"
(one-or-more anything)))))
(current-line (get-line)))
(when (string-match if-with-else-pattern current-line)
(modify-then-indent
(assert (goto-regex-on-line ",[[:space:]]*do:"))
(delete-char 1)
(assert (goto-regex-on-line ":"))
(delete-char 1)
(insert "\n")
(when (goto-regex-on-line-r ",[[:space:]]*else:")
(delete-char 1)
(insert "\n")
(assert (goto-regex-on-line ":"))
(delete-char 1)
(insert "\n"))
(goto-eol)
(insert "\nend"))
t)))
(comment
(+splitjoin/defsplit 'elixir-mode split-def
(let ((function-pattern (rx (and ","
(zero-or-more whitespace)
"do:")))
(line (thing-at-point 'line t)))
(when-let (idx (string-match function-pattern line))
(let ((beg (line-beginning-position))
(orig-line-char (- (point) (line-beginning-position))))
(save-mark-and-excursion
(goto-line-char idx)
(delete-char 1)
(goto-line-char (string-match ":" (thing-at-point 'line t)))
(delete-char 1)
(insert "\n")
(goto-eol)
(insert "\n")
(insert "end")
(evil-indent beg (+ (line-end-position) 1))))
(goto-line-char orig-line-char)
t))))
(+splitjoin/defjoin
'elixir-mode
join-if-with-else
(let* ((current-line (thing-at-point 'line)))))
(provide 'splitjoin)

View file

@ -0,0 +1,75 @@
;;; -*- lexical-binding: t; -*-
;;; https://www.emacswiki.org/emacs/StringAtPoint
(defun ourcomments-string-or-comment-bounds-1 (what)
(save-restriction
(widen)
(let* ((here (point))
;; Fix-me: when on end-point, how to handle that and which should be last hit point?
(state (parse-partial-sexp (point-min) (1+ here)))
(type (if (nth 3 state)
'string
(if (nth 4 state)
'comment)))
(start (when type (nth 8 state)))
end)
(unless start
(setq state (parse-partial-sexp (point-min) here))
(setq type (if (nth 3 state)
'string
(if (nth 4 state)
'comment)))
(setq start (when type (nth 8 state))))
(unless (or (not what)
(eq what type))
(setq start nil))
(if (not start)
(progn
(goto-char here)
nil)
(setq state (parse-partial-sexp (1+ start) (point-max)
nil nil state 'syntax-table))
(setq end (point))
(goto-char here)
(cons start end)))))
(defun ourcomments-bounds-of-string-at-point ()
"Return bounds of string at point if any."
(ourcomments-string-or-comment-bounds-1 'string))
(put 'string 'bounds-of-thing-at-point 'ourcomments-bounds-of-string-at-point)
(defun -sanitize-sql-string (str)
(->> str
(downcase)
(s-trim)
(replace-regexp-in-string
(rx (or (and string-start (or "\"\"\""
"\""))
(and (or "\"\"\""
"\"")
string-end)))
"")
(s-trim)))
(defun sql-string-p (str)
"Returns 't if STR looks like a string literal for a SQL statement"
(setq str (-sanitize-sql-string str))
(or (s-starts-with? "select" str)))
;;; tests
(require 'ert)
(ert-deftest sanitize-sql-string-test ()
(should (string-equal "select * from foo;"
(-sanitize-sql-string
"\"\"\"SELECT * FROM foo;\n\n\"\"\""))))
(ert-deftest test-sql-string-p ()
(dolist (str '("SELECT * FROM foo;"
"select * from foo;"))
(should (sql-string-p str)))
(dolist (str '("not a QUERY"))
(should-not (sql-string-p str))))

View file

@ -0,0 +1,31 @@
;;; -*- lexical-binding: t; -*-
(add-hook 'terraform-mode-hook #'terraform-format-on-save-mode)
(defun packer-format-buffer ()
(interactive)
(let ((buf (get-buffer-create "*packer-fmt*")))
(if (zerop (call-process-region (point-min) (point-max)
"packer" nil buf nil "fmt" "-"))
(let ((point (point))
(window-start (window-start)))
(erase-buffer)
(insert-buffer-substring buf)
(goto-char point)
(set-window-start nil window-start))
(message "packer fmt failed: %s" (with-current-buffer buf (buffer-string))))
(kill-buffer buf)))
(define-minor-mode packer-format-on-save-mode
"Run packer-format-buffer before saving the current buffer"
:lighter nil
(if packer-format-on-save-mode
(add-hook 'before-save-hook #'packer-format-buffer nil t)
(remove-hook 'before-save-hook #'packer-format-buffer t)))
(defun maybe-init-packer ()
(interactive)
(when (s-ends-with-p ".pkr" (file-name-base (buffer-file-name)))
(packer-format-on-save-mode)))
(add-hook 'hcl-mode-hook #'maybe-init-packer)

View file

@ -0,0 +1,68 @@
;;; private/grfn/tests/splitjoin_test.el -*- lexical-binding: t; -*-
(require 'ert)
;; (load! 'splitjoin)
;; (load! 'utils)
; (require 'splitjoin)
;;; Helpers
(defvar *test-buffer* nil)
(make-variable-buffer-local '*test-buffer*)
(defun test-buffer ()
(when (not *test-buffer*)
(setq *test-buffer* (get-buffer-create "test-buffer")))
*test-buffer*)
(defmacro with-test-buffer (&rest body)
`(with-current-buffer (test-buffer)
,@body))
(defun set-test-buffer-mode (mode)
(let ((mode (if (functionp mode) mode
(-> mode symbol-name (concat "-mode") intern))))
(assert (functionp mode))
(with-test-buffer (funcall mode))))
(defmacro set-test-buffer-contents (contents)
(with-test-buffer
(erase-buffer)
(insert contents)))
(defun test-buffer-contents ()
(with-test-buffer (substring-no-properties (buffer-string))))
(defmacro assert-test-buffer-contents (expected-contents)
`(should (equal (string-trim (test-buffer-contents))
(string-trim ,expected-contents))))
(defmacro should-join-to (mode original-contents expected-contents)
`(progn
(set-test-buffer-mode ,mode)
(set-test-buffer-contents ,original-contents)
(with-test-buffer (+splitjoin/join))
(assert-test-buffer-contents ,expected-contents)))
(defmacro should-split-to (mode original-contents expected-contents)
`(progn
(set-test-buffer-mode ,mode)
(set-test-buffer-contents ,original-contents)
(with-test-buffer (+splitjoin/split))
(assert-test-buffer-contents ,expected-contents)))
(defmacro should-splitjoin (mode joined-contents split-contents)
`(progn
(should-split-to ,mode ,joined-contents ,split-contents)
(should-join-to ,mode ,split-contents ,joined-contents)))
;;; Tests
;; Elixir
(ert-deftest elixir-if-splitjoin-test ()
(should-splitjoin 'elixir
"if predicate?(), do: result"
"if predicate?() do
result
end"))

View file

@ -0,0 +1,115 @@
(require 'solarized)
(eval-when-compile
(require 'solarized-palettes))
;; (defun grfn-solarized-theme ()
;; (custom-theme-set-faces
;; theme-name
;; `(font-lock-doc-face ((,class (:foreground ,s-base1))))
;; `(font-lock-preprocessor-face ((,class (:foreground ,red))))
;; `(font-lock-keyword-face ((,class (:foreground ,green))))
;; `(elixir-attribute-face ((,class (:foreground ,blue))))
;; `(elixir-atom-face ((,class (:foreground ,cyan))))))
(setq +solarized-s-base03 "#002b36"
+solarized-s-base02 "#073642"
;; emphasized content
+solarized-s-base01 "#586e75"
;; primary content
+solarized-s-base00 "#657b83"
+solarized-s-base0 "#839496"
;; comments
+solarized-s-base1 "#93a1a1"
;; background highlight light
+solarized-s-base2 "#eee8d5"
;; background light
+solarized-s-base3 "#fdf6e3"
;; Solarized accented colors
+solarized-yellow "#b58900"
+solarized-orange "#cb4b16"
+solarized-red "#dc322f"
+solarized-magenta "#d33682"
+solarized-violet "#6c71c4"
+solarized-blue "#268bd2"
+solarized-cyan "#2aa198"
+solarized-green "#859900"
;; Darker and lighter accented colors
;; Only use these in exceptional circumstances!
+solarized-yellow-d "#7B6000"
+solarized-yellow-l "#DEB542"
+solarized-orange-d "#8B2C02"
+solarized-orange-l "#F2804F"
+solarized-red-d "#990A1B"
+solarized-red-l "#FF6E64"
+solarized-magenta-d "#93115C"
+solarized-magenta-l "#F771AC"
+solarized-violet-d "#3F4D91"
+solarized-violet-l "#9EA0E5"
+solarized-blue-d "#00629D"
+solarized-blue-l "#69B7F0"
+solarized-cyan-d "#00736F"
+solarized-cyan-l "#69CABF"
+solarized-green-d "#546E00"
+solarized-green-l "#B4C342")
(deftheme grfn-solarized-light "The light variant of Griffin's solarized theme")
(setq grfn-solarized-faces
'("Griffin's solarized theme customization"
(custom-theme-set-faces
theme-name
`(font-lock-doc-face ((t (:foreground ,+solarized-s-base1))))
`(font-lock-preprocessor-face ((t (:foreground ,+solarized-red))))
`(font-lock-keyword-face ((t (:foreground ,+solarized-green))))
`(elixir-attribute-face ((t (:foreground ,+solarized-blue))))
`(elixir-atom-face ((t (:foreground ,+solarized-cyan))))
`(agda2-highlight-keyword-face ((t (:foreground ,green))))
`(agda2-highlight-string-face ((t (:foreground ,cyan))))
`(agda2-highlight-number-face ((t (:foreground ,violet))))
`(agda2-highlight-symbol-face ((((background ,base3)) (:foreground ,base01))))
`(agda2-highlight-primitive-type-face ((t (:foreground ,blue))))
`(agda2-highlight-bound-variable-face ((t nil)))
`(agda2-highlight-inductive-constructor-face ((t (:foreground ,green))))
`(agda2-highlight-coinductive-constructor-face ((t (:foreground ,yellow))))
`(agda2-highlight-datatype-face ((t (:foreground ,blue))))
`(agda2-highlight-field-face ((t (:foreground ,red))))
`(agda2-highlight-function-face ((t (:foreground ,blue))))
`(agda2-highlight-module-face ((t (:foreground ,yellow))))
`(agda2-highlight-postulate-face ((t (:foreground ,blue))))
`(agda2-highlight-primitive-face ((t (:foreground ,blue))))
`(agda2-highlight-record-face ((t (:foreground ,blue))))
`(agda2-highlight-dotted-face ((t nil)))
`(agda2-highlight-operator-face ((t nil)))
`(agda2-highlight-error-face ((t (:foreground ,red :underline t))))
`(agda2-highlight-unsolved-meta-face ((t (:background ,base2))))
`(agda2-highlight-unsolved-constraint-face ((t (:background ,base2))))
`(agda2-highlight-termination-problem-face ((t (:background ,orange :foreground ,base03))))
`(agda2-highlight-incomplete-pattern-face ((t (:background ,orange :foreground ,base03))))
`(agda2-highlight-typechecks-face ((t (:background ,cyan :foreground ,base03))))
`(font-lock-doc-face ((t (:foreground ,+solarized-s-base1))))
`(font-lock-preprocessor-face ((t (:foreground ,+solarized-red))))
`(font-lock-keyword-face ((t (:foreground ,+solarized-green :bold nil))))
`(font-lock-builtin-face ((t (:foreground ,+solarized-s-base01
:bold t))))
`(elixir-attribute-face ((t (:foreground ,+solarized-blue))))
`(elixir-atom-face ((t (:foreground ,+solarized-cyan))))
`(linum ((t (:background ,+solarized-s-base2 :foreground ,+solarized-s-base1))))
`(line-number ((t (:background ,+solarized-s-base2 :foreground ,+solarized-s-base1))))
`(haskell-operator-face ((t (:foreground ,+solarized-green))))
`(haskell-keyword-face ((t (:foreground ,+solarized-cyan))))
`(org-drawer ((t (:foreground ,+solarized-s-base1
:bold t)))))))
(solarized-with-color-variables
'light 'grfn-solarized-light solarized-light-color-palette-alist)
(provide-theme 'grfn-solarized-light)

View file

@ -0,0 +1,114 @@
;;; -*- lexical-binding: t; -*-
;; Elisp Extras
(defmacro comment (&rest _body)
"Comment out one or more s-expressions"
nil)
(defun inc (x) "Returns x + 1" (+ 1 x))
(defun dec (x) "Returns x - 1" (- x 1))
(defun average (ns)
"Arithmetic mean of xs"
(if (null ns) nil
(/ (apply #'+ ns)
(length ns))))
(comment
(average (list 1 2 3 4))
)
;;
;; Text editing utils
;;
;; Reading strings
(defun get-char (&optional point)
"Get the character at the given `point' (defaulting to the current point),
without properties"
(let ((point (or point (point))))
(buffer-substring-no-properties point (+ 1 point))))
(defun get-line (&optional lineno)
"Read the line number `lineno', or the current line if `lineno' is nil, and
return it as a string stripped of all text properties"
(let ((current-line (line-number-at-pos)))
(if (or (not lineno)
(= current-line lineno))
(thing-at-point 'line t)
(save-mark-and-excursion
(line-move (- lineno (line-number-at-pos)))
(thing-at-point 'line t)))))
(defun get-line-point ()
"Get the position in the current line of the point"
(- (point) (line-beginning-position)))
;; Moving in the file
(defun goto-line-char (pt)
"Moves the point to the given position expressed as an offset from the start
of the line"
(goto-char (+ (line-beginning-position) pt)))
(defun goto-eol ()
"Moves to the end of the current line"
(goto-char (line-end-position)))
(defun goto-regex-on-line (regex)
"Moves the point to the first occurrence of `regex' on the current line.
Returns nil if the regex did not match, non-nil otherwise"
(when-let ((current-line (get-line))
(line-char (string-match regex current-line)))
(goto-line-char line-char)))
(defun goto-regex-on-line-r (regex)
"Moves the point to the *last* occurrence of `regex' on the current line.
Returns nil if the regex did not match, non-nil otherwise"
(when-let ((current-line (get-line))
(modified-regex (concat ".*\\(" regex "\\)"))
(_ (string-match modified-regex current-line))
(match-start (match-beginning 1)))
(goto-line-char match-start)))
(comment
(progn
(string-match (rx (and (zero-or-more anything)
(group "foo" "foo")))
"foofoofoo")
(match-beginning 1)))
;; Changing file contents
(defun delete-line ()
"Remove the line at the current point"
(delete-region (line-beginning-position)
(inc (line-end-position))))
(defmacro modify-then-indent (&rest body)
"Modify text in the buffer according to body, then re-indent from where the
cursor started to where the cursor ended up, then return the cursor to where
it started."
`(let ((beg (line-beginning-position))
(orig-line-char (- (point) (line-beginning-position))))
(atomic-change-group
(save-mark-and-excursion
,@body
(evil-indent beg (+ (line-end-position) 1))))
(goto-line-char orig-line-char)))
(pcase-defmacro s-starts-with (prefix)
`(pred (s-starts-with-p ,prefix)))
(pcase-defmacro s-contains (needle &optional ignore-case)
`(pred (s-contains-p ,needle
,@(when ignore-case (list ignore-case)))))
(comment
(pcase "foo"
((s-contains "bar") 1)
((s-contains "o") 2))
)

View file

@ -0,0 +1,24 @@
;;; -*- lexical-binding: t; -*-
(defun require-vterm ()
(add-to-list
'load-path
(concat
(s-trim
(shell-command-to-string
"nix-build --no-out-link ~/code/depot -A third_party.emacs.vterm"))
"/share/emacs/site-lisp/elpa/vterm-20200515.1412"))
(require 'vterm))
(defun +grfn/vterm-setup ()
(hide-mode-line-mode)
(setq-local evil-collection-vterm-send-escape-to-vterm-p t))
(add-hook 'vterm-mode-hook #'+grfn/vterm-setup)
(map! (:map vterm-mode-map
"<C-escape>" #'evil-normal-state))
(comment
(require-vterm)
)