diff options
author | William Carroll <wpcarro@gmail.com> | 2018-04-25T17·26-0400 |
---|---|---|
committer | William Carroll <wpcarro@gmail.com> | 2018-07-19T16·00-0400 |
commit | 3c8e6f0cc5eac51e369b8ffbd0441366cdc6da40 (patch) | |
tree | e1c98f5b22dd258e4ae331c0591e0527fe5233a1 | |
parent | 56a7b9fa41c6edbb960686ccffb7a8949d242eab (diff) |
Support updated emacs
Finally ported my up-to-date emacs configuration here. I was putting this off for a long while, unsure of how to handle all of the work. All it took was my laptop being fried to force me to do this. So... voila!
51 files changed, 3186 insertions, 0 deletions
diff --git a/emacs.d/bookmarks b/emacs.d/bookmarks new file mode 100644 index 000000000000..a222a18de78c --- /dev/null +++ b/emacs.d/bookmarks @@ -0,0 +1,15 @@ +;;;; Emacs Bookmark Format Version 1 ;;;; +;;; This format is meant to be slightly human-readable; +;;; nevertheless, you probably don't want to edit it. +;;; -*- End Of Bookmark File Format Version Stamp -*- +(("org-capture-last-stored" + (filename . "~/org/notes.org") + (front-context-string . "** TODO testing ") + (rear-context-string) + (position . 9)) +("org-refile-last-stored" + (filename . "~/Dropbox/cryptocurrency/todo.org") + (front-context-string . "** TODO Maintain") + (rear-context-string . "h email & text)\n") + (position . 1465)) +) \ No newline at end of file diff --git a/emacs.d/custom.el b/emacs.d/custom.el new file mode 100644 index 000000000000..88219f70cd09 --- /dev/null +++ b/emacs.d/custom.el @@ -0,0 +1,52 @@ +(custom-set-variables + ;; custom-set-variables was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(ansi-color-names-vector + ["#10151C" "#D95468" "#8BD49C" "#EBBF83" "#5EC4FF" "#E27E8D" "#70E1E8" "#9CAABB"]) + '(custom-safe-themes + (quote + ("bd23e5e571f9b951eb79941ba3927fb493c26463654add2a53f4fb0de72ef08b" "013c62a1fcee7c8988c831027b1c38ae215f99722911b69e570f21fc19cb662e" "0b1ded82ebea8b76e3c17c628fe0d3c7aa46746c3efcf657f633d71989110585" "8ff5073d6c694a442c85505d6f885a752061b3738e2de7c2b9042ffd2c1579e5" "4f5fb2b25a9c71d584472abc5b6f850d616ac280a69e43df6e78ddf2b4aa68fa" "0a3a41085c19d8121ed0ad3eb658a475ccb948a70a83604641ee7d4c3575a4d5" "73e35ffa5ca98b57a9923954f296c3854ce6d8736b31fdbdda3d27502d4b4d69" "a7e7804313dbf827a441c86a8109ef5b64b03011383322cbdbf646eb02692f76" "77bddca0879cb3b0ecdf071d9635c818827c57d69164291cb27268ae324efa84" "3481e594ae6866d72c40ad77d86a1ffa338d01daa9eb0977e324f365cef4f47c" "6be42070d23e832a7493166f90e9bb08af348a818ec18389c1f21d33542771af" default))) + '(fci-rule-color "#56697A") + '(flycheck-javascript-flow-args nil) + '(jdee-db-active-breakpoint-face-colors (cons "#10151C" "#5EC4FF")) + '(jdee-db-requested-breakpoint-face-colors (cons "#10151C" "#8BD49C")) + '(jdee-db-spec-breakpoint-face-colors (cons "#10151C" "#384551")) + '(org-fontify-done-headline t t) + '(org-fontify-quote-and-verse-blocks t t) + '(org-fontify-whole-heading-line t t) + '(package-selected-packages + (quote + (writeroom-mode general rainbow-delimiters zen-mode flx-ido xterm-color evil-collection evil-text-objects-javascript evil-text-objects-haskell dired+ org-bullets slack emojify circe oauth2 engine-mode uniquify diminish elisp-slime-nav pcre2el magit-gh-pulls org-mode intero f cycle-themes ansi-term request dash-functional company-flow flycheck-flow flow-minor-mode elixir-mode oceanic-theme git-timemachine dockerfile-mode docker yaml-mode s key-chord yasnippet prettier-js rjsx-mode indium reason-mode flycheck markdown-mode smex magit all-the-icons-ivy which-key doom-themes cider hydra ace-window counsel-projectile counsel paredit projectile company evil exec-path-from-shell use-package))) + '(safe-local-variable-values + (quote + ((intero-targets "grid:lib" "grid:exe:grid-exe" "grid:test:doctests" "grid:test:grid-test")))) + '(vc-annotate-background "#10151C") + '(vc-annotate-color-map + (list + (cons 20 "#8BD49C") + (cons 40 "#abcd93") + (cons 60 "#cbc68b") + (cons 80 "#EBBF83") + (cons 100 "#e5ae6f") + (cons 120 "#df9e5b") + (cons 140 "#D98E48") + (cons 160 "#dc885f") + (cons 180 "#df8376") + (cons 200 "#E27E8D") + (cons 220 "#df7080") + (cons 240 "#dc6274") + (cons 260 "#D95468") + (cons 280 "#b05062") + (cons 300 "#884c5c") + (cons 320 "#604856") + (cons 340 "#56697A") + (cons 360 "#56697A"))) + '(vc-annotate-very-old-color nil)) +(custom-set-faces + ;; custom-set-faces was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + ) diff --git a/emacs.d/init.el b/emacs.d/init.el new file mode 120000 index 000000000000..f5f1a3a10914 --- /dev/null +++ b/emacs.d/init.el @@ -0,0 +1 @@ +/Users/wpcarro/dotfiles/init.el \ No newline at end of file diff --git a/emacs.d/network-security.data b/emacs.d/network-security.data new file mode 100644 index 000000000000..2b0c195b21ea --- /dev/null +++ b/emacs.d/network-security.data @@ -0,0 +1,4 @@ +( + (:id "sha1:88d4df3ce5800820f90edda8f27d13572234b2b9" :fingerprint "sha1:13:0e:b7:b0:c1:b7:75:40:89:0f:0b:64:20:6b:53:a5:d2:2a:a8:07" :host "api.github.com:443" :conditions (:insecure :unknown-ca :invalid)) + (:id "sha1:85b31c268009209a8d3c5387033b219264f7e62b" :fingerprint "sha1:f8:e1:fc:e4:34:8e:19:e8:48:c1:61:fe:3d:09:dd:d4:d2:7a:cb:db" :host "melpa.org:443" :conditions (:insecure :unknown-ca :invalid)) +) diff --git a/emacs.d/projectile-bookmarks.eld b/emacs.d/projectile-bookmarks.eld new file mode 100644 index 000000000000..d42f6e5512f6 --- /dev/null +++ b/emacs.d/projectile-bookmarks.eld @@ -0,0 +1 @@ +("~/urbint/meta/" "~/urbint/grid-front-end/" "~/dotfiles/" "~/urbint/evil-text-objects-javascript/" "~/pc_settings/" "~/programming/doom-emacs/" "~/urbint/evil-text-objects-haskell/" "~/urbint/grid/" "~/urbint/machina/" "~/urbint/grid-front-end-reasonml/" "~/urbint/docker-images/" "~/programming/cryptocurrency/" "~/programming/a-reason-react-tutorial/") \ No newline at end of file diff --git a/emacs.d/smex-items b/emacs.d/smex-items new file mode 100644 index 000000000000..c3613fa5b700 --- /dev/null +++ b/emacs.d/smex-items @@ -0,0 +1,160 @@ + +;; ----- smex-history ----- +( + load-theme + disable-theme + cycle-themes-mode + cycle-themes + rjsx-mode + enable-theme + package-list-packages +) + +;; ----- smex-data ----- +( + (global-company-mode . 12) + (global-flycheck-mode . 6) + (reason-mode . 1) + (imenu . 2) + (package-refresh-contents . 24) + (package-install . 30) + (indium-connect-to-chrome . 1) + (indium-interaction-mode . 1) + (rjsx-mode . 9) + (flycheck-info . 3) + (flycheck-mode . 12) + (flycheck-next-error . 1) + (flycheck-compile . 1) + (flycheck-select-checker . 10) + (yas-new-snippet . 2) + (yas-visit-snippet-file . 1) + (yas-expand . 2) + (yas-minor-mode . 1) + (describe-mode . 4) + (mark-defun . 5) + (wpc/find-or-create-js-test . 5) + (wpc/find-or-create-js-module . 2) + (wpc/toggle-between-js-test-and-module . 2) + (yaml-mode . 1) + (docker-images . 5) + (docker-mode . 1) + (dockerfile-mode . 2) + (snippet-mode . 2) + (prettier-js-mode . 23) + (describe-variable . 1) + (elixir-mode . 1) + (git-timemachine . 10) + (disable-theme . 15) + (load-theme . 15) + (xref-goto-xref . 2) + (flycheck-verify-setup . 7) + (company-flow . 15) + (company-mode . 8) + (js2-mode . 2) + (company-diag . 7) + (flow/set-flow-executable . 1) + (org-clubhouse-create-story . 9) + (org-clubhouse-mode . 1) + (slack-send-code-snippet . 13) + (indent-pp-sexp . 1) + (describe-personal-keybindings . 2) + (wpc/insert-flow-annotation . 3) + (ibuffer . 2) + (flow-minor-mode . 4) + (paredit-mode . 1) + (evil-mode . 6) + (global-evil-leader-mode . 9) + (help . 5) + (menu-bar-mode . 3) + (enable-theme . 4) + (ansi-term . 9) + (wpcarro/terminal . 17) + (toggle-frame-maximized . 2) + (previous-buffer . 1) + (paredit-forward-slurp-sexp . 1) + (paredit-raise-sexp . 1) + (sh-mode . 2) + (haskell-mode . 1) + (counsel-semantic-or-imenu . 1) + (evil-leader-mode . 9) + (intero-targets . 5) + (find-file . 1) + (electric-pair-mode . 8) + (eval-print-last-sexp . 3) + (evil-inner-haskell-comment-block . 5) + (evil-outer-haskell-comment-block . 1) + (elp-instrument-function . 1) + (elp-instrument-list . 1) + (magit-checkout . 2) + (magit-blame . 1) + (org-insert-link . 1) + (magit-mode . 2) + (emacs-lisp-mode . 1) + (magit-status . 3) + (magit-gh-pulls-popup . 1) + (magit-log . 1) + (org-capture . 1) + (flycheck-list-errors . 2) + (elisp-slime-nav-mode . 2) + (describe-font . 1) + (load-library . 1) + (diminish . 4) + (diminished-modes . 1) + (diminish-undo . 3) + (undo-tree-mode . 2) + (undo-tree-visualize . 21) + (undo-tree-save-state-to-register . 3) + (undo-tree-restore-state-from-register . 1) + (transpose-words . 9) + (downcase-dwim . 1) + (ffap . 2) + (william-carroll-kbds-minor-mode . 4) + (ffap-other-window . 2) + (eval-expression . 2) + (engine/search-google . 2) + (counsel-spotify-next . 2) + (counsel-spotify-search-track . 3) + (counsel-spotify-search-artist . 2) + (slack-im-select . 39) + (slack-start . 10) + (slack-group-select . 2) + (slack-channel-join . 1) + (slack-channel-select . 18) + (slack-select-unread-rooms . 31) + (slack-edit-message-mode . 1) + (slack-file-upload . 2) + (smerge-next . 3) + (smerge-mode . 2) + (smerge-keep-mine . 5) + (smerge-keep-other . 31) + (emacs-lisp-macroexpand . 43) + (woman . 1) + (package-delete . 4) + (ert . 12) + (caps->kebab . 1) + (text-mode . 1) + (org-mode . 1) + (dired-do-copy . 1) + (wpc/toggle-terminal . 1) + (eshell . 1) + (term . 1) + (counsel-git-grep . 12) + (fzf . 1) + (fzf-projectile . 1) + (fzf-git . 1) + (execute-extended-command . 1) + (package-menu-mark-upgrades . 1) + (package-list-packages . 7) + (repeat-complex-command . 1) + (apropos-value . 1) + (apropos-variable . 6) + (ido-find-file . 2) + (counsel-find-file . 1) + (counsel-projectile-find-file . 1) + (projectile-find-file . 1) + (zen-mode . 6) + (zen-mode-increase-margin-width . 1) + (writeroom-mode . 7) + (cycle-themes . 2) + (cycle-themes-mode . 2) +) diff --git a/emacs.d/snippets/emacs-lisp-mode/elisp-module-docs b/emacs.d/snippets/emacs-lisp-mode/elisp-module-docs new file mode 100644 index 000000000000..8ea7b8f07724 --- /dev/null +++ b/emacs.d/snippets/emacs-lisp-mode/elisp-module-docs @@ -0,0 +1,11 @@ +# -*- mode: snippet -*- +# name: Elisp module docs +# key: emd +# -- +;;; `(-> (buffer-file-name) f-filename)` --- $2 -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; $3 + +;;; Code: \ No newline at end of file diff --git a/emacs.d/snippets/emacs-lisp-mode/provide-footer b/emacs.d/snippets/emacs-lisp-mode/provide-footer new file mode 100644 index 000000000000..2a0bcc33f7bb --- /dev/null +++ b/emacs.d/snippets/emacs-lisp-mode/provide-footer @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: Provide footer +# key: elf +# -- +(provide '`(-> (buffer-file-name) f-filename f-no-ext)`) +;;; `(-> (buffer-file-name) f-filename)` ends here \ No newline at end of file diff --git a/emacs.d/snippets/org-mode/code-snippet b/emacs.d/snippets/org-mode/code-snippet new file mode 100644 index 000000000000..4215b15992b6 --- /dev/null +++ b/emacs.d/snippets/org-mode/code-snippet @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: Code Snippet +# key: src +# -- +#+BEGIN_SRC $1 +$2 +#+END_SRC \ No newline at end of file diff --git a/emacs.d/snippets/org-mode/href b/emacs.d/snippets/org-mode/href new file mode 100644 index 000000000000..ac65ea2e49be --- /dev/null +++ b/emacs.d/snippets/org-mode/href @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: Org mode URL +# key: href +# -- +[[$1][$2]] \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/console-log b/emacs.d/snippets/rjsx-mode/console-log new file mode 100644 index 000000000000..bc51f8313301 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/console-log @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: Console.log helper +# key: clo +# -- +console.log($1) \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/const-defn b/emacs.d/snippets/rjsx-mode/const-defn new file mode 100644 index 000000000000..8e35e61fc2c4 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/const-defn @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: const definition +# key: cn +# -- +const $1 = '$2' \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/const-function b/emacs.d/snippets/rjsx-mode/const-function new file mode 100644 index 000000000000..13f2018f2269 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/const-function @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: const function +# key: cfn +# -- +const $1 = ($2) => { + $3 +} \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/destructure-const b/emacs.d/snippets/rjsx-mode/destructure-const new file mode 100644 index 000000000000..2a52c57c75cd --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/destructure-const @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: Destructuring a const +# key: cds +# -- +const { $1 } = $2 \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/fat-arrow b/emacs.d/snippets/rjsx-mode/fat-arrow new file mode 100644 index 000000000000..187a2efc5a7c --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/fat-arrow @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: Fat arrow function +# key: fa +# -- +=> \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/fat-arrow-function b/emacs.d/snippets/rjsx-mode/fat-arrow-function new file mode 100644 index 000000000000..694914a83c95 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/fat-arrow-function @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: Fat arrow function +# key: faf +# -- +() => { + $1 +} \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/import-destructured b/emacs.d/snippets/rjsx-mode/import-destructured new file mode 100644 index 000000000000..ded3ce163a93 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/import-destructured @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: Import destructured +# key: ids +# -- +import { $1 } from '$2' \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/import-react b/emacs.d/snippets/rjsx-mode/import-react new file mode 100644 index 000000000000..0463f5cd5593 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/import-react @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: Import React dependency (ES6) +# key: ir +# -- +import React from 'react' diff --git a/emacs.d/snippets/rjsx-mode/import-type b/emacs.d/snippets/rjsx-mode/import-type new file mode 100644 index 000000000000..fcd51f687b61 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/import-type @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: import type +# key: ixt +# -- +import type { $1 } from '$2' \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/import-x-from-y b/emacs.d/snippets/rjsx-mode/import-x-from-y new file mode 100644 index 000000000000..09fa6df50506 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/import-x-from-y @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: import x from y +# key: ix +# -- +import $1 from '$2' \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/import-y b/emacs.d/snippets/rjsx-mode/import-y new file mode 100644 index 000000000000..9f550e300d12 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/import-y @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: import y +# key: iy +# -- +import '$1' \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/jest-describe-test b/emacs.d/snippets/rjsx-mode/jest-describe-test new file mode 100644 index 000000000000..ed382d4f74c4 --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/jest-describe-test @@ -0,0 +1,10 @@ +# -*- mode: snippet -*- +# name: Jest describe/test block +# key: dsc +# -- +describe('$1', () => { + test('$2', () => { + + expect($3).toEqual($4) + }) +}) \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/jest-test b/emacs.d/snippets/rjsx-mode/jest-test new file mode 100644 index 000000000000..12ca2e786ded --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/jest-test @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: Jest / Jasmine test +# key: tst +# -- +test('$1', () => { + expect($2).toBe($3) +}) \ No newline at end of file diff --git a/emacs.d/snippets/rjsx-mode/react-class-component b/emacs.d/snippets/rjsx-mode/react-class-component new file mode 100644 index 000000000000..f2a93a31d96d --- /dev/null +++ b/emacs.d/snippets/rjsx-mode/react-class-component @@ -0,0 +1,11 @@ +# -*- mode: snippet -*- +# name: React class extends +# key: clz +# -- +class $1 extends React.Component { + render() { + $2 + } +} + +export default $1 \ No newline at end of file diff --git a/emacs.d/vendor/org-clubhouse.el b/emacs.d/vendor/org-clubhouse.el new file mode 100644 index 000000000000..ba1f004a2410 --- /dev/null +++ b/emacs.d/vendor/org-clubhouse.el @@ -0,0 +1,365 @@ +;;; private/grfn/org-clubhouse.el + +(require 'dash) +(require 'dash-functional) +(require 's) +(require 'org) +(require 'org-element) +(require 'cl) + +;;; +;;; Configuration +;;; + +(defvar org-clubhouse-auth-token nil + "Authorization token for the Clubhouse API") + +(defvar org-clubhouse-team-name nil + "Team name to use in links to Clubhouse +ie https://app.clubhouse.io/<TEAM_NAME>/stories") + +(defvar org-clubhouse-project-ids nil + "Specific list of project IDs to synchronize with clubhouse. +If unset all projects will be synchronized") + +(defvar org-clubhouse-workflow-name "Default") + +(defvar org-clubhouse-state-alist + '(("LATER" . "Unscheduled") + ("[ ]" . "Ready for Development") + ("TODO" . "Ready for Development") + ("OPEN" . "Ready for Development") + ("ACTIVE" . "In Development") + ("PR" . "Review") + ("DONE" . "Merged") + ("[X]" . "Merged") + ("CLOSED" . "Merged"))) + +;;; +;;; Utilities +;;; + +(defun ->list (vec) (append vec nil)) + +(defun reject-archived (item-list) + (-filter (lambda (item) (equal :json-false (alist-get 'archived item))) item-list)) + +(defun alist->plist (key-map alist) + (->> key-map + (-map (lambda (key-pair) + (let ((alist-key (car key-pair)) + (plist-key (cdr key-pair))) + (list plist-key (alist-get alist-key alist))))) + (-flatten-n 1))) + +(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))) + +;;; +;;; Org-element interaction +;;; + +;; (defun org-element-find-headline () +;; (let ((current-elt (org-element-at-point))) +;; (if (equal 'headline (car current-elt)) +;; current-elt +;; (let* ((elt-attrs (cadr current-elt)) +;; (parent (plist-get elt-attrs :post-affiliated))) +;; (goto-char parent) +;; (org-element-find-headline))))) + +(defun org-element-find-headline () + (let ((current-elt (org-element-at-point))) + (when (equal 'headline (car current-elt)) + (cadr current-elt)))) + +(defun org-element-extract-clubhouse-id (elt) + (when-let ((clubhouse-id-link (plist-get elt :CLUBHOUSE-ID))) + (string-match + (rx "[[" (one-or-more anything) "]" + "[" (group (one-or-more digit)) "]]") + clubhouse-id-link) + (string-to-int (match-string 1 clubhouse-id-link)))) + + + +(defun org-element-clubhouse-id () + (org-element-extract-clubhouse-id + (org-element-find-headline))) + +;;; +;;; API integration +;;; + +(defvar org-clubhouse-base-url* "https://api.clubhouse.io/api/v2") + +(defun org-clubhouse-auth-url (url) + (concat url + "?" + (url-build-query-string + `(("token" ,org-clubhouse-auth-token))))) + +(defun org-clubhouse-baseify-url (url) + (if (s-starts-with? org-clubhouse-base-url* url) url + (concat org-clubhouse-base-url* + (if (s-starts-with? "/" url) url + (concat "/" url))))) + +(defun org-clubhouse-request (method url &optional data) + (message "%s %s %s" method url (prin1-to-string data)) + (let* ((url-request-method method) + (url-request-extra-headers + '(("Content-Type" . "application/json"))) + (url-request-data data) + (buf)) + + (setq url (-> url + org-clubhouse-baseify-url + org-clubhouse-auth-url)) + + (setq buf (url-retrieve-synchronously url)) + + (with-current-buffer buf + (goto-char url-http-end-of-headers) + (prog1 (json-read) (kill-buffer))))) + +(cl-defun to-id-name-pairs + (seq &optional (id-attr 'id) (name-attr 'name)) + (->> seq + ->list + (-map (lambda (resource) + (cons (alist-get id-attr resource) + (alist-get name-attr resource)))))) + +(cl-defun org-clubhouse-fetch-as-id-name-pairs + (resource &optional + (id-attr 'id) + (name-attr 'name)) + "Returns the given resource from clubhouse as (id . name) pairs" + (let ((resp-json (org-clubhouse-request "GET" resource))) + (-> resp-json + ->list + reject-archived + (to-id-name-pairs id-attr name-attr)))) + +(defun org-clubhouse-link-to-story (story-id) + (format "https://app.clubhouse.io/%s/story/%d" + org-clubhouse-team-name + story-id)) + +(defun org-clubhouse-link-to-epic (epic-id) + (format "https://app.clubhouse.io/%s/epic/%d" + org-clubhouse-team-name + epic-id)) + +(defun org-clubhouse-link-to-project (project-id) + (format "https://app.clubhouse.io/%s/project/%d" + org-clubhouse-team-name + project-id)) + +;;; +;;; Caching +;;; + + + +(defvar org-clubhouse-cache-clear-functions ()) + +(defmacro defcache (name &optional docstring &rest body) + (let* ((doc (when docstring (list docstring))) + (cache-var-name (intern (concat (symbol-name name) + "-cache"))) + (clear-cache-function-name + (intern (concat "clear-" (symbol-name cache-var-name))))) + `(progn + (defvar ,cache-var-name :no-cache) + (defun ,name () + ,@doc + (when (equal :no-cache ,cache-var-name) + (setq ,cache-var-name (progn ,@body))) + ,cache-var-name) + (defun ,clear-cache-function-name () + (interactive) + (setq ,cache-var-name :no-cache)) + + (push (quote ,clear-cache-function-name) + org-clubhouse-cache-clear-functions)))) + +(defun org-clubhouse-clear-cache () + (interactive) + (-map #'funcall org-clubhouse-cache-clear-functions)) + +;;; +;;; API resource functions +;;; + +(defcache org-clubhouse-projects + "Returns projects as (project-id . name)" + (org-clubhouse-fetch-as-id-name-pairs "projects")) + +(defcache org-clubhouse-epics + "Returns projects as (project-id . name)" + (org-clubhouse-fetch-as-id-name-pairs "epics")) + +(defcache org-clubhouse-workflow-states + "Returns worflow states as (name . id) pairs" + (let* ((resp-json (org-clubhouse-request "GET" "workflows")) + (workflows (->list resp-json)) + ;; just assume it exists, for now + (workflow (-find (lambda (workflow) + (equal org-clubhouse-workflow-name + (alist-get 'name workflow))) + workflows)) + (states (->list (alist-get 'states workflow)))) + (to-id-name-pairs states + 'name + 'id))) + +(defun org-clubhouse-stories-in-project (project-id) + "Returns the stories in the given project as org bugs" + (let ((resp-json (org-clubhouse-request "GET" (format "/projects/%d/stories" project-id)))) + (->> resp-json ->list reject-archived + (-reject (lambda (story) (equal :json-true (alist-get 'completed story)))) + (-map (lambda (story) + (cons + (cons 'status + (cond + ((equal :json-true (alist-get 'started story)) + 'started) + ((equal :json-true (alist-get 'completed story)) + 'completed) + ('t + 'open))) + story))) + (-map (-partial #'alist->plist + '((name . :title) + (id . :id) + (status . :status))))))) + +;;; +;;; Story creation +;;; + +(cl-defun org-clubhouse-create-story-internal + (title &key project-id epic-id) + (assert (and (stringp title) + (integerp project-id) + (or (null epic-id) (integerp epic-id)))) + (org-clubhouse-request + "POST" + "stories" + (json-encode + `((name . ,title) + (project_id . ,project-id) + (epic_id . ,epic-id))))) + +(defun org-clubhouse-prompt-for-project (cb) + (ivy-read + "Select a project: " + (-map #'cdr (org-clubhouse-projects)) + :require-match t + :history 'org-clubhouse-project-history + :action (lambda (selected) + (let ((project-id + (->> (org-clubhouse-projects) + (-find (lambda (proj) + (string-equal (cdr proj) selected))) + car))) + (message "%d" project-id) + (funcall cb project-id))))) + +(defun org-clubhouse-prompt-for-epic (cb) + (ivy-read + "Select an epic: " + (-map #'cdr (org-clubhouse-epics)) + :history 'org-clubhouse-epic-history + :action (lambda (selected) + (let ((epic-id + (->> (org-clubhouse-epics) + (-find (lambda (proj) + (string-equal (cdr proj) selected))) + car))) + (message "%d" epic-id) + (funcall cb epic-id))))) + +(defun org-clubhouse-populate-created-story (story) + (let ((elt (org-element-find-headline)) + (story-id (alist-get 'id story)) + (epic-id (alist-get 'epic_id story)) + (project-id (alist-get 'project_id story))) + + (org-set-property "clubhouse-id" + (org-make-link-string + (org-clubhouse-link-to-story story-id) + (number-to-string story-id))) + + (org-set-property "clubhouse-epic" + (org-make-link-string + (org-clubhouse-link-to-epic epic-id) + (alist-get epic-id (org-clubhouse-epics)))) + + (org-set-property "clubhouse-project" + (org-make-link-string + (org-clubhouse-link-to-project project-id) + (alist-get project-id (org-clubhouse-projects)))) + + (org-todo "TODO"))) + +(defun org-clubhouse-create-story () + (interactive) + ;; (message (org-element-find-headline)) + (when-let ((elt (org-element-find-headline)) + (title (plist-get elt :title))) + (if (plist-get elt :CLUBHOUSE-ID) + (message "This headline is already a clubhouse story!") + (org-clubhouse-prompt-for-project + (lambda (project-id) + (when project-id + (org-clubhouse-prompt-for-epic + (lambda (epic-id) + (let* ((story (org-clubhouse-create-story-internal + title + :project-id project-id + :epic-id epic-id))) + (org-clubhouse-populate-created-story story)))))))))) + +;;; +;;; Story updates +;;; + +(cl-defun org-clubhouse-update-story-internal + (story-id &rest attrs) + (assert (and (integerp story-id) + (listp attrs))) + (org-clubhouse-request + "PUT" + (format "stories/%d" story-id) + (json-encode attrs))) + +(defun org-clubhouse-update-status () + (when-let (clubhouse-id (org-element-clubhouse-id)) + (let* ((elt (org-element-find-headline)) + (todo-keyword (-> elt (plist-get :todo-keyword) (substring-no-properties)))) + (message todo-keyword) + (when-let ((clubhouse-workflow-state + (alist-get-equal todo-keyword org-clubhouse-state-alist)) + (workflow-state-id + (alist-get-equal clubhouse-workflow-state (org-clubhouse-workflow-states)))) + (org-clubhouse-update-story-internal + clubhouse-id + :workflow_state_id workflow-state-id) + (message "Successfully updated clubhouse status to \"%s\"" + clubhouse-workflow-state))))) + +(define-minor-mode org-clubhouse-mode + :init-value nil + :group 'org + :lighter "Org-Clubhouse" + :keymap '() + (add-hook 'org-after-todo-state-change-hook + 'org-clubhouse-update-status + nil + t)) diff --git a/emacs.d/vendor/reason-indent.el b/emacs.d/vendor/reason-indent.el new file mode 100644 index 000000000000..8fd3c9425866 --- /dev/null +++ b/emacs.d/vendor/reason-indent.el @@ -0,0 +1,304 @@ +;;; reason-indent.el --- Indentation functions for ReasonML -*-lexical-binding: t-*- + +;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved. + +;;; Commentary: + +;; Indentation functions for Reason. + +;;; Code: + +(defconst reason-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*") + +(defcustom reason-indent-offset 2 + "Indent Reason code by this number of spaces." + :type 'integer + :group 'reason-mode + :safe #'integerp) + +(defun reason-looking-back-str (str) + "Like `looking-back' but for fixed strings rather than regexps. +Works around some regexp slowness. +Argument STR string to search for." + (let ((len (length str))) + (and (> (point) len) + (equal str (buffer-substring-no-properties (- (point) len) (point)))))) + +(defun reason-paren-level () + "Get the level of nesting inside parentheses." + (nth 0 (syntax-ppss))) + +(defun reason-in-str-or-cmnt () + "Return whether point is currently inside a string or a comment." + (nth 8 (syntax-ppss))) + +(defun reason-rewind-past-str-cmnt () + "Rewind past string or comment." + (goto-char (nth 8 (syntax-ppss)))) + +(defun reason-rewind-irrelevant () + "Rewind past irrelevant characters (whitespace of inside comments)." + (interactive) + (let ((starting (point))) + (skip-chars-backward "[:space:]\n") + (if (reason-looking-back-str "*/") (backward-char)) + (if (reason-in-str-or-cmnt) + (reason-rewind-past-str-cmnt)) + (if (/= starting (point)) + (reason-rewind-irrelevant)))) + +(defun reason-align-to-expr-after-brace () + "Align the expression at point to the expression after the previous brace." + (save-excursion + (forward-char) + ;; We don't want to indent out to the open bracket if the + ;; open bracket ends the line + (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$")) + (when (looking-at "[[:space:]]") + (forward-word 1) + (backward-word 1)) + (current-column)))) + +(defun reason-align-to-prev-expr () + "Align the expression at point to the previous expression." + (let ((alignment (save-excursion + (forward-char) + ;; We don't want to indent out to the open bracket if the + ;; open bracket ends the line + (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$")) + (if (looking-at "[[:space:]]") + (progn + (forward-word 1) + (backward-word 1)) + (backward-char)) + (current-column))))) + (if (not alignment) + (save-excursion + (forward-char) + (forward-line) + (back-to-indentation) + (current-column)) + alignment))) + +;;; Start of a reason binding +(defvar reason-binding + (regexp-opt '("let" "type" "module" "fun"))) + +(defun reason-beginning-of-defun (&optional arg) + "Move backward to the beginning of the current defun. + +With ARG, move backward multiple defuns. Negative ARG means +move forward. + +This is written mainly to be used as `beginning-of-defun-function'. +Don't move to the beginning of the line. `beginning-of-defun', +which calls this, does that afterwards." + (interactive "p") + (re-search-backward (concat "^\\(" reason-binding "\\)\\_>") + nil 'move (or arg 1))) + +(defun reason-end-of-defun () + "Move forward to the next end of defun. + +With argument, do it that many times. +Negative argument -N means move back to Nth preceding end of defun. + +Assume that this is called after ‘beginning-of-defun’. So point is +at the beginning of the defun body. + +This is written mainly to be used as `end-of-defun-function' for Reason." + (interactive) + ;; Find the opening brace + (if (re-search-forward "[{]" nil t) + (progn + (goto-char (match-beginning 0)) + ;; Go to the closing brace + (condition-case nil + (forward-sexp) + (scan-error + ;; The parentheses are unbalanced; instead of being unable to fontify, just jump to the end of the buffer + (goto-char (point-max))))) + ;; There is no opening brace, so consider the whole buffer to be one "defun" + (goto-char (point-max)))) + +(defun reason-rewind-to-beginning-of-current-level-expr () + "Rewind to the beginning of the expression on the current level of nesting." + (interactive) + (let ((current-level (reason-paren-level))) + (back-to-indentation) + (when (looking-at "=>") + (reason-rewind-irrelevant) + (back-to-indentation)) + (while (> (reason-paren-level) current-level) + (backward-up-list) + (back-to-indentation)))) + +(defun reason-mode-indent-line () + "Indent current line." + (interactive) + (let ((indent + (save-excursion + (back-to-indentation) + ;; Point is now at beginning of current line + (let* ((level (reason-paren-level)) + (baseline + ;; Our "baseline" is one level out from the indentation of the expression + ;; containing the innermost enclosing opening bracket. That + ;; way if we are within a block that has a different + ;; indentation than this mode would give it, we still indent + ;; the inside of it correctly relative to the outside. + (if (= 0 level) + 0 + (save-excursion + (reason-rewind-irrelevant) + (if (save-excursion + (reason-rewind-to-beginning-of-current-level-expr) + (looking-at "<")) + (progn + (reason-rewind-to-beginning-of-current-level-expr) + (current-column)) + (progn + (backward-up-list) + (reason-rewind-to-beginning-of-current-level-expr) + + (cond + ((looking-at "switch") + (current-column)) + + ((looking-at "|") + (+ (current-column) (* reason-indent-offset 2))) + + (t + (let ((current-level (reason-paren-level))) + (save-excursion + (while (and (= current-level (reason-paren-level)) + (not (looking-at reason-binding))) + (reason-rewind-irrelevant) + (reason-rewind-to-beginning-of-current-level-expr)) + (+ (current-column) reason-indent-offset))))))))))) + (cond + ;; A function return type is indented to the corresponding function arguments + ((looking-at "=>") + (+ baseline reason-indent-offset)) + + ((reason-in-str-or-cmnt) + (cond + ;; In the end of the block -- align with star + ((looking-at "*/") (+ baseline 1)) + ;; Indent to the following shape: + ;; /* abcd + ;; * asdf + ;; */ + ;; + ((looking-at "*") (+ baseline 1)) + ;; Indent to the following shape: + ;; /* abcd + ;; asdf + ;; */ + ;; + (t (+ baseline (+ reason-indent-offset 1))))) + + ((looking-at "</") (- baseline reason-indent-offset)) + + ;; A closing brace is 1 level unindented + ((looking-at "}\\|)\\|\\]") + (save-excursion + (reason-rewind-irrelevant) + (let ((jsx? (reason-looking-back-str ">"))) + (backward-up-list) + (reason-rewind-to-beginning-of-current-level-expr) + (cond + ((looking-at "switch") baseline) + + (jsx? (current-column)) + + (t (- baseline reason-indent-offset)))))) + + ;; Doc comments in /** style with leading * indent to line up the *s + ((and (nth 4 (syntax-ppss)) (looking-at "*")) + (+ 1 baseline)) + + ;; If we're in any other token-tree / sexp, then: + (t + (or + ;; If we are inside a pair of braces, with something after the + ;; open brace on the same line and ending with a comma, treat + ;; it as fields and align them. + (when (> level 0) + (save-excursion + (reason-rewind-irrelevant) + (backward-up-list) + ;; Point is now at the beginning of the containing set of braces + (reason-align-to-expr-after-brace))) + + (progn + (back-to-indentation) + (cond ((looking-at (regexp-opt '("and" "type"))) + baseline) + ((save-excursion + (reason-rewind-irrelevant) + (= (point) 1)) + baseline) + ((save-excursion + (while (looking-at "|") + (reason-rewind-irrelevant) + (back-to-indentation)) + (looking-at (regexp-opt '("type")))) + (+ baseline reason-indent-offset)) + ((looking-at "|\\|/[/*]") + baseline) + ((and (> level 0) + (save-excursion + (reason-rewind-irrelevant) + (backward-up-list) + (reason-rewind-to-beginning-of-current-level-expr) + (looking-at "switch"))) + (+ baseline reason-indent-offset)) + ((save-excursion + (reason-rewind-irrelevant) + (looking-back "[{;,\\[(]" (- (point) 2))) + baseline) + ((and + (save-excursion + (reason-rewind-irrelevant) + (reason-rewind-to-beginning-of-current-level-expr) + (and (looking-at reason-binding) + (not (progn + (forward-sexp) + (forward-sexp) + (skip-chars-forward "[:space:]\n") + (looking-at "="))))) + (not (save-excursion + (skip-chars-backward "[:space:]\n") + (reason-looking-back-str "=>")))) + (save-excursion + (reason-rewind-irrelevant) + (backward-sexp) + (reason-align-to-prev-expr))) + ((save-excursion + (reason-rewind-irrelevant) + (looking-back "<\/.*?>" (- (point) 30))) + baseline) + (t + (save-excursion + (reason-rewind-irrelevant) + (reason-rewind-to-beginning-of-current-level-expr) + + (if (looking-at "|") + baseline + (+ baseline reason-indent-offset))))) + ;; Point is now at the beginning of the current line + )))))))) + + (when indent + ;; If we're at the beginning of the line (before or at the current + ;; indentation), jump with the indentation change. Otherwise, save the + ;; excursion so that adding the indentations will leave us at the + ;; equivalent position within the line to where we were before. + (if (<= (current-column) (current-indentation)) + (indent-line-to indent) + (save-excursion (indent-line-to indent)))))) + +(provide 'reason-indent) + +;;; reason-indent.el ends here diff --git a/emacs.d/vendor/reason-interaction.el b/emacs.d/vendor/reason-interaction.el new file mode 100644 index 000000000000..6ceaed1e9340 --- /dev/null +++ b/emacs.d/vendor/reason-interaction.el @@ -0,0 +1,216 @@ +;;; reason-interaction.el --- Phrase navitagion for rtop -*-lexical-binding: t-*- + +;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved. + +;;; Commentary: + +;; Phrase navigation for utop and maybe other REPLs. + +;; The utop compatibility layer for Reason was mainly taken from: +;; https://github.com/ocaml/tuareg/blob/master/tuareg-light.el (big thanks!) + +;;; Code: + +(defun reason-backward-char (&optional step) + "Go back one char. +Similar to `backward-char` but it does not signal errors +`beginning-of-buffer` and `end-of-buffer`. It optionally takes a +STEP parameter for jumping back more than one character." + (when step (goto-char (- (point) step)) + (goto-char (1- (point))))) + +(defun reason-forward-char (&optional step) + "Go forward one char. +Similar to `forward-char` but it does not signal errors +`beginning-of-buffer` and `end-of-buffer`. It optionally takes a +STEP parameter for jumping back more than one character." + (when step (goto-char (+ (point) step)) + (goto-char (1+ (point))))) + +(defun reason-in-literal-p () + "Return non-nil if point is inside an Reason literal." + (nth 3 (syntax-ppss))) + +(defconst reason-comment-delimiter-regexp "\\*/\\|/\\*" + "Regex for identify either open or close comment delimiters.") + +(defun reason-in-between-comment-chars-p () + "Return non-nil iff point is in between the comment delimiter chars. +It returns non-nil if point is between the chars only (*|/ or /|* +where | is point)." + (and (not (bobp)) (not (eobp)) + (or (and (char-equal ?/ (char-before)) (char-equal ?* (char-after))) + (and (char-equal ?* (char-before)) (char-equal ?/ (char-after)))))) + +(defun reason-looking-at-comment-delimiters-p () + "Return non-nil iff point in between comment delimiters." + (looking-at-p reason-comment-delimiter-regexp)) + +(defun reason-in-between-comment-delimiters-p () + "Return non-nil if inside /* and */." + (nth 4 (syntax-ppss))) + +(defun reason-in-comment-p () + "Return non-nil iff point is inside or right before a comment." + (or (reason-in-between-comment-delimiters-p) + (reason-in-between-comment-chars-p) + (reason-looking-at-comment-delimiters-p))) + +(defun reason-beginning-of-literal-or-comment () + "Skip to the beginning of the current literal or comment (or buffer)." + (interactive) + (goto-char (or (nth 8 (syntax-ppss)) (point)))) + +(defun reason-inside-block-scope-p () + "Skip to the beginning of the current literal or comment (or buffer)." + (and (> (nth 0 (syntax-ppss)) 0) + (let ((delim-start (nth 1 (syntax-ppss)))) + (save-excursion + (goto-char delim-start) + (char-equal ?{ (following-char)))))) + +(defun reason-at-phrase-break-p () + "Is the underlying `;' a phrase break?" + ;; Difference from OCaml, the phrase separator is a single semi-colon + (and (not (eobp)) + (char-equal ?\; (following-char)))) + +(defun reason-skip-to-close-delimiter (&optional limit) + "Skip to the end of a Reason block. +It basically calls `re-search-forward` in order to go to any +closing delimiter, not concerning itself with balancing of any +sort. Client code needs to check that. +LIMIT is passed to `re-search-forward` directly." + (re-search-forward "\\s)" limit 'move)) + +(defun reason-skip-back-to-open-delimiter (&optional limit) + "Skip to the beginning of a Reason block backwards. +It basically calls `re-search-backward` in order to go to any +opening delimiter, not concerning itself with balancing of any +sort. Client code needs to check that. +LIMIT is passed to `re-search-backward` directly." + (re-search-backward "\\s(" limit 'move)) + +(defun reason-find-phrase-end () + "Skip to the end of a phrase." + (while (and (not (eobp)) + (not (reason-at-phrase-break-p))) + (if (re-search-forward ";" nil 'move) + (progn (when (reason-inside-block-scope-p) + (reason-skip-to-close-delimiter)) + (goto-char (1- (point)))) + ;; avoid infinite loop at the end of the buffer + (re-search-forward "[[:space:]\\|\n]+" nil 'move))) + (min (goto-char (1+ (point))) (point-max))) + +(defun reason-skip-blank-and-comments () + "Skip blank spaces and comments." + (cond + ((eobp) (point)) + ((or (reason-in-between-comment-chars-p) + (reason-looking-at-comment-delimiters-p)) (progn + (reason-forward-char 1) + (reason-skip-blank-and-comments))) + ((reason-in-between-comment-delimiters-p) (progn + (search-forward "*/" nil t) + (reason-skip-blank-and-comments))) + ((eolp) (progn + (reason-forward-char 1) + (reason-skip-blank-and-comments))) + (t (progn (skip-syntax-forward " ") + (point))))) + +(defun reason-skip-back-blank-and-comments () + "Skip blank spaces and comments backwards." + (cond + ((bobp) (point)) + ((looking-back reason-comment-delimiter-regexp) (progn + (reason-backward-char 1) + (reason-skip-back-blank-and-comments))) + ((reason-in-between-comment-delimiters-p) (progn + (search-backward "/*" nil t) + (reason-backward-char 1) + (reason-skip-back-blank-and-comments))) + ((or (reason-in-between-comment-chars-p) + (reason-looking-at-comment-delimiters-p)) (progn + (reason-backward-char 1) + (reason-skip-back-blank-and-comments))) + ((bolp) (progn + (reason-backward-char 1) + (reason-skip-back-blank-and-comments))) + (t (progn (skip-syntax-backward " ") + (point))))) + +(defun reason-ro (&rest words) + "Build a regex matching iff at least a word in WORDS is present." + (concat "\\<" (regexp-opt words t) "\\>")) + +(defconst reason-find-phrase-beginning-regexp + (concat (reason-ro "end" "type" "module" "sig" "struct" "class" + "exception" "open" "let") + "\\|^#[ \t]*[a-z][_a-z]*\\>\\|;")) + +(defun reason-at-phrase-start-p () + "Return t if is looking at the beginning of a phrase. +A phrase starts when a toplevel keyword is at the beginning of a line." + (or (looking-at "#") + (looking-at reason-find-phrase-beginning-regexp))) + +(defun reason-find-phrase-beginning-backward () + "Find the beginning of a phrase and return point. +It scans code backwards, therefore the caller can assume that the +beginning of the phrase (if found) is always before the starting +point. No error is signalled and (point-min) is returned when a +phrease cannot be found." + (beginning-of-line) + (while (and (not (bobp)) (not (reason-at-phrase-start-p))) + (if (reason-inside-block-scope-p) + (reason-skip-back-to-open-delimiter) + (re-search-backward reason-find-phrase-beginning-regexp nil 'move))) + (point)) + +(defun reason-discover-phrase () + "Discover a Reason phrase in the buffer." + ;; TODO reason-with-internal-syntax ;; tuareg2 modifies the syntax table (removed for now) + ;; TODO stop-at-and feature for phrase detection (do we need it?) + ;; TODO tuareg2 has some custom logic for module and class (do we need it?) + (save-excursion + (let ((case-fold-search nil)) + (reason-skip-blank-and-comments) + (list (reason-find-phrase-beginning-backward) ;; beginning + (reason-find-phrase-end) ;; end + (save-excursion ;; end-with-comment + (reason-skip-blank-and-comments) + (point)))))) + +(defun reason-discover-phrase-debug () + "Discover a Reason phrase in the buffer (debug mode)." + (let ((triple (reason-discover-phrase))) + (message (concat "Evaluating: \"" (reason-fetch-phrase triple) "\"")) + triple)) + +(defun reason-fetch-phrase (triple) + "Fetch the phrase text given a TRIPLE." + (let* ((start (nth 0 triple)) + (end (nth 1 triple))) ;; we don't need end-with-comment + (buffer-substring-no-properties start end))) + +(defun reason-next-phrase () + "Skip to the beginning of the next phrase." + (cond + ((reason-at-phrase-start-p) (point)) + ((eolp) (progn + (forward-char 1) + (reason-skip-blank-and-comments) + (reason-next-phrase))) + ((reason-inside-block-scope-p) (progn (reason-skip-to-close-delimiter) + (reason-next-phrase))) + ((looking-at ";") (progn + (forward-char 1) + (reason-next-phrase))) + (t (progn (end-of-line) + (reason-next-phrase))))) + +(provide 'reason-interaction) + +;;; reason-interaction.el ends here diff --git a/emacs.d/vendor/reason-mode.el b/emacs.d/vendor/reason-mode.el new file mode 100644 index 000000000000..789735955db2 --- /dev/null +++ b/emacs.d/vendor/reason-mode.el @@ -0,0 +1,242 @@ +;;; reason-mode.el --- A major mode for editing ReasonML -*-lexical-binding: t-*- +;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved. + +;; Version: 0.4.0 +;; Author: Mozilla +;; Url: https://github.com/reasonml-editor/reason-mode +;; Keywords: languages, ocaml +;; Package-Requires: ((emacs "24.3")) + +;; This file is NOT part of GNU Emacs. + +;; This file is distributed under the terms of both the MIT license and the +;; Apache License (version 2.0). + +;;; Commentary: +;; This project provides useful functions and helpers for developing code +;; using the Reason programming language (https://facebook.github.io/reason). +;; +;; Reason is an umbrella project that provides a curated layer for OCaml. +;; +;; It offers: +;; - A new, familiar syntax for the battle-tested language that is OCaml. +;; - A workflow for compiling to JavaScript and native code. +;; - A set of friendly documentations, libraries and utilities. +;; +;; See the README.md for more details. + +;;; Code: + +(require 'reason-indent) +(require 'refmt) +(require 'reason-interaction) + +(eval-when-compile (require 'rx) + (require 'compile) + (require 'url-vars)) + +;; Syntax definitions and helpers +(defvar reason-mode-syntax-table + (let ((table (make-syntax-table))) + + ;; Operators + (dolist (i '(?+ ?- ?* ?/ ?& ?| ?^ ?! ?< ?> ?~ ?@)) + (modify-syntax-entry i "." table)) + + ;; Strings + (modify-syntax-entry ?\" "\"" table) + (modify-syntax-entry ?\\ "\\" table) + (modify-syntax-entry ?\' "_" table) + + ;; Comments + (modify-syntax-entry ?/ ". 124b" table) + (modify-syntax-entry ?* ". 23n" table) + (modify-syntax-entry ?\n "> b" table) + (modify-syntax-entry ?\^m "> b" table) + + table)) + +(defgroup reason nil + "Support for Reason code." + :link '(url-link "http://facebook.github.io/reason/") + :group 'languages) + +(defcustom reason-mode-hook nil + "Hook called by `reason-mode'." + :type 'hook + :group 'reason) + +;; Font-locking definitions and helpers +(defconst reason-mode-keywords + '("and" "as" + "else" "external" + "fun" "for" + "if" "impl" "in" "include" + "let" + "module" "match" "mod" "move" "mutable" + "open" + "priv" "pub" + "rec" "ref" "return" + "self" "static" "switch" "struct" "super" + "trait" "type" + "use" + "virtual" + "where" "when" "while")) + +(defconst reason-mode-consts + '("true" "false")) + +(defconst reason-special-types + '("int" "float" "string" "char" + "bool" "unit" "list" "array" "exn" + "option" "ref")) + +(defconst reason-camel-case + (rx symbol-start + (group upper (0+ (any word nonascii digit "_"))) + symbol-end)) + +(eval-and-compile + (defconst reason--char-literal-rx + (rx (seq (group "'") + (or (seq "\\" anything) + (not (any "'\\"))) + (group "'"))))) + +(defun reason-re-word (inner) + "Build a word regexp given INNER." + (concat "\\<" inner "\\>")) + +(defun reason-re-grab (inner) + "Build a grab regexp given INNER." + (concat "\\(" inner "\\)")) + +(defun reason-regexp-opt-symbols (words) + "Like `(regexp-opt words 'symbols)`, but will work on Emacs 23. +See rust-mode PR #42. +Argument WORDS argument to pass to `regexp-opt`." + (concat "\\_<" (regexp-opt words t) "\\_>")) + +;;; Syntax highlighting for Reason +(defvar reason-font-lock-keywords + `((,(reason-regexp-opt-symbols reason-mode-keywords) . font-lock-keyword-face) + (,(reason-regexp-opt-symbols reason-special-types) . font-lock-builtin-face) + (,(reason-regexp-opt-symbols reason-mode-consts) . font-lock-constant-face) + + (,reason-camel-case 1 font-lock-type-face) + + ;; Field names like `foo:`, highlight excluding the : + (,(concat (reason-re-grab reason-re-ident) ":[^:]") 1 font-lock-variable-name-face) + ;; Module names like `foo::`, highlight including the :: + (,(reason-re-grab (concat reason-re-ident "::")) 1 font-lock-type-face) + ;; Name punned labeled args like ::foo + (,(concat "[[:space:]]+" (reason-re-grab (concat "::" reason-re-ident))) 1 font-lock-type-face) + + ;; TODO jsx attribs? + (, + (concat "<[/]?" (reason-re-grab reason-re-ident) "[^>]*" ">") + 1 font-lock-type-face))) + +(defun reason-mode-try-find-alternate-file (mod-name extension) + "Switch to the file given by MOD-NAME and EXTENSION." + (let* ((filename (concat mod-name extension)) + (buffer (get-file-buffer filename))) + (if buffer (switch-to-buffer buffer) + (find-file filename)))) + +(defun reason-mode-find-alternate-file () + "Switch to implementation/interface file." + (interactive) + (let ((name buffer-file-name)) + (when (string-match "\\`\\(.*\\)\\.re\\([il]\\)?\\'" name) + (let ((mod-name (match-string 1 name)) + (e (match-string 2 name))) + (cond + ((string= e "i") + (reason-mode-try-find-alternate-file mod-name ".re")) + (t + (reason-mode-try-find-alternate-file mod-name ".rei"))))))) + +(defun reason--syntax-propertize-multiline-string (end) + "Propertize Reason multiline string. +Argument END marks the end of the string." + (let ((ppss (syntax-ppss))) + (when (eq t (nth 3 ppss)) + (let ((key (save-excursion + (goto-char (nth 8 ppss)) + (and (looking-at "{\\([a-z]*\\)|") + (match-string 1))))) + (when (search-forward (format "|%s}" key) end 'move) + (put-text-property (1- (match-end 0)) (match-end 0) + 'syntax-table (string-to-syntax "|"))))))) + +(defun reason-syntax-propertize-function (start end) + "Propertize Reason function. +Argument START marks the beginning of the function. +Argument END marks the end of the function." + (goto-char start) + (reason--syntax-propertize-multiline-string end) + (funcall + (syntax-propertize-rules + (reason--char-literal-rx (1 "\"") (2 "\"")) + ;; multi line strings + ("\\({\\)[a-z]*|" + (1 (prog1 "|" + (goto-char (match-end 0)) + (reason--syntax-propertize-multiline-string end))))) + (point) end)) + +(defvar reason-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "\C-c\C-a" #'reason-mode-find-alternate-file) + (define-key map "\C-c\C-r" #'refmt-region-ocaml-to-reason) + (define-key map "\C-c\C-o" #'refmt-region-reason-to-ocaml) + map)) + +;;;###autoload +(define-derived-mode reason-mode prog-mode "Reason" + "Major mode for Reason code. + +\\{reason-mode-map}" + :group 'reason + :syntax-table reason-mode-syntax-table + :keymap reason-mode-map + + ;; Syntax + (setq-local syntax-propertize-function #'reason-syntax-propertize-function) + ;; Indentation + (setq-local indent-line-function 'reason-mode-indent-line) + ;; Fonts + (setq-local font-lock-defaults '(reason-font-lock-keywords)) + ;; Misc + (setq-local comment-start "/*") + (setq-local comment-end "*/") + (setq-local indent-tabs-mode nil) + ;; Allow paragraph fills for comments + (setq-local comment-start-skip "/\\*+[ \t]*") + (setq-local paragraph-start + (concat "^[ \t]*$\\|\\*)$\\|" page-delimiter)) + (setq-local paragraph-separate paragraph-start) + (setq-local require-final-newline t) + (setq-local normal-auto-fill-function nil) + (setq-local comment-multi-line t) + + (setq-local beginning-of-defun-function 'reason-beginning-of-defun) + (setq-local end-of-defun-function 'reason-end-of-defun) + (setq-local parse-sexp-lookup-properties t)) + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.rei?\\'" . reason-mode)) + +(defun reason-mode-reload () + "Reload Reason mode." + (interactive) + (unload-feature 'reason-mode) + (unload-feature 'reason-indent) + (unload-feature 'reason-interaction) + (require 'reason-mode) + (reason-mode)) + +(provide 'reason-mode) + +;;; reason-mode.el ends here diff --git a/emacs.d/vendor/refmt.el b/emacs.d/vendor/refmt.el new file mode 100644 index 000000000000..b9ea2b43f0ce --- /dev/null +++ b/emacs.d/vendor/refmt.el @@ -0,0 +1,231 @@ +;;; refmt.el --- utility functions to format reason code + +;; Copyright (c) 2014 The go-mode Authors. All rights reserved. +;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved. + +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following conditions are +;; met: + +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following disclaimer +;; in the documentation and/or other materials provided with the +;; distribution. +;; * Neither the name of the copyright holder nor the names of its +;; contributors may be used to endorse or promote products derived from +;; this software without specific prior written permission. + +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.) + +;;; Commentary: +;; + +;;; Code: + +(require 'cl-lib) + +(defcustom refmt-command "refmt" + "The 'refmt' command." + :type 'string + :group 're-fmt) + +(defcustom refmt-show-errors 'buffer + "Where to display refmt error output. +It can either be displayed in its own buffer, in the echo area, or not at all. +Please note that Emacs outputs to the echo area when writing +files and will overwrite refmt's echo output if used from inside +a `before-save-hook'." + :type '(choice + (const :tag "Own buffer" buffer) + (const :tag "Echo area" echo) + (const :tag "None" nil)) + :group 're-fmt) + +(defcustom refmt-width-mode nil + "Specify width when formatting buffer contents." + :type '(choice + (const :tag "Window width" window) + (const :tag "Fill column" fill) + (const :tag "None" nil)) + :group 're-fmt) + +;;;###autoload +(defun refmt-before-save () + "Add this to .emacs to run refmt on the current buffer when saving: + (add-hook 'before-save-hook 'refmt-before-save)." + (interactive) + (when (eq major-mode 'reason-mode) (refmt))) + +(defun reason--goto-line (line) + (goto-char (point-min)) + (forward-line (1- line))) + +(defun reason--delete-whole-line (&optional arg) + "Delete the current line without putting it in the `kill-ring'. +Derived from function `kill-whole-line'. ARG is defined as for that +function." + (setq arg (or arg 1)) + (if (and (> arg 0) + (eobp) + (save-excursion (forward-visible-line 0) (eobp))) + (signal 'end-of-buffer nil)) + (if (and (< arg 0) + (bobp) + (save-excursion (end-of-visible-line) (bobp))) + (signal 'beginning-of-buffer nil)) + (cond ((zerop arg) + (delete-region (progn (forward-visible-line 0) (point)) + (progn (end-of-visible-line) (point)))) + ((< arg 0) + (delete-region (progn (end-of-visible-line) (point)) + (progn (forward-visible-line (1+ arg)) + (unless (bobp) + (backward-char)) + (point)))) + (t + (delete-region (progn (forward-visible-line 0) (point)) + (progn (forward-visible-line arg) (point)))))) + +(defun reason--apply-rcs-patch (patch-buffer &optional start-pos) + "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer." + (setq start-pos (or start-pos (point-min))) + (let ((first-line (line-number-at-pos start-pos)) + (target-buffer (current-buffer)) + ;; Relative offset between buffer line numbers and line numbers + ;; in patch. + ;; + ;; Line numbers in the patch are based on the source file, so + ;; we have to keep an offset when making changes to the + ;; buffer. + ;; + ;; Appending lines decrements the offset (possibly making it + ;; negative), deleting lines increments it. This order + ;; simplifies the forward-line invocations. + (line-offset 0)) + (save-excursion + (with-current-buffer patch-buffer + (goto-char (point-min)) + (while (not (eobp)) + (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") + (error "invalid rcs patch or internal error in reason--apply-rcs-patch")) + (forward-line) + (let ((action (match-string 1)) + (from (string-to-number (match-string 2))) + (len (string-to-number (match-string 3)))) + (cond + ((equal action "a") + (let ((start (point))) + (forward-line len) + (let ((text (buffer-substring start (point)))) + (with-current-buffer target-buffer + (cl-decf line-offset len) + (goto-char start-pos) + (forward-line (- from len line-offset)) + (insert text))))) + ((equal action "d") + (with-current-buffer target-buffer + (reason--goto-line (- (1- (+ first-line from)) line-offset)) + (cl-incf line-offset len) + (reason--delete-whole-line len))) + (t + (error "invalid rcs patch or internal error in reason--apply-rcs-patch"))))))))) + +(defun refmt--process-errors (filename tmpfile errorfile errbuf) + (with-current-buffer errbuf + (if (eq refmt-show-errors 'echo) + (progn + (message "%s" (buffer-string)) + (refmt--kill-error-buffer errbuf)) + (insert-file-contents errorfile nil nil nil) + ;; Convert the refmt stderr to something understood by the compilation mode. + (goto-char (point-min)) + (insert "refmt errors:\n") + (while (search-forward-regexp (regexp-quote tmpfile) nil t) + (replace-match (file-name-nondirectory filename))) + (compilation-mode) + (display-buffer errbuf)))) + +(defun refmt--kill-error-buffer (errbuf) + (let ((win (get-buffer-window errbuf))) + (if win + (quit-window t win) + (with-current-buffer errbuf + (erase-buffer)) + (kill-buffer errbuf)))) + +(defun apply-refmt (&optional start end from to) + (setq start (or start (point-min)) + end (or end (point-max)) + from (or from "re") + to (or to "re")) + (let* ((ext (file-name-extension buffer-file-name t)) + (bufferfile (make-temp-file "refmt" nil ext)) + (outputfile (make-temp-file "refmt" nil ext)) + (errorfile (make-temp-file "refmt" nil ext)) + (errbuf (if refmt-show-errors (get-buffer-create "*Refmt Errors*"))) + (patchbuf (get-buffer-create "*Refmt patch*")) + (coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + (width-args + (cond + ((equal refmt-width-mode 'window) + (list "--print-width" (number-to-string (window-body-width)))) + ((equal refmt-width-mode 'fill) + (list "--print-width" (number-to-string fill-column))) + (t + '())))) + (unwind-protect + (save-restriction + (widen) + (write-region start end bufferfile) + (if errbuf + (with-current-buffer errbuf + (setq buffer-read-only nil) + (erase-buffer))) + (with-current-buffer patchbuf + (erase-buffer)) + (if (zerop (apply 'call-process + refmt-command nil (list (list :file outputfile) errorfile) + nil (append width-args (list "--parse" from "--print" to bufferfile)))) + (progn + (call-process-region start end "diff" nil patchbuf nil "-n" "-" + outputfile) + (reason--apply-rcs-patch patchbuf start) + (message "Applied refmt") + (if errbuf (refmt--kill-error-buffer errbuf))) + (message "Could not apply refmt") + (if errbuf + (refmt--process-errors (buffer-file-name) bufferfile errorfile errbuf))))) + (kill-buffer patchbuf) + (delete-file errorfile) + (delete-file bufferfile) + (delete-file outputfile))) + +(defun refmt () + "Format the current buffer according to the refmt tool." + (interactive) + (apply-refmt)) + +(defun refmt-region-ocaml-to-reason (start end) + (interactive "r") + (apply-refmt start end "ml")) + +(defun refmt-region-reason-to-ocaml (start end) + (interactive "r") + (apply-refmt start end "re" "ml")) + +(provide 'refmt) + +;;; refmt.el ends here diff --git a/emacs.d/vendor/slack-snippets.el b/emacs.d/vendor/slack-snippets.el new file mode 100644 index 000000000000..6bf933cfb86d --- /dev/null +++ b/emacs.d/vendor/slack-snippets.el @@ -0,0 +1,228 @@ +;;; private/grfn/slack-snippets.el -*- lexical-binding: t; -*- + +(require 's) +(require 'json) +(require 'dash) +(require 'dash-functional) +(require 'request) +(require 'subr-x) + +;;; +;;; 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 + (-filter + (lambda (chan) (channel-label chan users))) + (-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))) + + (-map (lambda (chan) (let ((label (assoc-default 'label (cdr chan))) + (id (car chan))) + (propertize label 'channel-id id))) + --conversations) + + ) + +;;; +;;; Posting code snippets to slack +;;; + +(defun prompt-for-channel (cb) + (slack/conversations + (lambda (conversations) + (setq testing (-map (lambda (chan) (let ((label (assoc-default 'label (cdr chan))) + (id (car chan))) + (propertize label 'channel-id id))) + conversations)) + (ivy-read + "Select channel: " + ;; TODO want to potentially use purpose / topic stuff here + (-map (lambda (chan) (let ((label (assoc-default 'label (cdr chan))) + (id (car chan))) + (propertize label 'channel-id id))) + conversations) + :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) + +(defun slack-send-code-snippet (&optional snippet-text) + (interactive) + (when-let ((snippet-text (or snippet-text + (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))))) diff --git a/emacs.d/wpc/casing.el b/emacs.d/wpc/casing.el new file mode 100644 index 000000000000..0592d9dddb08 --- /dev/null +++ b/emacs.d/wpc/casing.el @@ -0,0 +1,39 @@ +;; casing.el --- Helper functions for formatting text -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; These functions are intended to be bound to KBDs for daily use and +;; refactoring. + +;;; Code: + +(require 's) +(require 'dash) + +;; todo - grab the string at point and replace it with the output of +;; each fn + +(defun caps->kebab (x) + "Change the casing of X from CAP_CASE to kebab-case." + (->> x + s-downcase + (s-replace "_" "-"))) + +(defun kebab->caps (x) + "Change the casing of X from CAP_CASE to kebab-case." + (->> x + s-upcase + (s-replace "-" "_"))) + +;;; Tests: + +(ert-deftest caps->kebab-test () + (should (string= (caps->kebab "CAPS_CASE_STRING") + "caps-case-string"))) + +(ert-deftest kebab->caps-test () + (should (string= (kebab->caps "kebab-case-string") + "KEBAB_CASE_STRING"))) + +(provide 'casing) +;;; casing.el ends here diff --git a/emacs.d/wpc/functions.el b/emacs.d/wpc/functions.el new file mode 100644 index 000000000000..f2514a1bed85 --- /dev/null +++ b/emacs.d/wpc/functions.el @@ -0,0 +1,227 @@ +;; functions.el --- Helper functions for my Emacs development -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; This file hopefully contains friendly APIs that making ELisp development more enjoyable. + +;;; Code: + +;; TODO: clean up this file so this isn't necessary +(setq evil-want-integration nil) +(require 'evil) + +(require 'projectile) +(require 'paredit) +(require 'term) +(require 'f) +(require 'yasnippet) +(require 'ido) + +(defun wpc/evil-window-vsplit-right () + (interactive) + (evil-window-vsplit) + (windmove-right)) + +(defun wpc/evil-window-split-down () + (interactive) + (evil-window-split) + (windmove-down)) + +(defun wpc/reindent-defun-and-align-clojure-map () + (interactive) + (call-interactively #'paredit-reindent-defun) + (call-interactively #'clojure-align)) + +(defun wpc/find-file () + "Prefer project-based file-finding if inside of project; otherwise gracefully fallback." + (interactive) + (with-current-buffer (current-buffer) + (if (projectile-project-p) + (call-interactively #'projectile-find-file) + (call-interactively #'find-file)))) + +(defun wpc/find-or-create-js-test () + (->> buffer-file-name + (s-chop-suffix ".js") + (s-append ".test.js") + (find-file))) + +(defun wpc/find-or-create-js-module () + (->> buffer-file-name + (s-chop-suffix ".test.js") + (s-append ".js") + (find-file))) + +(defun wpc/find-or-create-js-store () + (->> buffer-file-name + (s-replace "index.js" "store.js") + (find-file))) + +(defun wpc/find-or-create-js-component () + (->> buffer-file-name + (s-replace "store.js" "index.js") + (find-file))) + +(defun wpc/bind-ido-keys () + "Adds custom KBDs for ido. This function is recommended in the ido source code." + (define-key ido-completion-map (kbd "<tab>") #'ido-next-match) + (define-key ido-completion-map (kbd "<backtab>") #'ido-prev-match)) + +(defun wpc/toggle-between-js-test-and-module () + "Toggle between a Javascript test or module." + (interactive) + (if (s-ends-with? ".test.js" buffer-file-name) + (wpc/find-or-create-js-module) + (if (s-ends-with? ".js" buffer-file-name) + (wpc/find-or-create-js-test) + (message "Not in a Javascript file. Exiting...")))) + +(defun wpc/toggle-between-js-component-and-store () + "Toggle between a React component and its Redux store." + (interactive) + (if (s-ends-with? "index.js" buffer-file-name) + (wpc/find-or-create-js-store) + (if (or (s-ends-with? "store.js" buffer-file-name) + (s-ends-with? "store.test.js" buffer-file-name)) + (wpc/find-or-create-js-component) + (message "Not in a React/Redux file. Exiting...")))) + +(defun wpc/read-file-as-string (filename) + (with-temp-buffer + (insert-file-contents filename) + (s-trim (buffer-string)))) + +(defun wpc/create-snippet () + "Creates a window split and then opens the Yasnippet editor." + (interactive) + (evil-window-vsplit) + (call-interactively #'yas-new-snippet)) + +(defun wpc/edit-init-el () + "Creates a window split and then edits the init.el file." + (interactive) + (evil-window-vsplit) + (find-file "~/.emacs.d/init.el")) + +(defun wpc/set-flow-executable () + (interactive) + (let* ((root (locate-dominating-file buffer-file-name "node_modules/flow-bin")) + (executable (car (file-expand-wildcards + (concat root "node_modules/flow-bin/*osx*/flow"))))) + (setq-local company-flow-executable executable) + ;; These are not necessary for this package, but a good idea if you use + ;; these other packages + (setq-local flow-minor-default-binary executable) + (setq-local flycheck-javascript-flow-executable executable))) + +(defun wpc/jump-to-parent-file () + "Jumps to a React store or component's parent file. Useful for store or index file." + (interactive) + (-> buffer-file-name + f-dirname + (f-join "..") + (f-join (f-filename buffer-file-name)) + find-file)) + +(defun wpc/tmux-emacs-windmove (dir) + "Move windows in a Tmux-friendly way." + (let* ((dir->opts '((left . ("-L" . windmove-left)) + (right . ("-R" . windmove-right)) + (above . ("-U" . windmove-up)) + (below . ("-D" . windmove-down)))) + (opts (alist-get dir dir->opts)) + (tmux-opt (car opts)) + (emacs-fn (cdr opts))) + (if (window-in-direction dir) + (funcall emacs-fn) + (shell-command (format "tmux select-pane %s" tmux-opt))))) + +(defun wpc/tmux-emacs-windmove-left () + (interactive) + (wpc/tmux-emacs-windmove 'left)) + +(defun wpc/tmux-emacs-windmove-right () + (interactive) + (wpc/tmux-emacs-windmove 'right)) + +(defun wpc/tmux-emacs-windmove-up () + (interactive) + (wpc/tmux-emacs-windmove 'above)) + +(defun wpc/tmux-emacs-windmove-down () + (interactive) + (wpc/tmux-emacs-windmove 'below)) + +(defun wpc/get-window-by-buffername (buffername) + "Finds a window by the name of the buffer it's hosting." + (let ((buffer (get-buffer buffername))) + (when buffer + (get-buffer-window buffer)))) + +(defun wpc/add-earmuffs (x) + "Returns X surrounded by asterisks." + (format "*%s*" x)) + +(defun wpc/get-default-shell () + (or explicit-shell-file-name + (getenv "SHELL") + (getenv "ESHELL"))) + +(defun wpc/find-terminal-buffer () + (get-buffer (wpc/add-earmuffs wpc/terminal-name))) + +(defun wpc/find-terminal-window () + (wpc/get-window-by-buffername (wpc/add-earmuffs wpc/terminal-name))) + +(defun wpc/create-terminal-session () + (wpc/evil-window-vsplit-right) + (ansi-term (wpc/get-default-shell) wpc/terminal-name)) + +(defun wpc/toggle-terminal () + "Toggles a custom terminal session in Emacs." + (interactive) + (let ((window (wpc/find-terminal-window))) + (if window + (delete-window window) + (wpc/find-or-create-terminal)))) + +(defun wpc/find-or-create-terminal () + (let ((buffer (wpc/find-terminal-buffer))) + (if buffer + (display-buffer buffer) + (wpc/create-terminal-session)))) + +(defun wpc/put-file-name-on-clipboard () + "Put the current file name on the clipboard" + (interactive) + (let ((filename (if (equal major-mode 'dired-mode) + default-directory + (buffer-file-name)))) + (when filename + (with-temp-buffer + (insert filename) + (clipboard-kill-region (point-min) (point-max))) + (message filename)))) + +(defun wpc/evil-replace-under-point () + "Faster than typing %s//thing/g" + (interactive) + (save-excursion + (evil-ex (concat "%s/\\b" (symbol-name (symbol-at-point)) "\\b/")))) + +(defun wpc/disable-linum-mode () + "Convenience function defined to make adding hooks easier without a lambda." + (linum-mode -1)) + +(defun wpc/disable-company-mode () + "Convenience function defined to make adding hooks easier without a lambda." + (company-mode -1)) + +(defun wpc/toggle-term-mode () + "Toggle between term-line-mode and temr-char-mode." + (if (term-in-line-mode) + (term-char-mode) + (term-line-mode))) + +(provide 'functions) +;;; functions.el ends here diff --git a/emacs.d/wpc/macros.el b/emacs.d/wpc/macros.el new file mode 100644 index 000000000000..aedd6f5b3cb9 --- /dev/null +++ b/emacs.d/wpc/macros.el @@ -0,0 +1,33 @@ +;;; macros.el --- Helpful variables for making my ELisp life more enjoyable -*- lexical-binding: t -*- +;; Authpr: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; This file contains helpful variables that I use in my ELisp development. + +;;; Code: + +(require 'dash) +(require 's) +(require 'string-functions) + +(defmacro xi (&rest FORMS) + `(lambda ,(--filter (s-contains? (symbol-name it) + (prin1-to-string FORMS)) + '(x1 x2 x3 x4 x5)) + ,FORMS)) + +(defmacro enable (mode) + "Helper for enabling MODE. Useful in `add-hook' calls." + `#'(lambda nil (,mode 1))) + +(defmacro disable (mode) + "Helper for disabling MODE. Useful in `add-hook' calls." + `#'(lambda nil (,mode -1))) + +(defmacro add-hooks (modes) + "Add multiple MODES for the CALLBACK." + `(dolist (mode ,modes) + (add-hook (symbol/ensure-hookified mode) ,callback))) + +(provide 'macros) +;;; macros.el ends here diff --git a/emacs.d/wpc/packages/wpc-clojure.el b/emacs.d/wpc/packages/wpc-clojure.el new file mode 100644 index 000000000000..3644f76a70e2 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-clojure.el @@ -0,0 +1,63 @@ +;;; clojure.el --- My Clojure preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; Hosting my Clojure tooling preferences + +;;; Code: + +;; Helper functions +(defun wpc/buffer-name-for-clojure-mode (mode) + (let* ((project-name (projectile-project-name)) + (cljs-name (concat "*cider-repl CLJS " project-name "*")) + (clj-name (concat "*cider-repl " project-name "*"))) + (cond ((eq mode 'clojurescript-mode) cljs-name) + ((eq mode 'clojure-mode) clj-name) + ((eq mode 'clojurec-mode) cljs-name)))) + +(defun wpc/repl-function-for-clojure-mode (mode) + (let ((project-name (projectile-project-name)) + (cljs-fn #'cider-jack-in-clojurescript) + (clj-fn #'cider-jack-in)) + (cond ((eq mode 'clojurescript-mode) cljs-fn) + ((eq mode 'clojure-mode) clj-fn) + ((eq mode 'clojurec-mode) cljs-fn)))) + +(defun wpc/find-or-create-clojure-or-clojurescript-repl () + (interactive) + (with-current-buffer (current-buffer) + (let ((buffer-name (wpc/buffer-name-for-clojure-mode major-mode)) + (repl-function (wpc/repl-function-for-clojure-mode major-mode))) + (if (get-buffer buffer-name) + (switch-to-buffer buffer-name) + (funcall repl-function))))) + +;; (defun wpc/evil-leader/set-key-for-clojure-modes (kbd callback) +;; (evil-leader/set-key-for-mode 'clojure-mode kbd callback) +;; (evil-leader/set-key-for-mode 'clojurec-mode kbd callback) +;; (evil-leader/set-key-for-mode 'clojurescript-mode kbd callback)) + +;; ;; clojure +;; (wpc/evil-leader/set-key-for-clojure-modes "d" #'cider-doc) +;; (wpc/evil-leader/set-key-for-clojure-modes "e" #'cider-eval-defun-at-point) +;; (wpc/evil-leader/set-key-for-clojure-modes "r" #'wpc/find-or-create-clojure-or-clojurescript-repl) + +(use-package cider + :general + (cider-repl-mode-map + "C-l" 'cider-repl-clear-buffer + "C-u" 'kill-whole-line + "<up>" 'cider-repl-previous-input + "<down>" 'cider-repl-next-input + "C-c 'j" 'wpc/find-or-create-clojure-or-clojurescript-repl) + (n + "M-." 'cider-find-var) + :config + (setq cider-cljs-lein-repl + "(do (require 'figwheel-sidecar.repl-api) + (figwheel-sidecar.repl-api/start-figwheel!) + (figwheel-sidecar.repl-api/cljs-repl))" + cider-prompt-for-symbol nil)) + +(provide 'wpc-clojure) +;;; wpc-clojure.el ends here diff --git a/emacs.d/wpc/packages/wpc-company.el b/emacs.d/wpc/packages/wpc-company.el new file mode 100644 index 000000000000..ec96bdf90b3e --- /dev/null +++ b/emacs.d/wpc/packages/wpc-company.el @@ -0,0 +1,24 @@ +;;; company.el --- Autocompletion package, company, preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; Hosts my company mode preferences + +;;; Code: + +;; autocompletion client +(use-package company + :general + (company-active-map + "C-j" 'company-select-next + "C-n" 'company-select-next + "C-k" 'company-select-previous + "C-p" 'company-select-previous + "C-d" 'company-show-doc-buffer) + :config + (setq company-idle-delay 0) + (setq company-minimum-prefix-length 2) + (global-company-mode)) + +(provide 'wpc-company) +;;; company.el ends here diff --git a/emacs.d/wpc/packages/wpc-dired.el b/emacs.d/wpc/packages/wpc-dired.el new file mode 100644 index 000000000000..1c45ec35e2b4 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-dired.el @@ -0,0 +1,18 @@ +;;; dired.el --- My dired preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; Hosts my attempts at configuring dired + +;;; Code: + +(require 'dired) +(general-def 'dired-mode-map + "c" 'find-file + "f" 'wpc/find-file + "-" 'dired-up-directory) +(general-add-hook 'dired-mode-hook (list (enable dired-hide-details-mode) + #'auto-revert-mode)) + +(provide 'wpc-dired) +;;; dired.el ends here diff --git a/emacs.d/wpc/packages/wpc-docker.el b/emacs.d/wpc/packages/wpc-docker.el new file mode 100644 index 000000000000..586878fdeacd --- /dev/null +++ b/emacs.d/wpc/packages/wpc-docker.el @@ -0,0 +1,18 @@ +;;; docker.el --- Docker preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; My Docker preferences and configuration + +;;; Code: + +(use-package docker + :config + (setenv "DOCKER_TLS_VERIFY" "1") + (setenv "DOCKER_HOST" "tcp://10.11.12.13:2376") + (setenv "DOCKER_MACHINE_NAME" "name")) + +(use-package dockerfile-mode) + +(provide 'wpc-docker) +;;; docker.el ends here diff --git a/emacs.d/wpc/packages/wpc-flycheck.el b/emacs.d/wpc/packages/wpc-flycheck.el new file mode 100644 index 000000000000..66314af9877c --- /dev/null +++ b/emacs.d/wpc/packages/wpc-flycheck.el @@ -0,0 +1,14 @@ +;;; flycheck.el --- My flycheck configuration -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; Hosts my Flycheck preferences + +;;; Code: + +(use-package flycheck + :config + (global-flycheck-mode)) + +(provide 'wpc-flycheck) +;;; flycheck.el ends here diff --git a/emacs.d/wpc/packages/wpc-git.el b/emacs.d/wpc/packages/wpc-git.el new file mode 100644 index 000000000000..f07040fffb64 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-git.el @@ -0,0 +1,17 @@ +;;; git.el --- My version control preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; Things related to git, magit, etc belong here + +;;; Code: + +(use-package git-timemachine) + +(use-package magit) + +(use-package magit-gh-pulls + :ghook ('magit-mode-hook #'turn-on-magit-gh-pulls)) + +(provide 'wpc-git) +;;; git.el ends here diff --git a/emacs.d/wpc/packages/wpc-haskell.el b/emacs.d/wpc/packages/wpc-haskell.el new file mode 100644 index 000000000000..a37a84030f14 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-haskell.el @@ -0,0 +1,32 @@ +;;; haskell.el --- My Haskell preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; Hosts my Haskell development preferences + +;;; Code: + +;; Haskell support +(use-package intero + :config + (intero-global-mode 1)) + +;; text objects for Haskell +(quelpa '(evil-text-objects-haskell + :fetcher github + :repo "urbint/evil-text-objects-haskell")) +(require 'evil-text-objects-haskell) + +(use-package haskell-mode + :gfhook #'evil-text-objects-haskell/install + :after (intero evil-text-objects-haskell) + :config + (flycheck-add-next-checker 'intero 'haskell-hlint) + (let ((m-symbols + '(("`mappend`" . "⊕") + ("<>" . "⊕")))) + (dolist (item m-symbols) (add-to-list 'haskell-font-lock-symbols-alist item))) + (setq haskell-font-lock-symbols t)) + +(provide 'wpc-haskell) +;;; haskell.el ends here diff --git a/emacs.d/wpc/packages/wpc-javascript.el b/emacs.d/wpc/packages/wpc-javascript.el new file mode 100644 index 000000000000..5dcd5937b11d --- /dev/null +++ b/emacs.d/wpc/packages/wpc-javascript.el @@ -0,0 +1,87 @@ +;;; javascript.el --- My Javascript preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; This module hosts my Javascript tooling preferences + +;;; Code: + +;; Helper functions +(defun wpc/indium-setup (url) + "Setup the indium environment using URL." + (indium-eval (format "window.dispatchEvent(new CustomEvent('patch', {detail: {url: %s}}" url))) + +(defun wpc/insert-flow-annotation () + "Insert a flow type annotation to the beginning of a buffer." + (interactive) + (save-excursion + (goto-char (point-min)) + (insert "// @flow\n"))) + +;; ;; javascript +;; (evil-leader/set-key-for-mode 'rjsx-mode "t" #'wpc/toggle-between-js-test-and-module) +;; (evil-leader/set-key-for-mode 'rjsx-mode "x" #'wpc/toggle-between-js-component-and-store) +;; (evil-leader/set-key-for-mode 'rjsx-mode "u" #'wpc/jump-to-parent-file) + +;; javascript setup +(use-package indium + :hook (indium-update-script-source . wpc/indium-setup)) + +;; javascript text objects +(quelpa '(evil-text-objects-javascript + :fetcher github + :repo "urbint/evil-text-objects-javascript")) +(require 'evil-text-objects-javascript) + +;; Flow for Javascript +(use-package flow-minor-mode + :hook js2-mode + :requires evil-leader + :config + (evil-leader/set-key-for-mode 'rjsx-mode "F" #'wpc/insert-flow-annotation)) + +(use-package company-flow + :after (company) + :hook (rjsx-mode . wpc/set-flow-executable) + :config + (add-to-list 'company-flow-modes 'rjsx-mode) + (add-to-list 'company-backends 'company-flow)) + +(use-package flycheck-flow + :after (flycheck) + :config + (flycheck-add-mode 'javascript-flow 'rjsx-mode) + (flycheck-add-mode 'javascript-flow 'flow-minor-mode) + (flycheck-add-mode 'javascript-eslint 'flow-minor-mode) + (flycheck-add-next-checker 'javascript-flow 'javascript-eslint)) + +;; front-end indentation +(setq js-indent-level 2 + css-indent-offset 2) + +;; eslint integration with flycheck +(setq flycheck-javascript-eslint-executable "~/urbint/grid-front-end/node_modules/.bin/eslint") + +;; JS autoformatting +(use-package prettier-js + :after (rjsx-mode) + :ghook ('(rjsx-mode-hook + js2-mode-hook + json-mode-hook + css-mode-hook))) + +;; JSX highlighting +(use-package rjsx-mode + :after (evil-text-objects-javascript) + :general + (general-unbind rjsx-mode-map "<" ">" "C-d") + (n rjsx-mode-map + "K" 'flow-minor-type-at-pos) + :gfhook #'evil-text-objects-javascript/install + :mode "\\.js\\'" + :config + (setq js2-mode-show-parse-errors nil + js2-mode-show-strict-warnings nil)) + +(provide 'wpc-javascript) +;;; wpc-javascript.el ends here diff --git a/emacs.d/wpc/packages/wpc-keybindings.el b/emacs.d/wpc/packages/wpc-keybindings.el new file mode 100644 index 000000000000..0e74ce68bacb --- /dev/null +++ b/emacs.d/wpc/packages/wpc-keybindings.el @@ -0,0 +1,131 @@ +;;; keybindings.el --- My Evil preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; This module hosts my Evil preferences + +;;; Code: + +(quelpa + '(general + :repo "noctuid/general.el" + :fetcher github)) +(general-evil-setup t) + +;; vim... +(use-package evil + :general + (m + "RET" 'evil-goto-line + "H" 'evil-first-non-blank + "L" 'evil-end-of-line + "-" 'dired-jump + "sl" 'wpc/evil-window-vsplit-right + "sh" 'evil-window-vsplit + "sk" 'evil-window-split + "sj" 'wpc/evil-window-split-down + "sj" 'wpc/evil-window-split-down) + (general-unbind m "M-." "C-p") + (general-unbind n "s" "M-.") + (general-unbind i "C-d" "C-a" "C-e" "C-n" "C-p" "C-k") + (evil-ex-map + "M-p" 'previous-complete-history-element + "M-n" 'next-complete-history-element) + :init + (setq evil-want-integration nil) + :config + (setq evil-symbol-word-search t) + (evil-mode 1)) + +;; evil keybindings +(use-package evil-collection + :after evil + :config + (evil-collection-init)) + +;; expose a leader key +(use-package evil-leader + :after (evil counsel) + :config + (global-evil-leader-mode) + (evil-leader/set-leader "<SPC>") + ;; global + (evil-leader/set-key + "i" #'counsel-semantic-or-imenu + "j" #'jump-to-register + "h" #'help + "a" #'wpc/toggle-terminal + "p" #'counsel-git-grep + "P" #'counsel-git-grep + "f" #'wpc/find-file + "N" #'smerge-next + "P" #'smerge-prev + "s" #'slack-send-code-snippet + "S" #'slack-select-unread-rooms + "b" #'ivy-switch-buffer + "gs" #'magit-status + "es" #'wpc/create-snippet + "ev" #'wpc/edit-init-el + "B" #'magit-blame + "w" #'save-buffer + "x" #'evil-save-and-close + "W" #'save-all-buffers + "r" #'wpc/evil-replace-under-point + )) + +;; create comments easily +(use-package evil-commentary + :after (evil) + :config + (evil-commentary-mode)) + +;; evil surround +(use-package evil-surround + :after (evil) + :config + (global-evil-surround-mode 1)) + +;; Custom minor mode that ensures that my kbds are available no matter which major +;; or minor modes are active. +(add-hook 'after-load-functions #'ensure-william-carroll-kbds) + +(defun ensure-william-carroll-kbds (_ignore) + "Try to ensure that my keybindings retain priority over other minor modes." + (unless (eq (caar minor-mode-map-alist) 'wpc/kbds-minor-mode) + (let ((mykbds (assq 'wpc/kbds-minor-mode minor-mode-map-alist))) + (assq-delete-all 'wpc/kbds-minor-mode minor-mode-map-alist) + (add-to-list 'minor-mode-map-alist mykbds)))) + +(defvar wpc/kbds + (let ((map (make-sparse-keymap))) + (bind-keys :map map + ("M-q" . delete-window) + ("C-x C-;" . comment-or-uncomment-region) + ("C-x h" . help) + ("<s-return>" . toggle-frame-fullscreen) + ("<down-mouse-1>" . ffap-other-window) + ("M-h" . wpc/tmux-emacs-windmove-left) + ("M-l" . wpc/tmux-emacs-windmove-right) + ("M-k" . wpc/tmux-emacs-windmove-up) + ("M-j" . wpc/tmux-emacs-windmove-down) + ("M--" . split-window-below) + ("M-\\" . split-window-right) + ("M-q" . delete-window)) + map) + "William Carroll's keybindings that should have the highest precedence.") + +(define-minor-mode wpc/kbds-minor-mode + "A minor mode so that my key settings override annoying major modes." + :init-value t + :lighter " wpc/kbds" + :keymap wpc/kbds) + +;; allow jk to escape +(use-package key-chord + :after (evil) + :config + (key-chord-mode 1) + (key-chord-define evil-insert-state-map "jk" 'evil-normal-state)) + +(provide 'wpc-keybindings) +;;; wpc-keybindings.el ends here diff --git a/emacs.d/wpc/packages/wpc-lisp.el b/emacs.d/wpc/packages/wpc-lisp.el new file mode 100644 index 000000000000..487201d47625 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-lisp.el @@ -0,0 +1,37 @@ +;;; lisp.el --- Generic LISP preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; This hosts things like Paredit settings + +;;; Code: + +(defconst wpc/lisp-mode-hooks + '(emacs-lisp-mode-hook + clojure-mode-hook + clojurescript-mode-hook)) + +;; Elisp +(use-package elisp-slime-nav + :ghook + 'emacs-lisp-mode + 'ielm-mode) + +;; paredit LISP editing +(use-package paredit + :general + (general-unbind paredit-mode-map "C-j" "M-q") + (n paredit-mode-map + ">)" 'paredit-forward-slurp-sexp + "<(" 'paredit-backward-slurp-sexp + "<)" 'paredit-forward-barf-sexp + ">(" 'paredit-backward-barf-sexp + ">e" 'paredit-move-forward + "<e" 'paredit-move-backward + ">f" 'paredit-move-backward + "<f" 'paredit-move-backward + "go" 'paredit-raise-sexp) + :ghook (wpc/lisp-mode-hooks #'enable-paredit-mode)) + +(provide 'wpc-lisp) +;;; lisp.el ends here diff --git a/emacs.d/wpc/packages/wpc-misc.el b/emacs.d/wpc/packages/wpc-misc.el new file mode 100644 index 000000000000..d621f83d2101 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-misc.el @@ -0,0 +1,155 @@ +;;; misc.el --- Hosting miscellaneous configuration -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; This is the home of any configuration that couldn't find a better home. + +;;; Code: + +;; disable custom variable entries from being written to ~/.emacs.d/init.el +(setq custom-file "~/.emacs.d/custom.el") +(load custom-file 'noerror) + +;; transparently edit compressed files +(auto-compression-mode t) + +;; change emacs prompts from "yes or no" -> "y or n" +(fset 'yes-or-no-p 'y-or-n-p) + +;; open photos in Emacs +(auto-image-file-mode 1) + +;; disable line-wrapping +(setq-default truncate-lines 1) + +;; shell file indentation +(setq sh-basic-offset 2) +(setq sh-indentation 2) + +;; create file bookmarks +(set-register ?e '(file . "~/.emacs.d/wpc/packages")) +(set-register ?u '(file . "~/urbint")) +(set-register ?d '(file . "~/dotfiles")) +(set-register ?D '(file . "~/Dropbox")) +(set-register ?o '(file . "~/Dropbox/org/")) +(set-register ?c '(file . "~/Dropbox/org/chains.org")) +(set-register ?b '(file . "~/Dropbox/org/backlog.org")) +(set-register ?p '(file . "~/urbint/grid-front-end")) + +;; persist history etc b/w Emacs sessions +(setq desktop-save 'if-exists) +(desktop-save-mode 1) +(setq desktop-globals-to-save + (append '((extended-command-history . 30) + (file-name-history . 100) + (grep-history . 30) + (compile-history . 30) + (minibuffer-history . 50) + (query-replace-history . 60) + (read-expression-history . 60) + (regexp-history . 60) + (regexp-search-ring . 20) + (search-ring . 20) + (shell-command-history . 50) + tags-file-name + register-alist))) + +;; config Emacs to use $PATH values +(use-package exec-path-from-shell + :if (memq window-system '(mac ns)) + :config + (exec-path-from-shell-initialize)) + +;; Emacs autosave, backup, interlocking files +(setq auto-save-default nil + make-backup-files nil + create-lockfiles nil) + +;; ensure code wraps at 80 characters by default +(setq fill-column 80) + +(put 'narrow-to-region 'disabled nil) + +;; trim whitespace on save +(add-hook 'before-save-hook #'delete-trailing-whitespace) + +;; use tabs instead of spaces +(setq-default indent-tabs-mode nil) + +;; automatically follow symlinks +(setq vc-follow-symlinks t) + +;; fullscreen settings +(setq ns-use-native-fullscreen nil) + +;; auto-close parens, brackets, quotes +(electric-pair-mode 1) + +(use-package oauth2 + :init + ;; necessary to remove warnings: https://emacs.stackexchange.com/questions/37036/where-are-these-variables-defined-bytecomp-warnings + (defvar url-http-extra-headers ()) + (defvar oauth--token-data ()) + (defvar url-callback-function ()) + (defvar url-callback-arguments ())) + +(use-package smex + :general + ("M-x" 'smex) + :ghook ('ido-setup-hook #'wpc/bind-ido-keys) + :config + (smex-initialize)) + +(use-package flx-ido + :after (smex) + :config + (flx-ido-mode 1) + (setq ido-enable-flex-matching t + ido-use-faces nil)) + +(use-package swiper + :general + ("C-s" 'swiper + "C-r" 'swiper)) + +(use-package yasnippet + :config + (yas-global-mode 1)) + +(use-package ace-window + :general + ("C-x o" 'ace-window) + :config + (setq aw-keys '(?a ?s ?d ?f ?j ?k ?k ?\;))) + +(use-package projectile + :config + (projectile-mode t)) + +(use-package counsel + :config + (defun wpc/counsel-git-grep () + (interactive) + (let ((maybe-symbol (wpc/string-symbol-at-point))) + (if (string= maybe-symbol "nil") + (counsel-git-grep) + (counsel-git-grep nil maybe-symbol))))) + +;; projectile intergration with ivy +(use-package counsel-projectile) + +;; search Google, Stackoverflow from within Emacs +(use-package engine-mode + :config + (defengine google + "http://www.google.com/search?ie=utf-8&oe=utf-8&q=%s" + :keybinding "g") + (defengine stack-overflow + "https://stackoverflow.com/search?q=%s" + :keybinding "s")) + +(use-package markdown-mode) +(use-package yaml-mode) + +(provide 'wpc-misc) +;;; misc.el ends here diff --git a/emacs.d/wpc/packages/wpc-org.el b/emacs.d/wpc/packages/wpc-org.el new file mode 100644 index 000000000000..28f1f9308dee --- /dev/null +++ b/emacs.d/wpc/packages/wpc-org.el @@ -0,0 +1,45 @@ +;;; org.el --- My org preferences -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; Hosts my org mode preferences + +;;; Code: + +;; Griffin's org clubhouse integration +;;(load-file "~/.emacs.d/vendor/org-clubhouse.el") +;;(setq org-clubhouse-auth-token (wpc/read-file-as-string "~/dotfiles/configs/secrets/clubhouse_token.txt") +;; org-clubhouse-team-name "urbint") +;;(add-hook 'org-mode-hook #'org-clubhouse-mode) + +(use-package org + :ghook (nil (disable linum-mode)) + :general + (:prefix "C-c" + "l" 'org-store-link + "a" 'org-agenda + "c" 'org-capture) + :preface + (defconst wpc-org-directory + "~/Dropbox/org") + (defconst ub-org-directory + "~/Dropbox/sprint-planning-staging") + (defun wpc/org-file (file) + (f-join wpc-org-directory (f-swap-ext file "org"))) + (defun ub/org-file (file) + (f-join ub-org-directory (f-swap-ext file "org"))) + :config + (setq org-default-notes-file (wpc/org-file "notes")) + (setq org-log-done 'time) + (setq org-agenda-files (list (wpc/org-file "work") + (wpc/org-file "personal"))) + (setq org-capture-templates + `(("t" "Todo" entry (file+heading ,(ub/org-file "index") "Ideas") + "* TODO %?\n %i")))) + +(use-package org-bullets + :after (org) + :ghook ('org-mode-hook (enable org-bullets-mode))) + +(provide 'wpc-org) +;;; org.el ends here diff --git a/emacs.d/wpc/packages/wpc-package.el b/emacs.d/wpc/packages/wpc-package.el new file mode 100644 index 000000000000..472f9df623a4 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-package.el @@ -0,0 +1,32 @@ +;;; package.el --- My package configuration -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; This module hosts all of the settings required to work with ELPA, +;; MELPA, QUELPA, and co. + +;;; Code: + +(require 'package) +(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) +(add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/") t) +(package-initialize) + +(if (require 'quelpa nil t) + (quelpa-self-upgrade) + (with-temp-buffer + (url-insert-file-contents "https://raw.github.com/quelpa/quelpa/master/bootstrap.el") + (eval-buffer))) + +(require 'use-package) +(setq use-package-always-ensure t) +;; Remove this line once general integration with use-package calls +;; with-eval-after-load 'use-package-core instead of 'use-package +(require 'general) + +(add-to-list 'load-path "~/.emacs.d/vendor/") +(add-to-list 'load-path "~/.emacs.d/wpc/") +(add-to-list 'load-path "~/.emacs.d/wpc/packages") + +(provide 'wpc-package) +;;; package.el ends here diff --git a/emacs.d/wpc/packages/wpc-slack.el b/emacs.d/wpc/packages/wpc-slack.el new file mode 100644 index 000000000000..912cd14572b4 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-slack.el @@ -0,0 +1,65 @@ +;;; slack.el --- Slack settings -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; Wrangling the Slack client in Emacs + +;;; Code: + +;; Griffin's Slack plugin +;;(defconst slack/token (wpc/read-file-as-string "~/dotfiles/configs/secrets/slack_token.txt")) +;;(defconst wpc/slack-client-secret (wpc/read-file-as-string "~/dotfiles/configs/secrets/slack-client-secret")) +(defconst wpc/slack-client-secret "uncomment above line one day") +(load-file "~/.emacs.d/vendor/slack-snippets.el") + +;; Slack client +(use-package slack + :general + (n slack-info-mode-map + :prefix "," + "u" 'slack-room-update-messages) + (n slack-mode-map + :prefix "," + "c" 'slack-buffer-kill + "ra" 'slack-message-add-reaction + "rr" 'slack-message-remove-reaction + "rs" 'slack-message-show-reaction-users + "pl" 'slack-room-pins-list + "pa" 'slack-message-pins-add + "pr" 'slack-message-pins-remove + "mm" 'slack-message-write-another-buffer + "me" 'slack-message-edit + "md" 'slack-message-delete + "u" 'slack-room-update-messages + "2" 'slack-message-embed-mention + "3" 'slack-message-embed-channel) + (n slack-mode-map + "C-n" 'slack-buffer-goto-next-message + "C-p" 'slack-buffer-goto-prev-message) + (n slack-edit-message-mode-map + :prefix "," + "k" 'slack-message-cancel-edit + "s" 'slack-message-send-from-buffer + "2" 'slack-message-embed-mention + "3" 'slack-message-embed-channel) + :commands (slack-start) + :init + (setq slack-buffer-emojify t) ;; if you want to enable emoji, default nil + (setq slack-prefer-current-team t) + :config + (add-hook 'slack-mode-hook (disable company-mode)) + (setq slack-buffer-function #'switch-to-buffer) + (slack-register-team + :name "urbint" + :default t + :client-id "william@urbint.com" + :client-secret wpc/slack-client-secret + :token slack-token + :subscribed-channels '(dev dev_questions general random recruiting) + :full-and-display-names t)) + +(use-package circe) +(use-package emojify) + +(provide 'wpc-slack) +;;; wpc-slack.el ends here diff --git a/emacs.d/wpc/packages/wpc-terminal.el b/emacs.d/wpc/packages/wpc-terminal.el new file mode 100644 index 000000000000..36a24bbb3c46 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-terminal.el @@ -0,0 +1,19 @@ +;;; terminal.el --- My cobbled together terminal -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; My attempts at creating a sane Emacs terminal + +;;; Code: + +(setq wpc/terminal-name "wpc/terminal") + +;; 256 color support in term (instead of 8) +(use-package xterm-color) + +(use-package term + :config + (setq explicit-shell-file-name "/bin/zsh")) + +(provide 'wpc-terminal) +;;; terminal.el ends here diff --git a/emacs.d/wpc/packages/wpc-ui.el b/emacs.d/wpc/packages/wpc-ui.el new file mode 100644 index 000000000000..fff2db3e1160 --- /dev/null +++ b/emacs.d/wpc/packages/wpc-ui.el @@ -0,0 +1,136 @@ +;;; ui.el --- Any related to the UI/UX goes here -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; Hosts font settings, scrolling, color schemes. + +;;; Code: + +;; increase line height +(setq-default line-spacing 4) + +;; change font +(add-to-list 'default-frame-alist '(font . "Operator Mono-10")) + +;; smooth scrolling settings +(setq scroll-step 1 + scroll-conservatively 10000) + +;; theme mgt +(use-package cycle-themes + :after (doom-themes) + :config + ;; NOTE: may want to use `defconst' here + (setq wpc/doom-themes + (->> (custom-available-themes) + (-map #'symbol-name) + (-filter (-partial #'s-starts-with? "doom-")) + (-map #'intern))) + (setq cycle-themes-theme-list wpc/doom-themes)) + +;; clean up modeline +(use-package diminish + :after (yasnippet ivy which-key) + :config + (diminish 'evil-commentary-mode) + (diminish 'flycheck-mode "Flycheck") + (diminish 'company-mode "Company") + (diminish 'auto-revert-mode) + (diminish 'which-key-mode) + (diminish 'yas-minor-mode) + (diminish 'ivy-mode)) + +;; disable startup screen +(setq inhibit-startup-screen t) + +;; disable toolbar +(tool-bar-mode -1) + +;; enable line numbers +(general-add-hook '(prog-mode-hook + text-mode-hook + conf-mode-hook) + (enable linum-mode)) +;;(add-hook 'after-init-hook (lambda () (set-face-foreground 'linum "#da5468"))) + +;; set default buffer for Emacs +(setq initial-buffer-choice "~/urbint/grid-front-end") + +;; transparent Emacs +(set-frame-parameter (selected-frame) 'alpha '(100 . 100)) +(add-to-list 'default-frame-alist '(alpha . (100 . 100))) + +;; premium Emacs themes +(use-package doom-themes + :config + (setq doom-themes-enable-bold t + doom-themes-enable-italic t) + (load-theme 'doom-solarized-light t) + (doom-themes-visual-bell-config) + (doom-themes-org-config)) + +;; kbd discovery +(use-package which-key + :config + (setq which-key-idle-delay 0.25) + (which-key-mode)) + +;; completion framework +(use-package ivy + :config + (ivy-mode t)) + +;; icons for Ivy +(use-package all-the-icons-ivy + :after (ivy) + :config + (all-the-icons-ivy-setup)) + +;; disable menubar +(menu-bar-mode -1) +(when (string-equal system-type "darwin") + (setq ns-auto-hide-menu-bar t)) + +;; highlight lines that are over 100 characters long +(use-package whitespace + :config + (setq whitespace-line-column 100) + (setq whitespace-style '(face lines-tail))) + +;; disable GUI scrollbars +(when (display-graphic-p) + (scroll-bar-mode -1)) + +;; rebalance emacs windows after splits are created +(defadvice split-window-below (after rebalance-windows activate) + (balance-windows)) + +(defadvice split-window-right (after rebalance-windows activate) + (balance-windows)) + +(defadvice delete-window (after rebalance-window activate) + (balance-windows)) + +;; dirname/filename instead of filename<dirname> +(setq uniquify-buffer-name-style 'forward) + +;; highlight matching parens, brackets, etc +(show-paren-mode 1) + +;; GUI alerts in emacs +(use-package alert + :commands (alert) + :config + (setq alert-default-style 'notifier)) + +;; focus mode +(quelpa '(zen-mode + :fetcher github + :repo "aki237/zen-mode")) +(require 'zen-mode) + +;; focus mode +(use-package writeroom-mode) + +(provide 'wpc-ui) +;;; ui.el ends here diff --git a/emacs.d/wpc/string-functions.el b/emacs.d/wpc/string-functions.el new file mode 100644 index 000000000000..91b46b5b409e --- /dev/null +++ b/emacs.d/wpc/string-functions.el @@ -0,0 +1,41 @@ +;; functions.el --- String helper functions for my Emacs development -*- lexical-binding: t -*- +;; Author: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; String & Symbol helpers! + +;;; Code: + +(require 'dash) +(require 's) + +;; Strings +(defun string/hookify (x) + "Append \"-hook\" to X." + (s-append "-hook" x)) + +(defun string/ensure-hookified (x) + "Ensure that X has \"-hook\" appended to it." + (if (s-ends-with? "-hook" x) + x + (string/hookify x))) + +;; Symbols +(defun symbol/as-string (callback x) + "Treat the symbol, X, as a string while applying CALLBACK to it. +Coerce back to a symbol on the way out." + (->> x + symbol-name + callback + intern)) + +(defun symbol/hookify (x) + "Append \"-hook\" to X when X is a symbol." + (symbol/as-string #'string/hookify x)) + +(defun symbol/ensure-hookified (x) + "Ensure that X has \"-hook\" appended to it when X is a symbol." + (symbol/as-string #'string/ensure-hookified x)) + +(provide 'string-functions) +;;; string-functions.el ends here diff --git a/emacs.d/wpc/variables.el b/emacs.d/wpc/variables.el new file mode 100644 index 000000000000..af6bfde45a41 --- /dev/null +++ b/emacs.d/wpc/variables.el @@ -0,0 +1,18 @@ +;;; variables.el --- Helpful variables for making my ELisp life more enjoyable -*- lexical-binding: t -*- +;; Authpr: William Carroll <wpcarro@gmail.com> + +;;; Commentary: +;; This file contains helpful variables that I use in my ELisp development. + +;;; Code: + +(defvar wpc/mouse-kbds + '([mouse-1] [down-mouse-1] [drag-mouse-1] [double-mouse-1] [triple-mouse-1] + [mouse-2] [down-mouse-2] [drag-mouse-2] [double-mouse-2] [triple-mouse-2] + [mouse-3] [down-mouse-3] [drag-mouse-3] [double-mouse-3] [triple-mouse-3] + [mouse-4] [down-mouse-4] [drag-mouse-4] [double-mouse-4] [triple-mouse-4] + [mouse-5] [down-mouse-5] [drag-mouse-5] [double-mouse-5] [triple-mouse-5]) + "This variable stores all of the mouse-related keybindings that Emacs recognizes.") + +(provide 'variables) +;;; variables.el ends here |