From d362e94671ba5422e70be717131a63759c21e827 Mon Sep 17 00:00:00 2001 From: Drew Werner Date: Tue, 27 Jan 2015 14:35:25 -0800 Subject: [PATCH 1/8] add coffee mode --- init-packages.el | 1 + 1 file changed, 1 insertion(+) diff --git a/init-packages.el b/init-packages.el index 4128337..aeaa6e1 100644 --- a/init-packages.el +++ b/init-packages.el @@ -22,6 +22,7 @@ less-css-mode ;; programming modes web-mode js2-mode + coffee-mode cython-mode enh-ruby-mode haskell-mode From 37007e5d9034309fa602616ef85468684f5a52b4 Mon Sep 17 00:00:00 2001 From: Drew Werner Date: Wed, 28 Jan 2015 12:33:03 -0800 Subject: [PATCH 2/8] misc mode tweaks --- init-mode.el | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/init-mode.el b/init-mode.el index b34fd51..75c3557 100644 --- a/init-mode.el +++ b/init-mode.el @@ -49,14 +49,22 @@ (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode)) (setq js2-cleanup-whitespace t ;; clear whitespace on save js2-mirror-mode nil) ;; but do NOT match parens. -(add-hook - 'js2-mode-hook - '(lambda () - ;; Don't redefine M-j - we use it to navigate - (define-key js2-mode-map (kbd "M-j") nil))) +(add-hook 'js2-mode-hook + '(lambda () + ;; Don't redefine M-j - we use it to navigate + (define-key js2-mode-map (kbd "M-j") nil))) ;; Extra autocompletion fun ;; (add-hook 'js2-mode-hook 'ac-js2-mode) +;; typescript +(require 'typescript) +(setq typescript-indent-level 2) +(add-to-list 'auto-mode-alist '("\\.ts$" . typescript-mode)) + +(require 'tss) +(setq tss-popup-help-key "M-?") +(setq tss-jump-to-definition-key "C-.") + ;; ruby (require 'ruby-mode) (setq ruby-indent-level 2) From 660441cc74571331f92013fc7ce69c08c29f61be Mon Sep 17 00:00:00 2001 From: Drew Werner Date: Thu, 16 Apr 2015 07:27:32 -0700 Subject: [PATCH 3/8] Further local changes. --- general-config.el | 6 + init-mode.el | 11 +- init-packages.el | 1 + init.el | 2 +- local-packages/oracle.el | 224 ----------------------------------- local-packages/pyxl-mode.elc | Bin 0 -> 5062 bytes 6 files changed, 9 insertions(+), 235 deletions(-) delete mode 100644 local-packages/oracle.el create mode 100644 local-packages/pyxl-mode.elc diff --git a/general-config.el b/general-config.el index eac6d4d..73f7de4 100644 --- a/general-config.el +++ b/general-config.el @@ -19,3 +19,9 @@ This is particularly useful under Mac OSX, where GUI apps are not started from a (aw/set-env-from-shell "GOPATH") (aw/set-env-from-shell "GOOS") (aw/set-env-from-shell "GOARCH") + +;; deal with certain tramp errors with an overly-long temp directory +(let ((tmpdir "/tmp/emacs")) + (unless (file-directory-p "/tmp/emacs") + (mkdir tmpdir)) + (setenv "TMPDIR" tmpdir)) diff --git a/init-mode.el b/init-mode.el index 75c3557..c4eb654 100644 --- a/init-mode.el +++ b/init-mode.el @@ -23,6 +23,7 @@ (require 'web-mode) (add-to-list 'auto-mode-alist '("\\.html$" . web-mode)) (add-to-list 'auto-mode-alist '("\\.erb$" . web-mode)) +(add-to-list 'auto-mode-alist '("\\.ejs$" . web-mode)) (add-to-list 'auto-mode-alist '("\\.mako$" . web-mode)) (add-to-list 'auto-mode-alist '("\\.handlebars$" . web-mode)) ;; default to django templating for all HTML files @@ -164,21 +165,11 @@ (require 'go-autocomplete) (require 'go-eldoc) -;; this is annoying, but shrug. -(load "oracle.el") -(require 'go-oracle) -(setq go-oracle-command (executable-find "oracle")) - (add-hook 'go-mode-hook '(lambda () ;; eldoc stuff (go-eldoc-setup) - ;; oracle, which is fussy - (go-oracle-mode) - (make-local-variable 'go-oracle-scope) - (setq go-oracle-scope (aw/go-scope-from-buffer (current-buffer))) - ;; gofmt (add-hook 'before-save-hook 'gofmt-before-save) diff --git a/init-packages.el b/init-packages.el index aeaa6e1..69ac30b 100644 --- a/init-packages.el +++ b/init-packages.el @@ -23,6 +23,7 @@ web-mode js2-mode coffee-mode + tss cython-mode enh-ruby-mode haskell-mode diff --git a/init.el b/init.el index bda4283..2c15d65 100644 --- a/init.el +++ b/init.el @@ -6,7 +6,7 @@ (when (member "Menlo" (font-family-list)) (set-face-attribute 'default nil :family "Menlo" - :height 120)) + :height 140)) ;; Helper to load files based on relative paths (defun relative-to-full-path (filename) diff --git a/local-packages/oracle.el b/local-packages/oracle.el deleted file mode 100644 index 9782b9c..0000000 --- a/local-packages/oracle.el +++ /dev/null @@ -1,224 +0,0 @@ -;;; -;;; Integration of the Go 'oracle' analysis tool into Emacs. -;;; -;;; To install the Go oracle, run: -;;; % export GOROOT=... GOPATH=... -;;; % go get code.google.com/p/go.tools/cmd/oracle -;;; % mv $GOPATH/bin/oracle $GOROOT/bin/ -;;; -;;; Load this file into Emacs and set go-oracle-scope to your -;;; configuration. Then, find a file of Go source code, enable -;;; go-oracle-mode, select an expression of interest, and press `C-c C-o d' -;;; (for "describe") or run one of the other go-oracle-xxx commands. -;;; -;;; TODO(adonovan): simplify installation and configuration by making -;;; oracle a subcommand of 'go tool'. - -(require 'compile) -(require 'go-mode) -(require 'cl) - -(defgroup go-oracle nil - "Options specific to the Go oracle." - :group 'go) - -(defcustom go-oracle-command (concat (car (go-root-and-paths)) "/bin/oracle") - "The Go oracle command; the default is $GOROOT/bin/oracle." - :type 'string - :group 'go-oracle) - -(defcustom go-oracle-scope "" - "The scope of the analysis. See `go-oracle-set-scope'." - :type 'string - :group 'go-oracle) - -(defvar go-oracle--scope-history - nil - "History of values supplied to `go-oracle-set-scope'.") - -;; TODO(adonovan): I'd like to get rid of this separate mode since it -;; makes it harder to use the oracle. -(defvar go-oracle-mode-map - (let ((m (make-sparse-keymap))) - (define-key m (kbd "C-c C-o t") #'go-oracle-describe) ; t for type - (define-key m (kbd "C-c C-o f") #'go-oracle-freevars) - (define-key m (kbd "C-c C-o g") #'go-oracle-callgraph) - (define-key m (kbd "C-c C-o i") #'go-oracle-implements) - (define-key m (kbd "C-c C-o c") #'go-oracle-peers) ; c for channel - (define-key m (kbd "C-c C-o r") #'go-oracle-referrers) - (define-key m (kbd "C-c C-o d") #'go-oracle-definition) - (define-key m (kbd "C-c C-o p") #'go-oracle-pointsto) - (define-key m (kbd "C-c C-o s") #'go-oracle-callstack) - (define-key m (kbd "C-c C-o <") #'go-oracle-callers) - (define-key m (kbd "C-c C-o >") #'go-oracle-callees) - m)) - -;; TODO(dominikh): Rethink set-scope some. Setting it to a file is -;; painful because it doesn't use find-file, and variables/~ aren't -;; expanded. Setting it to an import path is somewhat painful because -;; it doesn't make use of go-mode's import path completion. One option -;; would be having two different functions, but then we can't -;; automatically call it when no scope has been set. Also it wouldn't -;; easily allow specifying more than one file/package. -(defun go-oracle-set-scope () - "Set the scope for the Go oracle, prompting the user to edit the -previous scope. - -The scope specifies a set of arguments, separated by spaces. -It may be: -1) a set of packages whose main() functions will be analyzed. -2) a list of *.go filenames; they will treated like as a single - package (see #3). -3) a single package whose main() function and/or Test* functions - will be analyzed. - -In the common case, this is similar to the argument(s) you would -specify to 'go build'." - (interactive) - (let ((scope (read-from-minibuffer "Go oracle scope: " - go-oracle-scope - nil - nil - 'go-oracle--scope-history))) - (if (string-equal "" scope) - (error "You must specify a non-empty scope for the Go oracle")) - (setq go-oracle-scope scope))) - -(defun go-oracle--run (mode) - "Run the Go oracle in the specified MODE, passing it the -selected region of the current buffer. Process the output to -replace each file name with a small hyperlink. Display the -result." - (if (not buffer-file-name) - (error "Cannot use oracle on a buffer without a file name")) - ;; It's not sufficient to save a modified buffer since if - ;; gofmt-before-save is on the before-save-hook, saving will - ;; disturb the selected region. - (if (buffer-modified-p) - (error "Please save the buffer before invoking go-oracle")) - (if (string-equal "" go-oracle-scope) - (go-oracle-set-scope)) - (let* ((filename (file-truename buffer-file-name)) - (buffer-go-oracle-scope go-oracle-scope) - (posflag (if (use-region-p) - (format "-pos=%s:#%d,#%d" - filename - (1- (go--position-bytes (region-beginning))) - (1- (go--position-bytes (region-end)))) - (format "-pos=%s:#%d" - filename - (1- (position-bytes (point)))))) - ;; This would be simpler if we could just run 'go tool oracle'. - (env-vars (go-root-and-paths)) - (goroot-env (concat "GOROOT=" (car env-vars))) - (gopath-env (concat "GOPATH=" (mapconcat #'identity (cdr env-vars) ":")))) - (with-current-buffer (get-buffer-create "*go-oracle*") - (setq buffer-read-only nil) - (erase-buffer) - (insert "Go Oracle\n") - (let ((args (append (list go-oracle-command nil t nil posflag mode) - (split-string buffer-go-oracle-scope " " t)))) - ;; Log the command to *Messages*, for debugging. - (message "Command: %s:" args) - (message nil) ; clears/shrinks minibuffer - - (message "Running oracle...") - ;; Use dynamic binding to modify/restore the environment - (let ((process-environment (list* goroot-env gopath-env process-environment))) - (apply #'call-process args))) - (insert "\n") - (compilation-mode) - (setq compilation-error-screen-columns nil) - - ;; Hide the file/line info to save space. - ;; Replace each with a little widget. - ;; compilation-mode + this loop = slooow. - ;; TODO(adonovan): have oracle give us JSON - ;; and we'll do the markup directly. - (let ((buffer-read-only nil) - (p 1)) - (while (not (null p)) - (let ((np (compilation-next-single-property-change p 'compilation-message))) - ;; TODO(adonovan): this can be verbose in the *Messages* buffer. - ;; (message "Post-processing link (%d%%)" (/ (* p 100) (point-max))) - (if np - (when (equal (line-number-at-pos p) (line-number-at-pos np)) - ;; np is (typically) the space following ":"; consume it too. - (put-text-property p np 'display "▶") - (goto-char np) - (insert " "))) - (setq p np))) - (message nil)) - - (let ((w (display-buffer (current-buffer)))) - (balance-windows) - (shrink-window-if-larger-than-buffer w) - (set-window-point w (point-min)))))) - -(defun go-oracle-callees () - "Show possible callees of the function call at the current point." - (interactive) - (go-oracle--run "callees")) - -(defun go-oracle-callers () - "Show the set of callers of the function containing the current point." - (interactive) - (go-oracle--run "callers")) - -(defun go-oracle-callgraph () - "Show the callgraph of the current program." - (interactive) - (go-oracle--run "callgraph")) - -(defun go-oracle-callstack () - "Show an arbitrary path from a root of the call graph to the -function containing the current point." - (interactive) - (go-oracle--run "callstack")) - -(defun go-oracle-definition () - "Show the definition of the selected identifier." - (interactive) - (go-oracle--run "definition")) - -(defun go-oracle-describe () - "Describe the selected syntax, its kind, type and methods." - (interactive) - (go-oracle--run "describe")) - -(defun go-oracle-pointsto () - "Show what the selected expression points to." - (interactive) - (go-oracle--run "pointsto")) - -(defun go-oracle-implements () - "Describe the 'implements' relation for types in the package -containing the current point." - (interactive) - (go-oracle--run "implements")) - -(defun go-oracle-freevars () - "Enumerate the free variables of the current selection." - (interactive) - (go-oracle--run "freevars")) - -(defun go-oracle-peers () - "Enumerate the set of possible corresponding sends/receives for -this channel receive/send operation." - (interactive) - (go-oracle--run "peers")) - -(defun go-oracle-referrers () - "Enumerate all references to the object denoted by the selected -identifier." - (interactive) - (go-oracle--run "referrers")) - -;; TODO(dominikh): better docstring -(define-minor-mode go-oracle-mode "Oracle minor mode for go-mode - -Keys specific to go-oracle-mode: -\\{go-oracle-mode-map}" - nil " oracle" go-oracle-mode-map) - -(provide 'go-oracle) diff --git a/local-packages/pyxl-mode.elc b/local-packages/pyxl-mode.elc new file mode 100644 index 0000000000000000000000000000000000000000..498fddfa8a69ae2d15cb4412b2dc64ee51e86914 GIT binary patch literal 5062 zcmcIoZExGi5!SalGR~LuLz-N0xG`Ovq{LK2QkD}tO&a9VCTMavAVCY%QqL=LWwEA6 zl`oEUZU1|pnI$FC@ui0bH6WS0JKUX@XP%kW@yj}*`%sbTjDl6H7NJi; z;%9jp#%I|+R?{pe6$Cn<^*PStzWey2J%R*##7Rzm66XrSMLx41VJ5bpzkBieHTiRu zDnEyk`*OUB1IiNeRZLS&QKA}$l*VMFRT_mlrF;%S*E8qB37_@H3ViNgyWQ?iSGl(R zB+z7yy}AUGe;SgT6&a`D&?2Haa>>lVUlcN4@sm@4W;1j)R)dHKi&((|~{<6)-kS z7MfnYfBWXyH|=hqXDSL+MjhFd!>=!Mdr5mIq8CgQ)9>7cXIyr`jmiUiy=rP}GxZhb zoO^J|!Cq?|hArP`ZV$ey-DvQOIh5`6FZCp_*GJCZL_h| zAqK-~@6PF#X#ZskW;z6`CEk)&CvcowpHBdV49%~MSdUWsf1}pCkp}aGM2l0=W%Nc<* za6a(5t*!Srv4|OGBr~^KTkmdS$-ULOF)cU5Dehn!@phdb@r8q3PlW%7{oNr;zv8EZ zJxQMXcpT9KOml_=dI>@^-m+(DvY=RJIedWdpXo~j1s;|TwZ`M_lanK5|McAc>!f-Z z*pGjl+#ip7(;357$e2TFpuC=H7ER18`W|?Fm=Mp*B!K98ThB~;fNK;^;0f`O1 zW=YE6q=m|XHbb)HR^~{ckeUx@2sTX4`Fxd=bwa=_)O>bSSJWQTz+~|?Fx)`b8Mudx zUCHLRNf6Ff<#_~cDlCCDuEkJP2hkcm1}y_%8>u-%tN>{VE&dQdpZx9VB|igTKv$S*Q*PR7v>XQ;vG)z;-7 z&)jKElOhiK?Njt;(g$=%6=)YwiG1{YE1CjW8HIVDs4LAL-pkWMO9Z(ql$u}dn)Q38 zhG1d?v`vFinsYF4Va}C^aCgtfy)U;St4AP5_Ci+%%b+PcEmzqROE!Hg|YCh z1ZN|`?v(P|RE^*3F4Q>+_)?{rX24H75!TCk5?lC7}ArJYN+_X)oo zT<4d@%cX;A`+bIg*mLV1mV1CTw~Heh2md0Eyc501*vS>C>6f*WY&D&x`r?0dNaN)$ z((5cH?pwJvYOEQ`6k8heVCCKhj*WNzf8K0p@Qb|pT%7dZy@^aq!wVglD+F=}c!hbw zzHI!2fGe=RJD|7f?}!Rd3T$<(|jf+ir0a{A-@V$75>o z?twATAIqiA0r8bfC2cqd+rp0>mKts2>yWtcv6MR)&-fhYYu;zn`T|o9{f-AuAdVh+ zhHvyzB9Fex9qtgm52SJq|J>T4on02kBc?}YacB2MdA57VXY)y3dD38qZtub>kegTC zSUvalT@1b3*wyofxA*XkT=%$@cLq4sL+dW1vK5ciYI6t8(A5GdG9hreLg8q6>XhGL ziV7FZxJ5>1v2x{;4yN^mMV(lGklP%PR9 Date: Mon, 8 Jun 2015 09:01:45 -0700 Subject: [PATCH 4/8] Dropbox specific changes --- init-mode.el | 25 +++++++++++++++++++++++-- init-packages.el | 8 ++++++-- init-ui.el | 3 +++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/init-mode.el b/init-mode.el index c4eb654..5387928 100644 --- a/init-mode.el +++ b/init-mode.el @@ -40,7 +40,6 @@ ad-do-it)) ;; pyxl-mode -(load "pyxl-mode.el") (require 'pyxl-mode) (add-to-list 'auto-mode-alist '("\\.py$" . pyxl-mode)) @@ -178,6 +177,28 @@ (local-set-key (kbd "M-,") 'pop-tag-mark) (local-set-key (kbd "M-?") 'godoc-at-point))) +;; Java / Scala + +;; Someday...someday... + +;; (require 'eclim) +;; (require 'ac-emacs-eclim-source) +;; (ac-emacs-eclim-config) +;; (require 'eclimd) +;; ;; don't wait for startup +;; (setq eclimd-wait-for-process nil) +;; (add-hook 'java-mode-hook + ;; (lambda () + ;; (unless eclimd-process + ;; (let* ((root-dir (expand-file-name + ;; (vc-find-root (buffer-file-name (current-buffer)) + ;; ".git"))) + ;; (project-dir (read-directory-name "Enter project directory: " + ;; root-dir nil t))) + ;; (start-eclimd project-dir))))) + + + ;; Shell (require 'tramp) (ansi-color-for-comint-mode-on) @@ -189,7 +210,7 @@ ;; little hack to avoid lockfiles (setq org-agenda-files (mapcar 'expand-file-name - (file-expand-wildcards "~/Dropbox/org/[A-Za-z]*.org"))) + (file-expand-wildcards "~/Dropbox (Dropbox)/org/[A-Za-z]*.org"))) (setq org-log-done t) ;; EWW diff --git a/init-packages.el b/init-packages.el index 69ac30b..1fbc1b6 100644 --- a/init-packages.el +++ b/init-packages.el @@ -38,6 +38,7 @@ ac-js2 go-autocomplete go-eldoc + emacs-eclim ag magit multiple-cursors @@ -45,6 +46,9 @@ twittering-mode hackernews)) +;; suppress magit startup warning +(setq magit-last-seen-setup-instructions "1.4.0") + (defun uninstalled-packages (packages) (delq nil (mapcar (lambda (p) (if (package-installed-p p) nil p)) packages))) @@ -88,8 +92,8 @@ (when (not (string-match-p "^\\." s)) (add-to-list 'load-path (concat local-elisp s))))))) ;; Datadog - (require 'datadog) - (global-set-key (kbd "C-c d d") 'datadog) + ;; (require 'datadog) + ;; (global-set-key (kbd "C-c d d") 'datadog) ;; multiple-cursors (require 'multiple-cursors) diff --git a/init-ui.el b/init-ui.el index 3746834..0723cc9 100644 --- a/init-ui.el +++ b/init-ui.el @@ -9,6 +9,9 @@ (defun track-mouse (e)) (setq mouse-sel-mode t) +;; No menu bar +(menu-bar-mode -1) + ;; No Toolbar (if window-system (tool-bar-mode -1)) From 2c36077750b51486dde414a30cec66573720ddc5 Mon Sep 17 00:00:00 2001 From: Drew Werner Date: Wed, 6 Jan 2016 18:53:56 -0500 Subject: [PATCH 5/8] dropbox stuff --- init-mode.el | 25 +- init-packages.el | 9 +- local-packages/datadog-graph.el | 695 ++++++++++++++++++++++++++++++++ local-packages/datadog.el | 543 +++++++++++++++++++++++++ local-packages/dogapi.el | 230 +++++++++++ 5 files changed, 1497 insertions(+), 5 deletions(-) create mode 100644 local-packages/datadog-graph.el create mode 100644 local-packages/datadog.el create mode 100644 local-packages/dogapi.el diff --git a/init-mode.el b/init-mode.el index 5387928..8bc8422 100644 --- a/init-mode.el +++ b/init-mode.el @@ -59,11 +59,15 @@ ;; typescript (require 'typescript) (setq typescript-indent-level 2) +(add-hook 'typescript-mode-hook + '(lambda () + (define-key typescript-mode-map (kbd "M-.") nil))) (add-to-list 'auto-mode-alist '("\\.ts$" . typescript-mode)) (require 'tss) (setq tss-popup-help-key "M-?") -(setq tss-jump-to-definition-key "C-.") +(setq tss-jump-to-definition-key "M-.") +(tss-config-default) ;; ruby (require 'ruby-mode) @@ -72,6 +76,10 @@ (add-to-list 'auto-mode-alist '("Rakefile$" . ruby-mode)) (add-to-list 'auto-mode-alist '("Vagrantfile$" . ruby-mode)) +;; php +(require 'php-mode) +(add-to-list 'auto-mode-alist '("\\.php$" . php-mode)) + ;; cython (require 'cython-mode) @@ -88,6 +96,11 @@ (add-to-list 'auto-mode-alist '("\\.markdown$" . markdown-mode)) (add-to-list 'auto-mode-alist '("\\.md$" . markdown-mode)) +;; lua +(require 'lua-mode) +(setq lua-indent-level 2) +(add-to-list 'auto-mode-alist '("\\.lua$" . lua-mode)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; UTILITY CONFIG @@ -124,6 +137,16 @@ (ac-config-default) ;; Show menu more quickly after autocomplete starts (setq ac-auto-show-menu (* ac-delay 2)) +;; custom modes for autocomplete +(add-to-list 'ac-modes 'typescript-mode) + +;; Flymake - show in minibuffer when over +(defun aw/flymake-show-help () + (when (get-char-property (point) 'flymake-overlay) + (let ((help (get-char-property (point) 'help-echo))) + (when help (popup-tip help))))) + +(add-hook 'post-command-hook 'aw/flymake-show-help) ;; Python mode ;; Don't accidentally make python buffer diff --git a/init-packages.el b/init-packages.el index 1fbc1b6..7f07a29 100644 --- a/init-packages.el +++ b/init-packages.el @@ -10,7 +10,7 @@ (defun my-init-packages (packages) (when packages (let ((this-package (car packages)) - (rest (cdr packages))) + (rest (cdr packages))) (add-to-list 'package-load-list (cons this-package t)) (my-init-packages rest)))) @@ -31,6 +31,7 @@ go-mode puppet-mode yaml-mode + lua-mode auto-complete ;; misc productivity projectile epc @@ -76,7 +77,7 @@ (require 'magit) (global-set-key (kbd "C-c g s") 'magit-status) - (global-set-key (kbd "C-c g b") 'magit-blame-mode) + (global-set-key (kbd "C-c g b") 'magit-blame) ;; my little github helper, here for thematic consistency (global-set-key (kbd "C-c g h") 'aw/get-github-link) @@ -92,8 +93,8 @@ (when (not (string-match-p "^\\." s)) (add-to-list 'load-path (concat local-elisp s))))))) ;; Datadog - ;; (require 'datadog) - ;; (global-set-key (kbd "C-c d d") 'datadog) + (require 'datadog) + (global-set-key (kbd "C-c d d") 'datadog) ;; multiple-cursors (require 'multiple-cursors) diff --git a/local-packages/datadog-graph.el b/local-packages/datadog-graph.el new file mode 100644 index 0000000..c115c0c --- /dev/null +++ b/local-packages/datadog-graph.el @@ -0,0 +1,695 @@ +;;; datadog-graph.el --- graph helpers for datadog-mode + +;; Copyright (C) 2014 Drew Werner + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;; Commentary: Does the actual work of rendering graphs and handling +;; navigation. Meant to be as stateless as possible (except we do save +;; the current series data) + +(defvar-local datadog--series-data nil + "Holds the current series data") + +(defvar-local datadog--active-query nil + "Stores the active query currently being navigated") + +(defvar-local datadog--active-interval nil) + +(defvar-local datadog--active-from-ts nil) + +(defvar-local datadog--active-to-ts nil) + +(defconst datadog--dim-names + '(margin-below margin-left margin-top width height) + "Names we use to refer to the graph size factors, +defined in `datadog--graph-sizes'") + +(defconst datadog--graph-sizes + '((0 0 0 40 8) + (0 0 0 40 16) + (2 2 3 40 16) + (3 4 4 60 20) + (3 4 4 60 30) + (3 4 4 60 40) + (3 4 4 120 40))) + +(defvar-local datadog--graph-size nil) + +(defvar-local datadog--graph-origin nil + "(column, row) pair") + +;; Timeframe constants and state variables + +(defvar-local datadog--timeframe 'one-hour + "Supports values described in `datadog--time-spans'.") + +(defconst datadog--time-spans + '((one-hour . 3600) + (four-hours . 14400) + (one-day . 86400) + (one-week . 604800))) + +;; Faces we use when graphing + +(defface datadog-chart-area + '((((class color) (min-colors 8)) + :foreground "blue" + :background "blue") + (t :inherit 'default :inverse-video t)) + "Color for showing chart area" + :group 'datadog-faces) + +(defface datadog-chart-title + '((((class color) (min-colors 8)) + :foreground "green") + (t :inherit 'default)) + "Color for showing chart title" + :group 'datadog-faces) + +(defface datadog-chart-label + '((((class color) (min-colors 8) (background dark)) + :foreground "yellow") + (t :inherit 'default)) + "Color for time and value labels on graph" + :group 'datadog-faces) + +(defface datadog-chart-highlight-bar + '((((class color) (min-colors 8)) + :foreground "magenta" + :background "magenta") + (t :inherit 'default :inverse-video nil)) + "Face for bars when selected by timecursor" + :group 'datadog-faces) + +(defface datadog-chart-timecursor + '((((class color) (min-colors 8) (background dark)) + :foreground "white" + :background "white") + (t :inverse-video t)) + "Face for chart area with timecursor" + :group 'datadog-faces) + +(defface datadog-chart-tooltip + '((t :inverse-video t)) + "Face for value shown at timecursor" + :group 'datadog-faces) + +;; Shortcut for inserting fonts with a given face + +(defun datadog--set-face (face-name start &optional end) + "Set text face at start, or optionally from start to end" + (save-excursion + (let ((end (or end (+ start 1)))) + (put-text-property start end 'face face-name)))) + +(defun datadog--insert-face (text face-name) + (let* ((start (point)) + (end (+ start (length text)))) + (insert text) + (put-text-property start end 'face face-name))) + +;; maybe could have done this as a bunch of alists, +;; but I'm lazy (and maybe stupid) + +(defun datadog--get-graph-dim (dim-name &optional dims) + "If dims not provided, defaults to datadog--graph-size" + ;; special case for one week + (defun week-adjusted-width (w) + "Helper that finds the largest number of points that still +divides the week nicely. Currently just finds the largest multiple +of 28 (7*4) <= n" + (- w (% w 28))) + ;; helper to search the list + (defun find-dim (dims dim-name names-list) + (when dims + (if (eq dim-name (car names-list)) + (car dims) + (find-dim (cdr dims) dim-name (cdr names-list))))) + (let* ((sz (or dims datadog--graph-size)) + (dim (find-dim sz dim-name datadog--dim-names))) + ;; special handling for one week + (if (and (equal dim-name 'width) + (equal datadog--timeframe 'one-week)) + (week-adjusted-width dim) + dim))) + +(defun datadog--graph-extent (&optional dims) + "returns (width, height) pair" + (let ((dims (or dims datadog--graph-size))) + ;; the "1" is space for the axis itself + (cons (+ (datadog--get-graph-dim 'margin-left dims) + (datadog--get-graph-dim 'width dims) + 1) + (+ (datadog--get-graph-dim 'margin-below dims) + (datadog--get-graph-dim 'margin-top dims) + (datadog--get-graph-dim 'height dims) + 1)))) + +(defun datadog--set-graph-size () + (defun graph-fits (dims w h) + (let ((extent (datadog--graph-extent dims))) + (and (< (car extent) w) + (< (cdr extent) h)))) + (defun iter-get-graph-size (current dims-list w h) + (if dims-list + (if (and (graph-fits (car dims-list) w h)) + (iter-get-graph-size (car dims-list) (cdr dims-list) w h) + current) + current)) + (setq datadog--graph-size + (iter-get-graph-size nil + datadog--graph-sizes + (window-width) + (window-height)))) + +(defun datadog--set-graph-origin () + (when datadog--graph-size + (let* ((extent (datadog--graph-extent)) + (extra-height (- (window-height) (cdr extent))) + (extra-width (- (window-width) (car extent)))) + (setq datadog--graph-origin + (cons (+ (/ extra-width 2) + (datadog--get-graph-dim 'margin-left)) + (+ (/ extra-height 2) + (- (cdr extent) + (datadog--get-graph-dim 'margin-below)))))))) + +(defun datadog--clear-chart-area () + (interactive) + (let ((buffer-read-only nil)) + (erase-buffer))) + +(defun datadog--get-graph-start-line () + "Little helper to tell us the first line on which to +start rendering graph items." + (- (cdr datadog--graph-origin) + (+ (datadog--get-graph-dim 'height) + (datadog--get-graph-dim 'margin-top)))) + +(defun datadog--draw-axes () + "Draw the axes for the graph, without tick marks or any +included graph data. Requires `datadog--graph-origin' to +be defined, as well as `datadog--graph-size'." + (interactive) + (let* ((buffer-read-only nil) + (graph-base (cdr datadog--graph-origin)) + (offset (car datadog--graph-origin)) + (graph-max-col (+ 1 offset (datadog--get-graph-dim 'width))) + (axes-start-line (+ (datadog--get-graph-start-line) + (datadog--get-graph-dim 'margin-top)))) + + (save-excursion + (goto-char (point-min)) + (newline axes-start-line) + (while (< (line-number-at-pos) graph-base) + (insert-char ?\s offset) + (insert-char ?|) + (newline)) + (insert-char ?\s offset) + (insert ?+) + (while (<= (current-column) graph-max-col) + (insert ?-))))) + +(defun datadog--reset-graph () + (datadog--set-graph-size) + (datadog--set-graph-origin) + (datadog--clear-chart-area) + (datadog--draw-axes)) + +(defun datadog--series-extent (points) + (let* ((vals (mapcar (lambda (p) (elt p 1)) points))) + (cons (apply 'min vals) + (apply 'max vals)))) + +;; Scaling convenience helpers +(defun datadog--scale-y (raw ymin yrange) + (- (cdr datadog--graph-origin) + (* (/ (- raw ymin) yrange) + (datadog--get-graph-dim 'height)))) + +(defun datadog--scale-t (timestamp &optional not-js) + "Finds the correct column for a given time, expressed +in absolute coordinates." + (let* ((factor (if not-js 1 1000)) + (ts (truncate (/ timestamp factor)))) + (+ (car datadog--graph-origin) 1 + (/ (- ts datadog--active-from-ts) datadog--active-interval)))) + + +(defun datadog--filter-points (points) + "Filter points out of time range as well as nil points" + (let ((min-t (datadog--scale-t datadog--active-from-ts t)) + (max-t (datadog--scale-t datadog--active-to-ts t))) + (delq nil + (mapcar (lambda (p) + (let ((ts (datadog--scale-t (elt p 0))) + (val (elt p 1))) + (if (or (< ts min-t) + (> ts max-t) + (null val)) + nil p))) + points)))) + +(defun datadog--set-title (title line-num) + "Where line number has a minimum value of 0, and the actual line +is calculated with regard to the known graph dimensions" + (let* ((buffer-read-only nil) + (w (window-width)) + (h-offset (max 0 (/ (- w (length title)) 2))) + (v-offset (datadog--get-graph-start-line)) + (title (if (> (length title) w) + (substring title 0 w) + title))) + + (when (> (datadog--get-graph-dim 'margin-top) line-num) + (save-excursion + (goto-line (+ v-offset line-num)) + (while (not (looking-at "$")) + (delete-char 1)) + (insert-char ?\s h-offset) + (datadog--insert-face title 'datadog-chart-title))))) + +(defun datadog--set-tile-title (title) + (datadog--set-title title 0)) + +(defun datadog--set-graph-title (title) + (datadog--set-title title 1)) + + +(defun datadog--render-graph (&optional series tile-title ymin ymax) + "All graph redrawing happens through here. Requires a series, +plus an optional tile title and range. If no series provided, +defaults to value cached in `datadog--series-data'." + (datadog--reset-graph) + + (let* ((buffer-read-only nil)) + + (when tile-title (datadog--set-tile-title tile-title)) + + (if series + ;; this is where the magic happens + (let* ((series (or series datadog--series-data)) + (points (datadog--filter-points (cdr (assoc 'pointlist series)))) + (extent (datadog--series-extent points)) + (ymin (or ymin + (min (car extent) 0.0))) + (ymax (or ymax + (cdr extent))) + (yrange (if (> (- ymax ymin) 0) + (- ymax ymin) + (max ymax 1))) + (scaled (mapcar (lambda (p) + (cons (datadog--scale-t (elt p 0)) + (datadog--scale-y (elt p 1) + ymin + yrange))) + points))) + + ;; Update the series data state variable + (setq datadog--series-data series) + ;; Set the title only if we have a real series + (datadog--set-graph-title (cdr (assoc 'expression series))) + ;; and only then render the graph + (goto-char (point-min)) + (forward-line (+ (datadog--get-graph-start-line) + (datadog--get-graph-dim 'margin-top))) + + + (while (< (line-number-at-pos) (cdr datadog--graph-origin)) + (forward-char (+ (car datadog--graph-origin) 1)) + (dolist (sc scaled) + (insert-char ?\s (- (car sc) (current-column))) + (if (> (line-number-at-pos) (cdr sc)) + (datadog--insert-face "#" 'datadog-chart-area) + (insert-char ?\s))) + (forward-line 1)) + ;; only draw y-axis ticks with data + (datadog--draw-y-ticks ymin ymax yrange) + ;; try to keep the last timecursor, if possible, + ;; by trying to use a bounds-checked version of it. + ;; otherwise default to the to current to-timestamp + (datadog--timecursor (or datadog--timecursor-at + datadog--active-to-ts))) + ;; store our series for future use + (datadog--set-graph-title (concat "No data: " datadog--active-query))) + + ;; always draw t-ticks + (datadog--draw-t-ticks) + ;; and end up in some vaguely sensible place + (goto-char (point-min)))) + +(defun datadog--ensure-next-line-empty () + "When you need one. Probably a little inefficient, +but I mean c'mon. It's 2014. Leaves you at the beginning +of the line where you started." + ;; ensure next line exists + (when (> (forward-line) 0) + (progn + (goto-char (point-max)) + (newline))) + ;; and is blank + (beginning-of-line) + (while (not (looking-at "$")) + (delete-char 1)) + ;; this is kinda ugly, but save-excursion didn't work + ;; for whatever reason + (previous-line) + (beginning-of-line)) + +;; Timecursor routines + +(defun datadog--set-time-params (from-ts to-ts interval) + "from-ts, to-ts, and interval should all be integers." + (setq datadog--active-interval interval) + (setq datadog--active-from-ts + (datadog--closest-below from-ts interval)) + (setq datadog--active-to-ts + (datadog--closest-below to-ts interval))) + +(defvar datadog--timecursor-at nil + "Current time being pointed at") + +(defun datadog--closest-t (timestamp) + "Expects an epoch timestamp" + (let* ((series datadog--series-data) + (points (cdr (assoc 'pointlist series))) + (i 0) + (out-ts nil)) + (when points + (let ((max-ts (truncate (/ (apply 'max + (mapcar (lambda (p) (elt p 0)) + (datadog--filter-points points))) + 1000)))) + (cond ((< timestamp datadog--active-from-ts) datadog--active-from-ts) + ((> timestamp max-ts) max-ts) + (t (datadog--closest-below timestamp + datadog--active-interval))))))) + +(defun datadog--timecursor (timestamp) + "Internal drawing routine to the timecursor at a given +time, subject to a bounds check. Expects an integer +epoch, or nil to simply flag the timecursor as off." + ;; Formatting helper + (defun format-column (bar-face blank-face col-num start-row end-row) + (goto-line start-row) + (beginning-of-line) + (forward-char 1) ;; FIXME: why do I need this? + + (while (< (line-number-at-pos) end-row) + (forward-char col-num) + (if (looking-at "[[:space:]]") + (datadog--set-face blank-face (point)) + (datadog--set-face bar-face (point))) + (forward-line 1))) + + ;; Set the global timestamp value if within graph bounds + ;; or if null (which turns it off, in effect + (if (null timestamp) + ;; FIXME: maybe more formal cleanup here, though + ;; currently this works + (setq datadog--timecursor-at timestamp) + (let* ((buffer-read-only nil) + (new-ts (datadog--closest-t timestamp)) + (old-ts datadog--timecursor-at) + (old-col-num (and old-ts (datadog--scale-t old-ts t))) + (new-col-num (and new-ts (datadog--scale-t new-ts t))) + (graph-end (cdr datadog--graph-origin)) + (graph-start (- graph-end (datadog--get-graph-dim 'height)))) + + ;; note that old column number *could* be out of range + (when (and old-col-num (>= old-col-num 0)) + (format-column 'datadog-chart-area nil + old-col-num graph-start graph-end)) + (when new-col-num + (format-column 'datadog-chart-highlight-bar 'datadog-chart-timecursor + new-col-num graph-start graph-end)) + + (datadog--draw-tooltip + (datadog--format-number (datadog--value-at new-ts)) + new-col-num) + ;; and update so we know what to erase next time + (setq datadog--timecursor-at new-ts))) + ;; and reset to sompelace sensiblei + (goto-char (point-min))) + +;; Helper routines called by UI layer + +(defun datadog--shift-timecursor (delta) + "Convenience function for bumping the timecursor +position in a certain direction" + (datadog--timecursor (+ datadog--timecursor-at + (* delta datadog--active-interval)))) + +(defun datadog--shift-by-tick (num-secs num-ticks) + "Move cursor by num-secs and num-ticks, and then snap to +next lowest tick. Helper function." + (interactive) + (let ((tick-size (cdr (assoc datadog--timeframe + datadog--ticks-for-timeframe)))) + (when datadog--timecursor-at + (datadog--timecursor + (datadog--closest-below (+ datadog--timecursor-at + num-secs + (* num-ticks tick-size)) + tick-size))))) + +(defun datadog--draw-tooltip (value column) + ;; need at least 3 lines of margin to show tooltip + (when (>= (datadog--get-graph-dim 'margin-top) 3) + (let* ((buffer-read-only nil) + (n-chars (length value)) + (w (window-width)) + (start-col (- column n-chars)) + ;; start line goes immediately above graph + (start-line (- (cdr datadog--graph-origin) + ;; FIXME: why do I need +1 here??? + (+ 1 (datadog--get-graph-dim 'height))))) + (goto-line start-line) + (beginning-of-line) + (while (not (looking-at "$")) + (delete-char 1)) + (insert-char ?\s start-col) + (datadog--insert-face value 'datadog-chart-tooltip)))) + +(defun datadog--value-at (timestamp &optional js-time) + "Get the value of the current series at the given timestamp. +If js-time is omitted or nil, assumes that timestamp is given in +seconds. Otherwise, it's in milliseconds." + (let* ((ts (if js-time timestamp (* timestamp 1000))) + (points (cdr (assoc 'pointlist datadog--series-data))) + (num-points (length points)) + (current 0) + (result nil)) + + (while (and (< current num-points) + (not result)) + (let ((current-point (elt points current))) + (when (= ts (elt current-point 0)) + (setq result (elt current-point 1))) + (setq current (+ current 1)))) + result)) + +;; Scaling and tick formatting + +;; Time axis formatting + +(defconst datadog--ticks-for-timeframe + '((one-hour . 900) + (four-hours . 3600) + (one-day . 21600) + (one-week . 172800))) + +(defconst datadog--format-for-timeframe + '((one-hour . "%H:%M") + (four-hours . "%H:%M") + (one-day . "%H:%M") + (one-week . "%b %d"))) + + +(defun datadog--set-current-timeframe (timeframe) + "A modest interface for setting the timeframe" + (setq datadog--timeframe timeframe)) + +(defun datadog--get-current-timeframe () + "Again, just because we don't want clients accessing private vars." + datadog--timeframe) + +(defun datadog--current-time-window () + (cdr (assoc datadog--timeframe + datadog--time-spans))) + +(defun datadog--set-time-params (from-ts to-ts interval) + "Helper to set time range information needed to successfully +render the graph." + (setq datadog--active-interval interval) + (setq datadog--active-from-ts + (datadog--closest-below from-utc interval)) + (setq datadog--active-to-ts + (datadog--closest-below to-utc interval))) + +(defun datadog--closest-below (ts interval) + (- ts (% ts interval))) + +(defun datadog--time-axis-ticks (start end interval tick-size) + (let* ((current (datadog--closest-below end tick-size)) + (ticks nil)) + (while (> current start) + (setq ticks (cons (datadog--closest-below current interval) ticks)) + (setq current (- current tick-size))) + ticks)) + +(defun datadog--draw-t-ticks () + "Helper to draw time axis ticks. Should only be called from +`datadog--render-graph'." + (let* ((timeframe datadog--timeframe) + (tick-size (cdr (assoc timeframe + datadog--ticks-for-timeframe))) + (ticks (datadog--time-axis-ticks datadog--active-from-ts + datadog--active-to-ts + datadog--active-interval + tick-size)) + (time-fmt (cdr (assoc timeframe + datadog--format-for-timeframe))) + (last-time-column 0) + (time-padding-cols 2)) + (save-excursion + (goto-line (+ 1 (cdr datadog--graph-origin))) + (datadog--ensure-next-line-empty) + ;; goto the char one past the origin + (goto-char (+ 1 (search-forward "+"))) + (dolist (tick ticks) + (let* ((col (datadog--scale-t tick t)) + (time (format-time-string time-fmt (seconds-to-time tick))) + (time-start-col (- col (/ (length time) 2)))) + + (goto-char (+ (point) (- col (current-column)))) + (delete-char 1) + (insert-char ?|) + ;; Can we insert the time? + (when (> time-start-col (+ last-time-column time-padding-cols)) + (save-excursion + (forward-line) + (end-of-line) + (insert-char ?\s (- time-start-col (current-column))) + (datadog--insert-face time 'datadog-chart-label) + (setq last-time-column (current-column))))))))) + +;; Y-axis formatting helpers + +(defun datadog--get-number-scale (x) + (floor (log10 x))) + +(defun datadog--y-axis-tick-size (ymin ymax) + (let* ((yrange (if (= ymax ymin) + (max ymax 1) + (- ymax ymin))) + (scale (floor (log10 yrange))) + (scale-factor (expt 10 scale)) + (scaled-yrange (/ yrange scale-factor))) + (* scale-factor + (cond ((< scaled-yrange 1) 0.1) + ((< scaled-yrange 2) 0.5) + ((< scaled-yrange 5) 1.0) + (t 2.0))))) + +(defun datadog--y-axis-ticks (ymin ymax) + "For now, assumes that the range includes zero. +Maybe we should relax that assumption at some point." + (let* ((tick-size (datadog--y-axis-tick-size ymin ymax)) + (ticks '(0.0)) + (last-tick ticks) + (current tick-size)) + ;; expand in both directions + (while (< current ymax) + (setcdr last-tick (list current)) + (setq last-tick (cdr last-tick)) + (setq current (+ current tick-size))) + (setq current (- tick-size)) + (while (> current ymin) + (setq ticks (cons current ticks)) + (setq current (- current tick-size))) + ticks)) + +(defun datadog--draw-y-ticks (ymin ymax yrange) + "Helper function to draw ticks and values on the y-axis. +Should only be called from `datadog--render-graph'." + (let* ((ticks (datadog--y-axis-ticks ymin ymax)) + (axis-offset (car datadog--graph-origin)) + (axis-line (cdr datadog--graph-origin)) + (min-tick-spacing 2) + (last-tick (+ axis-line min-tick-spacing 1)) + (graph-top (datadog--get-graph-start-line))) + + (dolist (tick ticks) + (let* ((line (floor (datadog--scale-y tick ymin yrange))) + (text (datadog--format-number tick)) + (text-field-size (+ 1 (length text)))) + (unless (or (< (- last-tick line) min-tick-spacing)) + ;; insert tick + (goto-line line) + (unless (or (= line axis-line) + (<= line graph-top)) + (goto-char (+ (point) axis-offset)) + (delete-char 1) + (insert-char ?-) + ;; insert formatted number, if space + (when (> (current-column) text-field-size) + (goto-char (- (point) (+ text-field-size 1))) + (delete-char text-field-size) + (datadog--insert-face text 'datadog-chart-label) + (insert-char ?-)))))))) + +;; Number formatting helpers + +(defvar datadog--number-scale + '(("" . 1.0) + ("K" . 1e3) + ("M" . 1e6) + ("G" . 1e9) + ("T" . 1e12))) + +(defvar datadog--precision 1) + +(defun datadog--format-number (x) + (defun scale-number (num order suffix orders) + (if orders + (let ((next-suffix (caar orders)) + (next-order (cdar orders))) + (if (> num next-order) + (scale-number num next-order next-suffix (cdr orders)) + (cons suffix (/ num order)))) + (cons suffix (/ num order)))) + + (defun strip-trailing-zeros (x) + (let ((xs (if (stringp x) x (number-to-string x)))) + (replace-regexp-in-string "\\.0+$" "" x))) + + (defun truncate-to-precision (x p) + (let ((scale (expt 10.0 p))) + (number-to-string (/ (truncate x (/ 1 scale)) scale)))) + (if (null x) ;; what if, like, this isn't even a number, maaan + "NaN" + (let* ((scale-data (scale-number x 1.0 "" datadog--number-scale)) + (unit (car scale-data)) + (val (cdr scale-data)) + (precision (if (= (length unit) 0) 3 datadog--precision)) + (string-val (truncate-to-precision val precision))) + + (if (= (length unit) 0) + (strip-trailing-zeros string-val) + (concat string-val unit))))) + + +(provide 'datadog-graph) diff --git a/local-packages/datadog.el b/local-packages/datadog.el new file mode 100644 index 0000000..3c548a4 --- /dev/null +++ b/local-packages/datadog.el @@ -0,0 +1,543 @@ +;;; datadog.el --- Emacs major mode for Datadog + +;; Copyright (C) 2014 Drew Werner + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;; Package-Requires: helm +;; URL: http://github.com/wernerandrew/datadog.el +;; Version: 0.0.1alpha + +;; Commentary: A simple interactive console for browsing Datadog +;; dashes and performing metric queries. Graphs are all rendered +;; as time series, and for ease of rendering, only a limited set of +;; time frames are supported. + +(require 'helm) +(require 'dogapi) +(require 'datadog-graph) + +;; User customizable variables: none at this time (though see dogapi) + +;; Internal variables and functions +;; Time utilities + +(defun datadog--utc-now (&optional offset-secs) + "Get seconds since EPOCH as an integer, with optional offset" + (let ((ts (truncate (float-time)))) + (if offset-secs (+ ts offset-secs) ts))) + +(defun datadog--to-js-time (timestamp) + "Our API expects string timestamps in milliseconds. Who are we to argue?" + (number-to-string (* 1000 timestamp))) + +;; Buffer state variables + +(defvar-local datadog--active-tile nil + "A (title . graphs) cons cell for the active tile.") + +(defvar-local datadog--all-queries nil + "A list of all queries that are navigable via +`datadog-next-series' and `datadog-previous-series'. +Most commonly, this is all queries within a graph tile.") + +(defvar-local datadog--current-result nil + "Stores the active result as a list of (scope . pointlist)") + +(defvar-local datadog--looking-at-query 0 + "The index of the query currently being browsed") + +(defvar-local datadog--looking-at-series 0 + "The current series within the current query") + +;; Various refresh / caching time limits + +(defconst datadog--query-refresh-time + '((one-hour . 60) + (four-hours . 300) + (one-day . 1800) + (one-week . 3600))) + +(defconst datadog--dash-list-refresh-time 300) + +(defconst datadog--dash-refresh-time 300) + +;; Cache variables + +(defvar-local datadog--dash-list nil + "Stored list of dashes so we don't always hit the server") + +(defvar-local datadog--query-cache nil + "Maps query string to (last-update-utc . result)") + +(defvar-local datadog--dash-tiles nil + "Stores tile definitions for current dash") + +;; Series retrieval commands + +(defun datadog-metric-query (query &optional skip-render) + "It's a metric query, kids." + (interactive) + (let* ((offset (datadog--current-time-window)) + (to-utc (datadog--utc-now)) + (from-utc (- to-utc offset)) + (interval (datadog--rollup-interval)) + (result (datadog--fetch-series query interval from-utc to-utc))) + (when result + (message "Retrieved series") + (setq datadog--active-query query) + (setq datadog--current-result result) + (setq datadog--looking-at-series 0) + (datadog--set-time-params from-utc to-utc interval) + (unless skip-render + (datadog--render-metric-graph))))) + +(defun datadog-explore-metric (query) + "Query exactly one metric." + (interactive "sEnter metric query: ") + (setq datadog--all-queries (list query)) + (setq datadog--active-tile nil) + (setq datadog--looking-at-query 0) + (datadog-metric-query query)) + +;; Series retrieval helpers + +(defun datadog--fetch-series (query interval from-utc to-utc) + "Get the query, but check the cache. Also hacks in the correct +rollup interval." + (let* ((query-str (datadog--make-query query interval)) + (cached-result (gethash query-str datadog--query-cache))) + (if (or (not cached-result) + (> (- to-utc (car cached-result)) + (cdr (assoc (datadog--get-current-timeframe) + datadog--query-refresh-time)))) + (let ((result (dogapi-metric-query query-str + (datadog--to-js-time from-utc) + (datadog--to-js-time to-utc)))) + (puthash query-str (cons to-utc result) datadog--query-cache) + result) + (cdr cached-result)))) + +;; Functions that like, you know, map to series +(defconst datadog--basic-rollup-hack + '(("}$" . "}.rollup(%d)") + ( "\\.rollup(\\([^0-9)]*\\)[^)]*)" . ".rollup(\\1,%d)") + ("}[ ]*\\." . "}.rollup(%d).") + ("}[ ]*)" . "}.rollup(%d))")) + "Kid tested. Mother approved.") + +(defconst datadog--multi-series-hack + '(("}[ ]*,[ ]*" . "}.rollup(%d),"))) + +(defconst datadog--series-arithmetic-hack + '(( "}[ ]*\\([\\+/\\*\\-]\\)" . "}.rollup(%d) \\1 "))) + +(defun datadog--make-query (query interval) + "Does some regex substitutions to ensure a proper interval +Hopefully to go the way of the dodo when / if we support +setting the interval directly through API requests." + (defun maybe-substitute (original-query patterns) + (if patterns + (let ((pat (caar patterns)) + (subst (format (cdar patterns) interval))) + (if (string-match pat original-query) + (replace-regexp-in-string pat subst original-query) + (maybe-substitute original-query (cdr patterns)))) + original-query)) + (let ((new-query query)) + (setq new-query (maybe-substitute new-query + datadog--basic-rollup-hack)) + (setq new-query (maybe-substitute new-query + datadog--multi-series-hack)) + (setq new-query (maybe-substitute new-query + datadog--series-arithmetic-hack)))) + +(defun datadog--rollup-interval () + (let* ((time-window (datadog--current-time-window)) + (num-points (datadog--get-graph-dim 'width))) + ;; One week requires special work to get "nice" rollups + (/ time-window num-points))) + +;; Navigation commands + +(defun datadog-next-series () + (interactive) + (datadog--checked-nav 1)) + +(defun datadog-previous-series () + (interactive) + (datadog--checked-nav -1)) + +(defun datadog--checked-nav (offset) + "Helper to navigate between graphs in a tile. Handles switching +between different metric queries for the same tile, hence some of +the complexity. Currently, only supports values of 1 or -1 for offset; +for bigger jumps, the best bet currently is something like +`datadog-jump-to-graph'." + (let ((next-idx (+ datadog--looking-at-series offset)) + (current-len (length datadog--current-result))) + (cond + ;; easy case: we can stay on this graph + ((and (< next-idx current-len) + (>= next-idx 0)) + (setq datadog--looking-at-series next-idx) + (datadog--render-metric-graph)) + ;; next: we are at the beginning but can go backwards + ((and (= next-idx -1) + (> datadog--looking-at-query 0)) + (setq datadog--looking-at-query (- datadog--looking-at-query 1)) + (datadog-metric-query (elt datadog--all-queries + datadog--looking-at-query) + t) ;; skip render for now + (let ((num-series (length datadog--current-result))) + (when (> num-series 0) + (setq datadog--looking-at-series (- num-series 1))) + (datadog--render-metric-graph))) + ;; or, we're at the end, and can go forwards + ((and (= next-idx 1) + (< datadog--looking-at-query (- (length datadog--all-queries) 1))) + (setq datadog--looking-at-query (+ datadog--looking-at-query 1)) + (datadog-metric-query (elt datadog--all-queries + datadog--looking-at-query))) + ((or (> offset 1) (< offset -1)) + ;; FIXME: uh, don't throw an error here. + (error "Can only move one spot for now!")) + (t (message (if (< next-idx 0) "Beginning of series" "End of series")))))) + +(defun datadog-timecursor-forward () + (interactive) + (datadog--shift-timecursor 1)) + +(defun datadog-timecursor-backward () + (interactive) + (datadog--shift-timecursor -1)) + +(defun datadog-forward-tick () + (interactive) + (datadog--shift-by-tick 0 1)) + +(defun datadog-backward-tick () + (interactive) + (datadog--shift-by-tick -1 0)) + +(defun datadog--make-graph-list () + "Creates a list of graphs, where the format depends slightly +on the composition of the graphs on the tile." + ;; still experimenting with what's best + (defun format-scope (s) + (cdr (assoc 'scope s))) + + (defun format-scope-and-metric (s) + (format "%s (%s)" + (cdr (assoc 'scope s)) + (cdr (assoc 'metric s)))) + + (let ((format-func 'format-scope) + (metric-names nil) + (i -1)) + ;; use different format strategy if metrics aren't unique + (mapcar (lambda (s) + (add-to-list 'metric-names (cdr (assoc 'metric s)))) + datadog--current-result) + (when (> (length metric-names) 1) + (setq format-func 'format-scope-and-metric)) + (mapcar (lambda (s) + (setq i (+ i 1)) + (cons (funcall format-func s) i)) + datadog--current-result))) + +(defun datadog--jump-set-graph (graph-idx) + "Jumps to a graph index and displays it. Only works if you're +looking at a tile." + (if datadog--active-tile + (progn + (setq datadog--looking-at-series graph-idx) + (datadog--render-metric-graph)) + (error "Not currently looking at a tile"))) + +(defun datadog-jump-to-graph () + (interactive) + (unless datadog--active-tile + (error "Not currently looking at a tile")) + (helm :sources + ;; can't pass a function name, alas + ;; due to scoping issues + (list (cons 'name "Select Graph by Scope") + (cons 'candidates (datadog--make-graph-list)) + (cons 'action + (list (cons "Select Graph" 'datadog--jump-set-graph)))))) + +;; Rendering helpers + +(defun datadog--active-tile-title () + "Little helper to access the tile title, or otherwise return nil." + (and datadog--active-tile + (cdr (assoc 'title datadog--active-tile)))) + +(defun datadog--render-metric-graph () + "Helper to render the current graph when navigating a tile. +Relies on `datadog--looking-at-series' and `datadog--active-tile' +to be appropriately set." + (if (< datadog--looking-at-series (length datadog--current-result)) + (datadog--render-graph + (elt datadog--current-result datadog--looking-at-series) + (datadog--active-tile-title)) + (datadog--render-graph + nil + (concat (datadog--active-tile-title))))) + +;; Graph redrawing / refreshing commands + +(defun datadog-refresh () + "Refreshes the graph display, querying the server for newer +data if necessary. Query results are cached for a period that +varies based on the timeframe of the query." + (interactive) + (when datadog--active-query + (let ((current-idx datadog--looking-at-series)) + ;; do the metric query but don't render + (datadog-metric-query datadog--active-query t) + ;; stay on current graph index if possible + (when (> (length datadog--current-result) current-idx) + (setq datadog--looking-at-series current-idx)) + (datadog--render-metric-graph)))) + +(defun datadog--check-size-change (frame) + "Hook function for resizing." + (let ((last-size datadog--graph-size)) + (datadog--set-graph-size) + (when (not (equal last-size datadog--graph-size)) + (datadog-refresh)))) + +;; Dash and tile selection interface + +(defvar datadog--current-dash-id nil) + +(defvar helm-source-datadog-dash-list + '((name . "Dash List") + (candidates . datadog--make-dash-list) + (action . (("Select Tile" . datadog-select-tile))))) + +(defun datadog--fetch-dash-list () + (if (or (null datadog--dash-list) + (> (- (datadog--utc-now) (car datadog--dash-list)) + datadog--dash-list-refresh-time)) + (let ((dash-list (dogapi-dash-list))) + (setq datadog--dash-list (cons (datadog--utc-now) dash-list)) + dash-list) + (cdr datadog--dash-list))) + +(defun datadog--make-title (dash-info) + (replace-regexp-in-string + "[[:space:]]+" + " " + (format "%s (%s)" + (cdr (assoc 'title d)) + (cdr (assoc 'description d))))) + +(defun datadog--make-dash-list () + (let ((dash-list (datadog--fetch-dash-list))) + (mapcar (lambda (d) + (cons (datadog--make-title d) + (string-to-number (cdr (assoc 'id d))))) + (cdr (assoc 'dashes dash-list))))) + +(defun datadog-select-dash () + "Opens an interactive dialog to browse your list of Datadog dashes. +Selecting a dash will bring up a follow-up dialog to choose a tile." + (interactive) + (helm :sources '(helm-source-datadog-dash-list))) + +(defvar helm-source-datadog-select-tile + '((name . "Select Tile") + (candidates . datadog--dash-tiles) + (action . (("Show Tile" . datadog-render-tile))))) + +(defun datadog--get-dash-graphs (dash-object) + (cdr (assoc 'graphs (cdr (assoc 'dash dash-object))))) + +(defun datadog--queries-from-tile (tile) + (let ((tile-def (cdr (assoc 'definition tile)))) + (mapcar (lambda (x) (cdr (assoc 'q x))) + (cdr (assoc 'requests tile-def))))) + +(defun datadog--fetch-dash (dash-id) + (when (or (not datadog--dash-tiles) + (not (= (car datadog--dash-tiles) dash-id))) + (setq datadog--dash-tiles (cons dash-id + (dogapi-dash dash-id)))) + (cdr datadog--dash-tiles)) + +(defun datadog--dash-tiles () + "Data source for the formatted list mapping tile names to +a list of queries." + (let ((dash (datadog--fetch-dash datadog--current-dash-id))) + (mapcar (lambda (tile) + (let ((tile-title (cdr (assoc 'title tile)))) + (cons tile-title + (list (cons 'title tile-title) + (cons 'queries + (datadog--queries-from-tile tile)))))) + (datadog--get-dash-graphs dash)))) + +(defun datadog-select-tile (&optional dash-id) + "Command to choose a tile, if there's a currently selected dash." + (interactive) + (when dash-id + (setq datadog--current-dash-id dash-id)) + (if datadog--current-dash-id + (helm :sources '(helm-source-datadog-select-tile)) + (error "No dash currently selected"))) + +(defun datadog-render-tile (tile) + (setq datadog--active-tile tile) + (setq datadog--looking-at-query 0) + (let ((queries (cdr (assoc 'queries tile)))) + (when queries + (setq datadog--all-queries queries) + (datadog-metric-query (car queries))))) + +;; Timeframe controls + +(defun datadog-timeframe-one-hour () + "Set the query timeframe to one hour" + (interactive) + (datadog--set-timeframe 'one-hour)) + +(defun datadog-timeframe-four-hours () + "Set the query timeframe to four hours" + (interactive) + (datadog--set-timeframe 'four-hours)) + +(defun datadog-timeframe-one-day () + "Set the query timeframe to one day" + (interactive) + (datadog--set-timeframe 'one-day)) + +(defun datadog-timeframe-one-week () + "Set the query timeframe to one week" + (interactive) + (datadog--set-timeframe 'one-week)) + +(defun datadog--set-timeframe (timeframe) + "Helper function to change the timeframe." + (let ((old-timeframe (datadog--get-current-timeframe))) + (when (not (eq old-timeframe timeframe)) + (datadog--set-current-timeframe timeframe) + ;; for now, this invalidates our query cache + (clrhash datadog--query-cache) + ;; and the timecursor position + (datadog--timecursor nil) + (datadog-refresh)))) + +;; Miscellaneous + +(defconst datadog--splash-screen-text + '("Welcome to" + "______ _______ _______ _______ ______ _____ ______" + "| \\ |_____| | |_____| | \\ | | | ____" + "|_____/ | | | | | |_____/ |_____| |_____|" + "" + "Press `D' (shift-D) to browse dashes." + "Press `m' to perform a metric query." + "Select the timeframe with `1', `4', `d' or `w'." + "C-h m for additional commands.")) + + +(defun datadog--splash-screen () + "Currently displayed only on start" + (let* ((buffer-read-only nil) + (n-chars (apply 'max (mapcar 'length datadog--splash-screen-text))) + (h-offset (/ (- (window-width) n-chars) 2)) + (v-offset (/ (- (window-height) + (length datadog--splash-screen-text)) + 2))) + (erase-buffer) + (if (and (>= h-offset 0) + (>= v-offset 0)) + (progn + (newline v-offset) + (dolist (text datadog--splash-screen-text) + (insert-char ?\s h-offset) + (insert text) + (newline))) + ;; fall back to extremely simple text + ;; should rarely be needed + (insert "Welcome to Datadog. C-h m for help.")))) + +;; Main entry function + +(defun datadog () + "Opens a Datadog session, or switches to an existing session." + (interactive) + (let ((existing-buffer (get-buffer "*datadog*"))) + (if existing-buffer + (switch-to-buffer existing-buffer) + (progn + (switch-to-buffer (get-buffer-create "*datadog*")) + (datadog-mode))))) + +;; Mode definition stuff + +(defvar datadog-mode-map + (let ((map (make-keymap))) + (define-key map (kbd "m") 'datadog-explore-metric) + (define-key map (kbd "n") 'datadog-next-series) + (define-key map (kbd "p") 'datadog-previous-series) + (define-key map (kbd "j") 'datadog-jump-to-graph) + (define-key map (kbd "f") 'datadog-timecursor-forward) + (define-key map (kbd "b") 'datadog-timecursor-backward) + (define-key map (kbd "F") 'datadog-forward-tick) + (define-key map (kbd "B") 'datadog-backward-tick) + (define-key map (kbd "r") 'datadog-refresh) + (define-key map (kbd "D") 'datadog-select-dash) + (define-key map (kbd "T") 'datadog-select-tile) + + ;; timeframes + (define-key map (kbd "1") 'datadog-timeframe-one-hour) + (define-key map (kbd "4") 'datadog-timeframe-four-hours) + (define-key map (kbd "d") 'datadog-timeframe-one-day) + (define-key map (kbd "w") 'datadog-timeframe-one-week) + map) + "Datadog mode keymap") + +(define-derived-mode datadog-mode special-mode "Datadog" + "Major mode for viewing and having all sorts of Datadog fun. + +\\{datadog-mode-map}" + (buffer-disable-undo) + (use-local-map datadog-mode-map) + + ;; local variable definitions + (datadog--init)) + +(defun datadog--init () + (interactive) + + ;; we won't highlight trailing whitespace + (make-local-variable 'show-trailing-whitespace) + (setq show-trailing-whitespace nil) + + ;; query cache; note strings as keys for the query + (setq datadog--query-cache (make-hash-table :test 'equal)) + + ;; hook setup + (add-hook 'window-size-change-functions 'datadog--check-size-change) + + ;; initialize display + (datadog--set-graph-size) + (datadog--set-graph-origin) + (datadog--splash-screen)) + +(provide 'datadog) diff --git a/local-packages/dogapi.el b/local-packages/dogapi.el new file mode 100644 index 0000000..f7ffb45 --- /dev/null +++ b/local-packages/dogapi.el @@ -0,0 +1,230 @@ +;;; dogapi.el --- emacs interface to the Datadog api + +;; Copyright (C) 2014 Drew Werner + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;; Package-Requires: url json +;; URL: http://github.com/wernerandrew/datadog.el +;; Version: 0.0.1alpha + +;; See LICENSE file for licensing details. + +;; Commentary: Handles a subset of the Datadog REST API, limited +;; (for now) only to a few GET requests. Responses come back as +;; parsed json objects (using json.el). + +(require 'url) +(require 'json) + +;; Configuration variables + +(defgroup dogapi nil + "Access Datadog API through Emacs") + +(defcustom dogapi-api-endpoint "https://app.datadoghq.com" + "URL to which API requests should be sent" + :group 'dogapi) + +(defcustom dogapi-api-version 1 + "Datadog API version" + :group 'dogapi + :type 'integer + :options '(1)) + +(defcustom dogapi-api-key nil + "API key for Datadog API." + :group 'dogapi + :type 'string) + +(defcustom dogapi-application-key nil + "Application key for Datadog API" + :group 'dogapi + :type 'string) + +(defcustom dogapi-credentials-path "~/.emacs.d" + "Directory in which to store the file described in +`dogapi-credentials-file'" + :group 'dogapi + :type 'string) + +(defcustom dogapi-credentials-file "dogapi-credentials" + "Name of file to store dogapi credentials, i.e., the values of +`dogapi-api-key' and `dogapi-application-key'. Serializes the +values as JSON." + :group 'dogapi + :type 'string) + +(defcustom dogapi-save-credentials t + "Set to non-nil to persist api credentials to a file" + :group 'dogapi + :type 'boolean) + +;; Internal variables + +(defconst dogapi-url-endpoints + '((dogapi-get-dash-list . "dash") + (dogapi-get-dash . "dash/%d") + (dogapi-get-metric-series . "series/query") + (dogapi-get-all-alerts . "alert")) + "Convenience synoyms for API endpoints.") + +;; Customization helpers + +(defun dogapi-set-credentials () + "Specify the API key and Application key to be used in submitting +dogapi requests." + (interactive) + (let* ((api-key (read-string "Enter api key: ")) + (app-key (read-string "Enter application key: "))) + (if (and api-key app-key) + (progn + (setq dogapi-api-key api-key) + (setq dogapi-application-key app-key) + (when dogapi-save-credentials + (dogapi--save-credentials-to-file))) + (error "Couldn't set credentials")))) + +;; Public API + +(defun dogapi-metric-query (query from-ts to-ts) + "Main metric query function. +Currently only a single query supported per call." + (let* ((response (dogapi--request + 'dogapi-get-metric-series + (list (cons "q" query) + (cons "from" from-ts) + (cons "to" to-ts)))) + (series (cdr (assoc 'series response)))) + series)) + +(defun dogapi-dash-list () + (dogapi--request 'dogapi-get-dash-list)) + +(defun dogapi-dash (dash-id) + (dogapi--request 'dogapi-get-dash nil dash-id)) + +;; Private API helpers + +(defun dogapi--request (request-name &optional request-params url-param) + "All API requests go through here. The request-name parameter, +is mandatory and must be one of the keys defined in `dogapi-url-endpoints'. + +Optional arguments include request-params, which is a list of cons cells +that pair string keys with string values. The url-param argument, +also optional, can be used to pass a parameter that constitues part +of the endpoint URL (e.g., as in `dogapi-dash')." + (dogapi--ensure-credentials) + (let* ((url (dogapi--request-url request-name request-params url-param)) + (url-request-method "GET")) + + (dogapi--json-from-buffer (url-retrieve-synchronously url)))) + +(defun dogapi--json-from-buffer (buffer) + "Helper to parse a JSON HTTP response." + (let ((json-response nil)) + (save-excursion + (switch-to-buffer buffer) + (widen) + (goto-char (point-min)) + (re-search-forward "^$" nil 'move) ;; skip header + (setq json-response (buffer-substring-no-properties (point) (point-max))) + (kill-buffer buffer)) + (json-read-from-string json-response))) + +;; URL building helpers + +(defun dogapi--request-url (request-name request-params url-param) + "Generates the actual URL to be used" + (let* ((request-base (cdr (assoc request-name dogapi-url-endpoints))) + (request-path (if url-param + (format request-base url-param) + request-base))) + + (concat (dogapi--join-url-segments (list dogapi-api-endpoint + (dogapi--get-api-base) + request-path)) + (format "?api_key=%s&application_key=%s" + dogapi-api-key dogapi-application-key) + (mapconcat + (lambda (x) + (format "&%s=%s" + (url-hexify-string (car x)) + (url-hexify-string (cdr x)))) + request-params "")))) + +(defun dogapi--get-api-base () + (format "api/v%d" dogapi-api-version)) + +(defun dogapi--join-url-segments (segments) + "Joins url-segments with at most 1 '/' character" + (replace-regexp-in-string + "([^\:])/+" + "\\1/" + (mapconcat 'identity segments "/"))) + +;; Credentials management + +(defun dogapi--credentials-file-full-path () + (expand-file-name + (concat (file-name-as-directory dogapi-credentials-path) + dogapi-credentials-file))) + +(defun dogapi--set-credentials-from-file () + "Reads the credentials file, if any, and sets `dogapi-api-key' and +`dogapi-application-key' appropriately." + (let ((cred-file (dogapi--credentials-file-full-path))) + (if (file-exists-p cred-file) + (let ((cred-data (json-read-from-string + (with-temp-buffer + (insert-file-contents cred-file) + (buffer-string))))) + (setq dogapi-api-key (cdr (assoc 'dogapi-api-key cred-data))) + (setq dogapi-application-key + (cdr (assoc 'dogapi-application-key cred-data))) + t) + nil))) + +(defun dogapi--save-credentials-to-file () + "Persists the currently stored `dogapi-api-key' and +`dogapi-application-key' to the file defined through the +`dogapi-credentials-path' and `dogapi-credentials-file' variables" + (let* ((cred-file (dogapi--credentials-file-full-path)) + (cred-data (list (cons 'dogapi-api-key dogapi-api-key) + (cons 'dogapi-application-key dogapi-application-key)))) + ;; lots must go right to save this + ;; credentials path must be a directory + (if (and (file-directory-p (expand-file-name dogapi-credentials-path)) + (or (not (file-exists-p cred-file)) + (yes-or-no-p (format "Overwrite credentials file: %s " + cred-file)))) + (with-temp-file cred-file + (insert (json-encode cred-data))) + (message "Error writing credentials to file.")))) + +(defun dogapi--has-credentials () + (not (or (null dogapi-api-key) + (null dogapi-application-key)))) + +(defun dogapi--ensure-credentials () + "If either API or application key are nil, tries to read from file, +and alternatively prompts user to enter in minibuffer." + (unless (dogapi--has-credentials) + ;; first attempt: read from file + (when dogapi-save-credentials + (dogapi--set-credentials-from-file)) + (unless (dogapi--has-credentials) + (dogapi-set-credentials)))) + +(provide 'dogapi) From 9c553f11165401d88d5a1993c5a0e29ddb149e95 Mon Sep 17 00:00:00 2001 From: Drew Werner Date: Wed, 13 Jul 2016 16:14:42 -0400 Subject: [PATCH 6/8] yet more workflow stuff --- init-keys.el | 3 +++ init-mode.el | 55 ++++++++++++++++++++++++++++++++++++++++++++++-- init-packages.el | 1 + 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/init-keys.el b/init-keys.el index 27b1e83..39bcaca 100644 --- a/init-keys.el +++ b/init-keys.el @@ -7,6 +7,9 @@ (when (boundp 'mac-command-modifier) (setq mac-command-modifier 'meta)) +(when (boundp 'mac-option-modifier) + (setq mac-option-modifier 'super)) + ;; Window splitting ;; Rebind these since rarely used (global-set-key (kbd "M-0") 'delete-window) diff --git a/init-mode.el b/init-mode.el index 8bc8422..933e094 100644 --- a/init-mode.el +++ b/init-mode.el @@ -26,6 +26,8 @@ (add-to-list 'auto-mode-alist '("\\.ejs$" . web-mode)) (add-to-list 'auto-mode-alist '("\\.mako$" . web-mode)) (add-to-list 'auto-mode-alist '("\\.handlebars$" . web-mode)) +(add-to-list 'auto-mode-alist '("\\.jsx$" . web-mode)) +(add-to-list 'auto-mode-alist '("\\.tsx$" . web-mode)) ;; default to django templating for all HTML files (add-to-list 'web-mode-engine-file-regexps '("django" . "\\.html$")) @@ -226,16 +228,65 @@ (require 'tramp) (ansi-color-for-comint-mode-on) +(require 'multi-term) +(add-hook 'multi-term-hook + (lambda () + (setq show-trailing-whitespace nil))) + ;; Org-mode (require 'org) (global-set-key (kbd "C-c l") 'org-store-link) (global-set-key (kbd "C-c a") 'org-agenda) -;; little hack to avoid lockfiles +(global-set-key (kbd "C-c c") 'org-capture) + +(defun my-org-path (f) (concat "~/Dropbox (Dropbox)/org/" f)) + +;; capture / refile flow +(let ((dbx-task-categories '("Analytics" "Reliability" "Data Layer" + "Performance" "Streaming" "Management" + "Uncategorized"))) + (setq org-capture-templates + (mapcar (lambda (cat) + (list (substring cat 0 1) (concat "Backlog: " cat) 'entry + (list 'file+olp (my-org-path "backlog.org") cat) + "** NEW %?\n")) + dbx-task-categories))) + (setq org-agenda-files (mapcar 'expand-file-name - (file-expand-wildcards "~/Dropbox (Dropbox)/org/[A-Za-z]*.org"))) + (file-expand-wildcards (my-org-path "[A-Za-z]*.org")))) + +(setq org-agenda-custom-commands + `(("d" todo "DELEGATED") + ("T" todo "TODO" ((org-agenda-files '(,(my-org-path "current.org"))))) + ("b" todo "*" ((org-agenda-files '(,(my-org-path "backlog.org"))))) + ("s" todo "SOURCED") + ("S" todo "SEARCHED") + ("M" todo "TO_EMAIL") + ("E" todo "EMAILED") + ("R" todo "TODO" ((org-agenda-files '(,(my-org-path "recruiting.org"))))) + ("A" todo "MET|FIT|IIP|ONSITE" + ((org-agenda-files '(,(my-org-path "recruiting.org"))))))) + +;; little hack to avoid lockfiles (setq org-log-done t) +;; right now we refile only to the "current" tasklist +;; maaaybe we add recruiting at some point +(setq org-refile-targets + '(("~/Dropbox (Dropbox)/org/current.org" :level . 1))) + +(setq org-refile-use-outline-path t) +(setq org-outline-path-complete-in-steps t) + +(defun my-org-archive-done-in-buffer () + (interactive) + (org-map-entries + 'org-archive-subtree + "/DONE|CANCELLED|ACCEPTED|REJECTED|NOT_INTERESTED|OTHER")) + +(global-set-key (kbd "C-c C-$") 'my-org-archive-done-in-buffer) + ;; EWW (add-hook 'eww-mode-hook '(lambda () diff --git a/init-packages.el b/init-packages.el index 7f07a29..e593ba4 100644 --- a/init-packages.el +++ b/init-packages.el @@ -34,6 +34,7 @@ lua-mode auto-complete ;; misc productivity projectile + multi-term epc jedi ac-js2 From d54d0f9d10a4825255f4dbda7081b108c1a9e154 Mon Sep 17 00:00:00 2001 From: Drew Werner Date: Thu, 20 Apr 2017 14:45:05 -0400 Subject: [PATCH 7/8] more changes --- init-mode.el | 45 +++++++++++++++++++++++++++++---------------- init-packages.el | 10 +++++++++- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/init-mode.el b/init-mode.el index 933e094..6046fc7 100644 --- a/init-mode.el +++ b/init-mode.el @@ -27,7 +27,7 @@ (add-to-list 'auto-mode-alist '("\\.mako$" . web-mode)) (add-to-list 'auto-mode-alist '("\\.handlebars$" . web-mode)) (add-to-list 'auto-mode-alist '("\\.jsx$" . web-mode)) -(add-to-list 'auto-mode-alist '("\\.tsx$" . web-mode)) +;; (add-to-list 'auto-mode-alist '("\\.tsx$" . web-mode)) ;; default to django templating for all HTML files (add-to-list 'web-mode-engine-file-regexps '("django" . "\\.html$")) @@ -56,20 +56,26 @@ ;; Don't redefine M-j - we use it to navigate (define-key js2-mode-map (kbd "M-j") nil))) ;; Extra autocompletion fun -;; (add-hook 'js2-mode-hook 'ac-js2-mode) -;; typescript -(require 'typescript) +(require 'typescript-mode) (setq typescript-indent-level 2) (add-hook 'typescript-mode-hook '(lambda () - (define-key typescript-mode-map (kbd "M-.") nil))) -(add-to-list 'auto-mode-alist '("\\.ts$" . typescript-mode)) - -(require 'tss) -(setq tss-popup-help-key "M-?") -(setq tss-jump-to-definition-key "M-.") -(tss-config-default) + (define-key typescript-mode-map (kbd "M-.") nil) + (eldoc-mode +1) + (company-mode +1))) +(add-to-list 'auto-mode-alist '("\\.tsx$" . typescript-mode)) + +;; ;; tide +(require 'tide) +(add-hook 'typescript-mode-hook #'tide-setup) + +;; tsx +;; (add-to-list 'auto-mode-alist '("\\.tsx\\'" . web-mode)) +;; (add-hook 'web-mode-hook + ;; (lambda () + ;; (when (string-equal "tsx" (file-name-extension buffer-file-name)) + ;; (setup-tide-mode)))) ;; ruby (require 'ruby-mode) @@ -108,6 +114,9 @@ ;; projectile (require 'projectile) +(setq projectile-enable-caching nil) +;; slightly less accurate but faster files getting command +(setq projectile-git-command "git ls-files -zc --exclude-standard") (projectile-global-mode) (setq projectile-completion-system 'ido) @@ -139,8 +148,11 @@ (ac-config-default) ;; Show menu more quickly after autocomplete starts (setq ac-auto-show-menu (* ac-delay 2)) -;; custom modes for autocomplete -(add-to-list 'ac-modes 'typescript-mode) + +;; Company +(require 'company) +(add-to-list 'company-backends 'typescript-mode) +(setq company-idle-delay 0.2) ;; Flymake - show in minibuffer when over (defun aw/flymake-show-help () @@ -185,6 +197,7 @@ (local-set-key (kbd "M-,") 'jedi:goto-definition-pop-marker) (local-set-key (kbd "M-/") 'jedi:get-in-function-call))) + ;; Go (require 'go-autocomplete) (require 'go-eldoc) @@ -239,7 +252,7 @@ (global-set-key (kbd "C-c a") 'org-agenda) (global-set-key (kbd "C-c c") 'org-capture) -(defun my-org-path (f) (concat "~/Dropbox (Dropbox)/org/" f)) +(defun my-org-path (f) (concat "~/Dropbox Dropbox/Drew Werner/org/" f)) ;; capture / refile flow (let ((dbx-task-categories '("Analytics" "Reliability" "Data Layer" @@ -260,7 +273,7 @@ `(("d" todo "DELEGATED") ("T" todo "TODO" ((org-agenda-files '(,(my-org-path "current.org"))))) ("b" todo "*" ((org-agenda-files '(,(my-org-path "backlog.org"))))) - ("s" todo "SOURCED") + ("r" todo "SOURCED") ("S" todo "SEARCHED") ("M" todo "TO_EMAIL") ("E" todo "EMAILED") @@ -274,7 +287,7 @@ ;; right now we refile only to the "current" tasklist ;; maaaybe we add recruiting at some point (setq org-refile-targets - '(("~/Dropbox (Dropbox)/org/current.org" :level . 1))) + '(("~/Dropbox Dropbox/Drew Werner/org/current.org" :level . 1))) (setq org-refile-use-outline-path t) (setq org-outline-path-complete-in-steps t) diff --git a/init-packages.el b/init-packages.el index e593ba4..09d2a10 100644 --- a/init-packages.el +++ b/init-packages.el @@ -24,6 +24,8 @@ js2-mode coffee-mode tss + typescript-mode + tide cython-mode enh-ruby-mode haskell-mode @@ -33,6 +35,7 @@ yaml-mode lua-mode auto-complete ;; misc productivity + company projectile multi-term epc @@ -44,6 +47,7 @@ ag magit multiple-cursors + expand-region helm twittering-mode hackernews)) @@ -101,5 +105,9 @@ (require 'multiple-cursors) (global-set-key (kbd "C-.") 'mc/mark-next-like-this) (global-set-key (kbd "C-,") 'mc/mark-previous-like-this) - (global-set-key (kbd "C-c C-,") 'mc/mark-all-like-this))) + (global-set-key (kbd "C-c C-,") 'mc/mark-all-like-this) + + ;; the little expand region code + (require 'expand-region) + (global-set-key (kbd "C-=") 'er/expand-region))) From 0b02c730f317c3edbd59d125d49018ad4ac1d565 Mon Sep 17 00:00:00 2001 From: Drew Werner Date: Wed, 2 Aug 2017 09:40:36 -0400 Subject: [PATCH 8/8] some fixes before my computer died --- init-mode.el | 18 ++++++++++++++---- init-packages.el | 3 +++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/init-mode.el b/init-mode.el index 6046fc7..654edc6 100644 --- a/init-mode.el +++ b/init-mode.el @@ -41,6 +41,15 @@ ad-do-it) ad-do-it)) +;; Certain weirdo Dropbox things get python-mode +;; e.g., pystachio +(add-to-list 'auto-mode-alist '("\\.pyst$" . python-mode)) +(add-to-list 'auto-mode-alist '("\\.pyst-include$" . python-mode)) +;; and bazel stuff +(add-to-list 'auto-mode-alist '("\\(^\\|/\\)BUILD" . python-mode)) +(add-to-list 'auto-mode-alist '("\\(^\\|/\\)WORKSPACE" . python-mode)) +(add-to-list 'auto-mode-alist '("\\.bzl$" . python-mode)) + ;; pyxl-mode (require 'pyxl-mode) (add-to-list 'auto-mode-alist '("\\.py$" . pyxl-mode)) @@ -103,6 +112,9 @@ (require 'markdown-mode) (add-to-list 'auto-mode-alist '("\\.markdown$" . markdown-mode)) (add-to-list 'auto-mode-alist '("\\.md$" . markdown-mode)) +(defun aw/markdown-mode-setup () + (visual-line-mode)) +(add-hook 'markdown-mode-hook 'aw/markdown-mode-setup) ;; lua (require 'lua-mode) @@ -114,9 +126,7 @@ ;; projectile (require 'projectile) -(setq projectile-enable-caching nil) -;; slightly less accurate but faster files getting command -(setq projectile-git-command "git ls-files -zc --exclude-standard") +(setq projectile-enable-caching t) (projectile-global-mode) (setq projectile-completion-system 'ido) @@ -262,7 +272,7 @@ (mapcar (lambda (cat) (list (substring cat 0 1) (concat "Backlog: " cat) 'entry (list 'file+olp (my-org-path "backlog.org") cat) - "** NEW %?\n")) + "** TODO %?\n")) dbx-task-categories))) (setq org-agenda-files diff --git a/init-packages.el b/init-packages.el index 09d2a10..b1fd345 100644 --- a/init-packages.el +++ b/init-packages.el @@ -79,6 +79,9 @@ (setq solarized-use-variable-pitch nil) (setq x-underline-at-descent-line t) (load-theme 'solarized-dark t) + ;; (require 'color-theme) + ;; (color-theme-initialize) + ;; (color-theme-hober) (require 'magit) (global-set-key (kbd "C-c g s") 'magit-status)