From 8d97d1f2063f19c0c679e54fc082691a495c9303 Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Sun, 10 Jan 2016 23:43:14 -0500 Subject: Rewrite configuration with use-package --- .gitignore | 3 +- Makefile | 19 + init.el | 1854 ++++++++++++++++++++++++++++++++++++++++++++- lisp/init-abbrev.el | 71 -- lisp/init-ace.el | 113 --- lisp/init-appearance.el | 99 --- lisp/init-bib.el | 225 ------ lisp/init-bog.el | 37 - lisp/init-buffers.el | 72 -- lisp/init-diminish.el | 39 - lisp/init-dired.el | 202 ----- lisp/init-editing.el | 397 ---------- lisp/init-elisp.el | 46 -- lisp/init-ess.el | 65 -- lisp/init-external.el | 350 --------- lisp/init-files.el | 245 ------ lisp/init-framewin.el | 87 --- lisp/init-general.el | 110 --- lisp/init-git.el | 817 -------------------- lisp/init-god.el | 68 -- lisp/init-haskell.el | 40 - lisp/init-helm.el | 72 -- lisp/init-mail.el | 274 ------- lisp/init-org.el | 823 -------------------- lisp/init-outline.el | 116 --- lisp/init-projectile.el | 322 -------- lisp/init-python.el | 319 -------- lisp/init-server.el | 46 -- lisp/init-snakemake.el | 75 -- lisp/init-tex.el | 61 -- lisp/init-util.el | 38 - lisp/init-view.el | 33 - lisp/init-yas.el | 47 -- lisp/km-abbrev.el | 64 ++ lisp/km-ace-link.el | 92 +++ lisp/km-avy.el | 70 ++ lisp/km-bib.el | 205 +++++ lisp/km-buffer-cleanup.el | 52 ++ lisp/km-buffers.el | 60 ++ lisp/km-compile.el | 94 +++ lisp/km-diff.el | 57 ++ lisp/km-dired.el | 121 +++ lisp/km-editing.el | 220 ++++++ lisp/km-ess.el | 46 ++ lisp/km-files.el | 158 ++++ lisp/km-framewin.el | 65 ++ lisp/km-god.el | 36 + lisp/km-helm.el | 48 ++ lisp/km-hydra.el | 203 +++++ lisp/km-magit.el | 579 ++++++++++++++ lisp/km-mail.el | 150 ++++ lisp/km-org.el | 583 ++++++++++++++ lisp/km-outline.el | 78 ++ lisp/km-projectile.el | 182 +++++ lisp/km-python.el | 267 +++++++ lisp/km-shell.el | 117 +++ lisp/km-snakemake.el | 53 ++ lisp/km-tex.el | 49 ++ lisp/km-theme.el | 85 +++ lisp/km-util.el | 38 + lisp/km-webjump.el | 48 ++ lisp/setkey.el | 48 ++ 62 files changed, 5707 insertions(+), 5346 deletions(-) create mode 100644 Makefile delete mode 100644 lisp/init-abbrev.el delete mode 100644 lisp/init-ace.el delete mode 100644 lisp/init-appearance.el delete mode 100644 lisp/init-bib.el delete mode 100644 lisp/init-bog.el delete mode 100644 lisp/init-buffers.el delete mode 100644 lisp/init-diminish.el delete mode 100644 lisp/init-dired.el delete mode 100644 lisp/init-editing.el delete mode 100644 lisp/init-elisp.el delete mode 100644 lisp/init-ess.el delete mode 100644 lisp/init-external.el delete mode 100644 lisp/init-files.el delete mode 100644 lisp/init-framewin.el delete mode 100644 lisp/init-general.el delete mode 100644 lisp/init-git.el delete mode 100644 lisp/init-god.el delete mode 100644 lisp/init-haskell.el delete mode 100644 lisp/init-helm.el delete mode 100644 lisp/init-mail.el delete mode 100644 lisp/init-org.el delete mode 100644 lisp/init-outline.el delete mode 100644 lisp/init-projectile.el delete mode 100644 lisp/init-python.el delete mode 100644 lisp/init-server.el delete mode 100644 lisp/init-snakemake.el delete mode 100644 lisp/init-tex.el delete mode 100644 lisp/init-util.el delete mode 100644 lisp/init-view.el delete mode 100644 lisp/init-yas.el create mode 100644 lisp/km-abbrev.el create mode 100644 lisp/km-ace-link.el create mode 100644 lisp/km-avy.el create mode 100644 lisp/km-bib.el create mode 100644 lisp/km-buffer-cleanup.el create mode 100644 lisp/km-buffers.el create mode 100644 lisp/km-compile.el create mode 100644 lisp/km-diff.el create mode 100644 lisp/km-dired.el create mode 100644 lisp/km-editing.el create mode 100644 lisp/km-ess.el create mode 100644 lisp/km-files.el create mode 100644 lisp/km-framewin.el create mode 100644 lisp/km-god.el create mode 100644 lisp/km-helm.el create mode 100644 lisp/km-hydra.el create mode 100644 lisp/km-magit.el create mode 100644 lisp/km-mail.el create mode 100644 lisp/km-org.el create mode 100644 lisp/km-outline.el create mode 100644 lisp/km-projectile.el create mode 100644 lisp/km-python.el create mode 100644 lisp/km-shell.el create mode 100644 lisp/km-snakemake.el create mode 100644 lisp/km-tex.el create mode 100644 lisp/km-theme.el create mode 100644 lisp/km-util.el create mode 100644 lisp/km-webjump.el create mode 100644 lisp/setkey.el diff --git a/.gitignore b/.gitignore index 79c2cab..a3da545 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ auto-save-list cache bookmarks -/lisp/init-untracked.el +/lisp/km-untracked.el /.mc-lists.el /projectile-bookmarks.eld /projectile.cache @@ -21,3 +21,4 @@ bookmarks /network-security.data /.pydoc-names /eww-bookmarks +/lisp/km-emacs-autoloads.el diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..54b6d92 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ + +LOAD_PATH ?= +BATCH = emacs -Q --batch $(LOAD_PATH) + +LOADDEFS_FNAME = km-emacs-autoloads.el +LOADDEFS = lisp/$(LOADDEFS_FNAME) + +ELS = $(shell find lisp -maxdepth 1 \ + -type f \( -name "*.el" -and ! -name "$(LOADDEFS_FNAME)" \) -print) + +$(LOADDEFS): $(ELS) + @$(BATCH) --eval "\ + (let* ((make-backup-files nil) \ + (default-directory \"$(CURDIR)\") \ + (generated-autoload-file (expand-file-name \"$(LOADDEFS)\"))) \ + (update-directory-autoloads \"lisp/\"))" + +%.elc: %.el + @$(BATCH) -f batch-byte-compile $< diff --git a/init.el b/init.el index 315dca9..5fb4bdb 100644 --- a/init.el +++ b/init.el @@ -22,9 +22,6 @@ ;; (package-initialize) -(defvar km/init-lisp-dir (expand-file-name "lisp/" user-emacs-directory)) -(add-to-list 'load-path km/init-lisp-dir) - (require 'package) (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/")) (package-initialize) @@ -34,52 +31,1837 @@ (require 'pallet) (pallet-mode 1) -(require 'init-appearance) - (require 'cl-lib) (require 'dash) (require 's) -(require 'init-util) -(require 'init-general) -(require 'init-abbrev) -(require 'init-diminish) +(require 'bind-key) +(require 'use-package) +(require 'use-package-chords) +(key-chord-mode 1) + +(defvar km/init-lisp-dir (expand-file-name "lisp/" user-emacs-directory)) +(add-to-list 'load-path km/init-lisp-dir) + +(require 'km-util) +(require 'km-emacs-autoloads) + + +;;; Appearance + +(setq inhibit-splash-screen t + initial-scratch-message nil) + +(blink-cursor-mode -1) +(menu-bar-mode -1) +(scroll-bar-mode -1) +(tool-bar-mode -1) + +(column-number-mode) +(line-number-mode) +(size-indication-mode) + +(require 'km-theme) + + +;;; Custom prefix maps + +(pcase-dolist (`(,key ,map) '(("C-c c" km/compile-map) + ("C-c e" km/editing-map) + ("C-c f" km/file-map) + ("C-c g" km/git-map) + ("C-c k" km/kill-map) + ("C-x m" km/mail-map) + ("C-c o" km/global-org-map) + ("C-c z" km/external-map))) + (define-prefix-command map) + (global-set-key (kbd key) map)) + + +;;; Org + +(use-package org + :load-path "~/src/emacs/org-mode/lisp/" + :mode ("\\.org.txt\\'" . org-mode) + :init + (require 'org-loaddefs) + (add-to-list 'load-path "~/src/emacs/org-mode/contrib/lisp/" t) + (add-to-list 'Info-directory-list "~/src/emacs/org-mode/doc/") + (bind-keys :map km/global-org-map + ("b" . org-iswitchb) + ("l" . org-store-link) + ("o" . org-open-at-point) + ("s" . org-save-all-org-buffers) + ("w" . org-refile-goto-last-stored)) + (define-prefix-command 'km/org-prefix-map) + :config + (setq org-log-done t + org-log-into-drawer t + org-clock-into-drawer t + org-todo-keywords '((sequence "TODO(t)" "STARTED(s)" "WAITING(w@)" + "|" "DONE(d)" "NA(n@)"))) + (setq org-catch-invisible-edits 'error + org-special-ctrl-k t + org-insert-heading-respect-content t + org-M-RET-may-split-line nil + org-adapt-indentation nil + org-blank-before-new-entry '((heading . t) (plain-list-item . auto))) + (setq org-use-speed-commands t + org-use-extra-keys t + org-fast-tag-selection-single-key 'expert) + (setq org-outline-path-complete-in-steps nil + org-goto-interface 'outline-path-completionp + org-goto-max-level 3) + + (setq org-default-notes-file "~/notes/agenda/tasks.org") + (setq org-agenda-text-search-extra-files + (file-expand-wildcards "~/notes/extra/*.org")) + + (setq org-reverse-note-order t) + (setq org-refile-targets '((nil :maxlevel . 2)) + org-refile-cache nil) + + (setq org-link-search-must-match-exact-headline nil) + + (setq org-confirm-babel-evaluate nil + org-src-fontify-natively t) + + (org-babel-do-load-languages + 'org-babel-load-languages + '((shell . t) + (python . t) + (R . t) + (emacs-lisp . t) + (latex . t))) + + (defadvice org-open-file (after km/org-open-add-to-recentf activate) + (recentf-add-file path)) + + (put 'org-goto-max-level 'safe-local-variable #'integerp) + + (add-to-list 'org-latex-packages-alist '("" "amsmath" t)) + + (setq org-structure-template-alist + (append '(("p" "#+property: ") + ("o" "#+options: ") + ("d" "#+date: ") + ("t" "#+title: ") + ("S" "#+setupfile: ?") + ("n" "#+name: ") + ("w" "#+begin_note\n ?\n#+end_note") + ("C" "#+caption: ") + ("b" "#+label: ") + ("r" "#+attr_latex: ") + ("R" "#+attr_html: ")) + (mapcar (lambda (i) (list (car i) (downcase (cadr i)))) + org-structure-template-alist))) + + (add-hook 'next-error-hook (lambda () + (when (eq major-mode 'org-mode) + (org-show-context)))) + + (bind-keys :map org-mode-map + ("C-c l" . org-goto) + ("C-c m" . km/org-prefix-map) + ;; Don't let `org-cycle-agenda-files' binding override + ;; custom `backward-kill-word' binding + ;; (`org-cycle-agenda-files' is still bound to C-,). + ("C-'" . nil) + ("C-c C-x d" . org-metadown) + ("C-c C-x n" . org-next-item) + ("C-c C-x s" . org-set-property) + ("C-c C-x p" . org-previous-item) + ("C-c C-x w" . org-insert-drawer))) + +(use-package ox-latex + :defer t + :config + (add-to-list 'org-latex-classes + '("short" + "\\documentclass{short}" + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}") + ("\\paragraph{%s}" . "\\paragraph*{%s}") + ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))) + +(use-package org-table + :defer t + :diminish (orgtbl-mode . "Ot")) + +(use-package org-capture + :defer t + :init (define-key km/global-org-map "c" #'org-capture) + :config + (setq org-capture-templates + '(("t" "task" entry (file+headline "~/notes/tasks.org" "Inbox") + "* TODO %?%i" :prepend t) + ("d" "date" entry (file+headline "~/notes/calendar.org" "Inbox") + "* %?%i" :prepend t) + ("b" "bookmark" entry (file+headline "~/notes/bookmarks.org" "Inbox") + "* %?%i" :prepend t) + ("v" "Visit" checkitem (file+headline "~/notes/tasks.org" "Visit") + "- [ ] %?%i\n" :prepend t) + ("r" "Revisit" checkitem (file+headline "~/notes/tasks.org" "Revisit") + "- [ ] %?%i\n" :prepend t) + ;; Link counterparts + ("T" "task link" entry (file+headline "~/notes/tasks.org" "Inbox") + "* TODO %?%i\n\n%a" :prepend t) + ("D" "date link" entry (file+headline "~/notes/calendar.org" "Inbox") + "* %?%i\n\n%a" :prepend t) + ("B" "bookmark link" entry + (file+headline "~/notes/bookmarks.org" "Inbox") + "* %?%i\n\n%a" :prepend t) + ;; Clipboard + ("x" "task clipboard" entry (file+headline "~/notes/tasks.org" "Inbox") + "* TODO %?%i\n\n%x" :prepend t) + ("X" "bookmark clipboard" entry + (file+headline "~/notes/bookmarks.org" "Inbox") + "* %?%i\n\n%x" :prepend t)))) + +(use-package org-agenda + :defer t + :init (define-key km/global-org-map "a" #'org-agenda) + :config + (setq org-agenda-restore-windows-after-quit t + org-agenda-window-setup 'only-window + org-agenda-sticky t) + (setq org-agenda-dim-blocked-tasks nil + org-agenda-show-all-dates t + org-agenda-skip-deadline-if-done t + org-agenda-skip-scheduled-if-done t + org-agenda-start-on-weekday nil + org-agenda-use-time-grid nil) + (setq org-agenda-sorting-strategy + '((agenda time-up deadline-up scheduled-up priority-down category-keep) + (todo priority-down category-keep) + (tags priority-down category-keep) + (search category-keep))) + (setq org-agenda-custom-commands + '(("d" todo "DONE" nil) + ("u" "Unschedule TODO entries" alltodo "" + ((org-agenda-skip-function + (lambda nil + (org-agenda-skip-entry-if 'scheduled 'deadline + 'regexp "\n]+>"))) + (org-agenda-overriding-header "Unscheduled TODO entries: "))) + ("p" "Past timestamps" tags "TIMESTAMP<=\"\""))) + + ;; Bind `org-agenda-follow-mode' to same key as + ;; `next-error-follow-minor-mode'. + (define-key org-agenda-mode-map (kbd "C-c C-f") #'org-agenda-follow-mode) + ;; Free up 'j' for `km/org-agenda-avy-goto-subword-1'. + (define-key org-agenda-mode-map (kbd "C-j") #'org-agenda-goto-date)) + +(use-package org-contacts + :defer t + :after org + :init + (setq org-contacts-files '("~/notes/contacts.org")) + (after 'org-capture + (add-to-list 'org-capture-templates + '("a" "email address" entry + (file+headline "~/notes/contacts.org" "Inbox") + " +** %(org-contacts-template-name) +:PROPERTIES: +:EMAIL: %(org-contacts-template-email) +:END:")))) + +(use-package km-org + :defer t + :after org-agenda + :init + (bind-keys :map km/global-org-map + ("j" . km/org-goto-agenda-heading) + ("m" . km/org-open-default-notes-file-inbox) + ("n" . km/org-agenda-add-or-remove-file)) + (bind-keys :map km/org-prefix-map + ("c" . km/org-clone-and-shift-by-repeater) + ("d" . km/org-link-dired-jump) + ("D" . km/org-delete-checked-items) + ("i" . km/org-refile-list-item) + ("l" . km/org-remove-title-leader) + ("n" . km/org-normalize-spaces) + ("s" . km/org-sort-parent) + ("w" . km/org-refile-to-other-org-buffer)) + (bind-keys :map km/file-map + ("a" . km/org-open-annex-file) + ("o" . km/org-open-file) + ("p" . km/org-open-file-at-point) + ("r" . km/org-open-recent-file)) + + (define-key ctl-x-4-map "o" #'km/org-switch-to-buffer-other-window) + + (after 'org + (define-key org-mode-map (kbd "C-c C-x B") + #'km/org-tree-to-indirect-buffer-current-window) + (define-key org-mode-map [remap org-tree-to-indirect-buffer] + #'km/org-tree-to-indirect-buffer) + (define-key org-mode-map [remap org-refile] #'km/org-refile-dwim) + + (add-to-list 'org-speed-commands-user '("o" . km/org-open-at-point-stay)) + (add-to-list 'org-speed-commands-user '("w" . km/org-refile-dwim)) + + (setq org-refile-target-verify-function #'km/org-refile-verify-target) + (add-hook 'org-after-refile-insert-hook #'km/org-maybe-sort-parent) + + (org-add-link-type "pmid" #'km/org-pmid-open)) + (after 'org-agenda + (setq km/org-agenda-file-directory "~/notes/agenda/" + org-agenda-files (list km/org-agenda-file-directory)) + (add-hook 'org-agenda-mode-hook #'km/org-agenda-cd-and-read-dir-locals) + (add-hook 'org-agenda-finalize-hook #'km/org-agenda-store-current-span)) + (after 'ox-md + (advice-add 'org-md-paragraph :filter-return #'km/org-md--fill-string)) + :config + (add-to-list 'safe-local-variable-values + (cons 'org-refile-targets km/org-agenda-refile-targets))) + +(use-package poporg + :defer t + :init + (define-key km/global-org-map "p" #'poporg-dwim) + :config + (define-key poporg-mode-map (kbd "C-c C-c") #'poporg-edit-exit)) + +(use-package org-link-edit + :load-path "~/src/emacs/org-link-edit/" + :defer t + :init (require 'org-link-edit-autoloads)) + +(use-package bog + :load-path "~/src/emacs/bog/" + :defer t + :init + (require 'bog-autoloads) + (after 'org + (add-hook 'org-mode-hook #'bog-mode)) + (setq bog-keymap-prefix (kbd "C-c b")) + (global-set-key bog-keymap-prefix bog-command-map) + :config + (setq bog-subdirectory-group 2 + bog-combined-bib-ignore-not-found t + bog-use-citekey-cache t)) + + +;;; Editing, navigation, and search + +(setq set-mark-command-repeat-pop t) +(setq recenter-positions '(top middle bottom)) + +(setq-default indicate-empty-lines t + indent-tabs-mode nil) + +(electric-indent-mode -1) +(electric-pair-mode) +(show-paren-mode) + +(put 'upcase-region 'disabled nil) +(put 'downcase-region 'disabled nil) + +(put 'set-goal-column 'disabled nil) + +(bind-keys :map km/editing-map + ("C-i" . indent-relative) + ("l" . toggle-truncate-lines)) + +(bind-keys :map occur-mode-map + ("n" . next-line) + ("p" . previous-line)) +(bind-keys :map search-map + ("s" . query-replace) + ("S" . replace-string) + ("r" . query-replace-regexp) + ("R" . replace-regexp)) + +;; Avoid shift key for `backward-paragraph' and `forward-paragraph'. +(global-set-key (kbd "M-}") nil) +(global-set-key (kbd "M-]") #'forward-paragraph) +(global-set-key (kbd "M-{") nil) +(global-set-key (kbd "M-[") #'backward-paragraph) + +(global-set-key (kbd "C-z") #'zap-to-char) +(use-package misc + :bind ("M-z" . zap-up-to-char)) + +(global-set-key (kbd "C-'") #'backward-kill-word) + +(put 'fill-paragraph-function 'safe-local-variable + (lambda (v) (equal v (lambda (_) t)))) + +(key-chord-define-global "qp" #'fill-paragraph) + +(use-package align + :bind ("C-x \\" . align-regexp)) + +(use-package expand-region + :bind ("C-." . er/expand-region)) + +(use-package iedit + :defer t + :init (define-key km/editing-map "i" #'iedit-mode) + :config + (setq iedit-toggle-key-default nil)) + +(use-package easy-kill + :defer t + :init + (global-set-key [remap kill-ring-save] #'easy-kill)) + +(use-package whitespace + :defer 5 + :diminish global-whitespace-mode + :config + (setq whitespace-style '(face trailing indentation)) + (global-whitespace-mode)) + +(use-package km-editing + :defer t + :chords ("jx" . km/toggle-line-or-region-comment) + :init + (define-key search-map "o" #'km/occur) + (define-key narrow-map "c" #'km/narrow-to-comment-heading) + + (bind-keys :map km/editing-map + ("f" . km/fill-surrounding-indented) + ("u" . km/unfill-paragraph)) + :config + (bind-keys :map km/kill-map + ("." . km/kill-sentence-at-point) + ("j" . km/join-next-line-with-space) + ("l" . km/kill-line-at-point) + ("p" . km/kill-paragraph-at-point) + ("s" . km/kill-string-at-point) + ("w" . km/kill-word-at-point))) + +(use-package km-outline + :defer t) + +(put 'narrow-to-page 'disabled nil) +(put 'narrow-to-region 'disabled nil) + +(use-package narrow-indirect + :defer t + :init + (bind-keys :map ctl-x-4-map + ("nd" . ni-narrow-to-defun-indirect-other-window) + ("nn" . ni-narrow-to-region-indirect-other-window) + ("np" . ni-narrow-to-page-indirect-other-window))) + +(use-package avy + :defer t + :chords ("jf" . avy-goto-subword-1) + :init + (define-key isearch-mode-map (kbd "C-'") #'avy-isearch)) + +(use-package ace-link + :defer t + :init + (after 'org + (define-key org-mode-map (kbd "C-c m o") #'ace-link-org)) + (after 'info + (define-key Info-mode-map "o" #'ace-link-info)) + (after 'compile + (define-key compilation-mode-map "o" #'ace-link-compilation)) + (after 'help-mode + (define-key help-mode-map "o" #'ace-link-help)) + (after 'woman + (define-key woman-mode-map "o" #'ace-link-woman)) + (after 'eww + (define-key eww-link-keymap "o" #'ace-link-eww) + (define-key eww-mode-map "o" #'ace-link-eww))) + +(use-package km-ace-link + :defer t + :init + (after 'dired + ;; This overrides the binding for `dired-find-file-other-window'. + (define-key dired-mode-map "o" #'km/ace-link-dired) + (define-key dired-mode-map "r" #'dired-find-file-other-window)) + (after 'notmuch + (define-key notmuch-hello-mode-map "o" #'km/ace-link-widget)) + (after 'gnus-sum + (define-key gnus-summary-mode-map "o" #'km/ace-link-widget)) + (after 'gnus-art + (define-key gnus-article-mode-map "o" #'km/ace-link-widget))) + +(use-package km-avy + :defer t + :init + (after 'gnus-sum + (define-key gnus-summary-mode-map "j" + #'km/gnus-avy-goto-subword-and-select)) + (after 'grep + (define-key grep-mode-map "j" #'km/grep-avy-goto-subword-1)) + (after 'org-agenda + (define-key org-agenda-mode-map "j" #'km/org-agenda-avy-goto-subword-1)) + (after 'magit + (define-key magit-refs-mode-map "j" #'km/magit-avy-goto-subword-1)) + (after 'magit-log + (define-key magit-log-mode-map "j" #'km/magit-avy-goto-subword-1) + (define-key magit-cherry-mode-map "j" #'km/magit-avy-goto-subword-1)) + (after 'replace + (define-key occur-mode-map "j" #'km/occur-avy-goto-subword-1))) + + +;;; Buffers and files + +(setq require-final-newline t) + +;; Disable `suspend-frame' binding. +(global-set-key (kbd "C-x C-z") nil) + +(use-package uniquify + :config + (setq uniquify-buffer-name-style 'forward)) + +(use-package autorevert + :config + (setq auto-revert-use-notify nil) + (global-auto-revert-mode)) + +(use-package ibuffer + :bind ("C-x C-b" . ibuffer) + :init + (setq ibuffer-expert t + ibuffer-restore-window-config-on-quit t + ibuffer-show-empty-filter-groups nil)) + +(use-package km-buffer-cleanup + :config + (add-hook 'before-save-hook #'km/cleanup-buffer) + (define-key km/editing-map "t" #'km/toggle-prevent-cleanup)) + +(use-package km-buffers + :defer t + :chords ("js" . km/save-buffers) + :bind ("C-x k" . km/kill-buffer)) + +(use-package ffap + :defer t + :config + (setq ffap-machine-p-known 'reject)) + +(use-package tramp + :defer t + :config + (setq tramp-default-method "sshx")) + +(use-package recentf + :config + (setq recentf-max-menu-items 15 + recentf-max-saved-items 200 + recentf-save-file "~/.emacs.d/cache/recentf") + (recentf-mode)) + +(use-package nlines + :load-path "~/src/emacs/nlines/" + :defer t + :init + (require 'nlines-autoloads) + (define-key km/file-map "l" #'nlines-run-command)) + +(use-package view + :diminish (view-mode . "Vw") + :defer t + :chords ("hq" . view-mode) + :init + (define-key ctl-x-4-map "v" #'view-file-other-window) + :config + (bind-keys :map view-mode-map + ("l" . recenter-top-bottom) + ("f" . forward-word) + ("b" . backward-word) + ("]" . forward-paragraph) + ("[" . backward-paragraph) + ("j" . avy-goto-subword-1))) + +(use-package km-files + :bind (("C-c s" . km/scratch-find-file) + ("C-x C-w" . km/write-file)) + :init + (bind-keys :map km/file-map + ("j" . km/dired-jump-file-at-point) + ("n" . km/rename-current-buffer-file) + ("R" . km/find-file-as-root)) + (bind-keys :map ctl-x-4-map + ("r" . km/recentf-find-file-other-window) + ("s" . km/scratch-find-file-other-window))) + + +;;; Frames and windows + +(use-package winner + :defer 10 + :chords ("lq" . winner-undo) + :config + (winner-mode)) + +(use-package km-framewin + :defer t + :init + (define-key ctl-x-4-map "c" + #'km/clone-indirect-buffer-other-window-and-widen)) + +(use-package ace-window + :commands km/ace-window + :chords ("jw" . km/ace-window) + :init + (defun km/ace-window (arg) + "Run `ace-window', swapping single and double C-u's." + (interactive "p") + (cl-case arg + (4 (setq arg 16)) + (16 (setq arg 4))) + (ace-window arg)) + :config + (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l) + aw-scope 'frame)) + + +;;; Projectile + +(use-package projectile + :defer t + :diminish projectile-mode + :chords ("jq" . projectile-commander) + :init + (define-prefix-command 'km/projectile-ctl-x-4-map) + (define-key ctl-x-4-map "p" 'km/projectile-ctl-x-4-map) + (bind-keys :map km/projectile-ctl-x-4-map + ("C-o" . projectile-display-buffer) + ("b" . projectile-switch-to-buffer-other-window) + ("d" . projectile-find-dir-other-window) + ("f" . projectile-find-file-other-window) + ("t" . projectile-find-implementation-or-test-other-window)) + :config + (setq projectile-find-dir-includes-top-level t + projectile-completion-system 'helm + projectile-use-git-grep t) + + (defun km/projectile-ignore-directory-p (name) + (or (file-remote-p name) + (string-prefix-p "/tmp/" name))) + (setq projectile-ignored-project-function #'km/projectile-ignore-directory-p) + + (projectile-register-project-type 'snakemake + '("Snakefile") "snakemake -p" "") + + (bind-keys :map projectile-command-map + ("g" . projectile-vc) + ("i" . projectile-ibuffer) + ("I" . projectile-invalidate-cache) + ("l" . projectile-project-buffers-other-buffer) + ("q" . projectile-replace) + ("s" . projectile-grep)) + + ;; I'm redefining a lot of bindings, so unset pre-defined methods + ;; and define everyting here. + (setq projectile-commander-methods nil) + + (def-projectile-commander-method ?c + "Run project compilation command." + (call-interactively 'projectile-compile-project)) + (def-projectile-commander-method ?D + "Find a project directory in other window." + (call-interactively 'projectile-find-dir-other-window)) + (def-projectile-commander-method ?F + "Find project file in other window." + (call-interactively 'projectile-find-file-other-window)) + (def-projectile-commander-method ?g + "Open project root in vc-dir or magit." + (projectile-vc)) + (def-projectile-commander-method ?i + "Open an IBuffer window showing all buffers in the current project." + (call-interactively 'projectile-ibuffer)) + (def-projectile-commander-method ?o + "Display a project buffer in other window." + (call-interactively 'projectile-display-buffer)) + (def-projectile-commander-method ?O + "Run multi-occur on project buffers." + (projectile-multi-occur)) + (def-projectile-commander-method ?r + "Find recently visited file in project." + (projectile-recentf)) + (def-projectile-commander-method ?s + "Run grep on project." + (call-interactively #'projectile-grep)) + + (projectile-global-mode)) + +(use-package helm-projectile + :after projectile + :config + (bind-keys :map projectile-command-map + ("b" . helm-projectile-switch-to-buffer) + ("d" . helm-projectile-find-dir) + ("f" . helm-projectile-find-file) + ("F" . helm-projectile-find-file-in-known-projects) + ("p" . helm-projectile-switch-project) + ("r" . helm-projectile-recentf)) + (def-projectile-commander-method ?b + "Find project buffer." + (call-interactively 'helm-projectile-switch-to-buffer)) + (def-projectile-commander-method ?d + "Find directory in project." + (helm-projectile-find-dir)) + (def-projectile-commander-method ?f + "Open project file." + (helm-projectile-find-file)) + (def-projectile-commander-method ?p + "Switch project." + (helm-projectile-switch-project))) + +(use-package km-projectile + :defer t + :chords ("gp" . km/projectile-switch-project) + :init + (define-key km/projectile-ctl-x-4-map "v" + #'km/projectile-view-file-other-window) + + (after 'projectile + (bind-keys :map projectile-command-map + ("4 v" . km/projectile-view-file-other-window) + ("." . km/projectile-copy-project-filename-as-kill) + ("e" . km/projectile-restore-thing) + ("k" . km/projectile-kill-buffers) + ("v" . km/projectile-view-file) + ("w" . km/projectile-save-thing)) + + (def-projectile-commander-method ?e + "Restore saved thing." + (km/projectile-restore-thing)) + (def-projectile-commander-method ?k + "Kill all project buffers." + (call-interactively #'km/projectile-kill-buffers)) + (def-projectile-commander-method ?v + "View project file." + (km/projectile-view-file)) + (def-projectile-commander-method ?V + "View project file in other window." + (km/projectile-view-file-other-window)) + (def-projectile-commander-method ?w + "Save thing." + (call-interactively #'km/projectile-save-thing)))) + + +;;; Version control + +(use-package vc + :defer t + :config + (setq vc-follow-symlinks t)) + +(use-package vc-git + :commands vc-git-grep) + +(use-package smerge-mode + :defer t + :config + (setq smerge-diff-switches '("-d" "-b" "-u"))) + +(use-package git-annex + :defer t + :after dired + :config + (setq git-annex-commit nil)) + +(use-package magit + :load-path "~/src/emacs/magit/lisp/" + :defer t + :chords ("jg" . magit-status) + :init + (load "magit-autoloads.el") + (bind-keys :map km/git-map + ("d" . magit-dispatch-popup) + ("l" . magit-log-buffer-file) + ("s" . magit-stage-file)) + (define-prefix-command 'km/magit-map) + (define-prefix-command 'km/magit-wip-map) + (define-key km/git-map "w" 'km/magit-wip-map) + :config + (setq magit-revert-buffers 'silent + magit-revert-buffers-only-for-tracked-files nil + magit-push-always-verify nil + magit-delete-by-moving-to-trash nil + magit-diff-auto-show-delay 0.1 + magit-revision-show-gravatars nil + magit-log-section-arguments nil + magit-log-show-margin nil) + (setq magit-uniquify-buffer-names nil + magit-buffer-name-format "*%M%v: %t*") + (setq magit-no-confirm '(stage-all-changes unstage-all-changes reverse)) + + (setq magit-popup-show-help-echo nil + magit-popup-show-common-commands nil + magit-popup-use-prefix-argument 'default) + (setq magit-branch-arguments + (delete "--track" magit-branch-arguments)) + (setq magit-patch-popup + (plist-put magit-patch-popup :use-prefix 'popup)) + + (remove-hook 'magit-refs-sections-hook #'magit-insert-tags) + + (add-hook 'magit-post-display-buffer-hook + (lambda () + (when (eq major-mode 'magit-status-mode) + (delete-other-windows)))) + + (define-key ctl-x-4-map "g" #'magit-find-file-other-window) + (define-key km/file-map "g" #'magit-find-file) + + (bind-keys :map magit-mode-map + ("o" . magit-push-popup) + ;; Remove `magit-add-change-log-entry-other-window', + ;; which overrides my binding for + ;; `km/zsh-ansi-term-other-window'. + ("P" . magit-submodule-popup) + ("C-x 4 a" . nil)) + + (define-key magit-file-section-map (kbd "C-j") + #'magit-diff-visit-file-worktree) + (define-key magit-hunk-section-map (kbd "C-j") + #'magit-diff-visit-file-worktree) + + (define-key magit-process-mode-map (kbd "C-c C-k") #'magit-process-kill) + + (define-key magit-mode-map "." 'km/magit-map) + (bind-keys :map km/magit-map + ("l" . magit-toggle-buffer-lock)) + + (bind-keys :map km/magit-wip-map + ("a" . magit-wip-after-apply-mode) + ("b" . magit-wip-before-change-mode) + ("c" . magit-wip-commit) + ("f" . magit-wip-commit-buffer-file) + ("l" . magit-wip-log-current) + ("o" . magit-wip-log) + ("s" . magit-wip-after-save-mode) + ("S" . magit-wip-after-save-local-mode)) + + (define-key magit-popup-mode-map (kbd "SPC ") + #'magit-invoke-popup-switch) + (define-key magit-popup-mode-map (kbd "SPC SPC ") + #'magit-invoke-popup-option) + + (magit-define-popup-action 'magit-diff-popup + ?e "Edit options" #'magit-diff-refresh-popup) + + (magit-change-popup-key 'magit-stash-popup :action + ?Z ?s) + + (magit-define-popup-switch 'magit-log-popup + ?p "First parent" "--first-parent") + (magit-define-popup-switch 'magit-log-popup + ?n "No merges" "--no-merges") + (magit-define-popup-switch 'magit-log-popup + ?t "Date order" "--date-order") + + (magit-define-popup-action 'magit-log-popup + ?e "Edit options" 'magit-log-refresh-popup) + (magit-define-popup-action 'magit-log-popup + ?w "Log current WIP" 'magit-wip-log-current) + (magit-define-popup-action 'magit-log-popup + ?W "Log other WIP" 'magit-wip-log) + + (magit-change-popup-key 'magit-branch-popup :action + ?c ?o) + (magit-change-popup-key 'magit-branch-popup :action + ?n ?C) + (magit-change-popup-key 'magit-branch-popup :action + ?m ?R) + (magit-change-popup-key 'magit-branch-popup :action + ?s ?v) + + (defadvice magit-merge-editmsg (around km/magit-merge-editmsg-no-ff activate) + "Set '--no-ff' flag when running `magit-merge-editmsg'." + (let ((args '("--no-ff"))) + ad-do-it))) + +(use-package km-magit + :defer t + :after magit + :init + (bind-keys :map km/git-map + ("." . km/magit-show-commit-at-point) + ("c" . km/magit-copy-commit-summary) + ("e" . km/magit-commit-extend-with-file) + ("f" . km/magit-reset-file) + ("i" . km/magit-insert-staged-file) + ("n" . km/magit-shorten-hash-at-point) + ("p" . km/magit-pin-file) + ("r" . km/magit-find-recently-changed-file) + ("u" . km/magit-auto-commit) + ("v" . km/magit-revfile-reset)) + :config + (bind-keys :map magit-mode-map + ("Q" . km/magit-mode-bury-all-windows) + ("C-w" . km/magit-copy-as-kill)) + + (define-key magit-file-section-map (kbd "C-o") + #'km/magit-diff-visit-file-other-window) + (define-key magit-hunk-section-map (kbd "C-o") + #'km/magit-diff-visit-file-other-window) + + (define-key magit-log-select-mode-map "." + #'km/magit-log-select-guess-fixup-commit) + + (define-key magit-refs-mode-map (kbd "C-c C-t") #'km/magit-refs-toggle-tags) + + (define-key magit-file-section-map [remap magit-visit-thing] + #'km/magit-diff-visit-file) + (define-key magit-hunk-section-map [remap magit-visit-thing] + #'km/magit-diff-visit-file) + + (define-key magit-refs-mode-map (kbd "C-c C-f") + #'km/magit-refs-filter-recent) + + (bind-keys :map km/magit-map + ("c" . km/magit-find-commit-file) + ("g" . km/git-map) + ("f" . km/magit-flip-revs)) + + (define-key km/magit-wip-map "w" #'km/magit-commit-wip-with-file) + + (magit-define-popup-action 'magit-commit-popup + ?u "Auto commit" #'km/magit-auto-commit) + + (magit-define-popup-action 'magit-push-popup + ?a "Push all" #'km/magit-push-all) + (magit-define-popup-action 'magit-push-popup + ?h "Push HEAD" #'km/magit-push-head) + + (magit-define-popup-action 'magit-merge-popup + ?u "Merge upstream" #'km/magit-ff-merge-upstream) + + (magit-define-popup-action 'magit-branch-popup + ?c "Create & checkout from current" + #'km/magit-branch-and-checkout-from-current) + + (magit-define-popup-action 'magit-branch-popup + ?K "Delete previous branch" #'km/magit-delete-previous-branch) + (magit-define-popup-action 'magit-branch-popup + ?m "Checkout master" #'km/magit-checkout-master) + (magit-define-popup-action 'magit-branch-popup + ?n "Checkout recent ref" #'km/magit-checkout-recent-ref) + (magit-define-popup-action 'magit-branch-popup + ?N "Track recent ref" #'km/magit-checkout-track-recent-ref) + (magit-define-popup-action 'magit-branch-popup + ?l "Checkout previous" #'km/magit-checkout-previous-branch) + (magit-define-popup-action 'magit-branch-popup + ?r "Rename branch" #'km/magit-branch-rename) + (magit-define-popup-action 'magit-branch-popup + ?s "Backup current branch" #'km/magit-backup-branch) + (magit-define-popup-action 'magit-branch-popup + ?t "Local tracking" #'km/magit-checkout-local-tracking)) + +(use-package magit-annex + :load-path "~/src/emacs/magit-annex/" + :defer t + :init (require 'magit-annex-autoloads) + :config + (setq magit-annex-unused-open-function #'org-open-file) + (setq magit-annex-all-action-arguments + (delete "--auto" magit-annex-all-action-arguments))) + +(use-package git-commit + :load-path "~/src/emacs/magit/lisp/" + :defer t + :config + (setq git-commit-finish-query-functions nil) + + (add-hook 'git-commit-setup-hook + (lambda () + (add-hook 'with-editor-pre-finish-hook + #'git-commit-save-message nil t))) + (add-hook 'git-commit-setup-hook #'git-commit-turn-on-flyspell)) + +(use-package orgit + :load-path "~/src/emacs/orgit/" + :defer t + :init (require 'orgit-autoloads)) + + +;;; Command interfaces + +(use-package setkey + :bind ("C-c v" . setkey-call)) + +(use-package god-mode + :bind (("C-c d" . god-local-mode) + ("C-x C-1" . delete-other-windows) + ("C-x C-2" . split-window-below) + ("C-x C-3" . split-window-right) + ("C-x C-0" . delete-window)) + :config + (add-hook 'view-mode-hook + (lambda () + (if view-mode (god-local-mode-pause) (god-local-mode-resume)))) + (add-hook 'org-capture-mode-hook + (lambda () (god-local-mode -1))) + + (add-hook 'god-mode-enabled-hook + (lambda () + (when view-mode + (view-mode -1)) + (when (derived-mode-p 'emacs-lisp-mode) + (lispy-mode -1)))) + (add-hook 'god-mode-disabled-hook + (lambda () + (when (derived-mode-p 'emacs-lisp-mode) + (lispy-mode 1)))) + (bind-keys :map god-local-mode-map + ("." . repeat) + ("i" . god-local-mode))) + +(use-package km-god + :defer t + :after god-mode + :config + (add-to-list 'god-exempt-predicates #'km/god-gnus-p) + (add-hook 'god-mode-enabled-hook #'km/god-update-cursor) + (add-hook 'god-mode-disabled-hook #'km/god-update-cursor)) + +(use-package km-hydra + :bind (("C-c n" . km/hydra-outline-mode) + ("C-c w" . hydra-window-map/body)) + :commands (hydra-file-search-map/body + hydra-kmacro/body + hydra-multiple-cursors/body + hydra-org-link-edit/body + hydra-smerge/body) + :init + (bind-keys :map km/editing-map + ("k" . hydra-kmacro/body) + ("o" . hydra-multiple-cursors/body)) + (define-key km/git-map "m" #'hydra-smerge/body) + (define-key km/file-map "s" #'hydra-file-search-map/body) + (after 'org + (define-key km/org-prefix-map "." #'hydra-org-link-edit/body))) + + +;;; Helm + +(use-package helm + :defer t + :config + (setq helm-move-to-line-cycle-in-source t)) + +(use-package helm-config + :defer t + :config + (global-set-key (kbd "C-x c") nil) + (customize-set-value 'helm-command-prefix-key "C-c h")) + +(use-package helm-buffers + :defer t + :chords ("jt" . helm-mini)) + +(use-package helm-files + :defer t + :chords ("jc" . helm-find-files) + :config + (setq helm-ff-newfile-prompt-p nil + helm-ff-file-name-history-use-recentf t + helm-ff-skip-boring-files t)) + +(use-package km-helm + :defer t + :after helm + :init + (after 'helm-files + (bind-keys :map helm-find-files-map + ("C-c x" . km/helm-ff-org-open-file) + ("C-c C-o" . km/helm-display-file))) + (after 'helm-locate + (bind-keys :map helm-generic-files-map + ("C-c x" . km/helm-ff-org-open-file) + ("C-c C-o" . km/helm-display-file))) + (after 'helm-buffers + (define-key helm-buffer-map (kbd "C-c C-o") #'km/helm-display-buffer))) + +(use-package helm-mode + :diminish helm-mode + :after helm + :config + (helm-mode 1)) + +(use-package helm-command + :defer t + :chords ("kx" . helm-M-x)) + +(use-package helm-swoop + :defer t + :init (define-key search-map "k" #'helm-swoop)) + +(use-package helm-apropos + :bind ("C-h a" . helm-apropos)) + +(use-package helm-imenu + :bind ("C-c l" . helm-imenu)) + +(use-package helm-man + :defer t + :init (define-key km/external-map "m" #'helm-man-woman)) + + +;;; Expansion + +(use-package abbrev + :commands abbrev-mode + :diminish (abbrev-mode . "Ab") + :init + (add-hook 'text-mode-hook #'abbrev-mode) + (add-hook 'prog-mode-hook #'abbrev-mode) + :config + (define-abbrev-table 'typo-abbrev-table nil) + (abbrev-table-put global-abbrev-table :parents + (cons typo-abbrev-table + (abbrev-table-get global-abbrev-table :parents)))) + +(use-package km-abbrev + :defer t + :init + (after 'abbrev + (bind-keys :map abbrev-map + ("c" . km/abbrev-add-case-global) + ("iu" . km/abbrev-inverse-add-uppercase-global)))) + +(use-package hippie-exp + :bind ("M-/" . hippie-expand) + :config + (setq hippie-expand-try-functions-list '(try-complete-file-name-partially + try-complete-file-name + try-expand-all-abbrevs + try-expand-dabbrev + try-expand-dabbrev-all-buffers + try-expand-dabbrev-from-kill + try-complete-lisp-symbol-partially + try-complete-lisp-symbol))) + +(use-package yasnippet + :diminish yas-minor-mode + :commands km/yas-dummy + :init + (global-set-key (kbd "C-c i") #'km/yas-dummy) + :config + (defun km/yas-dummy () + (interactive)) + (global-set-key (kbd "C-c i") nil) + (setq yas-fallback-behavior nil) + + (defun km/yas-with-comment (str) + (concat comment-start + (unless (s-ends-with? " " comment-start) " ") + str comment-end)) + + (defvar km/personal-snippets + (file-name-as-directory (expand-file-name "psnippets" + user-emacs-directory))) + (when (file-exists-p km/personal-snippets) + (add-to-list 'yas-snippet-dirs km/personal-snippets)) + + (bind-keys :map yas-minor-mode-map + ("C-c i" . yas-expand) + ;; Remove commands with 'C-c &' prefix, which conflicts + ;; with `org-mark-ring-goto' binding' + ("C-c &" . nil) + ("" . nil) + ("TAB" . nil)) + (yas-global-mode)) + + +;;; Dired + +(use-package dired + :defer t + :init + (define-prefix-command 'km/dired-prefix-map) + :config + (require 'dired-x) + ;; .git is present as part of `dired-omit-extensions', but this + ;; seems to only be taken into account if a non-exension part + ;; exists. + (setq dired-omit-files + (concat dired-omit-files + "\\|^\\.git$\\|^\\.gitignore$" + "\\|^__pycache__$\\|^\\.snakemake$")) + + (setq dired-omit-extensions + (append dired-omit-extensions + (list ".aux" ".fdb_latexmk" ".fls" + ".log" ".nav" ".out" ".snm"))) + + (setq-default dired-omit-mode t) + + (setq dired-dwim-target t + dired-listing-switches "-alht") + + (setq dired-guess-shell-alist-user '(("\\.pdf\\'" "zathura"))) + + (setq dired-recursive-copies t + dired-recursive-deletes t) + + (put 'dired-find-alternate-file 'disabled nil) + + (add-hook 'dired-mode-hook #'dired-hide-details-mode) + + (define-key dired-mode-map "c" #'dired-do-copy) + + (define-key dired-mode-map (kbd "C-c m") 'km/dired-prefix-map)) + +(use-package km-dired + :bind ("C-x C-d" . km/dired-switch-to-buffer) + :init + (define-prefix-command 'km/dired-copy-filename-map) + (after 'projectile + (define-key km/dired-copy-filename-map "p" + #'km/dired-copy-project-filename-as-kill)) + (define-key km/dired-copy-filename-map "o" + #'km/dired-copy-relative-filename-as-kill) + (define-key km/dired-copy-filename-map "w" + #'dired-copy-filename-as-kill) + + (after 'dired + ;; This overrides the default binding for `dired-copy-filename-as-kill'. + (define-key dired-mode-map "w" #'km/dired-copy-filename-map) + (bind-keys :map dired-mode-map + ("w" . km/dired-copy-filename-map) + ("C" . km/dired-copy-and-edit) + ("V" . km/dired-view-file-other-window))) + + (define-key ctl-x-4-map "D" #'km/dired-switch-to-buffer-other-window) + + (after 'org + ;; This overrides `dired-find-file', which is also bound to "f". + (define-key dired-mode-map "e" #'km/org-open-dired-marked-files))) + +(use-package dired-narrow + :defer t + :init + (after 'dired + (define-prefix-command 'km/dired-narrow-prefix-map) + (bind-keys :map km/dired-narrow-prefix-map + ("f" . dired-narrow-fuzzy) + ("n" . dired-narrow) + ("r" . dired-narrow-regexp)) + (define-key km/dired-prefix-map "n" 'km/dired-narrow-prefix-map) + (define-key dired-mode-map "/" #'dired-narrow))) + +(use-package dired-subtree + :defer t + :init + (after 'dired + (define-prefix-command 'km/dired-subtree-prefix-map) + (bind-keys :map km/dired-subtree-prefix-map + ("@" . dired-subtree-mark-subtree) + ("." . dired-subtree-unmark-subtree) + ("<" . dired-subtree-beginning) + (">" . dired-subtree-end) + ("g" . dired-subtree-revert) + ("d" . dired-subtree-down) + ("i" . dired-subtree-insert) + ("n" . dired-subtree-next-sibling) + ("p" . dired-subtree-previous-sibling) + ("r" . dired-subtree-remove) + ("s" . dired-subtree-narrow) + ("u" . dired-subtree-up)) + (define-key km/dired-prefix-map "s" 'km/dired-subtree-prefix-map))) + + +;;; Compilation and shells + +(setq shell-file-name "/bin/bash") +(setq shell-command-switch "-c") + +(bind-keys :map km/external-map + ("r" . shell-command-on-region) + ("s" . shell-command) + ("S" . shell)) + +(use-package grep + :defer t + :config + (defun km/grep-hide-header () + (narrow-to-region + (save-excursion (goto-char (point-min)) + (line-beginning-position 5)) + (point-max))) + + (add-hook 'grep-setup-hook #'km/grep-hide-header) + (add-hook 'grep-mode-hook #'toggle-truncate-lines)) + +(use-package compile + :defer t + :init + (bind-keys :map km/compile-map + ("c" . compile) + ("g" . recompile)) + :config + (defadvice compile (around prevent-duplicate-compilation-windows activate) + "Pop to compilation buffer only if it isn't visible. +This is useful for using multiple frames (e.g., with a two +monitor setup)." + (if (get-buffer-window (km/compilation-name-by-directory) + 'visible) + (save-window-excursion ad-do-it) + ad-do-it)) + + (defadvice recompile (around prevent-window-on-compilation activate) + "Prevent recompiling from spawning new windows." + (save-window-excursion ad-do-it))) + +(use-package km-compile + :after compile + :chords ("hv" . km/compilation-recompile) + :init + (bind-keys :map km/compile-map + ("h" . km/compile-in-home-dir) + ("o" . km/compilation-display-buffer)) + :config + (setq compilation-buffer-name-function #'km/compilation-name-by-directory)) + +(use-package km-shell + :chords ("kz" . km/zsh-toggle-ansi-term-home) + :init + (define-key ctl-x-4-map "a" #'km/zsh-ansi-term-other-window) + (bind-keys :map km/external-map + ("a" . km/zsh-ansi-term) + ("t" . km/open-external-terminal))) + + +;;; Other external programs + +(use-package browse-url + :defer t + :init + (define-key km/external-map "b" #'browse-url) + :config + (setq browse-url-browser-function 'browse-url-generic + browse-url-generic-program "firefox")) + +(use-package webjump + :defer t + :config + (setq webjump-sites + '(("Arch User Repository" . + [simple-query "https://aur.archlinux.org" + "https://aur.archlinux.org/packages/?K=" ""]) + ("DuckDuckGo" . + [simple-query "https://duckduckgo.com" + "https://duckduckgo.com/?q=" ""]) + ("Emacs Wiki" . + [simple-query "www.emacswiki.org" + "www.emacswiki.org/cgi-bin/wiki/" ""]) + ("GitHub" . "https://github.com") + ("GitHub search" . + [simple-query "https://github.com" + "https://github.com/search?q=" ""]) + ("Google" . + [simple-query "www.google.com" + "www.google.com/search?q=" ""]) + ("Google Scholar" . + [simple-query "http://scholar.google.com" + "http://scholar.google.com/scholar?&q=" ""]) + ("Wikipedia" . + [simple-query "wikipedia.org" + "wikipedia.org/wiki/" ""])))) + +(use-package km-webjump + :after webjump + :defer t + :init + (define-key km/external-map "j" #'km/webjump)) + +(use-package select + :defer t + :config + (setq x-select-enable-clipboard t + x-select-enable-primary t)) + +(use-package man + :defer t + :config + (setq Man-notify-method 'aggressive)) + +(use-package mailcap + :config + (mailcap-parse-mailcaps) + (pcase-dolist (`(_ . ,info) + (cdr (assoc-string "application" mailcap-mime-data))) + ;; Instead of deleting doc-view-mode entry, just make its test + ;; always fail. + (when (eq (cdr (assq 'viewer info)) 'doc-view-mode) + (setf (cdr (assq 'test info)) (lambda (&rest _) nil))))) + +(use-package diff + :defer t + :config + (setq diff-command "/bin/diff" + diff-switches "-u")) + +(use-package ediff + :defer t + :init + (define-key km/external-map "e" #'ediff) + :config + (setq ediff-window-setup-function #'ediff-setup-windows-plain)) + +(use-package km-diff + :defer t + :init + (bind-keys :map km/external-map + ("o" . km/ediff-with-other-window) + ("d" . km/diff)) + :config + (after 'diff-mode-map + (define-key diff-mode-map (kbd "C-c C-g") #'km/revert-buffer-and-view))) + + +;;; Text modes + +(add-hook 'text-mode-hook #'turn-on-auto-fill) + +(use-package footnote + :defer t + :config + (setq footnote-section-tag "")) + +(use-package ispell + :defer t + :init + (define-key km/external-map "i" #'ispell-buffer) + (define-key km/editing-map "w" #'ispell-word) + :config + (setq ispell-program-name "aspell")) + +(use-package flyspell + :defer t + :diminish (flyspell-mode . "Fy") + :config + (setq flyspell-auto-correct-binding (kbd "C-c e ;")) + (define-key flyspell-mode-map (kbd "C-.") nil)) + +(use-package tex-site + :mode ("\\.[tT]e[xX]\\'" . TeX-latex-mode) + :config + (setq font-latex-fontify-sectioning 'color) + (setq TeX-electric-math '("$" . "$")) + + (after 'latex + (put 'LaTeX-narrow-to-environment 'disabled nil) + (add-hook 'LaTeX-mode-hook #'turn-on-reftex) + (add-hook 'LaTeX-mode-hook #'flyspell-mode))) + +(use-package km-tex + :defer t + :after latex + :config + (add-hook 'LaTeX-mode-hook + (lambda () + (setq imenu-create-index-function + #'km/latex-imenu-create-index-function)))) + +(use-package reftex + :diminish (reftex-mode . "Rf") + :defer t + :config + (setq reftex-default-bibliography '("refs.bib"))) + +(use-package bibtex + :defer t + :config + ;; Make cite key have form . + (setq bibtex-autokey-titlewords 1 + bibtex-autokey-titleword-ignore '("A" "An" "On" "The" "[0-9].*") + bibtex-autokey-titleword-length nil + bibtex-autokey-titlewords-stretch 0 + bibtex-autokey-year-length 4 + bibtex-autokey-year-title-separator "") + (setq bibtex-align-at-equal-sign t) ; Used by `bibtex-fill-entry'. + (setq bibtex-entry-format + (append '(realign whitespace last-comma delimiters sort-fields) + bibtex-entry-format))) + +(use-package km-bib + :after bibtex + :config + (dolist (h '(km/bibtex-delete-article-fields + km/bibtex-downcase-author-and + km/bibtex-downcase-entry + km/bibtex-downcase-keys + km/bibtex-pages-use-double-hyphen + km/bibtex-use-title-case + km/bibtex-remove-doi-leader + km/bibtex-remove-entry-space + km/bibtex-set-coding-system + km/bibtex-single-space-author-list)) + (add-hook 'bibtex-clean-entry-hook h))) + + +;;; Language modes + +(add-to-list 'auto-mode-alist '("\\.zsh\\'" . shell-script-mode)) +(add-to-list 'auto-mode-alist '("\\.*rc\\'" . conf-unix-mode)) + +(add-hook 'after-save-hook + #'executable-make-buffer-file-executable-if-script-p) + +(global-set-key (kbd "C-c x") #'eval-expression) + +(use-package haskell-mode + :defer t + :config + (setq haskell-process-show-debug-tips nil) + (add-hook 'haskell-mode-hook #'turn-on-haskell-indentation) + (add-hook 'haskell-mode-hook #'interactive-haskell-mode) + (add-hook 'haskell-mode-hook #'turn-on-haskell-doc) + (bind-keys :map haskell-mode-map + ("C-x C-d" . nil) + ("C-c C-z" . haskell-interactive-switch) + ("C-c C-l" . haskell-process-load-file) + ("C-c C-b" . haskell-interactive-switch) + ("C-c C-t" . haskell-process-do-type) + ("C-c C-i" . haskell-process-do-info) + ("C-c M-." . nil) + ("C-c C-d" . nil))) + +(use-package elisp-mode + :defer t + :config + (defun km/elisp-outline-level () + (and (looking-at (concat "^" outline-regexp)) + (- (match-end 0) (match-beginning 0) 3))) + + (defun km/elisp-set-outline-vars () + (setq outline-regexp ";;;;* ") + (setq outline-level #'km/elisp-outline-level)) + + (add-hook 'emacs-lisp-mode-hook #'km/elisp-set-outline-vars)) + +(use-package find-function + :bind (("C-h ;" . find-function) + ("C-h 4 ;" . find-function-other-window))) + +(use-package paredit + :defer t + :diminish (paredit-mode . "Pe")) + +(use-package lispy + :defer t + :init + (add-hook 'emacs-lisp-mode-hook #'lispy-mode) + :config + (setq lispy-no-permanent-semantic t) + (add-hook 'lispy-mode-hook #'km/elisp-set-outline-vars) + ;; This is ugly, but I haven't found another way to stop + ;; `imenu-create-index-function' from being set to + ;; `semantic-create-imenu-index'. Trying to set it in + ;; `emacs-lisp-mode-hook' or `lispy-mode-hook' doesn't work. + (defalias 'semantic-create-imenu-index 'imenu-default-create-index-function)) + +(use-package python + :defer t + :init + (add-to-list 'interpreter-mode-alist '("python2" . python-mode)) + (add-to-list 'interpreter-mode-alist '("python3" . python-mode)) + (define-prefix-command 'km/python-prefix-map) + :config + (setq python-fill-docstring-style 'pep-257-nn + python-indent-guess-indent-offset nil) + (setq python-shell-interpreter "ipython" + python-shell-prompt-detect-enabled nil) + + (defun km/python-outline-level () + (and (looking-at (concat "^" outline-regexp)) + (- (match-end 0) (match-beginning 0) 3))) + + (defun km/python-set-local-vars () + (setq outline-regexp "####* ") + (setq outline-level #'km/python-outline-level) + ;; Stop semantic from taking over imenu. + (setq imenu-create-index-function #'python-imenu-create-index) + (set (make-local-variable 'compile-command) "py.test")) + (add-hook 'python-mode-hook #'km/python-set-local-vars) + + (bind-keys :map python-mode-map + ("C-c C-b" . python-shell-send-buffer) + ("C-c C-f" . python-shell-send-defun) + ("C-M-x" . python-eldoc-at-point))) + +(use-package pydoc + :load-path "~/src/emacs/pydoc/" + :defer t + :config + (setq pydoc-make-method-buttons nil) + ;; Don't shadow my `ace-link' binding. + (define-key pydoc-mode-map "o" #'ace-link-help)) + +(use-package km-python + :after python + :bind ("C-h y" . km/pydoc) + :init + (bind-keys :map km/python-prefix-map + ("c" . km/python-copy-last-shell-line-as-comment) + ("t" . km/find-python-test-file-other-window)) + :config + (bind-keys :map python-mode-map + ("C-c C-." . km/python-shell-send-buffer-up-to-line) + ("C-c C-c" . km/python-shell-send-function-or-paragraph-and-step) + ("C-c C-d" . km/python-shell-send-set-string) + ("C-c m" . km/python-prefix-map)) + + (add-hook 'python-mode-hook + (lambda () + (add-hook + 'post-self-insert-hook + #'km/python-indent-post-self-insert-function 'append 'local))) + + (add-hook 'pydoc-after-finish-hook #'km/pydoc-store-name) + (when (file-exists-p km/pydoc-names-file) + (km/pydoc-read-names-file km/pydoc-names-file))) + +(use-package snakemake-mode + :load-path "~/src/emacs/snakemake-mode/" + :defer t + :init + (require 'snakemake-mode-autoloads) + (autoload 'snakemake-compile-command "snakemake-mode") + (setq snakemake-compile-command-options '("-p")) + :config + (defun km/snakemake-set-local-vars () + (set (make-local-variable 'compile-command) + (snakemake-compile-command)) + (set (make-local-variable 'imenu-create-index-function) + #'snakemake-imenu-create-index)) + + ;; Although `compile-command' and `imenu-create-index-function' are + ;; set when snakemake-mode is derived from Python mode, I need to + ;; define them again here because I have a Python mode hook + ;; overrides the Python versions. + (add-hook 'snakemake-mode-hook #'km/snakemake-set-local-vars)) + +(use-package km-snakemake + :defer t + :init + (bind-keys :map km/compile-map + ("b" . km/snakemake-compile-project-rule) + ("p" . km/snakemake-compile-project-file)) + (after 'dired + (define-key dired-mode-map "b" #'km/snakemake-compile-project-file))) + +(use-package ess-site + :mode ("\\.[rR]\\'" . R-mode) + :config + (setq ess-smart-S-assign-key ";") + (ess-toggle-S-assign nil) + (ess-toggle-S-assign nil) + (setq ess-use-ido nil) + + (define-abbrev-table 'ess-mode-abbrev-table + '(("true" "TRUE") + ("false" "FALSE")) + :system t) + (dolist (hook '(ess-mode-hook inferior-ess-mode-hook)) + (add-hook hook (lambda () + (setq local-abbrev-table ess-mode-abbrev-table))) + (add-hook hook #'abbrev-mode))) + +(use-package km-ess + :defer t + :config + (bind-keys :map ess-mode-map + ("C-c C-." . km/ess-eval-buffer-up-to-line) + ("|" . km/ess-insert-dplyr-pipe)) + (after 'ess-inf + (define-key inferior-ess-mode-map "|" #'km/ess-insert-dplyr-pipe))) + + +;;; Mail + +(use-package notmuch + :defer t + :init (define-key km/mail-map "n" #'notmuch) + :config + (setq notmuch-fcc-dirs nil + notmuch-search-oldest-first nil) + (add-to-list 'notmuch-saved-searches + '(:name "today" :query "date:today.." :key ".")) + (define-prefix-command 'km/notmuch-show-prefix-map) + (define-key notmuch-show-mode-map (kbd "C-c m") 'km/notmuch-show-prefix-map) + + (define-key km/notmuch-show-prefix-map "i" + #'km/notmuch-show-copy-message-id-as-kill)) + +(use-package message + :defer t + :config + (setq message-send-mail-function 'message-send-mail-with-sendmail + message-sendmail-envelope-from 'header + message-kill-buffer-on-exit t) + (add-hook 'message-mode-hook #'flyspell-mode)) + +(use-package mml + :defer t + :diminish (mml-mode . "ML")) + +(use-package mm-decode + :defer t + :config + (setq mm-discouraged-alternatives '("text/html" "text/richtext"))) + +(use-package gnus + :defer t + :init + (bind-keys :map km/mail-map + ("g" . gnus) + ("p" . gnus-plugged) + ("u" . gnus-unplugged)) + :config + (setq gnus-home-directory "~/.gnus.d/" + gnus-directory gnus-home-directory + gnus-article-save-directory (expand-file-name "saved/" gnus-directory) + gnus-kill-files-directory (expand-file-name "scores/" gnus-directory)) + (setq gnus-startup-file (expand-file-name "newsrc" gnus-home-directory) + gnus-init-file (expand-file-name "gnus" gnus-home-directory) + gnus-save-newsrc-file nil + gnus-read-newsrc-file nil + gnus-inhibit-startup-message t) + (setq gnus-summary-line-format "%U%R %&user-date;%-20= %-15,15f %B %S \n") + (setq gnus-gcc-mark-as-read t + gnus-visible-headers '("^From" "^Subject" "^Date" "^To" "^Cc" "^User-Agent") + gnus-confirm-mail-reply-to-news t) + (setq gnus-agent-go-online t + gnus-agent-synchronize-flags t) + (setq gnus-inhibit-images t) + (setq gnus-interactive-exit nil) + + (setq gnus-topic-display-empty-topics nil + gnus-group-line-format "%M\%S\%p\%P\%5y:%B%(%G%)\n" + gnus-group-list-inactive-groups nil) + + (setq gnus-group-use-permanent-levels t) + + (setq gnus-auto-select-next 'quietly) + + (setq gnus-thread-hide-subtree t + gnus-thread-sort-functions '(gnus-thread-sort-by-most-recent-number)) + (define-key gnus-group-mode-map "e" #'gnus-group-select-group)) + +(use-package gnus-sum + :defer t + :init + (define-prefix-command 'km/gnus-summary-prefix-map) + :config + (setq gnus-sum-thread-tree-indent " " + gnus-sum-thread-tree-root "." + gnus-sum-thread-tree-false-root "o " + gnus-sum-thread-tree-single-indent "" + gnus-sum-thread-tree-leaf-with-other "+-> " + gnus-sum-thread-tree-vertical "| " + gnus-sum-thread-tree-single-leaf "`-> ") + (bind-keys :map gnus-summary-mode-map + ("e" . gnus-summary-scroll-up) + ("v" . org-capture) + (";" . gnus-summary-universal-argument)) + (define-key gnus-summary-mode-map (kbd "C-c m") + 'km/gnus-summary-prefix-map)) + +(use-package gnus-art + :defer t + :init + (define-prefix-command 'km/gnus-article-prefix-map) + :config + (define-key gnus-article-mode-map "v" #'org-capture) + (define-key gnus-article-mode-map (kbd "C-c m") + 'km/gnus-article-prefix-map)) + +(use-package shr + :defer t + :config + (bind-keys :map shr-map + ;; Allow `km/ace-link-widget' binding to work even when + ;; on shr widget. + ("o" . nil) + ("O" . shr-save-contents) + ("v" . nil))) + +(use-package sendmail + :defer t + :config + (setq sendmail-program "/usr/bin/msmtp")) + +(use-package km-mail + :defer t + :after gnus + :config + (add-hook 'message-send-hook #'km/message-confirm-sender) + (add-hook 'kill-emacs-hook #'km/gnus-grace-exit-before-kill-emacs) + + (setq gnus-group-sort-function '(gnus-group-sort-by-alphabet + km/gnus-group-sort-by-topic + gnus-group-sort-by-level)) -(require 'init-org) -(require 'init-helm) + (after 'gnus-sum + (bind-keys :map gnus-summary-mode-map + ("c" . km/gnus-summary-catchup) + ("l" . km/gnus-copy-message-link) + ("o" . km/ace-link-widget))) + (after 'gnus-art + (bind-keys :map gnus-article-mode-map + ("C-c l" . km/gnus-follow-last-message-link) + ("e" . km/shr-browse-url-and-goto-next) + ("o" . km/ace-link-widget))) -(require 'init-files) -(require 'init-buffers) -(require 'init-framewin) -(require 'init-ace) -(require 'init-view) + (bind-keys :map km/gnus-summary-prefix-map + ("p" . km/gnus-open-github-patch) + ("l" . km/gnus-copy-gmane-link-as-kill)) + (bind-keys :map km/gnus-article-prefix-map + ("p" . km/gnus-open-github-patch) + ("l" . km/gnus-copy-gmane-link-as-kill))) -(require 'init-editing) -(require 'init-outline) -(require 'init-god) + +;;; Miscellaneous configuration -(require 'init-elisp) -(require 'init-haskell) -(require 'init-python) -(require 'init-ess) +(setq echo-keystrokes 0.1 + use-dialog-box nil + visible-bell t + enable-recursive-minibuffers t) -(require 'init-tex) -(require 'init-bib) -(require 'init-bog) +(setq bookmark-save-flag nil) -(require 'init-dired) -(require 'init-git) -(require 'init-projectile) -(require 'init-snakemake) +;; This is intentionally not loaded. +(setq custom-file "~/.emacs.d/.custom.el") -(require 'init-external) +(setq default-input-method "TeX") -(require 'init-yas) +(defalias 'yes-or-no-p 'y-or-n-p) -(when (file-exists-p (expand-file-name "init-untracked.el" km/init-lisp-dir)) - (require 'init-untracked)) +(when (file-exists-p (expand-file-name "km-untracked.el" km/init-lisp-dir)) + (require 'km-untracked)) -(require 'init-mail) -(require 'init-server) +(use-package server + :config + (setq server-use-tcp t) + (unless (server-running-p) + (server-start)) + (let ((server (daemonp))) + (cond + ((string= server "default") + ;; Remove all mail map bindings except notmuch. + (global-set-key (kbd "C-x m") nil) + (global-set-key (kbd "C-x m n") #'notmuch) + (after 'km-python + (add-hook 'kill-emacs-hook #'km/pydoc-save-names-file)) + (setq save-abbrevs 'silently + bookmark-save-flag 1)) + ((string= server "mail") + (setq mode-line-misc-info + (cons (propertize " [Mail] " 'face 'font-lock-doc-face) + mode-line-misc-info)) + (key-chord-define-global "jg" 'km/mail-map) + (setq recentf-save-file "~/.emacs.d/cache/recentf-mail") + (setq save-abbrevs nil))))) ;;; init.el ends here diff --git a/lisp/init-abbrev.el b/lisp/init-abbrev.el deleted file mode 100644 index f1030e3..0000000 --- a/lisp/init-abbrev.el +++ /dev/null @@ -1,71 +0,0 @@ -;;; init-abbrev.el --- Abbrev mode configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(add-hook 'text-mode-hook 'abbrev-mode) -(add-hook 'prog-mode-hook 'abbrev-mode) - -(define-abbrev-table 'typo-abbrev-table nil) -(abbrev-table-put global-abbrev-table :parents - (cons typo-abbrev-table - (abbrev-table-get global-abbrev-table :parents))) - -(defun km/abbrev-add-case-global () - "Define lower abbreviation for the word before point. -Like `add-global-abbrev', but always make the abbreviation the -lower case variant of the word before point." - (interactive) - ;; Modified from `add-abbrev'. - (let* ((table global-abbrev-table) - (exp (buffer-substring-no-properties - (point) - (save-excursion (forward-word -1) (point)))) - (name (downcase exp))) - (when (or (not (abbrev-expansion name table)) - (y-or-n-p (format "%s expands to \"%s\"; redefine? " - name (abbrev-expansion name table)))) - (define-abbrev table name exp)))) - -(defun km/abbrev-inverse-add-uppercase-global () - "Define uppercase expansion for the word before point. -Like `inverse-add-global-abbrev', but always use the lower case -version of the word before point as the abbreviation and the -upper case version as the expansion." - (interactive) - ;; Modified from `inverse-add-abbrev'. - (let* ((table global-abbrev-table) - (end (point)) - (start (save-excursion (forward-word -1) (point))) - (name (downcase (buffer-substring-no-properties start end))) - (exp (upcase name))) - (when (or (not (abbrev-expansion name table)) - (y-or-n-p (format "%s expands to \"%s\"; redefine? " - name (abbrev-expansion name table)))) - (define-abbrev table name exp) - (save-excursion - (goto-char end) - (expand-abbrev))))) - -(define-key abbrev-map "c" 'km/abbrev-add-case-global) -(define-key abbrev-map "iu" 'km/abbrev-inverse-add-uppercase-global) - -(provide 'init-abbrev) -;;; init-abbrev.el ends here diff --git a/lisp/init-ace.el b/lisp/init-ace.el deleted file mode 100644 index 5cc588b..0000000 --- a/lisp/init-ace.el +++ /dev/null @@ -1,113 +0,0 @@ -;;; init-ace.el --- Configuration for AceJump-inspired friends - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -;;; Avy - -(require 'avy) - -(key-chord-define-global "jf" 'avy-goto-subword-1) -(define-key isearch-mode-map (kbd "C-'") 'avy-isearch) - -;;; Ace Link - -(defun km/ace-link-dired () - "Ace jump to files in dired buffers." - (interactive) - (avy-with km/ace-link-dired - (setq avy-action - (lambda (pt) - (goto-char pt) - (org-open-file (dired-get-filename)))) - (avy--process - (km/ali--dired-collect-references) - #'avy--overlay-post))) - -(defun km/ali--dired-collect-references () - (let ((end (window-end)) - points) - (save-excursion - (goto-char (window-start)) - (while (< (point) end) - (--when-let (dired-next-line 1) - (push it points))) - (nreverse points)))) - -(defun km/ace-link-widget () - "Press a widget that is visible in the current buffer. -This can be used in place of `ace-link-gnus' and has the -advantage of working for gwene buffers in addition to normal mail -buffers because it doesn't rely on the 'gnus-string' text -property." - (interactive) - (when (eq major-mode 'gnus-summary-mode) - (gnus-summary-widget-forward 1)) - (avy-with km/ace-link-widget - (setq avy-action - (lambda (pt) - (goto-char (1+ pt)) - (widget-button-press (point)))) - (avy--process - (km/ali--widget-collect-references) - #'avy--overlay-post))) - -(defun km/ali--widget-collect-references () - "Collect the positions of visible widgets in buffer." - (require 'wid-edit) - (let (candidates pt) - (save-excursion - (save-restriction - (narrow-to-region - (window-start) - (window-end)) - (goto-char (point-min)) - (setq pt (point)) - (while (progn (ignore-errors (widget-forward 1)) - (> (point) pt)) - (setq pt (point)) - (push (point) candidates)) - (nreverse candidates))))) - -(ace-link-setup-default) -(after 'org - (define-key org-mode-map (kbd "C-c m o") 'ace-link-org)) -(after 'dired - ;; This overrides the binding for `dired-find-file-other-window'. - (define-key dired-mode-map "o" 'km/ace-link-dired) - (define-key dired-mode-map "r" 'dired-find-file-other-window)) - -;;; Ace Window - -(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l) - aw-scope 'frame) - -(defun km/ace-window (arg) - "Run `ace-window', swapping single and double C-u's." - (interactive "p") - (cl-case arg - (4 (setq arg 16)) - (16 (setq arg 4))) - (ace-window arg)) - -(key-chord-define-global "jw" 'km/ace-window) - -(provide 'init-ace) -;;; init-ace.el ends here diff --git a/lisp/init-appearance.el b/lisp/init-appearance.el deleted file mode 100644 index 72c575d..0000000 --- a/lisp/init-appearance.el +++ /dev/null @@ -1,99 +0,0 @@ -;;; init-appearance.el --- Appearance-related configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(setq inhibit-splash-screen t - initial-scratch-message nil) - -(scroll-bar-mode -1) -(tool-bar-mode -1) -(menu-bar-mode -1) -(blink-cursor-mode -1) - -(line-number-mode) -(column-number-mode) -(size-indication-mode) - -;;; Theme - -(load-theme 'stekene-light t) - -(custom-theme-set-faces - 'stekene-light - '(default ((t (:background "white")))) - '(outline-1 ((t (:inherit font-lock-function-name-face)))) - '(outline-2 ((t (:inherit font-lock-doc-face)))) - '(outline-3 ((t (:inherit font-lock-constant-face)))) - '(org-todo ((t (:foreground "#ba2727" :background "gray85")))) - '(org-done ((t (:foreground "#557755" :background "gray85")))) - '(org-date ((t (:inherit font-lock-builtin-face)))) - '(org-upcoming-deadline ((t (:inherit font-lock-constant-face)))) - '(org-warning ((t (:inherit font-lock-string-face)))) - '(org-agenda-date-today ((t (:weight semi-bold)))) - '(org-agenda-date-weekend ((t (:inherit org-agenda-date)))) - '(org-agenda-structure ((t (:foreground "gray40")))) - '(org-agenda-date ((t (:inherit default)))) - '(org-scheduled ((t (:inherit org-scheduled-today)))) - '(org-agenda-calendar-event ((t (:inherit font-lock-function-name-face)))) - '(gnus-group-mail-1-empty ((t (:inherit font-lock-string-face)))) - '(gnus-group-mail-2-empty ((t (:inherit font-lock-function-name-face)))) - '(gnus-group-mail-3-empty ((t (:inherit font-lock-type-face)))) - '(gnus-group-mail-1 ((t (:inherit gnus-group-mail-1-empty :weight semi-bold)))) - '(gnus-group-mail-2 ((t (:inherit gnus-group-mail-2-empty :weight semi-bold)))) - '(gnus-group-mail-3 ((t (:inherit gnus-group-mail-3-empty :weight semi-bold)))) - '(gnus-subject ((t (:inherit font-lock-doc-face)))) - '(gnus-button ((t (:inherit default)))) - '(gnus-cite-1 ((t (:inherit font-lock-function-name-face)))) - '(gnus-cite-2 ((t (:inherit font-lock-string-face)))) - '(gnus-header-name ((t (:inherit font-lock-constant-face)))) - '(gnus-header-content ((t (:inherit default)))) - '(magit-section-highlight ((t (:background "white")))) - '(magit-diff-hunk-heading-highlight ((t (:foreground "gray30" :background "grey80")))) - '(magit-diff-hunk-heading ((t (:foreground "gray30" :background "grey90")))) - '(magit-diff-added-highlight ((t (:foreground "DarkGreen" :background "grey98")))) - '(magit-diff-added ((t (:foreground "DarkGreen" :background "grey98")))) - '(magit-diff-removed-highlight ((t (:foreground "#aa2222" :background "grey98")))) - '(magit-diff-removed ((t (:foreground "#aa2222" :background "grey98")))) - '(magit-diff-context-highlight ((t (:foreground "grey50" :background "grey98")))) - '(magit-diff-context ((t (:foreground "grey50" :background "grey98")))) - '(diff-added ((t (:foreground "DarkGreen" :background "grey98")))) - '(diff-removed ((t (:foreground "#aa2222" :background "grey98")))) - '(diff-context ((t (:foreground "grey50" :background "grey98")))) - '(term-color-black ((t (:inherit default)))) - '(term-color-red ((t (:inherit font-lock-string-face)))) - '(term-color-green ((t (:foreground "#3cb371")))) - '(term-color-yellow ((t (:inherit font-lock-doc-face)))) - '(term-color-blue ((t (:inherit font-lock-function-name-face)))) - '(term-color-magenta ((t (:foreground "#8b008b")))) - '(term-color-cyan ((t (:foreground "#008b8b")))) - ;; Darken a bit (was gray40). - '(font-lock-keyword-face ((t (:foreground "gray33"))))) - -(custom-set-faces - '(Info-quoted ((t (:inherit default)))) - '(git-timemachine-minibuffer-detail-face ((t (:inherit font-lock-doc-face)))) - '(dired-subtree-depth-1-face ((t (:background "gray80")))) - '(dired-subtree-depth-2-face ((t (:background "gray90")))) - '(dired-subtree-depth-3-face ((t (:background "gray95")))) - '(whitespace-indentation ((t (:background "gray90"))))) - -(provide 'init-appearance) -;;; init-appearance.el ends here diff --git a/lisp/init-bib.el b/lisp/init-bib.el deleted file mode 100644 index b998c6a..0000000 --- a/lisp/init-bib.el +++ /dev/null @@ -1,225 +0,0 @@ -;;; init-bib.el --- Bibliography configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -;; Make cite key have form . -(setq bibtex-autokey-titlewords 1 - bibtex-autokey-titleword-ignore '("A" "An" "On" "The" "[0-9].*") - bibtex-autokey-titleword-length nil - bibtex-autokey-titlewords-stretch 0 - bibtex-autokey-year-length 4 - bibtex-autokey-year-title-separator "") - -(setq bibtex-align-at-equal-sign t) ; Used by `bibtex-fill-entry'. - -(after 'bibtex - (setq bibtex-entry-format - (append '(realign whitespace last-comma delimiters sort-fields) - bibtex-entry-format))) - -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-use-title-case) -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-single-space-author-list) -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-pages-use-double-hyphen) -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-remove-doi-leader) -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-set-coding-system) -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-remove-entry-space) -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-downcase-entry) -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-downcase-keys) -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-downcase-author-and) -(add-hook 'bibtex-clean-entry-hook 'km/bibtex-delete-article-fields) - -(defvar km/bibtex-unimportant-title-words - '("a" "aboard" "about" "above" "absent" "across" "after" "against" - "along" "alongside" "amid" "amidst" "among" "amongst" "an" "and" - "around" "as" "aslant" "astride" "at" "athwart" "atop" - "barring" "before" "behind" "below" "beneath" "beside" "besides" "between" - "beyond" "but" "by" "despite" "down" "during" "except" "failing" - "following" "for" "from" "in" "inside" "into" "like" - "mid" "minus" "near" "next" "nor" "notwithstanding" "of" "off" - "on" "onto" "opposite" "or" "out" "outside" "over" "past" - "per" "plus" "regarding" "round" "save" "since" "so" "than" - "the" "through" "throughout" "till" "times" "to" "toward" "towards" - "under" "underneath" "unlike" "until" "up" "upon" "via" "vs." - "when" "with" "within" "without" "worth" "yet") - "Words to ignore when running `km/bibtex-use-title-case'. -These are taken from -http://lanecc.libguides.com/content.php?pid=38483&sid=295540 and -have only been modified to remove duplicates. This means that -there are some unlikely words in there, but you never know when -the next article you read will have \"athwart\" in the title.") - -(defun km/bibtex-use-title-case () - "Convert title of current BibTeX entry to title case. -Change words in `km/bibtex-unimportant-title-words' to lower -case, unless the word is the first word in the title. Capitalize -all other words unless they are protected by brackets." - (save-excursion - (bibtex-beginning-of-entry) - (let* ((bounds (bibtex-search-forward-field "title" t)) - (beg (bibtex-start-of-text-in-field bounds))) - (goto-char (1- beg)) - (while (re-search-forward "\\(\\W\\)\\(\\w+\\)\\(\\W\\)" - (bibtex-end-of-text-in-field bounds) t) - (cond - ((and (string= (match-string 1) "{") - (string= (match-string 3) "}")) - ;; Go to previous character in case '}' is within the word. - (backward-char)) - ;; Leave commands alone. - ((string= (match-string 1) "\\")) - ;; Capitalize the first word of the title. This will fail if - ;; there is a space after '{'. - ((= (match-beginning 1) beg) - (backward-word) - (capitalize-word 1)) - ;; Subword is separated by '-' or '{'. - ((or (string= (match-string 1) "-") - (string= (match-string 1) "}")) - (backward-word) - (downcase-word 1)) - (t - (backward-word) - (if (member (downcase (match-string-no-properties 2)) - km/bibtex-unimportant-title-words) - (downcase-word 1) - (capitalize-word 1)))))))) - -(defun km/bibtex-single-space-author-list () - "Convert multiple spaces in author list to single space." - (save-excursion - (bibtex-beginning-of-entry) - (let* ((bounds (bibtex-search-forward-field "author" t)) - (beg (bibtex-start-of-text-in-field bounds))) - (when bounds - (goto-char beg) - (while (re-search-forward "\\(\\s-+\\) and" - (bibtex-end-of-text-in-field bounds) t) - (replace-match "" nil nil nil 1)) - (goto-char beg) - (fill-paragraph))))) - -(defun km/bibtex-set-coding-system () - (set-buffer-file-coding-system 'utf-8)) - -(defun km/bibtex-remove-entry-space () - "Remove space in entry header. -For example, convert - - @article { - -to - - @article{" - (save-excursion - (bibtex-beginning-of-entry) - (when (looking-at "@\\w+\\(\\s-+\\)") - (replace-match "" nil nil nil 1)))) - -(defun km/bibtex-downcase-entry () - (save-excursion - (bibtex-beginning-of-entry) - (when (looking-at "^@\\([^{]+\\){") - (replace-match (downcase (match-string-no-properties 1)) - 'fixedcase nil nil 1)))) - -(defun km/bibtex-pages-use-double-hyphen () - "Use double hyphen for page range." - (save-excursion - (bibtex-beginning-of-entry) - (let ((bounds (bibtex-search-forward-field "pages" t))) - (when bounds - (goto-char (bibtex-start-of-text-in-field bounds)) - (and (re-search-forward "[^A-z0-9]*-[^A-z0-9]*" - (bibtex-end-of-text-in-field bounds) t) - (replace-match "--")))))) - -(defun km/bibtex-remove-doi-leader () - "Remove leading part (http:...) of doi URL." - (save-excursion - (bibtex-beginning-of-entry) - (let ((bounds (bibtex-search-forward-field "doi" t))) - (when bounds - (goto-char (bibtex-start-of-text-in-field bounds)) - (and (re-search-forward "http://dx.doi.org/" - (bibtex-end-of-text-in-field bounds) t) - (replace-match "")))))) - -(defun km/bibtex-downcase-keys () - "Downcase keys that are all caps." - (save-excursion - (bibtex-beginning-of-entry) - (let (case-fold-search) - (while (re-search-forward "^\\s-*\\([A-Z]+\\)\\s-*=" nil t) - (replace-match (downcase (match-string 1)) 'fixedcase - nil nil 1))))) - -(defun km/bibtex-downcase-author-and () - (save-excursion - (bibtex-beginning-of-entry) - (let ((bounds (bibtex-search-forward-field "author" t))) - (when bounds - (goto-char (bibtex-start-of-text-in-field bounds)) - (let (case-fold-search) - (while (re-search-forward "\\bAND\\b" - (bibtex-end-of-text-in-field bounds) t) - (replace-match (downcase (match-string 0)) 'fixedcase))))))) - -(defvar km/bibtex-article-fields-to-delete - '("abstract" "issn" "pubmedid" "url" "eprint" "keywords")) - -(defun km/bibtex-delete-article-fields () - (save-excursion - (when (and (bibtex-beginning-of-entry) - (looking-at bibtex-entry-maybe-empty-head) - (string= (downcase (bibtex-type-in-head)) "article")) - (dolist (f km/bibtex-article-fields-to-delete) - (let (bounds) - ;; Make sure field is removed even if it is repeated. - (while (progn (bibtex-beginning-of-entry) - (setq bounds (bibtex-search-forward-field f t))) - (goto-char (bibtex-end-of-field bounds)) - (skip-chars-backward " \t\n") - (delete-region (bibtex-start-of-field bounds) - (point)))))))) - -(defun km/browse-doi (doi) - "Open DOI in browser. -When called interactively, take the DOI from the text under -point. The link is opened using the settings of -`org-doi-server-url'." - (interactive (list (km/doi-at-point))) - (browse-url (org-link-escape-browser (concat org-doi-server-url doi)))) - -(defun km/copy-doi-as-kill () - "Copy DOI at point to kill ring." - (interactive) - (-when-let (doi (km/doi-at-point)) - (kill-new (message "%s" (concat "doi:" doi))))) - -(defun km/doi-at-point () - "Return DOI at point." - (save-excursion - (skip-chars-backward "-.A-z0-9/") - (and (looking-at "\\(doi:[ \t\n]*\\)*\\([-./A-z0-9]+[A-z0-9]\\)\\b") - (match-string-no-properties 2)))) - -(provide 'init-bib) -;;; init-bib.el ends here diff --git a/lisp/init-bog.el b/lisp/init-bog.el deleted file mode 100644 index b0a845d..0000000 --- a/lisp/init-bog.el +++ /dev/null @@ -1,37 +0,0 @@ -;;; init-bog.el --- Bog mode configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(add-to-list 'load-path "~/src/emacs/bog/") -(require 'bog-autoloads) - -(setq bog-subdirectory-group 2 - bog-combined-bib-ignore-not-found t - bog-use-citekey-cache t) - -(setq bog-keymap-prefix (kbd "C-c b")) - -(add-hook 'org-mode-hook 'bog-mode) - -(global-set-key bog-keymap-prefix bog-command-map) - -(provide 'init-bog) -;;; init-bog.el ends here diff --git a/lisp/init-buffers.el b/lisp/init-buffers.el deleted file mode 100644 index e16d6e6..0000000 --- a/lisp/init-buffers.el +++ /dev/null @@ -1,72 +0,0 @@ -;;; init-buffers.el --- Buffer-related configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'uniquify) - -(setq uniquify-buffer-name-style 'forward) - -(setq ibuffer-expert t - ibuffer-restore-window-config-on-quit t - ibuffer-show-empty-filter-groups nil) - -(defun km/save-and-kill-buffer () - "Save current buffer and then kill it." - (interactive) - (save-buffer) - (kill-this-buffer)) - -(defun km/kill-buffer (&optional arg) - "Kill this buffer. -With single C-u, prompt for buffer to kill. With double C-u, -kill this buffer and the window." - (interactive "P") - (cond - ((not arg) - (kill-buffer)) - ((equal arg '(16)) - (kill-buffer-and-window)) - (t - (call-interactively #'kill-buffer)))) - -(defun km/save-buffers () - "Run `save-some-buffers', but don't ask to save the current buffer. -`save-some-buffers' is called interactively." - (interactive) - (let* ((base-buf (buffer-base-buffer)) - (buf (or base-buf (current-buffer))) - (buf-file (buffer-file-name buf))) - (when (and (buffer-live-p buf) - (buffer-modified-p buf) - buf-file) - (with-current-buffer buf - (save-buffer)))) - (call-interactively #'save-some-buffers)) - -(global-set-key (kbd "C-x k") 'km/kill-buffer) - -(key-chord-define-global "js" 'km/save-buffers) - -;; Replace `list-buffers' with ibuffer. -(global-set-key (kbd "C-x C-b") 'ibuffer) - -(provide 'init-buffers) -;;; init-buffers.el ends here diff --git a/lisp/init-diminish.el b/lisp/init-diminish.el deleted file mode 100644 index 48baf5a..0000000 --- a/lisp/init-diminish.el +++ /dev/null @@ -1,39 +0,0 @@ -;;; init-diminish.el --- Diminish mode configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'diminish) - -(diminish 'abbrev-mode "Ab") -(after 'helm-mode (diminish 'helm-mode)) -(after 'flyspell (diminish 'flyspell-mode "Fy")) -(after 'paredit (diminish 'paredit-mode " Pe")) -(after 'mml (diminish 'mml-mode "Ml")) -(after 'org (diminish 'orgstruct-mode "Os")) -(after 'org-table (diminish 'orgtbl-mode "Ot")) -(after 'projectile (diminish 'projectile-mode)) -(after 'reftex (diminish 'reftex-mode "Rf")) -(after 'view (diminish 'view-mode "Vw")) -(after 'whitespace (diminish 'global-whitespace-mode)) -(after 'yasnippet (diminish 'yas-minor-mode)) - -(provide 'init-diminish) -;;; init-diminish.el ends here diff --git a/lisp/init-dired.el b/lisp/init-dired.el deleted file mode 100644 index ba976ea..0000000 --- a/lisp/init-dired.el +++ /dev/null @@ -1,202 +0,0 @@ -;;; init-dired.el --- Dired configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'dired-x) - -(put 'dired-find-alternate-file 'disabled nil) - -;; .git is present as part of `dired-omit-extensions', but this seems to -;; only be taken into account if a non-exension part exists. -(setq dired-omit-files - (concat dired-omit-files - "\\|^\\.git$\\|^\\.gitignore$" - "\\|^__pycache__$\\|^\\.snakemake$")) - -(defvar km/latex-omit-extensions '(".aux" - ".fdb_latexmk" - ".fls" - ".log" - ".nav" - ".out" - ".snm") - "Intermediate LaTeX files") - -(setq dired-omit-extensions - (append dired-omit-extensions km/latex-omit-extensions)) - -(setq-default dired-omit-mode t) -(setq dired-dwim-target t - dired-listing-switches "-alht") - -(setq dired-guess-shell-alist-user - '(("\\.pdf\\'" "zathura"))) - -(setq dired-recursive-copies t - dired-recursive-deletes t) - -(add-hook 'dired-mode-hook 'dired-hide-details-mode) - -(defun km/dired-switch-to-buffer () - (interactive) - (switch-to-buffer (km/dired-completing-buffer))) - -(defun km/dired-switch-to-buffer-other-window () - (interactive) - (pop-to-buffer (km/dired-completing-buffer))) - -(defun km/dired-completing-buffer () - (completing-read "Dired buffer: " - (mapcar #'buffer-name (km/mode-buffers 'dired-mode)))) - -(defun km/org-open-dired-marked-files (&optional arg) - "Open marked files (or next ARG) with `org-open-file'." - (interactive "p") - (setq arg (and current-prefix-arg arg)) - (let* ((files (dired-get-marked-files nil arg)) - (num-files (length files))) - (when (or (< num-files 5) - (yes-or-no-p (format "Open %s files?" num-files))) - (dolist (f files) (org-open-file f))))) - -(defun km/dired-view-file-other-window () - "In Dired, view this file in another window." - (interactive) - (view-file-other-window (dired-get-file-for-visit))) - -(defun km/dired-copy-and-edit () - "Copy file and enter `wdired-mode' for completing rename." - (interactive) - (unless (derived-mode-p 'dired-mode) - (user-error "Must be in a Dired buffer")) - (let* ((fname (dired-get-filename)) - (flag "---copy---") - (new-fname (concat fname flag))) - (when (file-directory-p fname) - (user-error "File cannot be directory")) - (copy-file fname new-fname) - (dired-revert) - (wdired-change-to-wdired-mode) - (goto-char (point-min)) - (re-search-forward (format "%s\\(%s\\)" - (file-name-nondirectory fname) - flag)) - (replace-match "" t nil nil 1))) - -;; This overrides the binding for `list-directory'. -(global-set-key (kbd "C-x C-d") 'km/dired-switch-to-buffer) -(define-key dired-mode-map "c" 'dired-do-copy) -(define-key dired-mode-map "C" 'km/dired-copy-and-edit) -;; This overrides `dired-do-run-mail'. -(define-key dired-mode-map "V" 'km/dired-view-file-other-window) - -(define-key ctl-x-4-map "D" 'km/dired-switch-to-buffer-other-window) - -(define-prefix-command 'km/dired-prefix-map) -(define-key dired-mode-map (kbd "C-c m") 'km/dired-prefix-map) - -(after 'org - ;; This overrides `dired-find-file', which is also bound to "f". - (define-key dired-mode-map "e" 'km/org-open-dired-marked-files)) - - -;;; Dired Narrow - -(define-key dired-mode-map "/" 'dired-narrow) - -(define-prefix-command 'km/dired-narrow-prefix-map) -(define-key km/dired-prefix-map "n" 'km/dired-narrow-prefix-map) - -(define-key km/dired-narrow-prefix-map "f" 'dired-narrow-fuzzy) -(define-key km/dired-narrow-prefix-map "n" 'dired-narrow) -(define-key km/dired-narrow-prefix-map "r" 'dired-narrow-regexp) - - -;;; Dired Subtree - -(define-prefix-command 'km/dired-subtree-prefix-map) -(define-key km/dired-prefix-map "s" 'km/dired-subtree-prefix-map) - -(define-key km/dired-subtree-prefix-map "@" 'dired-subtree-mark-subtree) -(define-key km/dired-subtree-prefix-map "." 'dired-subtree-unmark-subtree) -(define-key km/dired-subtree-prefix-map "<" 'dired-subtree-beginning) -(define-key km/dired-subtree-prefix-map ">" 'dired-subtree-end) -(define-key km/dired-subtree-prefix-map "g" 'dired-subtree-revert) -(define-key km/dired-subtree-prefix-map "d" 'dired-subtree-down) -(define-key km/dired-subtree-prefix-map "i" 'dired-subtree-insert) -(define-key km/dired-subtree-prefix-map "n" 'dired-subtree-next-sibling) -(define-key km/dired-subtree-prefix-map "p" 'dired-subtree-previous-sibling) -(define-key km/dired-subtree-prefix-map "r" 'dired-subtree-remove) -(define-key km/dired-subtree-prefix-map "s" 'dired-subtree-narrow) -(define-key km/dired-subtree-prefix-map "u" 'dired-subtree-up) - - -;;; Copying file names - -(defun km/dired-copy-project-filename-as-kill () - "Copy names of marked project files into kill ring. -This is similar to `dired-copy-filename-as-kill', but the leading -path is always relative to `projectile-project-root'." - (interactive) - (km/dired-copy-filename-relative-to-directory - (projectile-project-root))) - -(defun km/dired-copy-relative-filename-as-kill (&optional arg) - "Copy names of marked (or next ARG) files into kill ring. -This is similar to `dired-copy-filename-as-kill', but the leading -path is always relative to the `default-directory' of the other -window." - (interactive "p") - (setq arg (and current-prefix-arg arg)) - (km/dired-copy-filename-relative-to-directory - (km/other-default-directory) arg)) - -(defun km/dired-copy-filename-relative-to-directory (directory &optional arg) - "Like `dired-copy-filename-as-kill', but the filename is always -relative to DIRECTORY." - (let* ((string - (mapconcat #'identity - (mapcar (lambda (f) (file-relative-name f directory)) - (dired-get-marked-files t arg)) - " "))) - (if (eq last-command 'kill-region) - (kill-append string nil) - (kill-new string)) - (message "%s" string))) - -(defun km/other-default-directory () - "Get `default-directory' for result of `(other-window 1)'." - (save-window-excursion - (other-window 1) - default-directory)) - -(define-prefix-command 'km/dired-copy-filename-map) -;; This overrides the default binding for `dired-copy-filename-as-kill'. -(define-key dired-mode-map "w" 'km/dired-copy-filename-map) - -(after 'projectile - (define-key km/dired-copy-filename-map "p" - 'km/dired-copy-project-filename-as-kill)) -(define-key km/dired-copy-filename-map "o" 'km/dired-copy-relative-filename-as-kill) -(define-key km/dired-copy-filename-map "w" 'dired-copy-filename-as-kill) - -(provide 'init-dired) -;;; init-dired.el ends here diff --git a/lisp/init-editing.el b/lisp/init-editing.el deleted file mode 100644 index 618431c..0000000 --- a/lisp/init-editing.el +++ /dev/null @@ -1,397 +0,0 @@ -;;; init-editing.el --- Editing-related configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -;; http://irreal.org/blog/?p=1536 -(autoload 'zap-up-to-char "misc" - "Kill up to, but not including ARGth occurrence of CHAR.") - -(setq hippie-expand-try-functions-list '(try-complete-file-name-partially - try-complete-file-name - try-expand-all-abbrevs - try-expand-dabbrev - try-expand-dabbrev-all-buffers - try-expand-dabbrev-from-kill - try-complete-lisp-symbol-partially - try-complete-lisp-symbol)) - -;; This is bound separately in `km/editing-map'. -(setq iedit-toggle-key-default nil) - -(setq flyspell-auto-correct-binding (kbd "C-c e ;")) - -(after 'flyspell - (define-key flyspell-mode-map (kbd "C-.") nil)) - -(put 'fill-paragraph-function 'safe-local-variable - (lambda (v) (equal v (lambda (_) t)))) - -;; http://www.emacswiki.org/emacs/UnfillParagraph -(defun km/unfill-paragraph () - "Convert a multi-line paragraph to a single line of text." - (interactive) - (let ((fill-column (point-max))) - (fill-paragraph nil))) - -(defun km/fill-surrounding-indented () - "Fill current line with all surrounding lines of same indentation. -This is like `fill-individual-paragraphs', but 1) it acts only on -a single paragraph at point, not all paragraphs in a region, and -2) it doesn't treat lines with the following structure as a -special case. - - foo> This line with extra indentation starts - foo> a paragraph that continues on more lines." - (interactive) - (save-excursion - (let ((orig-point (point)) - (level (current-indentation)) - beg end) - (beginning-of-line) - (while (and (not beg) (not (bobp))) - (forward-line -1) - (when (or (/= level (current-indentation)) - (looking-at "^\\s-*$")) - (forward-line) - (setq beg (point)))) - (goto-char orig-point) - (beginning-of-line) - (while (and (not end) (not (eobp))) - (forward-line) - (when (or (/= level (current-indentation)) - (looking-at "^\\s-*$")) - (forward-line -1) - (end-of-line) - (setq end (point)))) - (fill-region (or beg (point-min)) (or end (point-max)))))) - -(defun km/reduce-to-single-spaces () - "Reduce consecutive blank lines to a single line." - (interactive) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward "\\([ \t]*\n\\)\\{3,\\}" nil t) - (forward-line -1) - (delete-blank-lines)))) - -(defun km/export-wrapped-text (&optional xselect) - "Export the text in current buffer as wrapped text. - -This is useful for preparing text in emacs and then exporting to -a wrapped buffer for pasting text (e.g., into a web form). - -With an active region, restrict export to this region. If -XSELECT is non-nil, copy the region with `x-select-text'." - (interactive "P") - (let ((wrapped-buffer (get-buffer-create "*Wrapped export*"))) - (apply #'copy-to-buffer wrapped-buffer - (if (use-region-p) - (list (region-beginning) (region-end)) - (list (point-min) (point-max)))) - (with-current-buffer wrapped-buffer - (while (not (eobp)) - (forward-paragraph) - (forward-line -1) - (km/unfill-paragraph) - (forward-line 1)) - (when xselect - (x-select-text - (buffer-substring-no-properties (point-min) (point-max))))) - (pop-to-buffer wrapped-buffer))) - -(defun km/narrow-to-comment-heading () - "Narrow to the current comment heading subtree. - -Narrow the buffer from the current comment heading to the next -comment heading of the same level or, if not found, to the end of -the buffer. - -A comment is considered a heading if it is at the beginning of -the line and if it conists of 3 or more occurences of -`comment-start'. The number of `comment-start' characters is -taken to indicate the level of the heading (with 3 being the top -level). - -In the examples below, 'x' indicates the current point and '>>>' -and '<<<' mark the bounds of the narrowed region. - ---------------------------------------------------------------- - >>>;;; Level one heading - x - - ;;;; Level two heading - - <<< - ;;; Another level one heading -------------------------------eob------------------------------ - ---------------------------------------------------------------- - ;;; Level one heading - - >>>;;;; Level two heading - x - <<< - ;;;; Another level one heading -------------------------------eob------------------------------ - ---------------------------------------------------------------- - >>>;;; Level one heading - x - - ;;;; Level two heading - <<< -------------------------------eob------------------------------" - (interactive) - (unless comment-start - (user-error "Comment syntax is not defined for current buffer")) - (unless (= (length comment-start) 1) - (user-error "Buffer's comment string consists of more than one character")) - (save-excursion - (widen) - (let ((outline-regexp (concat (s-repeat 4 comment-start) "*"))) - (outline-mark-subtree) - (narrow-to-region (region-beginning) (region-end))))) - -(defun km/toggle-line-or-region-comment (beg end) - "Comment or uncomment the current line or region. -If there is an active region, act on all lines that the region -touches." - (interactive "*r") - (unless (use-region-p) - (setq beg (point) - end (point))) - (let ((bol (save-excursion (goto-char beg) - (line-beginning-position))) - (eol (save-excursion (goto-char end) - (line-end-position)))) - (unless (eq bol eol) - (comment-or-uncomment-region bol eol) - (forward-line)))) - -;; Modified from http://oremacs.com/2015/01/26/occur-dwim/. -(defun km/occur () - "Call `occur' with active region or symbol at point." - (interactive) - (--when-let (if (use-region-p) - (buffer-substring-no-properties - (region-beginning) (region-end)) - (thing-at-point 'symbol)) - (push it regexp-history)) - (call-interactively 'occur)) - -(defun km/occur-avy-goto-subword-1 () - "Like `avy-goto-subword-1', but display occurence." - (interactive) - (let (avy-all-windows) - (call-interactively #'avy-goto-subword-1)) - (occur-mode-display-occurrence)) - -(global-set-key (kbd "C-x \\") 'align-regexp) - -(global-set-key (kbd "C-.") 'er/expand-region) - -(global-set-key [remap kill-ring-save] 'easy-kill) - -;; Overrides `suspend-emacs' (which is also bound to C-x C-z). -(global-set-key (kbd "C-z") 'zap-to-char) -(global-set-key (kbd "M-z") 'zap-up-to-char) -(global-set-key (kbd "C-'") 'backward-kill-word) - -(global-set-key (kbd "M-/") 'hippie-expand) - -(key-chord-define-global "jx" 'km/toggle-line-or-region-comment) -(key-chord-define-global "qp" 'fill-paragraph) - -(define-key ctl-x-4-map "nd" 'ni-narrow-to-defun-indirect-other-window) -(define-key ctl-x-4-map "nn" 'ni-narrow-to-region-indirect-other-window) -(define-key ctl-x-4-map "np" 'ni-narrow-to-page-indirect-other-window) - -(define-key narrow-map "c" 'km/narrow-to-comment-heading) - -(define-key occur-mode-map "n" 'next-line) -(define-key occur-mode-map "p" 'previous-line) -(define-key occur-mode-map "j" 'km/occur-avy-goto-subword-1) - -;; Override default `occur'. -(define-key search-map "o" 'km/occur) -(define-key search-map "s" 'query-replace) -(define-key search-map "S" 'replace-string) -(define-key search-map "r" 'query-replace-regexp) -(define-key search-map "R" 'replace-regexp) - -(define-prefix-command 'km/editing-map) -(global-set-key (kbd "C-c e") 'km/editing-map) - -(define-key km/editing-map (kbd "C-i") 'indent-relative) -(define-key km/editing-map "f" 'km/fill-surrounding-indented) -(define-key km/editing-map "i" 'iedit-mode) -(define-key km/editing-map "l" 'toggle-truncate-lines) -(define-key km/editing-map "u" 'km/unfill-paragraph) -(define-key km/editing-map "w" 'ispell-word) - -(electric-indent-mode -1) -(electric-pair-mode 1) - - -;;; Buffer cleanup - -(setq whitespace-style '(face trailing indentation)) - -(global-whitespace-mode 1) - -(add-hook 'before-save-hook 'km/cleanup-buffer) - -(defvar-local km/prevent-cleanup nil - "If set, `km/cleanup-buffer' does not perform clean up on save.") - -(defun km/toggle-prevent-cleanup () - "Toggle state of `km/prevent-cleanup'." - (interactive) - (if km/prevent-cleanup - (progn - (message "Allowing cleanup on save") - (kill-local-variable 'whitespace-style) - (global-whitespace-mode 0) - (global-whitespace-mode 1)) - (message "Preventing cleanup on save") - (setq-local whitespace-style - '(face trailing indentation - tab-mark space-mark newline-mark)) - (global-whitespace-mode 0) - (global-whitespace-mode 1)) - (setq km/prevent-cleanup (not km/prevent-cleanup))) - -(defun km/cleanup-buffer () - (interactive) - (unless km/prevent-cleanup - (whitespace-cleanup) - (delete-trailing-whitespace))) - -(define-key km/editing-map "t" 'km/toggle-prevent-cleanup) - - -;;; Kill map - -(defun km/kill-string-at-point () - (interactive) - (let ((string-start (nth 8 (syntax-ppss)))) - (goto-char string-start) - (kill-sexp))) - -;; Taken from prelude-core.el. -(defun km/join-next-line-with-space () - "Join current line to the next line with a space in between." - (interactive) - (delete-indentation 1)) - -(defmacro km/make-kill-thing-at-point (thing) - `(defun ,(intern (concat "km/kill-" thing "-at-point")) () - ,(format "Kill %s at point." thing) - (interactive) - (goto-char (beginning-of-thing (quote ,(make-symbol thing)))) - (,(intern (concat "kill-" thing)) 1))) - -(km/make-kill-thing-at-point "line") -(km/make-kill-thing-at-point "paragraph") -(km/make-kill-thing-at-point "sentence") -(km/make-kill-thing-at-point "word") - -(define-prefix-command 'km/kill-map) -(global-set-key (kbd "C-c k") 'km/kill-map) - -(define-key km/kill-map "." 'km/kill-sentence-at-point) -(define-key km/kill-map "j" 'km/join-next-line-with-space) -(define-key km/kill-map "l" 'km/kill-line-at-point) -(define-key km/kill-map "p" 'km/kill-paragraph-at-point) -(define-key km/kill-map "s" 'km/kill-string-at-point) -(define-key km/kill-map "w" 'km/kill-word-at-point) - - -;;; Multiple cursors - -;; Multiple cursors hydra is modified from -;; https://github.com/abo-abo/hydra/wiki/multiple-cursors -(define-key km/editing-map "o" - (defhydra hydra-multiple-cursors (:hint nil) - " - ^^Up ^^Down -_p_ Next _n_ Next -_P_ Skip _N_ Skip -_y_ Unmark _u_ Unmark - -" - ("l" mc/edit-lines "edit lines" :exit t) - ("a" mc/mark-all-like-this "mark all" :exit t) - ("n" mc/mark-next-like-this) - ("N" mc/skip-to-next-like-this) - ("u" mc/unmark-next-like-this) - ("p" mc/mark-previous-like-this) - ("P" mc/skip-to-previous-like-this) - ("y" mc/unmark-previous-like-this))) - - -;;; Keyboard macros - -(define-key km/editing-map "k" - (defhydra hydra-kmacro (:hint nil :exit t) - " - ^^Defining ^^Applying -_?_ Query _c_ Call -_b_ Bind to key _o_ Call 2nd in ring -_e_ Edit _r_ Apply to region lines -_E_ Step edit -_N_ Name last -_l_ Use recent strokes -_s_ Start defining -_x_ To register - - ^^Ring ^^Counter -_D_ Delete ring head _+_ Add counter -_n_ Cycle next _=_ Set counter -_p_ Cycle previous _f_ Set format -_t_ Swap _i_ Insert counter -_v_ View - -" - ("+" kmacro-add-counter) - ("=" kmacro-set-counter) - ("D" kmacro-delete-ring-head) - ("f" kmacro-set-format) - ("i" kmacro-insert-counter) - ("c" kmacro-end-and-call-macro :exit nil) - ("o" kmacro-call-ring-2nd-repeat) - ("e" kmacro-edit-macro) - ("E" kmacro-step-edit-macro) - ("n" kmacro-cycle-ring-next :exit nil) - ("p" kmacro-cycle-ring-previous :exit nil) - ("t" kmacro-swap-ring) - ("v" kmacro-view-macro-repeat) - ("b" kmacro-bind-to-key) - ("l" kmacro-edit-lossage) - ("N" kmacro-name-last-macro) - ("?" kbd-macro-query) - ("r" apply-macro-to-region-lines) - ("s" kmacro-start-macro) - ("x" kmacro-to-register) - ("q" nil "quit" :hint t))) - -(provide 'init-editing) -;;; init-editing.el ends here diff --git a/lisp/init-elisp.el b/lisp/init-elisp.el deleted file mode 100644 index 987dab8..0000000 --- a/lisp/init-elisp.el +++ /dev/null @@ -1,46 +0,0 @@ -;;; init-elisp.el --- Elisp-related configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(setq lispy-no-permanent-semantic t) - -(add-hook 'emacs-lisp-mode-hook 'lispy-mode) -(add-hook 'emacs-lisp-mode-hook 'km/elisp-set-outline-vars) -;; This likely breaks lispy's outline functions, but I don't use them. -(add-hook 'lispy-mode-hook 'km/elisp-set-outline-vars) - -(after 'lispy - ;; This is ugly, but I haven't found another way to stop - ;; `imenu-create-index-function' from being set to - ;; `semantic-create-imenu-index'. Trying to set it in - ;; `emacs-lisp-mode-hook' or `lispy-mode-hook' doesn't work. - (defalias 'semantic-create-imenu-index 'imenu-default-create-index-function)) - -(defun km/elisp-outline-level () - (and (looking-at (concat "^" outline-regexp)) - (- (match-end 0) (match-beginning 0) 3))) - -(defun km/elisp-set-outline-vars () - (setq outline-regexp ";;;;* ") - (setq outline-level 'km/elisp-outline-level)) - -(provide 'init-elisp) -;;; init-elisp.el ends here diff --git a/lisp/init-ess.el b/lisp/init-ess.el deleted file mode 100644 index d951ede..0000000 --- a/lisp/init-ess.el +++ /dev/null @@ -1,65 +0,0 @@ -;;; init-ess.el --- ESS configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(autoload 'R-mode "ess-site") - -(setq ess-smart-S-assign-key ";") - -(setq ess-use-ido nil) - -(add-to-list 'auto-mode-alist '("\\.[rR]\\'" . R-mode)) - -(define-abbrev-table 'ess-mode-abbrev-table - '(("true" "TRUE") - ("false" "FALSE")) - :system t) - -(dolist (hook '(ess-mode-hook inferior-ess-mode-hook)) - (add-hook hook (lambda () - (setq local-abbrev-table ess-mode-abbrev-table))) - (add-hook hook 'abbrev-mode)) - -(defun km/ess-eval-buffer-up-to-line () - "Send up to the current line to inferior ESS process." - (interactive) - (ess-eval-region (point-min) (line-end-position) nil)) - -(defvar km/ess-dplry-pipe-key "|") - -(defun km/ess-insert-dplyr-pipe () - "Insert `km/ess-dplry-pipe' using `ess-smart-S-assign'. -Based on instructions in `ess-smart-S-assign-key', I didn't think -this would work, but it seems to so far." - (interactive) - (let ((ess-S-assign " %>% ") - (ess-smart-S-assign-key km/ess-dplry-pipe-key)) - (call-interactively #'ess-smart-S-assign))) - -(after 'ess-mode - (define-key ess-mode-map (kbd "C-c C-.") 'km/ess-eval-buffer-up-to-line) - (define-key ess-mode-map "|" 'km/ess-insert-dplyr-pipe)) - -(after 'ess-inf - (define-key inferior-ess-mode-map "|" 'km/ess-insert-dplyr-pipe)) - -(provide 'init-ess) -;;; init-ess.el ends here diff --git a/lisp/init-external.el b/lisp/init-external.el deleted file mode 100644 index 2d1f11c..0000000 --- a/lisp/init-external.el +++ /dev/null @@ -1,350 +0,0 @@ -;;; init-external.el --- Configuration for shells and external programs - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(setq x-select-enable-clipboard t ; Share clipboard with system. - x-select-enable-primary t) - -(setq browse-url-browser-function 'browse-url-generic - browse-url-generic-program "firefox") - -(setq ispell-program-name "aspell") - -(setq Man-notify-method 'aggressive) - -(define-prefix-command 'km/external-map) -(global-set-key (kbd "C-c z") 'km/external-map) - -(define-key km/external-map "b" 'browse-url) -(define-key km/external-map "m" 'helm-man-woman) -(define-key km/external-map "i" 'ispell-buffer) - - -;;; Shells - -(setq shell-file-name "/bin/bash") -(setq shell-command-switch "-c") - -(defvar km/terminal "urxvt") - -(defun km/open-external-terminal () - (interactive) - (start-process "ext-term" nil km/terminal)) -(define-key km/external-map "t" 'km/open-external-terminal) - -(defun km/zsh-ansi-term (&optional directory name) - "Open an ansi-term buffer running ZSH in DIRECTORY. - -If a terminal for DIRECTORY already exists, switch to that -buffer. If the current buffer is a terminal for DIRECTORY, -create an additional terminal. - -By default, DIRECTORY is `default-directory'. - -With a numeric prefix argument 0, prompt the user with existing -ZSH terminal directories. - -With a C-u prefix argument, set DIRECTORY to the user home -directory. - -With any other non-nil value, prompt for a directory. - -If NAME is non-nil, use *NAME* for the buffer name instead of -*zsh: DIRECTORY*. If that buffer already exists, it will be -grabbed regardless of whether its default-directory matches -DIRECTORY." - (interactive (km/zsh-ansi-term--args)) - (let* ((dir (abbreviate-file-name directory)) - (name (or name (concat "zsh: " dir))) - (full-name (concat "*" name "*")) - (default-directory dir)) - (pop-to-buffer-same-window - (cond - ((and (not (string= (km/zsh-ansi-term-directory) dir)) - (get-buffer full-name))) - (t - (cl-letf (((symbol-function 'switch-to-buffer) - (lambda (b &rest _) (get-buffer b)))) - (ansi-term "zsh" name))))) - (comint-goto-process-mark))) - -(defun km/zsh-toggle-ansi-term-home (&optional other-window) - (interactive "P") - (if (string= "*zsh*" (buffer-name)) - (bury-buffer) - (let ((display-buffer-overriding-action - (and other-window '(nil (inhibit-same-window . t))))) - (km/zsh-ansi-term "~/" "zsh")))) - -(defun km/zsh-ansi-term-other-window (&optional directory) - (interactive (km/zsh-ansi-term--args)) - (let ((display-buffer-overriding-action - '(nil (inhibit-same-window . t)))) - (km/zsh-ansi-term directory))) - -(defun km/zsh-ansi-term--args () - (list (cond - ((not current-prefix-arg) - default-directory) - ((= (prefix-numeric-value current-prefix-arg) 4) - "~/") - ((= (prefix-numeric-value current-prefix-arg) 0) - (let ((dirs (km/zsh-ansi-term-current-directories))) - (cl-case (length dirs) - (0 (user-error "No ZSH buffers found")) - (1 (car dirs)) - (t (completing-read "Directory: " dirs - nil nil nil nil (car dirs)))))) - (t - (read-directory-name "Directory: "))))) - -(defun km/zsh-ansi-term-directory (&optional buffer) - "Return directory name for ZSH terminal in BUFFER. -BUFFER defaults to current buffer." - (with-current-buffer (or buffer (current-buffer)) - (let ((bname (buffer-name))) - (and (derived-mode-p 'term-mode) - (string-match "^\\*zsh: \\(.*\\)\\*\\(<[0-9]+>\\)*$" - bname) - (match-string 1 bname))))) - -(defun km/zsh-ansi-term-current-directories () - (-distinct (-keep #'km/zsh-ansi-term-directory (buffer-list)))) - -(define-key km/external-map "a" 'km/zsh-ansi-term) -(define-key km/external-map "r" 'shell-command-on-region) -(define-key km/external-map "s" 'shell-command) -(define-key km/external-map "S" 'shell) - -(key-chord-define-global "kz" 'km/zsh-toggle-ansi-term-home) - -;; This overrides binding for `add-change-log-entry-other-window'. -(define-key ctl-x-4-map "a" 'km/zsh-ansi-term-other-window) - - -;;; Compilation - -(defvar km/compilation-buffer-name-prefix "compilation: ") - -(defun km/compilation-name-by-directory (&optional mode) - (let ((name (if (and mode (not (equal mode "compilation"))) - (downcase mode) - (concat km/compilation-buffer-name-prefix - (abbreviate-file-name default-directory))))) - (concat "*" name "*"))) - -(setq compilation-buffer-name-function 'km/compilation-name-by-directory) - -(defun km/compilation-buffer-p (buffer) - (with-current-buffer buffer - (and (derived-mode-p 'compilation-mode) - (string-prefix-p (concat "*" km/compilation-buffer-name-prefix) - (buffer-name))))) - -(defadvice compile (around prevent-duplicate-compilation-windows activate) - "Pop to compilation buffer only if it isn't visible. -This is useful for using multiple frames (e.g., with a two -monitor setup)." - (if (get-buffer-window (km/compilation-name-by-directory) - 'visible) - (save-window-excursion ad-do-it) - ad-do-it)) - -(defun km/compile-in-home-dir () - (interactive) - (let ((default-directory "~/")) - (call-interactively #'compile))) - -(defadvice recompile (around prevent-window-on-compilation activate) - "Prevent recompiling from spawning new windows." - (save-window-excursion ad-do-it)) - -(defun km/compilation-recompile (&optional arg) - "Recompile buffer. -By default, use `compilation-last-buffer'. If ARG is 0, get -buffer with name given by `km/compilation-name-by-directory'. -Otherwise, if ARG is non-nil, prompt with buffers from -`km/compilation-buffer-list'." - (interactive (list (and current-prefix-arg - (prefix-numeric-value current-prefix-arg)))) - (with-current-buffer (km/compilation--get-buffer arg) - (if (derived-mode-p 'occur-mode) - (revert-buffer) - (recompile)))) - -(defun km/compilation-display-buffer (&optional arg) - "Display compilation buffer. -By default, use `compilation-last-buffer'. If ARG is 0, get -buffer with name given by `km/compilation-name-by-directory'. -Otherwise, if ARG is non-nil, prompt with buffers from -`km/compilation-buffer-list'." - (interactive (list (and current-prefix-arg - (prefix-numeric-value current-prefix-arg)))) - (display-buffer (km/compilation--get-buffer arg))) - -(defun km/compilation--get-buffer (&optional arg) - (cond - ((and (not arg) - (buffer-live-p compilation-last-buffer) - compilation-last-buffer)) - ((and (numberp arg) - (= arg 0)) - (get-buffer (km/compilation-name-by-directory))) - (t - (let ((cbufs (-map #'buffer-name (km/compilation-buffer-list))) - buf) - (cl-case (length cbufs) - (0 (user-error "No compilation buffers found")) - (1 (setq buf (car cbufs))) - (t (setq buf (completing-read "Compilation buffer: " cbufs - nil nil nil nil (car cbufs))))) - buf)))) - -(defun km/compilation-buffer-list () - (-filter #'km/compilation-buffer-p (buffer-list))) - -(define-prefix-command 'km/compile-map) -(global-set-key (kbd "C-c c") 'km/compile-map) - -(define-key km/compile-map "c" 'compile) -(define-key km/compile-map "g" 'recompile) -(define-key km/compile-map "h" 'km/compile-in-home-dir) -(define-key km/compile-map "o" 'km/compilation-display-buffer) - -(key-chord-define-global "hv" 'km/compilation-recompile) - - -;;; Diff - -(setq diff-command "/bin/diff" - diff-switches "-u") - -(setq ediff-window-setup-function 'ediff-setup-windows-plain) - -(defun km/diff () - "Run `diff' and then select buffer and turn on View mode." - (interactive) - (call-interactively #'diff) - (select-window (get-buffer-window "*Diff*")) - (view-mode 1)) - -(defun km/revert-buffer-and-view () - (interactive) - (revert-buffer) - (view-mode 1)) -(after 'diff - (define-key diff-mode-map (kbd "C-c C-g") 'km/revert-buffer-and-view)) - -(defun km/ediff-with-other-window () - "Run `ediff' on current window's file and other window's file." - (interactive) - (let ((windows (window-list))) - (unless (= (length windows) 2) - (user-error "Function restricted to two-window frames")) - (-if-let* ((file-a (buffer-file-name - (window-buffer (car windows)))) - (file-b (buffer-file-name - (window-buffer (cadr windows))))) - (ediff file-a file-b) - (user-error "At least one buffer is not visiting a file")))) - -(define-key km/external-map "d" 'km/diff) -(define-key km/external-map "e" 'ediff) -(define-key km/external-map "o" 'km/ediff-with-other-window) - - -;;; WebJump - -(setq webjump-sites - '(("Arch User Repository" . - [simple-query "https://aur.archlinux.org" - "https://aur.archlinux.org/packages/?K=" ""]) - ("DuckDuckGo" . - [simple-query "https://duckduckgo.com" - "https://duckduckgo.com/?q=" ""]) - ("Emacs Wiki" . - [simple-query "www.emacswiki.org" - "www.emacswiki.org/cgi-bin/wiki/" ""]) - ("GitHub" . "https://github.com") - ("GitHub search" . - [simple-query "https://github.com" - "https://github.com/search?q=" ""]) - ("Google" . - [simple-query "www.google.com" - "www.google.com/search?q=" ""]) - ("Google Scholar" . - [simple-query "http://scholar.google.com" - "http://scholar.google.com/scholar?&q=" ""]) - ("Wikipedia" . - [simple-query "wikipedia.org" - "wikipedia.org/wiki/" ""]))) - -(defun km/webjump-read-string (prompt) - "Like `webjump-read-string', but set default." - (let* ((default (if (use-region-p) - (buffer-substring-no-properties - (region-beginning) (region-end)) - (thing-at-point 'symbol))) - (prompt (if default - (format "%s (%s): " prompt default) - (concat prompt ": "))) - (input (read-string prompt nil nil default))) - (unless (webjump-null-or-blank-string-p input) - (substring-no-properties input)))) - -(defun km/webjump () - "Run`webjump' with symbol at point or region as default query. -This affects only sites in the `simple-query' format." - (interactive) - (cl-letf (((symbol-function 'webjump-read-string) #'km/webjump-read-string)) - (call-interactively #'webjump))) - -(define-key km/external-map "j" 'km/webjump) - - -;;; Other - -(defun km/columnify-file (delim) - "Separate current file on DELIM using column program. - -By default, DELIM is set to \",\". With a single prefix argument, -use whitespace as the delimiter. With two prefix arguments, -prompt for a delimiter. - -If a columnified buffer already exists, just switch to it." - (interactive (list (cond ((not current-prefix-arg) ",") - ((> (prefix-numeric-value current-prefix-arg) 4) - (read-string "Delimiter: ")) - (t nil)))) - (unless buffer-file-name - (user-error "Buffer not visiting a file")) - (let* ((output-buffer-name (concat "*cols: " (buffer-name) "*")) - (output-buffer (get-buffer output-buffer-name)) - (fname (file-relative-name buffer-file-name)) - (args (cons "--table" - (and delim (list "--separator" delim))))) - (unless output-buffer - (setq output-buffer (get-buffer-create output-buffer-name)) - (apply #'call-process "column" fname output-buffer nil args)) - (switch-to-buffer output-buffer))) - -(provide 'init-external) -;;; init-external.el ends here diff --git a/lisp/init-files.el b/lisp/init-files.el deleted file mode 100644 index 8b53a38..0000000 --- a/lisp/init-files.el +++ /dev/null @@ -1,245 +0,0 @@ -;;; init-files.el --- File-related configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'uniquify) - -(add-to-list 'load-path "~/src/emacs/nlines/") -(require 'nlines-autoloads) - -(setq require-final-newline t - ffap-machine-p-known 'reject) - -(after 'mailcap - (mailcap-parse-mailcaps) - (pcase-dolist (`(_ . ,info) - (cdr (assoc-string "application" mailcap-mime-data))) - ;; Instead of deleting doc-view-mode entry, just make its test - ;; always fail. - (when (eq (cdr (assq 'viewer info)) 'doc-view-mode) - (setf (cdr (assq 'test info)) (lambda (&rest _) nil))))) - -(defun km/rename-current-buffer-file () - "Rename current buffer and file it is visiting." - (interactive) - (let ((name (buffer-name)) - (filename (buffer-file-name))) - (if (not (and filename (file-exists-p filename))) - (user-error "Buffer '%s' is not visiting a file!" name) - (let ((new-name (read-file-name "New name: " filename))) - (if (get-buffer new-name) - (user-error "A buffer named '%s' already exists!" new-name) - (rename-file filename new-name 1) - (rename-buffer new-name) - (set-visited-file-name new-name) - (set-buffer-modified-p nil) - (message "File '%s' successfully renamed to '%s'" - name (file-name-nondirectory new-name))))))) - -;; https://github.com/purcell/emacs.d/blob/master/lisp/init-utils.el -(defun km/delete-this-file () - "Delete the current file, and kill the buffer." - (interactive) - (or (buffer-file-name) (user-error "No file is currently being edited")) - (when (yes-or-no-p (format "Really delete '%s'?" - (file-name-nondirectory buffer-file-name))) - (delete-file (buffer-file-name)) - (kill-this-buffer))) - -;; http://emacs-fu.blogspot.com/2013/03/editing-with-root-privileges-once-more.html -(defun km/find-file-as-root () - "Automatically edit file with root-privileges." - (interactive) - (let ((file (read-file-name "Edit as root: "))) - (unless (file-writable-p file) - (setq file (concat "/sudo:root@localhost:" file))) - (find-file file))) - -(defun km/dired-jump-file-at-point () - "Run `dired-jump' on file at point." - (interactive) - (let ((file (or (and (use-region-p) - (buffer-substring-no-properties - (region-beginning) (region-end))) - (thing-at-point 'filename)))) - (if (and file (file-exists-p file)) - (dired-jump 'other-window (expand-file-name file)) - (user-error "No file at point")))) - -(defun km/touch-buffer-file () - "Run touch on `buffer-file-name'." - (interactive) - (call-process "touch" nil nil nil - (or (buffer-file-name (buffer-base-buffer)) - (user-error "Not visiting file")))) - -(defun km/write-file () - "Run `write-file'. -Use the current file name as initial input of prompt." - (interactive) - (let* ((init-file (and buffer-file-name - (file-name-nondirectory buffer-file-name))) - (new-file (read-file-name "Write file: " nil nil nil - init-file))) - (write-file new-file t))) - -(global-set-key (kbd "C-x C-w") 'km/write-file) - -(define-key ctl-x-4-map "v" 'view-file-other-window) - -(define-prefix-command 'km/file-map) -(global-set-key (kbd "C-c f") 'km/file-map) - -(define-key km/file-map "j" 'km/dired-jump-file-at-point) -(define-key km/file-map "R" 'km/find-file-as-root) -(define-key km/file-map "n" 'km/rename-current-buffer-file) -(define-key km/file-map "l" 'nlines-run-command) -(define-key km/file-map "t" 'km/touch-buffer-file) -(define-key km/file-map "v" 'view-file) - - -;;; Search - -(autoload 'vc-git-grep "vc-git" - "Run git grep, searching for REGEXP in FILES in directory DIR. -The search is limited to file names matching shell pattern FILES. -FILES may use abbreviations defined in `grep-files-aliases', e.g. -entering `ch' is equivalent to `*.[ch]'.") - -(add-hook 'grep-setup-hook 'km/grep-hide-header) -(add-hook 'grep-mode-hook 'toggle-truncate-lines) - -(defun km/grep-hide-header () - (let ((beg (save-excursion (goto-char (point-min)) - (line-beginning-position 5)))) - (narrow-to-region beg (point-max)))) - -(defun km/grep-avy-goto-subword-1 () - "Like `avy-goto-subword-1', but call `compilation-display-error'." - (interactive) - (let (avy-all-windows) - (call-interactively #'avy-goto-subword-1)) - (compilation-display-error)) - -(after 'grep - (define-key grep-mode-map "j" 'km/grep-avy-goto-subword-1)) - -(define-prefix-command 'km/file-search-map) -(define-key km/file-map "s" 'hydra-file-search-map/body) - -(defhydra hydra-file-search-map (:hint nil :color blue) - " -^^Grep ^^Dired -^^------------ ^^------------------ -_f_: grep-find _d_: find-grep-dired -_g_: lgrep _D_: find-dired -_G_: grep _n_: find-name-dired -_r_: rgrep -_v_: vc-git-grep -_z_: zgrep -\n" - ("f" grep-find) - ("g" lgrep) - ("G" grep) - ("r" rgrep) - ("v" vc-git-grep) - ("z" zrgrep) - - ("d" find-grep-dired) - ("D" find-dired) - ("n" find-name-dired) - - ("q" nil "quit")) - - -;;; Recent files - -(setq recentf-max-menu-items 15 - recentf-max-saved-items 200 - recentf-save-file "~/.emacs.d/cache/recentf") -(recentf-mode) - -;; Modified from prelude -(defun km/recentf-find-file () - "Find a file from `recentf-list'." - (interactive) - (find-file (km/read-recent-file))) - -(defun km/recentf-find-file-other-window () - "Find a file from `recentf-list' in other window." - (interactive) - (find-file-other-window (km/read-recent-file))) -;; This overrides `find-file-read-only-other-window', but -;; `view-file-other-window', which I map to 'v', has the same -;; functionality. -(defun km/read-recent-file () - (completing-read "Choose recent file: " recentf-list nil t)) - -(define-key ctl-x-4-map "r" 'km/recentf-find-file-other-window) - - -;;; Scratch files - -(defvar km/find-scratch-buffers - '((?e ".el" "Elisp") - (?g ".scm" "Guile scheme") - (?h ".hs" "Haskell") - (?m ".md" "Markdown") - (?n "" "No mode") - (?o ".org" "Org") - (?p ".py" "Python") - (?r ".r" "R") - (?s ".sh" "Shell") - (?t ".txt" "Text")) - "List of scratch buffers. -Format of each element should be (CHARACTER EXTENSION DOC). DOC -is not required.") - -(defun km/scratch-find-file (&optional pwd) - "Find scratch buffer. - -Prompt with characters from `km/find-scratch-buffers' to -determine the extension of the scratch file. - -With prefix argument PWD, find the scratch file in -`default-directory' instead of /tmp." - (interactive "P") - (switch-to-buffer (km/scratch--find-file-no-select pwd))) - -(defun km/scratch-find-file-other-window (&optional pwd) - "Like `km/find-scratch-file', but open buffer in another window." - (interactive "P") - (switch-to-buffer-other-window (km/scratch--find-file-no-select pwd))) - -(defun km/scratch--find-file-no-select (pwd) - (find-file-noselect (km/scratch--get-file-name pwd))) - -(defun km/scratch--get-file-name (pwd) - (let* ((choices (mapcar #'car km/find-scratch-buffers)) - (ch (read-char-choice (concat "[" choices "]") choices)) - (ext (cadr (assq ch km/find-scratch-buffers)))) - (concat (if pwd default-directory "/tmp/") "scratch" ext))) - -(global-set-key (kbd "C-c s") 'km/scratch-find-file) -(define-key ctl-x-4-map "s" 'km/scratch-find-file-other-window) - -(provide 'init-files) -;;; init-files.el ends here diff --git a/lisp/init-framewin.el b/lisp/init-framewin.el deleted file mode 100644 index 8821610..0000000 --- a/lisp/init-framewin.el +++ /dev/null @@ -1,87 +0,0 @@ -;;; init-framewin.el --- Frame and window configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(defun km/clone-indirect-buffer-other-window-and-widen () - "Clone as indirect buffer and then widen." - (interactive) - (call-interactively #'clone-indirect-buffer-other-window) - (widen)) - -;; From prelude -(defun km/swap-windows () - "Swap 2 windows." - (interactive) - (if (/= (count-windows) 2) - (message "You need exactly 2 windows to do this.") - (let* ((w1 (car (window-list))) - (w2 (cadr (window-list))) - (b1 (window-buffer w1)) - (b2 (window-buffer w2)) - (s1 (window-start w1)) - (s2 (window-start w2))) - (set-window-buffer w1 b2) - (set-window-buffer w2 b1) - (set-window-start w1 s2) - (set-window-start w2 s1))) - (other-window 1)) - -;; http://www.emacswiki.org/emacs/ToggleWindowSplit -(defun km/switch-window-split () - "If the window is split vertically, split it horizontally or vice versa. -Assumes that the window is only split into two." - (interactive) - (unless (= (length (window-list)) 2) - (user-error "Can only toggle a window split in two")) - (let ((split-vertically-p (window-combined-p))) - (delete-window) - (if split-vertically-p - (split-window-horizontally) - (split-window-vertically)) - (switch-to-buffer nil))) - -(defhydra hydra-window-map (:hint nil) - " -_l_: Switch split _r_: Winner redo _o_: Scroll other -_s_: Swap _u_: Winner undo _i_: Scroll other down -\n" - ("l" km/switch-window-split) - ("s" km/swap-windows) - - ("r" winner-redo) - ("u" winner-undo) - - ("o" scroll-other-window) - ("i" scroll-other-window-down) - - ("f" make-frame "new frame" :color blue) - ("q" nil "quit")) - -(global-set-key (kbd "C-c w") 'hydra-window-map/body) - -(key-chord-define-global "lq" 'winner-undo) - -(define-key ctl-x-4-map "c" 'km/clone-indirect-buffer-other-window-and-widen) - -(winner-mode 1) - -(provide 'init-framewin) -;;; init-framewin.el ends here diff --git a/lisp/init-general.el b/lisp/init-general.el deleted file mode 100644 index b62016f..0000000 --- a/lisp/init-general.el +++ /dev/null @@ -1,110 +0,0 @@ -;;; init-general.el --- Things without a more specific home - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(setq echo-keystrokes 0.1 - use-dialog-box nil - visible-bell t - enable-recursive-minibuffers t) - -(setq tramp-default-method "sshx") - -(setq-default indicate-empty-lines t - indent-tabs-mode nil) - -(setq set-mark-command-repeat-pop t) - -(setq recenter-positions '(top middle bottom)) - -;; This is intentionally not loaded. -(setq custom-file "~/.emacs.d/.custom.el") - -(setq bookmark-save-flag nil) - -(setq default-input-method "TeX") - -(defalias 'yes-or-no-p 'y-or-n-p) - -(put 'narrow-to-region 'disabled nil) -(put 'narrow-to-page 'disabled nil) -(put 'set-goal-column 'disabled nil) -(put 'downcase-region 'disabled nil) -(put 'upcase-region 'disabled nil) - -(add-hook 'after-save-hook - 'executable-make-buffer-file-executable-if-script-p) - -(add-hook 'text-mode-hook 'turn-on-auto-fill) - -(add-to-list 'auto-mode-alist '("\\.zsh\\'" . shell-script-mode)) -(add-to-list 'auto-mode-alist '("\\.*rc\\'" . conf-unix-mode)) - -(global-set-key (kbd "C-h ;") 'find-function) -(global-set-key (kbd "C-h 4 ;") 'find-function-other-window) - - -(global-set-key (kbd "C-c l") 'helm-imenu) - -;; Disable `suspend-frame' binding. -(global-set-key (kbd "C-x C-z") nil) - -;; Avoid shift key for `backward-paragraph' and `forward-paragraph'. -(global-set-key (kbd "M-}") nil) -(global-set-key (kbd "M-]") 'forward-paragraph) -(global-set-key (kbd "M-{") nil) -(global-set-key (kbd "M-[") 'backward-paragraph) - -(global-set-key (kbd "C-c x") 'eval-expression) - -(show-paren-mode) -(global-auto-revert-mode) -(key-chord-mode 1) - - -;;; Set key - -(defvar km/setkey-command nil) -(defvar km/setkey-last-call-time nil) -(defvar km/setkey-seconds-timeout 600) - -(defun km/setkey-call () - "Call `km/setkey-command'. -When `km/setkey-command' is nil or the time since the last call -has exceeded `km/setkey-seconds-timeout', read the command to -call." - (interactive) - (when (or (not km/setkey-command) - (> (- (float-time) km/setkey-last-call-time) - km/setkey-seconds-timeout)) - (setq km/setkey-command (read-command "Command: " km/setkey-command))) - (setq km/setkey-last-call-time (float-time)) - (call-interactively km/setkey-command)) - -(defun km/setkey-reset () - "Reset `km/setkey-call' command." - (interactive) - (setq km/setkey-command nil - km/setkey-last-call-time nil)) - -(global-set-key (kbd "C-c v") 'km/setkey-call) - -(provide 'init-general) -;;; init-general.el ends here diff --git a/lisp/init-git.el b/lisp/init-git.el deleted file mode 100644 index ef954eb..0000000 --- a/lisp/init-git.el +++ /dev/null @@ -1,817 +0,0 @@ -;;; init-git.el --- (Ma)git configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'git-annex) - -(setq vc-follow-symlinks t) - -(setq git-annex-commit nil) - -(define-prefix-command 'km/git-map) -(global-set-key (kbd "C-c g") 'km/git-map) - - -;;; Magit - -(add-to-list 'load-path "~/src/emacs/magit/lisp/") -(require 'magit) - -(add-to-list 'load-path "~/src/emacs/orgit/") -(require 'orgit) - -(setq magit-revert-buffers 'silent - magit-revert-buffers-only-for-tracked-files nil - magit-push-always-verify nil - magit-delete-by-moving-to-trash nil - magit-diff-auto-show-delay 0.1 - magit-revision-show-gravatars nil - magit-log-section-arguments nil - magit-log-show-margin nil) - -(setq magit-uniquify-buffer-names nil - magit-buffer-name-format "*%M%v: %t*") - -(setq git-commit-finish-query-functions nil) - -(setq magit-no-confirm '(stage-all-changes unstage-all-changes reverse)) - -(setq git-commit-finish-query-functions nil) - -(remove-hook 'magit-refs-sections-hook 'magit-insert-tags) - -(add-hook 'git-commit-setup-hook - (lambda () - (add-hook 'with-editor-pre-finish-hook - 'git-commit-save-message nil t))) -(add-hook 'git-commit-setup-hook 'git-commit-turn-on-flyspell) - -(add-hook 'magit-post-display-buffer-hook - (lambda () - (when (eq major-mode 'magit-status-mode) - (delete-other-windows)))) - -(defun km/magit-auto-commit () - "Commit all changes with \"auto\" commit message. -This can be useful for non-source code repos (e.g., Org mode note -files) or commiting incomplete changes that will be extended into -a proper commit." - (interactive) - (magit-run-git "commit" "--all" "--message=auto")) - -(defun km/magit-show-commit-at-point (&optional choose-project) - "Show commit point. -If there is no current project or if the prefix argument -CHOOSE-PROJECT is non-nil, prompt for the project name." - (interactive "P") - (if (save-excursion (skip-chars-backward "A-z0-9") - (looking-at "\\b[A-z0-9]\\{4,40\\}\\b")) - (let* ((hash (match-string-no-properties 0)) - (project - (and (or choose-project - (not (projectile-project-p)) - (not (magit-rev-verify (concat hash "^{commit}")))) - (completing-read "Project: " - (projectile-relevant-known-projects)))) - (default-directory (or project default-directory))) - (magit-show-commit hash (car (magit-diff-arguments)))) - (user-error "No hash found at point"))) - -(defun km/magit-commit-extend-with-file () - "Extend last commit with changes in the current file." - (interactive) - (let ((file (or (magit-current-file) - (user-error "No current file")))) - (cond ((magit-anything-staged-p) - (user-error "There are already staged changes")) - ((member file (nconc (magit-untracked-files) - (magit-modified-files))) - (magit-with-toplevel (magit-stage-file file)) - (magit-commit-extend)) - (t - (message "No changes to %s" file))))) - -(defun km/magit-commit-wip-with-file () - "Make a WIP commit for the current file. -Unlike `magit-wip-*' commands, this commit is made to the current -branch." - (interactive) - (let ((file (or (magit-current-file) - (user-error "No current file")))) - (cond ((magit-anything-staged-p) - (user-error "There are already staged changes")) - ((member file (nconc (magit-untracked-files) - (magit-modified-files))) - (magit-with-toplevel (magit-stage-file file)) - (magit-run-git "commit" (concat "--message=WIP " file))) - (t - (message "No changes to %s" file))))) - -(defun km/magit-ff-merge-upstream () - "Perform fast-forward merge of upstream branch. -\n(git merge --no-edit --ff-only )" - (interactive) - (--if-let (magit-get-tracked-branch) - (magit-merge it '("--ff-only")) - (user-error "No upstream branch"))) - -(defun km/magit-stage-file-intent (file) - "Stage FILE but not its content. -With a prefix argument or when there is no file at point, ask for -the file to be staged. Otherwise, stage the file at point -without requiring confirmation. -\n(git add -N FILE)" - ;; Modified from `magit-stage-file'. - (interactive - (let* ((atpoint (magit-section-when (file))) - (current (magit-file-relative-name)) - (choices (magit-untracked-files)) - (default (car (member (or atpoint current) choices)))) - (list (if (or current-prefix-arg (not default)) - (magit-completing-read "Stage file" choices - nil t nil nil default) - default)))) - (magit-run-git "add" "-N" file)) - -(defun km/magit-push-all () - "Push all branches." - (interactive) - (magit-run-git-async "push" "-v" - (magit-read-remote "Remote") - "--all")) - -(defun km/magit-push-head (remote &optional args) - "Push current branch to same name on remote. -\n(git push [ARGS] REMOTE HEAD)" - (interactive (list (magit-read-remote "Remote") (magit-push-arguments))) - (magit-run-git-async "push" "-v" args remote "HEAD")) - -(defun km/magit-checkout-local-tracking (remote-branch) - "Create and checkout a local tracking branch for REMOTE-BRANCH. -\n(git checkout -t REMOTE-BRANCH\)" - (interactive - (list (let ((branches (magit-list-remote-branch-names))) - (magit-completing-read - "Remote branch" branches nil t nil nil - (car (member (magit-branch-or-commit-at-point) - branches)))))) - (magit-run-git "checkout" "-t" remote-branch)) - -(defun km/magit-branch-rename (old new &optional force) - "Like `magit-branch-rename', but use old branch as initial prompt." - (interactive - (let ((branch (magit-read-local-branch "Rename branch"))) - (list branch - (magit-read-string-ns (format "Rename branch '%s' to" branch) - branch) - current-prefix-arg))) - (unless (string= old new) - (magit-run-git-no-revert "branch" (if force "-M" "-m") old new))) - -(defun km/magit-delete-previous-branch (&optional force) - "Delete previous branch. -\n(git branch -d @{-1})" - (interactive "P") - (magit-run-git "branch" (if force "-D" "-d") "@{-1}")) - -(defun km/magit-checkout-previous-branch () - "Checkout previous branch. -\n(git checkout -)" - (interactive) - (magit-run-git "checkout" "-")) - -(defun km/magit-list-recent-refs (n &optional remote) - "List N recent refs. -If REMOTE is non-nil, limit to remote refs." - (magit-git-lines - "for-each-ref" "--sort=-committerdate" "--format=%(refname:short)" - (format "--count=%s" n) - (if remote "refs/remotes" "refs/heads"))) - -(defun km/magit-checkout-recent-ref (n) - "Checkout branch from N recent refs. -Refs are sorted by committer date." - (interactive (list (or (and current-prefix-arg - (prefix-numeric-value current-prefix-arg)) - 5))) - (magit-run-git "checkout" - (magit-completing-read - "Ref" (km/magit-list-recent-refs n)))) - -(defun km/magit-checkout-track-recent-ref (n) - "Create and checkout a local tracking branch. -Listed refs are limited to N most recent, sorted by committer -date." - (interactive (list (or (and current-prefix-arg - (prefix-numeric-value current-prefix-arg)) - 5))) - (magit-run-git "checkout" "-t" - (magit-completing-read - "Ref" (km/magit-list-recent-refs n 'remote)))) - -(defun km/magit-refs-filter-recent (n) - "Limit branch list to N most recent. -Warning: I find this useful, but it's a hack that breaks -magit-section-backward and probably other things. Hit `g` to -refresh the buffer, and all should be right again." - (interactive (list (or (and current-prefix-arg - (prefix-numeric-value current-prefix-arg)) - 5))) - (unless (derived-mode-p 'magit-refs-mode) - (user-error "Not in Magit Refs mode")) - (let ((sec (magit-current-section)) - remote refs line-sec) - (when (eq (magit-section-type sec) 'branch) - (setq sec (magit-section-parent sec))) - (when (eq (magit-section-type sec) 'remote) - (setq remote (magit-section-value sec))) - (setq refs - (magit-git-lines - "for-each-ref" "--sort=-committerdate" "--format=%(refname:short)" - (format "--count=%s" n) - (if remote (format "refs/remotes/%s" remote) "refs/heads"))) - (save-excursion - (save-restriction - (narrow-to-region (magit-section-content sec) (magit-section-end sec)) - (goto-char (point-min)) - (while (and (not (eobp)) - (setq line-sec (magit-current-section)) - (eq (magit-section-type line-sec) 'branch)) - (if (member (magit-section-value line-sec) refs) - (forward-line 1) - (let ((inhibit-read-only t)) - (delete-region (magit-section-start line-sec) - (magit-section-end line-sec))))))))) - -(defun km/magit-checkout-master () - "Checkout master branch. -\n(git checkout master)" - (interactive) - (magit-run-git "checkout" "master")) - -(defun km/magit-branch-and-checkout-from-current (branch) - "Create and checkout BRANCH at current branch. -This is equivalent to running `magit-branch-and-checkout' with -START-POINT set to the current branch. -\n(git checkout -b BRANCH)" - (interactive (list (magit-read-string "Branch name"))) - (magit-run-git "checkout" "-b" branch)) - -(defun km/magit-backup-branch () - "Create a backup branch for the current branch. -\n(git branch b/)" - (interactive) - (--if-let (magit-get-current-branch) - (magit-run-git "branch" (concat "b/" it)) - (user-error "No current branch"))) - -(defun km/magit-mode-bury-all-windows (&optional kill-buffer) - "Run `magit-mode-quit-window' until no longer in Magit buffer." - (interactive "P") - (while (derived-mode-p 'magit-mode) - (magit-mode-bury-buffer kill-buffer))) - -(defun km/magit-log-select-guess-fixup-commit (&optional ntop) - "Guess commit from fixup/squash commmits. -Consider NTOP commits (default is 5) when searching for 'fixup!' -and 'squash!' titles." - (interactive (list (or (and current-prefix-arg - (prefix-numeric-value current-prefix-arg)) - 5))) - (let (ntop-end msgs commit-pts) - (save-excursion - ;; Get limit for fixup/squash search. - (goto-char (point-min)) - (setq ntop-end (line-end-position (1+ ntop))) - ;; Get fixup and squash messages. - (while (re-search-forward "[a-z0-9]+ \\(fixup!\\|squash!\\) \\(.+\\)" - ntop-end t) - (push (match-string-no-properties 2) msgs)) - (when (not msgs) - (user-error "No fixup or squash commits found")) - ;; Find earliest commit. - (goto-char (point-min)) - (dolist (msg msgs) - (when (re-search-forward (concat "[a-z0-9]+ " msg "\n") nil t) - (push (match-beginning 0) commit-pts)))) - (if commit-pts - (goto-char (apply #'max commit-pts)) - (message "No matching commits found")))) - -(defun km/magit-reset-file (rev file &optional checkout) - "Reset FILE from revision REV. - -If prefix argument CHECKOUT is non-nil, checkout FILE from REV -instead. - -\(git reset REV -- FILE) -\(git checkout REV -- FILE)" - (interactive - (let ((rev (magit-read-branch-or-commit "Revision" magit-buffer-revision))) - (list rev (magit-read-file-from-rev rev "File") current-prefix-arg))) - (magit-with-toplevel - (magit-run-git (if checkout "checkout" "reset") - rev "--" file))) - -(defun km/magit-pin-file (&optional other-rev) - "Pin this file to the current revision. - -Visit the current file and current revision with -`magit-find-file'. Position point as in the original buffer. -This may not correspond to same content if text before point has -changed since the current commit. - -If OTHER-REV is non-nil, prompt for another revision instead of -the current. If buffer is already a revision buffer, then find -the working tree copy instead. In both these cases, point may -not land in a reasonable location depending on how the content of -the file has changed." - (interactive "P") - (magit-with-toplevel - (let* ((line (+ (if (bolp) 1 0) (count-lines 1 (point)))) - (col (current-column)) - (rev (cond (other-rev - (magit-read-branch-or-commit "Find file from revision")) - ((not magit-buffer-file-name) - (or (magit-get-current-branch) - (magit-rev-parse "HEAD"))))) - (fname (file-relative-name - (or buffer-file-name - magit-buffer-file-name - (user-error "Buffer not visiting file"))))) - (if rev (magit-find-file rev fname) (find-file fname)) - (goto-char (point-min)) - (forward-line (1- line)) - (move-to-column col)))) - -(defun km/magit-revfile-reset (&optional checkout) - "Reset to revision from current revfile. -If CHECKOUT is non-nil, checkout file instead." - (interactive "P") - (unless (and magit-buffer-refname magit-buffer-file-name) - (user-error "Not in Magit revfile buffer")) - (magit-with-toplevel - (magit-run-git (if checkout "checkout" "reset") - magit-buffer-refname "--" magit-buffer-file-name))) - -(defun km/magit-find-recently-changed-file (n) - "Find a file that changed from \"HEAD~N..HEAD\". -N defaults to 20." - (interactive "p") - (unless current-prefix-arg (setq n 10)) - (magit-with-toplevel - (find-file (magit-completing-read - "File" - (magit-changed-files (format "HEAD~%s..HEAD" n)) - nil t)))) - -(defun km/magit-find-commit-file (commit) - "Find file changed in COMMIT." - (interactive (list (or (magit-branch-or-commit-at-point) - (and (derived-mode-p 'magit-revision-mode) - (car magit-refresh-args)) - (magit-read-branch-or-commit "Commit")))) - (let ((files (magit-changed-files (format "%s~..%s" commit commit)))) - (find-file - (cl-case (length files) - (0 (user-error "No changed files in %s" commit)) - (1 (car files)) - (t (magit-completing-read "File" files nil t)))))) - -(defun km/magit-insert-staged-file (&optional no-directory) - "Select staged file to insert. - -This is useful for referring to file names in commit messages. -By default, the path for the file name is relative to the top -directory of the repository. Remove the directory component from -the file name if NO-DIRECTORY is non-nil. - -If there are no staged files, look instead at files that changed -in HEAD. These rules will usually offer the files of interest -while commiting, but this is not the case if you are amending a -commit with the \"--only\" flag and have staged files (i.e., this -command will still offer the staged files)." - (interactive "P") - (magit-with-toplevel - (let* ((files (or (magit-staged-files) - (magit-changed-files "HEAD^..HEAD"))) - (file (cl-case (length files) - (1 (car files)) - (0 (error "No files found")) - (t - (completing-read "Staged file: " files nil t))))) - (insert (if no-directory (file-name-nondirectory file) file))))) - -(defun km/magit-shorten-hash (hash &optional n) - (magit-rev-parse (format "--short=%s" (or n (magit-abbrev-length))) hash)) - -(defun km/magit-shorten-hash-at-point (&optional n) - "Shorten hash at point to N characters. - -N defaults to `magit-abbrev-length'. If the commit belongs to -the current repo and the hash is ambiguous, the hash is extended -as needed. - -To explicitly set the hash length, use a numeric prefix -argument." - (interactive (list (or (and current-prefix-arg - (prefix-numeric-value current-prefix-arg)) - (magit-abbrev-length)))) - (cond - ((< n 4) - (user-error "Hash must be at least 4 characters")) - ((>= n 40) - (user-error "Full hashes are 40 characters")) - ((> n 30) - (message "That doesn't seem incredibly useful, but OK"))) - (let ((offset (- (skip-chars-backward "A-z0-9")))) - (if (looking-at "\\b[A-z0-9]\\{5,40\\}\\b") - (let ((hash-len (- (match-end 0) (match-beginning 0))) - (hash (match-string 0))) - (when (< hash-len n) - (user-error "Desired hash length is greater than current")) - (replace-match (or (km/magit-shorten-hash hash n) - ;; We're not in a repo. - (substring hash 0 n)) - 'fixedcase) - (when (< offset n) - (skip-chars-backward "A-z0-9") - (goto-char (+ (point) offset)))) - (goto-char (+ (point) offset)) - (user-error "No hash found at point")))) - -(defvar km/magit-copy-hook - '(km/magit-copy-commit-summary-from-header - km/magit-copy-commit-message - km/magit-copy-region-commits - km/magit-copy-region-hunk - km/magit-copy-hunk) - "Functions tried by `km/magit-copy-as-kill'. -These will be given one argument (the current prefix value) and -should succeed by copying and returning non-nil or fail by -returning nil.") - -(defun km/magit-copy-commit-summary (commit) - "Copy a citation for the COMMIT at point. -Format the reference as ', (, )'. If there -is no commit at point or with a prefix argument, prompt for -COMMIT." - (interactive - (let ((atpoint (or (and magit-blame-mode (magit-blame-chunk-get :hash)) - (magit-branch-or-commit-at-point) - (magit-tag-at-point)))) - (list (or (and (not current-prefix-arg) atpoint) - (magit-read-branch-or-commit "Commit" atpoint))))) - (if (magit-rev-verify (concat commit "^{commit}")) - (kill-new (message - ;; Using `magit-git-string' instead of - ;; `magit-rev-format' to pass --date flag. - (magit-git-string "show" "-s" "--date=short" - "--format=%h (%s, %ad)" - commit "--"))) - (user-error "%s does not exist" commit))) - -(defun km/magit-copy-commit-summary-from-header (&optional arg) - (magit-section-when headers - (km/magit-copy-commit-summary (car magit-refresh-args)))) - -(defun km/magit-copy-region-commits (&optional arg) - (--when-let (magit-region-values 'commit) - (deactivate-mark) - (kill-new - (mapconcat #'identity it - (if arg (read-string "Separator: ") ", "))))) - -(defun km/magit-copy-commit-message (&optional arg) - (magit-section-when message - (kill-new (replace-regexp-in-string - "^ " "" - (buffer-substring-no-properties (magit-section-start it) - (magit-section-end it)))))) - -(defun km/magit-copy-region-hunk (&optional no-column) - (when (magit-section-internal-region-p) - (magit-section-when hunk - (deactivate-mark) - (let ((text (buffer-substring-no-properties - (region-beginning) (region-end)))) - (kill-new (if no-column - (replace-regexp-in-string "^[ \\+\\-]" "" text) - text)))))) - -(defun km/magit-copy-hunk (&optional arg) - (magit-section-when hunk - (let ((start (save-excursion (goto-char (magit-section-start it)) - (1+ (point-at-eol))))) - (kill-new (buffer-substring-no-properties - start (magit-section-end it)))))) - -(defun km/magit-copy-as-kill () - "Try `km/magit-copy-hook' before calling `magit-copy-as-kill'. -With a prefix argument of -1, always call `magit-copy-section-value' -Otherwise, the current prefix argument is passed to each hook -function." - (interactive) - (or (unless (= (prefix-numeric-value current-prefix-arg) -1) - (run-hook-with-args-until-success - 'km/magit-copy-hook current-prefix-arg)) - (magit-copy-section-value))) - -(defun km/magit-avy-goto-subword-1 () - "Like `km/avy-goto-subword-1', but maybe show commit and limit to window." - (interactive) - (let (avy-all-windows) - (call-interactively #'avy-goto-subword-1)) - (when (derived-mode-p 'magit-log-mode) - (magit-diff-show-or-scroll-up))) - -(defun km/magit-rev-ancestor-p (rev-a rev-b) - "Report whether REV-A is the ancestor of REV-B. -Use the revision at point as REV-B. With prefix argument or if -there is no revision at point, prompt for the revision. Always -prompt for REV-A." - (interactive - (let* ((atpoint (or (and magit-blame-mode (magit-blame-chunk-get :hash)) - (magit-branch-or-commit-at-point) - (magit-tag-at-point))) - (commit (or (and (not current-prefix-arg) atpoint) - (magit-read-branch-or-commit "Descendant" atpoint)))) - (list (magit-read-other-branch-or-commit - (format "Test if ancestor of %s" commit) commit) - commit))) - (message "%s is %san ancestor of %s" rev-a - (if (magit-git-success "merge-base" "--is-ancestor" - rev-a rev-b) - "" "NOT ") - rev-b)) - -(defun km/magit-refs-toggle-tags () - "Toggle showing tags in `magit-refs-mode'. -This only affects the current buffer and is useful if you do not -show tags by default." - (interactive) - (if (memq 'magit-insert-tags magit-refs-sections-hook) - (remove-hook 'magit-refs-sections-hook 'magit-insert-tags t) - (add-hook 'magit-refs-sections-hook 'magit-insert-tags t t)) - (magit-refresh-buffer)) - -(defun km/magit-log-flip-revs () - "Swap revisions in log range." - (interactive) - (let ((range (caar magit-refresh-args))) - (if (and range - (derived-mode-p 'magit-log-mode) - (string-match magit-range-re range)) - (progn - (setf (caar magit-refresh-args) - (concat (match-string 3 range) - (match-string 2 range) - (match-string 1 range))) - (magit-refresh)) - (user-error "No range to swap")))) - -(defun km/magit-flip-revs () - (interactive) - (cond ((derived-mode-p 'magit-diff-mode) - (call-interactively #'magit-diff-flip-revs)) - ((derived-mode-p 'magit-log-mode) - (call-interactively #'km/magit-log-flip-revs)))) - -(defun km/magit-diff-visit-file (&optional prev-rev other-window) - "Like `magit-diff-visit-file', but with the option to visit REV^. - -If prefix argument PREV-REV is non-nil, visit file for REV^ -instead of REV. If not in `magit-revision-mode', the prefix -argument has no effect. - -OTHER-WINDOW corresponds to `magit-diff-visit-file's OTHER-WINDOW -argument. Interactively, this can be accessed using the command -`km/magit-diff-visit-file-other-window'." - (interactive "P") - (let ((magit-refresh-args (if (and prev-rev - (derived-mode-p 'magit-revision-mode)) - (cons (concat (car magit-refresh-args) "^") - (cdr magit-refresh-args)) - magit-refresh-args)) - (current-prefix-arg (and other-window (list 4)))) - (call-interactively #'magit-diff-visit-file))) - -(defun km/magit-diff-visit-file-other-window (&optional prev-rev) - (interactive "P") - (km/magit-diff-visit-file prev-rev t)) - -(define-key ctl-x-4-map "g" 'magit-find-file-other-window) -(define-key km/file-map "g" 'magit-find-file) - -(key-chord-define-global "jg" 'magit-status) - -;; Remove `magit-add-change-log-entry-other-window', which overrides -;; my binding for `km/zsh-ansi-term-other-window'. -(define-key magit-mode-map (kbd "C-x 4 a") nil) -(define-key magit-mode-map "o" 'magit-push-popup) -(define-key magit-mode-map "P" 'magit-submodule-popup) -(define-key magit-mode-map "Q" 'km/magit-mode-bury-all-windows) -(define-key magit-mode-map (kbd "C-w") 'km/magit-copy-as-kill) - -;; `magit-diff-visit-file-worktree' is also on C-RET. -(define-key magit-file-section-map (kbd "C-j") 'magit-diff-visit-file-worktree) -(define-key magit-hunk-section-map (kbd "C-j") 'magit-diff-visit-file-worktree) -(define-key magit-file-section-map (kbd "C-o") 'km/magit-diff-visit-file-other-window) -(define-key magit-hunk-section-map (kbd "C-o") 'km/magit-diff-visit-file-other-window) - -(define-key magit-log-mode-map "j" 'km/magit-avy-goto-subword-1) -(define-key magit-refs-mode-map "j" 'km/magit-avy-goto-subword-1) -(define-key magit-cherry-mode-map "j" 'km/magit-avy-goto-subword-1) - -(define-key magit-refs-mode-map (kbd "C-c C-t") 'km/magit-refs-toggle-tags) - -(define-key magit-file-section-map [remap magit-visit-thing] - 'km/magit-diff-visit-file) -(define-key magit-hunk-section-map [remap magit-visit-thing] - 'km/magit-diff-visit-file) - -(define-key magit-refs-mode-map (kbd "C-c C-f") 'km/magit-refs-filter-recent) - -(define-key magit-process-mode-map (kbd "C-c C-k") 'magit-process-kill) - -(define-prefix-command 'km/magit-map) -(define-key magit-mode-map "." 'km/magit-map) -(define-key km/magit-map "c" 'km/magit-find-commit-file) -(define-key km/magit-map "g" 'km/git-map) -(define-key km/magit-map "l" 'magit-toggle-buffer-lock) -(define-key km/magit-map "f" 'km/magit-flip-revs) - -(define-prefix-command 'km/magit-wip-map) -(define-key km/git-map "w" 'km/magit-wip-map) -(define-key km/magit-wip-map "a" 'magit-wip-after-apply-mode) -(define-key km/magit-wip-map "b" 'magit-wip-before-change-mode) -(define-key km/magit-wip-map "c" 'magit-wip-commit) -(define-key km/magit-wip-map "f" 'magit-wip-commit-buffer-file) -(define-key km/magit-wip-map "l" 'magit-wip-log-current) -(define-key km/magit-wip-map "o" 'magit-wip-log) -(define-key km/magit-wip-map "s" 'magit-wip-after-save-mode) -(define-key km/magit-wip-map "S" 'magit-wip-after-save-local-mode) -(define-key km/magit-wip-map "w" 'km/magit-commit-wip-with-file) - -(define-key km/git-map "." 'km/magit-show-commit-at-point) -(define-key km/git-map "c" 'km/magit-copy-commit-summary) -(define-key km/git-map "d" 'magit-dispatch-popup) -(define-key km/git-map "e" 'km/magit-commit-extend-with-file) -(define-key km/git-map "f" 'km/magit-reset-file) -(define-key km/git-map "i" 'km/magit-insert-staged-file) -(define-key km/git-map "n" 'km/magit-shorten-hash-at-point) -(define-key km/git-map "l" 'magit-log-buffer-file) -(define-key km/git-map "p" 'km/magit-pin-file) -(define-key km/git-map "r" 'km/magit-find-recently-changed-file) -(define-key km/git-map "s" 'magit-stage-file) -(define-key km/git-map "u" 'km/magit-auto-commit) -(define-key km/git-map "v" 'km/magit-revfile-reset) - -(define-key magit-log-select-mode-map "." - 'km/magit-log-select-guess-fixup-commit) - - -;;; Magit popups - -(setq magit-popup-show-help-echo nil - magit-popup-show-common-commands nil - magit-popup-use-prefix-argument 'default) - -(setq magit-branch-arguments - (delete "--track" magit-branch-arguments)) - -(define-key magit-popup-mode-map (kbd "SPC ") 'magit-invoke-popup-switch) -(define-key magit-popup-mode-map (kbd "SPC SPC ") 'magit-invoke-popup-option) - -(setq magit-patch-popup - (plist-put magit-patch-popup :use-prefix 'popup)) - -(magit-define-popup-action 'magit-commit-popup - ?u "Auto commit" 'km/magit-auto-commit) - -(magit-define-popup-action 'magit-push-popup - ?a "Push all" 'km/magit-push-all) -(magit-define-popup-action 'magit-push-popup - ?h "Push HEAD" 'km/magit-push-head) - -(magit-define-popup-action 'magit-merge-popup - ?u "Merge upstream" 'km/magit-ff-merge-upstream) - -(magit-define-popup-action 'magit-diff-popup - ?e "Edit options" 'magit-diff-refresh-popup) - -(magit-change-popup-key 'magit-stash-popup :action - ?Z ?s) - -(magit-define-popup-switch 'magit-log-popup - ?p "First parent" "--first-parent") -(magit-define-popup-switch 'magit-log-popup - ?n "No merges" "--no-merges") -(magit-define-popup-switch 'magit-log-popup - ?t "Date order" "--date-order") - -(magit-define-popup-action 'magit-log-popup - ?e "Edit options" 'magit-log-refresh-popup) -(magit-define-popup-action 'magit-log-popup - ?w "Log current WIP" 'magit-wip-log-current) -(magit-define-popup-action 'magit-log-popup - ?W "Log other WIP" 'magit-wip-log) - -(magit-change-popup-key 'magit-branch-popup :action - ?c ?o) -(magit-change-popup-key 'magit-branch-popup :action - ?n ?C) -(magit-change-popup-key 'magit-branch-popup :action - ?m ?R) -(magit-change-popup-key 'magit-branch-popup :action - ?s ?v) - -(magit-define-popup-action 'magit-branch-popup - ?c "Create & checkout from current" - 'km/magit-branch-and-checkout-from-current) - -(magit-define-popup-action 'magit-branch-popup - ?K "Delete previous branch" 'km/magit-delete-previous-branch) -(magit-define-popup-action 'magit-branch-popup - ?m "Checkout master" 'km/magit-checkout-master) -(magit-define-popup-action 'magit-branch-popup - ?n "Checkout recent ref" 'km/magit-checkout-recent-ref) -(magit-define-popup-action 'magit-branch-popup - ?N "Track recent ref" 'km/magit-checkout-track-recent-ref) -(magit-define-popup-action 'magit-branch-popup - ?l "Checkout previous" 'km/magit-checkout-previous-branch) -(magit-define-popup-action 'magit-branch-popup - ?r "Rename branch" 'km/magit-branch-rename) -(magit-define-popup-action 'magit-branch-popup - ?s "Backup current branch" 'km/magit-backup-branch) -(magit-define-popup-action 'magit-branch-popup - ?t "Local tracking" 'km/magit-checkout-local-tracking) - -(defadvice magit-merge-editmsg (around km/magit-merge-editmsg-no-ff activate) - "Set '--no-ff' flag when running `magit-merge-editmsg'." - (let ((args '("--no-ff"))) - ad-do-it)) - - -;;; Magit Annex - -(add-to-list 'load-path "~/src/emacs/magit-annex/") -(require 'magit-annex) - -(setq magit-annex-unused-open-function #'org-open-file) - -(setq magit-annex-all-action-arguments - (delete "--auto" magit-annex-all-action-arguments)) - - -;;; Other git - -(setq smerge-diff-switches '("-d" "-b" "-u")) - -(define-key km/git-map "m" - (defhydra hydra-smerge (:hint nil) - " -_b_ keep base _d_ diff _n_ next -_m_ keep mine _e_ ediff _p_ previous -_o_ keep other _h_ refine -_a_ keep all -\n" - ("b" smerge-keep-base) - ("m" smerge-keep-mine) - ("o" smerge-keep-other) - ("a" smerge-keep-all) - ("n" smerge-next) - ("p" smerge-prev) - ("h" smerge-refine) - ("e" smerge-ediff :color blue) - ("d" (call-interactively - (pcase (read-char-choice - "< base-mine, > base-other, = mine-other" - (list ?< ?> ?=)) - (?< #'smerge-diff-base-mine) - (?> #'smerge-diff-base-other) - (?= #'smerge-diff-mine-other)))) - ("l" recenter-top-bottom "recenter") - ("u" undo "undo") - ("q" nil "quit"))) - -(provide 'init-git) -;;; init-git.el ends here diff --git a/lisp/init-god.el b/lisp/init-god.el deleted file mode 100644 index 306f476..0000000 --- a/lisp/init-god.el +++ /dev/null @@ -1,68 +0,0 @@ -;;; init-god.el --- God mode configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'god-mode) - -(add-to-list 'god-exempt-predicates #'km/god-gnus-p) - -(add-hook 'view-mode-hook (lambda () - (if view-mode - (god-local-mode-pause) - (god-local-mode-resume)))) - -(add-hook 'org-capture-mode-hook (lambda () (god-local-mode -1))) - -(add-hook 'god-mode-enabled-hook (lambda () - (when view-mode - (view-mode -1)) - (when (derived-mode-p 'emacs-lisp-mode) - (lispy-mode -1)))) - -(add-hook 'god-mode-disabled-hook (lambda () - (when (derived-mode-p 'emacs-lisp-mode) - (lispy-mode 1)))) - -(add-hook 'god-mode-enabled-hook 'km/god-update-cursor) -(add-hook 'god-mode-disabled-hook 'km/god-update-cursor) - -(defun km/god-update-cursor () - (setq cursor-type (if god-local-mode 'bar 'box))) - -(defun km/god-gnus-p () - "Return non-nil if a Gnus-related mode is enabled." - (derived-mode-p 'gnus-group-mode - 'gnus-summary-mode - 'gnus-article-mode - 'message-mode)) - -(global-set-key (kbd "C-c d") 'god-local-mode) - -(define-key god-local-mode-map "." 'repeat) -(define-key god-local-mode-map "i" 'god-local-mode) - -(global-set-key (kbd "C-x C-1") 'delete-other-windows) -(global-set-key (kbd "C-x C-2") 'split-window-below) -(global-set-key (kbd "C-x C-3") 'split-window-right) -(global-set-key (kbd "C-x C-0") 'delete-window) - -(provide 'init-god) -;;; init-god.el ends here diff --git a/lisp/init-haskell.el b/lisp/init-haskell.el deleted file mode 100644 index f5a4064..0000000 --- a/lisp/init-haskell.el +++ /dev/null @@ -1,40 +0,0 @@ -;;; init-haskell.el --- Haskell configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(setq haskell-process-show-debug-tips nil) - -(add-hook 'haskell-mode-hook 'turn-on-haskell-indentation) -(add-hook 'haskell-mode-hook 'interactive-haskell-mode) -(add-hook 'haskell-mode-hook 'turn-on-haskell-doc) - -(after 'haskell-mode - (define-key haskell-mode-map (kbd "C-x C-d") nil) - (define-key haskell-mode-map (kbd "C-c C-z") 'haskell-interactive-switch) - (define-key haskell-mode-map (kbd "C-c C-l") 'haskell-process-load-file) - (define-key haskell-mode-map (kbd "C-c C-b") 'haskell-interactive-switch) - (define-key haskell-mode-map (kbd "C-c C-t") 'haskell-process-do-type) - (define-key haskell-mode-map (kbd "C-c C-i") 'haskell-process-do-info) - (define-key haskell-mode-map (kbd "C-c M-.") nil) - (define-key haskell-mode-map (kbd "C-c C-d") nil)) - -(provide 'init-haskell) -;;; init-haskell.el ends here diff --git a/lisp/init-helm.el b/lisp/init-helm.el deleted file mode 100644 index cacdbb6..0000000 --- a/lisp/init-helm.el +++ /dev/null @@ -1,72 +0,0 @@ -;;; init-helm.el --- Helm configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'helm) -(require 'helm-config) - -(setq helm-move-to-line-cycle-in-source t - helm-ff-newfile-prompt-p nil - helm-ff-file-name-history-use-recentf t - helm-ff-skip-boring-files t) - -(defun km/helm-display-buffer () - (interactive) - (with-helm-alive-p - (helm-quit-and-execute-action #'display-buffer))) - -(defun km/helm-display-file () - (interactive) - (with-helm-alive-p - (helm-quit-and-execute-action - (lambda (f) - (display-buffer (find-file-noselect f)))))) - -(defun km/helm-ff-org-open-file () - "Run `org-open-file' from `helm-source-find-files'." - (interactive) - (with-helm-alive-p - (helm-quit-and-execute-action #'org-open-file))) - -(global-set-key (kbd "C-c h") 'helm-command-prefix) -(global-set-key (kbd "C-x c") nil) - -(after 'helm-files - (define-key helm-find-files-map (kbd "C-c x") 'km/helm-ff-org-open-file) - (define-key helm-generic-files-map (kbd "C-c x") 'km/helm-ff-org-open-file) - ;; Overrides `helm-ff-run-switch-other-frame'. - (define-key helm-find-files-map (kbd "C-c C-o") 'km/helm-display-file) - (define-key helm-generic-files-map (kbd "C-c C-o") 'km/helm-display-file) - ;; Overrides `helm-buffer-switch-other-frame'. - (define-key helm-buffer-map (kbd "C-c C-o") 'km/helm-display-buffer)) - -(key-chord-define-global "jc" 'helm-find-files) -(key-chord-define-global "jt" 'helm-mini) -(key-chord-define-global "kx" 'helm-M-x) - -(define-key search-map "k" 'helm-swoop) - -(global-set-key (kbd "C-h a") 'helm-apropos) - -(helm-mode 1) - -(provide 'init-helm) -;;; init-helm.el ends here diff --git a/lisp/init-mail.el b/lisp/init-mail.el deleted file mode 100644 index c840eee..0000000 --- a/lisp/init-mail.el +++ /dev/null @@ -1,274 +0,0 @@ -;;; init-mail.el --- Mail configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'gnus) -(require 'org-gnus) - -(setq gnus-home-directory "~/.gnus.d/" - gnus-directory gnus-home-directory - gnus-article-save-directory (expand-file-name "saved/" gnus-directory) - gnus-kill-files-directory (expand-file-name "scores/" gnus-directory)) - -(setq gnus-startup-file (expand-file-name "newsrc" gnus-home-directory) - gnus-init-file (expand-file-name "gnus" gnus-home-directory) - gnus-save-newsrc-file nil - gnus-read-newsrc-file nil - gnus-inhibit-startup-message t) - -(setq sendmail-program "/usr/bin/msmtp" - gnus-gcc-mark-as-read t - gnus-visible-headers '("^From" "^Subject" "^Date" "^To" "^Cc" "^User-Agent") - gnus-confirm-mail-reply-to-news t) - -(setq gnus-agent-go-online t - gnus-agent-synchronize-flags t) - -(setq mm-discouraged-alternatives '("text/html" "text/richtext")) -(setq gnus-inhibit-images t) - -(setq gnus-interactive-exit nil) - -(add-hook 'kill-emacs-hook 'gnus-grace-exit-before-kill-emacs) - -;; http://www.emacswiki.org/emacs/GnusSync -(defun gnus-grace-exit-before-kill-emacs () - (if (and (fboundp 'gnus-alive-p) - (gnus-alive-p)) - (let ((noninteractive t)) - (gnus-group-exit)))) - -(define-prefix-command 'km/mail-map) -(global-set-key (kbd "C-x m") 'km/mail-map) - -(define-key km/mail-map "g" 'gnus) -(define-key km/mail-map "p" 'gnus-plugged) -(define-key km/mail-map "u" 'gnus-unplugged) - - -;;; Gnus group buffer - -(setq gnus-topic-display-empty-topics nil - gnus-group-line-format "%M\%S\%p\%P\%5y:%B%(%G%)\n" - gnus-group-list-inactive-groups nil) - -(setq gnus-group-use-permanent-levels t) - -(setq gnus-group-sort-function '(gnus-group-sort-by-alphabet - km/gnus-group-sort-by-topic - gnus-group-sort-by-level)) - -(defun km/gnus-group-sort-by-topic (info1 info2) - "Sort alphabetically by group topic. -This allows groups to be ordered by topics even when topic mode -is off." - (require 'gnus-topic) - (string< (gnus-group-topic (gnus-info-group info1)) - (gnus-group-topic (gnus-info-group info2)))) - -(define-key gnus-group-mode-map "e" 'gnus-group-select-group) - - -;;; Gnus summary and article buffer - -(setq gnus-summary-line-format "%U%R %&user-date;%-20= %-15,15f %B %S \n" - gnus-sum-thread-tree-indent " " - gnus-sum-thread-tree-root "." - gnus-sum-thread-tree-false-root "o " - gnus-sum-thread-tree-single-indent "" - gnus-sum-thread-tree-leaf-with-other "+-> " - gnus-sum-thread-tree-vertical "| " - gnus-sum-thread-tree-single-leaf "`-> ") - -(setq gnus-auto-select-next 'quietly) - -(setq gnus-thread-hide-subtree t - gnus-thread-sort-functions '(gnus-thread-sort-by-most-recent-number)) - -(defun km/gnus-avy-goto-subword-and-select () - (interactive) - (let (avy-all-windows) - (call-interactively #'avy-goto-subword-1)) - (gnus-summary-scroll-up 0)) - -(defun km/gnus-summary-set-current-article () - (unless gnus-summary-buffer - (user-error "No summary buffer")) - (with-current-buffer gnus-summary-buffer - (save-window-excursion (gnus-summary-select-article)))) - -(defun km/gnus--last-message-link () - (with-current-buffer gnus-article-buffer - (save-excursion - (goto-char (point-max)) - (widget-forward -1) - (--when-let (or (get-text-property (point) 'gnus-string) - (get-text-property (point) 'shr-url)) - (kill-new it))))) - -(defun km/gnus--gmane-link (&optional perma) - (with-current-buffer gnus-original-article-buffer - (-when-let* ((blink (message-field-value "Archived-At")) - (link (or (and (string-match "\\`<\\(.*\\)>\\'" blink) - (match-string 1 blink)) - blink))) - (if perma - link - (replace-regexp-in-string "\\`http://permalink\.gmane\.org/" - "http://thread.gmane.org/" - link))))) - -(defun km/gnus-copy-gmane-link-as-kill (&optional perma) - (interactive "P") - (km/gnus-summary-set-current-article) - (with-current-buffer gnus-original-article-buffer - (--when-let (km/gnus--gmane-link perma) - (kill-new (message it))))) - -(defun km/gnus-copy-message-link (follow) - "Copy link for current message. -If it has an \"Archived-At\" header, use that. Otherwise, get -the link from the last widget in the buffer. With prefix -argument FOLLOW, follow link instead of copying it." - (interactive "P") - (km/gnus-summary-set-current-article) - (with-current-buffer gnus-original-article-buffer - (save-excursion - (--when-let (or (km/gnus--gmane-link) - (km/gnus--last-message-link)) - (funcall (if follow - #'browse-url - (lambda (s) (kill-new (message s)))) - it))))) - -(defun km/gnus-open-github-patch () - "Open patch from github email. -A new buffer with the patch contents is opened in another window." - (interactive) - (km/gnus-summary-set-current-article) - (let ((bufname (generate-new-buffer-name "*gnus-github-patch*")) - url) - (with-current-buffer gnus-original-article-buffer - (save-excursion - (goto-char (point-min)) - (if (re-search-forward "https://github.com/.*\\.patch") - (setq url (match-string-no-properties 0)) - (user-error "No patch found")))) - (with-current-buffer (get-buffer-create bufname) - (url-insert-file-contents url) - (diff-mode) - (view-mode 1)) - (pop-to-buffer bufname))) - -(defun km/gnus-summary-catchup (&optional no-next) - "Mark all articles as read. -Don't ask for confirmation. With prefix argument NO-NEXT, exit -to group buffer instead of moving to next group." - (interactive "P") - (let ((gnus-auto-select-next (unless no-next 'quietly))) - (gnus-summary-catchup-and-exit nil t))) - -(defun km/shr-browse-url-and-goto-next () - "Run `shr-browse-url' followed by `shr-next-link'." - (interactive) - (shr-browse-url) - (shr-next-link)) - -;; This overrides `gnus-summary-goto-last-article', which is also -;; bound to 'G l'. -(define-key gnus-summary-mode-map "l" 'km/gnus-copy-message-link) -(define-key gnus-summary-mode-map "v" 'org-capture) -(define-key gnus-summary-mode-map ";" 'gnus-summary-universal-argument) -;; This overrides `gnus-summary-post-news', which is also bound to -;; 'S p'. -(define-key gnus-summary-mode-map "c" 'km/gnus-summary-catchup) -(define-key gnus-summary-mode-map "e" 'gnus-summary-scroll-up) -(define-key gnus-summary-mode-map "j" 'km/gnus-avy-goto-subword-and-select) -(define-key gnus-summary-mode-map "o" 'km/ace-link-widget) - -;; Allow `km/ace-link-widget' binding to work even when on shr widget. -(after 'shr - (define-key shr-map "o" nil) - (define-key shr-map "O" 'shr-save-contents) - (define-key shr-map "v" nil)) - -(define-key gnus-article-mode-map - (kbd "C-c l") 'km/gnus-follow-last-message-link) -(define-key gnus-article-mode-map "e" 'km/shr-browse-url-and-goto-next) -;; This overrides `gnus-summary-save-article', which is also on 'O o'. -(define-key gnus-article-mode-map "o" 'km/ace-link-widget) -(define-key gnus-article-mode-map "v" 'org-capture) - -(define-prefix-command 'km/gnus-summary-prefix-map) -(define-key gnus-summary-mode-map (kbd "C-c m") 'km/gnus-summary-prefix-map) -(define-key km/gnus-summary-prefix-map "p" 'km/gnus-open-github-patch) -(define-key km/gnus-summary-prefix-map "l" 'km/gnus-copy-gmane-link-as-kill) - -(define-prefix-command 'km/gnus-article-prefix-map) -(define-key gnus-article-mode-map (kbd "C-c m") 'km/gnus-article-prefix-map) -(define-key km/gnus-article-prefix-map "p" 'km/gnus-open-github-patch) -(define-key km/gnus-article-prefix-map "l" 'km/gnus-copy-gmane-link-as-kill) - - -;;; Message mode - -(setq message-send-mail-function 'message-send-mail-with-sendmail - message-sendmail-envelope-from 'header - message-kill-buffer-on-exit t - footnote-section-tag "") - -(add-hook 'message-send-hook 'km/message-confirm-sender) -(add-hook 'message-mode-hook 'flyspell-mode) - -(defun km/message-confirm-sender () - "Stop sending message from the wrong address." - (unless (yes-or-no-p (format "Send message from %s?" - (message-field-value "From"))) - (user-error "Not sending message"))) - - -;;; Notmuch - -(require 'notmuch) -(require 'org-notmuch) - -(setq notmuch-fcc-dirs nil - notmuch-search-oldest-first nil) - -(add-to-list 'notmuch-saved-searches - '(:name "today" :query "date:today.." :key ".")) - -(defun km/notmuch-show-copy-message-id-as-kill () - (interactive) - (kill-new (message "%s" (notmuch-show-get-message-id)))) - -(define-key notmuch-hello-mode-map "o" 'km/ace-link-widget) - -(define-key km/mail-map "n" 'notmuch) - -(define-prefix-command 'km/notmuch-show-prefix-map) -(define-key notmuch-show-mode-map (kbd "C-c m") 'km/notmuch-show-prefix-map) - -(define-key km/notmuch-show-prefix-map "i" - 'km/notmuch-show-copy-message-id-as-kill) - -(provide 'init-mail) -;;; init-mail.el ends here diff --git a/lisp/init-org.el b/lisp/init-org.el deleted file mode 100644 index 147f6e9..0000000 --- a/lisp/init-org.el +++ /dev/null @@ -1,823 +0,0 @@ -;;; init-org.el --- Org mode configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(add-to-list 'load-path "~/src/emacs/org-mode/lisp/") -(add-to-list 'load-path "~/src/emacs/org-mode/contrib/lisp/" t) -(add-to-list 'Info-directory-list "~/src/emacs/org-mode/doc/") - -(setq org-modules '(org-bibtex org-gnus org-info ox-md)) - -(setq org-log-done t - org-log-into-drawer t - org-clock-into-drawer t - org-todo-keywords '((sequence "TODO(t)" "STARTED(s)" "WAITING(w@)" - "|" "DONE(d)" "NA(n@)"))) - -(setq org-catch-invisible-edits 'error - org-special-ctrl-k t - org-insert-heading-respect-content t - org-M-RET-may-split-line nil - org-adapt-indentation nil - org-blank-before-new-entry '((heading . t) (plain-list-item . auto))) - -(setq org-use-speed-commands t - org-use-extra-keys t - org-fast-tag-selection-single-key 'expert) - -(setq org-outline-path-complete-in-steps nil - org-goto-interface 'outline-path-completionp - org-goto-max-level 3) - -(put 'org-goto-max-level 'safe-local-variable #'integerp) - -(after 'org - (setq org-structure-template-alist - (append '(("p" "#+property: ") - ("o" "#+options: ") - ("d" "#+date: ") - ("t" "#+title: ") - ("S" "#+setupfile: ?") - ("n" "#+name: ") - ("w" "#+begin_note\n ?\n#+end_note") - ("C" "#+caption: ") - ("b" "#+label: ") - ("r" "#+attr_latex: ") - ("R" "#+attr_html: ")) - (mapcar (lambda (i) (list (car i) (downcase (cadr i)))) - org-structure-template-alist)))) - -(add-to-list 'auto-mode-alist '("\\.org.txt\\'" . org-mode)) - -(add-hook 'next-error-hook (lambda () - (when (eq major-mode 'org-mode) - (org-show-context)))) - -(defun km/org-tree-to-indirect-buffer (&optional arg) - "Run `org-tree-to-indirect-buffer', keeping previous buffer. -By default, `org-tree-to-indirect-buffer' deletes the previous -indirect buffer when making a new one to avoid accumulating -buffers, which can be overriden by a C-u prefix. Reverse this -behavior so that the prefix must be given in order to delete the -previous indirect buffer. If the argument is a number, which has -a different meaning, it is left untouched." - (interactive "P") - (unless (numberp arg) - (setq arg (not arg))) - (org-tree-to-indirect-buffer arg)) - -(defun km/org-tree-to-indirect-buffer-current-window (&optional arg) - "Create indirect buffer and narrow to subtree in this window. -Before running `org-tree-to-indirect-buffer', set -`org-indirect-buffer-display' to `current-window'." - (interactive "P") - (let ((org-indirect-buffer-display 'current-window)) - (km/org-tree-to-indirect-buffer arg))) - -(defun km/org-clone-and-shift-by-repeater () - "Clone current subtree, shifting new timestamp by repeater. -The repeater is removed from the original subtree." - (interactive) - (save-excursion - (org-back-to-heading) - (let ((repeater - (and (re-search-forward - ;; Regexp taken from `org-clone-subtree-with-time-shift'. - "<[^<>\n]+ +\\([.+]?\\+[0-9]+[hdwmy]\\)" - (save-excursion (org-end-of-subtree)) t) - (match-string-no-properties 1)))) - (unless repeater - (user-error "Subtree does not have repeater")) - (org-clone-subtree-with-time-shift 0 repeater)))) - -(defun km/org-delete-checked-items () - "Delete checked items. - -If the element at point is not a plain list, search the parent -elements for a plain list, stopping when the first plain list or -headline is found. - -After deleting checked items, move to the first item of the list. -If there are no items of the list remaining, move to the parent -heading." - (interactive) - (let* ((el (or (org-element-lineage (org-element-context) '(plain-list) t) - (user-error "Point is not within a plain list"))) - (beg (org-element-property :begin el)) - ;; Check maximum point because, if narrowed to a heading, - ;; org-element can return a point beyond this. - (end (min (org-element-property :end el) (point-max))) - (struct (org-element-property :structure el)) - (list-level (org-list-get-ind beg struct)) - (deleted-count 0) - (text (buffer-substring beg end)) - new-text) - (with-temp-buffer - (insert text) - (let ((offset (1- beg)) - (pmax (point-max)) - level box bpos epos) - (dolist (item (reverse struct)) - (setq level (nth 1 item) - box (nth 4 item) - bpos (- (nth 0 item) offset) - ;; Minimum check here is for the same reason as - ;; above with `end'. This only comes into play for - ;; the last item. - epos (min (- (nth 6 item) offset) pmax)) - (when (and (= list-level level) - (string= box "[X]")) - (delete-region bpos epos) - (setq deleted-count (1+ deleted-count))))) - (setq new-text (buffer-string))) - (if (= deleted-count 0) - (message "No checked boxes found") - (delete-region beg end) - (goto-char beg) - (insert new-text) - (goto-char beg) - (unless (eq (car (org-element-at-point)) 'plain-list) - (outline-previous-heading)) - (org-update-checkbox-count-maybe) - (message "Deleted %s item(s)" deleted-count)))) - -(defmacro km/org--save-pos-on-sort (&rest body) - "Try to return to the orginal position after sorting. - -Sorting doesn't play well with `save-restriction' or markers, so -just put the point where it was relative to the original heading. -This may not actually be the same tree if there are redundant -headings. - -This relies on point being placed at the heading that was sorted, -as `org-sort-entries' does." - `(let ((starting-pos (point))) - (org-back-to-heading t) - (let ((heading-line (buffer-substring-no-properties - (point-at-bol) (point-at-eol))) - (chars-after-heading (- starting-pos (point)))) - ,@body - (search-forward heading-line) - (beginning-of-line) - (goto-char (+ (point) chars-after-heading))))) - -(defun km/org-sort-parent (arg) - "Sort on parent heading ARG levels up. -After sorting, return point to its previous location under the -current heading." - (interactive "p") - (km/org--save-pos-on-sort - (outline-up-heading arg) - (call-interactively #'org-sort))) - -(defun km/org--prop-sort-args () - "Return `org-sort-entries' arguments based on \"SORT\" property." - (when (save-excursion (org-goto-first-child)) - (let ((prop (org-entry-get nil "sort" 'inherit))) - (when prop - (let* ((current-level (org-current-level)) - (sort-prop (s-split " by " prop)) - (levels (mapcar #'string-to-number (s-split nil (car sort-prop)))) - (sorting-type (cadr sort-prop)) - sorting-func) - (if sorting-type - (progn - (setq sorting-type (read sorting-type)) - (cond - ((characterp sorting-type)) - ((fboundp sorting-type) - (setq sorting-func sorting-type - sorting-type ?f)) - (t - (user-error "Invalid sorting type: %s" sorting-type)))) - (setq sorting-type ?a)) - (when (or (equal levels (list 0)) - (memq current-level levels)) - (list nil sorting-type sorting-func))))))) - -(defun km/org-maybe-sort () - "Sort current heading based on \"SORT\" property. - -Property value should have the format \"LEVELS by TYPE\", where -LEVELS specifies the level of heading to sort and TYPE is the -sorting type. - -If LEVELS is a space-seperated list of positive integers, only -sort heading if it is at one of these levels. If LEVELS is zero -or a non-numeric string, sort heading regardless of its level. -If LEVELS is a negative number, do not sort. (Notice that there -is only support for sorting subheadings in a tree, not top-level -headings.) - -If TYPE is a character, pass it as the SORTING-TYPE argument to -`org-sort-entries'. If TYPE is the name of a bound function, -pass it as the GETKEY-FUNC argument to `org-sort-entries' (with -?f as the SORTING-TYPE value). If \"by TYPE\" is omitted from -the property value, sort alphabetically. - -For example - - 2 by ?a Sort alphabetically if level 2 heading. - 2 Same as above. - - t Sort heading alphabetically. - all Same as above. - - 1 by func Sort heading using function if level 1 heading. - - -1 Don't sort. Useful for overriding parent value." - (let ((sort-args (km/org--prop-sort-args))) - (when sort-args - (apply #'org-sort-entries sort-args)))) - -(defun km/org-maybe-sort-buffer-headings () - "Call `km/org-maybe-sort' on buffer headings." - (interactive) - (org-map-entries #'km/org-maybe-sort)) - -(defun km/org-maybe-sort-parent () - "Sort parent heading based on \"SORT\" property. -See `km/org-maybe-sort' for details of property value format." - (let (heading-pos sort-args) - (save-excursion - (and (org-up-heading-safe) - (setq heading-pos (point) - sort-args (km/org--prop-sort-args)))) - (when sort-args - (km/org--save-pos-on-sort - (goto-char heading-pos) - (apply #'org-sort-entries sort-args))))) - -(defun km/org-sort-heading-ignoring-articles () - "Sort alphabetically, but ignore any leading articles." - (let* ((ignored-words '("a" "an" "the")) - (heading (org-no-properties - (org-get-heading 'no-tags 'no-todo))) - (heading-words (split-string heading))) - (when (member (downcase (car heading-words)) - ignored-words) - (setq heading-words (cdr heading-words))) - (mapconcat #'identity heading-words " "))) - -(defun km/org-remove-title-leader () - "Remove leader from Org heading title. - -Convert - - * TODO leader: Rest of title :tag: - -to - - * TODO Rest of title :tag:" - (interactive) - (save-excursion - (let ((regex (format "^%s\\(?:%s \\)?\\(?:%s \\)?\\(.*: \\)\\w+" - org-outline-regexp org-todo-regexp - org-priority-regexp))) - (org-back-to-heading) - (when (re-search-forward regex (point-at-eol) t) - (replace-match "" nil nil nil 4) - (org-set-tags nil t))))) - -(defun km/org-add-blank-before-heading () - "Add a blank line before Org headings in buffer." - (interactive) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward "[^\n]\n\\*" nil t) - (when (org-at-heading-p) - (beginning-of-line) - (open-line 1))))) - -(defun km/org-normalize-spaces () - "Reduce to single spaces and add space before headings." - (interactive) - (km/reduce-to-single-spaces) - (km/org-add-blank-before-heading)) - -(defun km/org-switch-to-buffer-other-window (&optional arg) - (interactive "P") - (cl-letf (((symbol-function 'org-pop-to-buffer-same-window) - (lambda (buffer-or-name &rest args) - (funcall #'pop-to-buffer buffer-or-name)))) - (org-switchb arg))) - -(defun km/org-open-at-point-stay () - "Like `org-open-at-point', but stay on heading. -This variant is convient to use in `org-speed-commands-user' -because remaining on the heading allows additional commands to be -called through the speed command interface." - (interactive) - (unless (org-at-heading-p) - (user-error "Not at heading")) - (save-excursion - (call-interactively #'org-open-at-point))) - -(after 'org - (define-key org-mode-map (kbd "C-c C-x B") - 'km/org-tree-to-indirect-buffer-current-window) - (define-key org-mode-map [remap org-tree-to-indirect-buffer] - 'km/org-tree-to-indirect-buffer) - - ;; Rebind `org-insert-drawer' to so that `org-metadown' has the - ;; expected "C-c C-x" keybinding. - (define-key org-mode-map (kbd "C-c C-x d") 'org-metadown) - (define-key org-mode-map (kbd "C-c C-x w") 'org-insert-drawer) - - ;; Rebind `org-set-property' to free up binding for - ;; `org-previous-item'. - (define-key org-mode-map (kbd "C-c C-x s") 'org-set-property) - (define-key org-mode-map (kbd "C-c C-x n") 'org-next-item) - (define-key org-mode-map (kbd "C-c C-x p") 'org-previous-item) - - ;; Override global `imenu' binding. - (define-key org-mode-map (kbd "C-c l") 'org-goto) - ;; Don't let `org-cycle-agenda-files' binding override custom - ;; `backward-kill-word' binding (`org-cycle-agenda-files' is still bound - ;; to C-,). - (define-key org-mode-map (kbd "C-'") nil) - - (define-key org-mode-map (kbd "C-c m") 'km/org-prefix-map) - - (add-to-list 'org-speed-commands-user '("o" . km/org-open-at-point-stay))) - -(define-prefix-command 'km/org-prefix-map) -(define-key km/org-prefix-map "c" 'km/org-clone-and-shift-by-repeater) -(define-key km/org-prefix-map "D" 'km/org-delete-checked-items) -(define-key km/org-prefix-map "l" 'km/org-remove-title-leader) -(define-key km/org-prefix-map "n" 'km/org-normalize-spaces) -(define-key km/org-prefix-map "s" 'km/org-sort-parent) - - -(define-prefix-command 'km/global-org-map) -(global-set-key (kbd "C-c o") 'km/global-org-map) - -(define-key km/global-org-map "b" 'org-iswitchb) -(define-key km/global-org-map "o" 'org-open-at-point-global) -(define-key km/global-org-map "p" 'poporg-dwim) -(define-key km/global-org-map "s" 'org-save-all-org-buffers) - -(define-key ctl-x-4-map "o" 'km/org-switch-to-buffer-other-window) - -(after 'poporg - (define-key poporg-mode-map (kbd "C-c C-c") 'poporg-edit-exit)) - - -;;; Agenda - -(setq org-default-notes-file "~/notes/agenda/tasks.org") -(defvar km/org-agenda-file-directory "~/notes/agenda/") -(setq org-agenda-files (list km/org-agenda-file-directory)) -(setq org-agenda-text-search-extra-files - (file-expand-wildcards "~/notes/extra/*.org")) - -(setq org-agenda-restore-windows-after-quit t - org-agenda-window-setup 'only-window - org-agenda-sticky t) - -(setq org-agenda-dim-blocked-tasks nil - org-agenda-show-all-dates t - org-agenda-skip-deadline-if-done t - org-agenda-skip-scheduled-if-done t - org-agenda-start-on-weekday nil - org-agenda-use-time-grid nil) - -(setq org-agenda-sorting-strategy - '((agenda time-up deadline-up scheduled-up priority-down category-keep) - (todo priority-down category-keep) - (tags priority-down category-keep) - (search category-keep))) - -(setq org-agenda-custom-commands - '(("d" todo "DONE" nil) - ("u" "Unschedule TODO entries" alltodo "" - ((org-agenda-skip-function - (lambda nil - (org-agenda-skip-entry-if 'scheduled 'deadline - 'regexp "\n]+>"))) - (org-agenda-overriding-header "Unscheduled TODO entries: "))) - ("p" "Past timestamps" tags "TIMESTAMP<=\"\""))) - -(setq org-capture-templates - '(("t" "task" entry (file+headline "~/notes/tasks.org" "Inbox") - "* TODO %?%i" :prepend t) - ("d" "date" entry (file+headline "~/notes/calendar.org" "Inbox") - "* %?%i" :prepend t) - ("b" "bookmark" entry (file+headline "~/notes/bookmarks.org" "Inbox") - "* %?%i" :prepend t) - ("v" "Visit" checkitem (file+headline "~/notes/tasks.org" "Visit") - "- [ ] %?%i\n" :prepend t) - ("r" "Revisit" checkitem (file+headline "~/notes/tasks.org" "Revisit") - "- [ ] %?%i\n" :prepend t) - ;; Link counterparts - ("T" "task link" entry (file+headline "~/notes/tasks.org" "Inbox") - "* TODO %?%i\n\n%a" :prepend t) - ("D" "date link" entry (file+headline "~/notes/calendar.org" "Inbox") - "* %?%i\n\n%a" :prepend t) - ("B" "bookmark link" entry - (file+headline "~/notes/bookmarks.org" "Inbox") - "* %?%i\n\n%a" :prepend t) - ;; Clipboard - ("x" "task clipboard" entry (file+headline "~/notes/tasks.org" "Inbox") - "* TODO %?%i\n\n%x" :prepend t) - ("X" "bookmark clipboard" entry - (file+headline "~/notes/bookmarks.org" "Inbox") - "* %?%i\n\n%x" :prepend t))) - -(add-hook 'org-agenda-mode-hook 'km/org-agenda-cd-and-read-dir-locals) -(add-hook 'org-agenda-finalize-hook 'km/org-agenda-store-current-span) - -(defun km/org-agenda-cd-and-read-dir-locals () - (setq default-directory "~/notes/") - (hack-local-variables)) - -(defun km/org-agenda-store-current-span () - "Store the current span value in `org-agenda-span'. -This allows the view to persist when the agenda buffer is -killed." - (when org-agenda-current-span - (setq org-agenda-span org-agenda-current-span))) - -(defun km/org-agenda-add-or-remove-file (file) - "Add or remove link to FILE in `km/org-agenda-file-directory'. -If a link for FILE does not exist, create it. Otherwise, remove -it. Like `org-agenda-file-to-front', this results in FILE being -displayed in the agenda." - (interactive (list (cl-case major-mode - (org-mode (buffer-file-name)) - (dired-mode (dired-get-filename)) - (org-agenda-mode (ignore-errors (save-window-excursion - (org-agenda-goto) - (buffer-file-name)))) - (t (read-file-name "Link file: "))))) - (let ((agenda-file (expand-file-name (file-name-nondirectory file) - km/org-agenda-file-directory))) - (if (file-equal-p (file-truename agenda-file) file) - (progn - (when (called-interactively-p) (message "Deleting %s" agenda-file)) - (delete-file agenda-file)) - (when (called-interactively-p) (message "Adding %s" agenda-file)) - (make-symbolic-link file agenda-file)))) - -(defun km/org-open-default-notes-file-inbox () - "Open \"Inbox\" heading of `org-default-notes-file'." - (interactive) - (find-file org-default-notes-file) - (goto-char (org-find-exact-headline-in-buffer "Inbox" nil t)) - (recenter-top-bottom 0) - (show-children)) - -(defun km/org-goto-agenda-heading () - "Jump to heading in agenda files." - (interactive) - (let ((org-refile-targets - '((org-agenda-files :maxlevel . 3) - (org-agenda-text-search-extra-files :maxlevel . 3)))) - (org-refile '(4)))) - -(defun km/org-agenda-avy-goto-subword-1 () - (interactive) - (let (avy-all-windows) - (call-interactively #'avy-goto-subword-1)) - (org-agenda-do-context-action)) - -(define-key km/global-org-map "a" 'org-agenda) -(define-key km/global-org-map "c" 'org-capture) -(define-key km/global-org-map "j" 'km/org-goto-agenda-heading) -(define-key km/global-org-map "m" 'km/org-open-default-notes-file-inbox) -(define-key km/global-org-map "n" 'km/org-agenda-add-or-remove-file) - -(after 'org-agenda - ;; Bind `org-agenda-follow-mode' to same key as - ;; `next-error-follow-minor-mode'. - (define-key org-agenda-mode-map (kbd "C-c C-f") 'org-agenda-follow-mode) - ;; Free up 'j' for `km/org-agenda-avy-goto-subword-1'. - (define-key org-agenda-mode-map (kbd "C-j") 'org-agenda-goto-date) - (define-key org-agenda-mode-map "j" 'km/org-agenda-avy-goto-subword-1)) - - -;;; Refiling - -(setq org-reverse-note-order t) - -(setq org-refile-target-verify-function 'km/org-refile-verify-target) - -(setq org-refile-targets '((nil :maxlevel . 2)) - org-refile-cache nil) - -(add-hook 'org-after-refile-insert-hook 'km/org-maybe-sort-parent) - -(defvar km/org-agenda-refile-targets - '((nil :maxlevel . 3) - (org-agenda-files :maxlevel . 2) - (org-agenda-text-search-extra-files :maxlevel . 2))) - -(add-to-list 'safe-local-variable-values - (cons 'org-refile-targets km/org-agenda-refile-targets)) - -(defun km/org-refile-verify-target () - "Exclude DONE state from refile targets." - (not (member (nth 2 (org-heading-components)) org-done-keywords))) - -(defvar km/org-refile-list-item-tag "bref" - "Tag marking heading with list that can be refiled to.") - -(defun km/org-refile-list-item (&optional copy) - "Refile list item to a heading. - -Consider targets to be headings with the tag -`km/org-refile-list-item-tag' in any file listed in -`org-refile-targets'. - -The item is dropped directly under the heading, after any -planning information or property drawers. No attempt is made to -make sure that it is part of any previous list. - -With prefix argument COPY, the item is not deleted from the -original list." - (interactive "P") - (unless (org-at-item-p) - (user-error "Not at an item")) - (let* ((beg (save-excursion (beginning-of-line) (point-marker))) - (end (save-excursion - (goto-char - (nth 6 (assoc (marker-position beg) (org-list-struct)))) - (point-marker))) - (item (buffer-substring-no-properties beg end)) - (ftargets (mapcar #'car org-refile-targets)) - (org-refile-targets (mapcar - (lambda (f) - (cons f (cons :tag km/org-refile-list-item-tag))) - ftargets)) - (loc (org-refile-get-location "Bullet heading")) - (fname (nth 1 loc)) - (heading-pos (nth 3 loc))) - (with-current-buffer (or (find-buffer-visiting fname) - (find-file-noselect fname)) - (org-with-wide-buffer - (goto-char heading-pos) - (forward-line) - (while (and (not (eobp)) - (memq (org-element-type (org-element-at-point)) - '(planning property-drawer node-property))) - (forward-line)) - (insert item) - (org-update-checkbox-count-maybe))) - (goto-char beg) - (unless copy - (delete-region beg end) - (org-update-checkbox-count-maybe)))) - -(defvar km/org-refile-dwim-maxlevel 2) - -(defun km/org-refile-dwim () - "Rebind `org-refile-targets' if next window is an Org buffer. -A target is determined by `km/org-refile-dwim-target-file'." - (interactive) - (let* ((dwim-target (km/org-refile-dwim-target-file)) - (org-refile-targets (if dwim-target - `((nil - :maxlevel . ,km/org-refile-dwim-maxlevel) - (dwim-target - :maxlevel . ,km/org-refile-dwim-maxlevel)) - org-refile-targets))) - (call-interactively #'org-refile))) - -(defun km/org-refile-dwim-target-file () - "Return next window that is an Org buffer." - (let* ((from-buffer (current-buffer)) - (other-win (get-window-with-predicate - (lambda (w) - (with-current-buffer (window-buffer w) - (and (derived-mode-p 'org-mode) - (not (eq from-buffer (current-buffer))))))))) - (and other-win - (buffer-file-name (window-buffer other-win))))) - -(defun km/org-refile-to-other-file (file &optional maxlevel) - "Refile with `org-refile-targets' set to FILE. -A numeric prefix sets MAXLEVEL (defaults to 2)." - (interactive "fFile: \nP") - (let* ((maxlevel (prefix-numeric-value (or maxlevel 2))) - (file (substring-no-properties file)) - (org-refile-targets `((,file :maxlevel . ,maxlevel)))) - (org-refile))) - -(defun km/org-refile-to-other-org-buffer (buffer &optional maxlevel) - "Refile with `org-refile-targets' set to BUFFER file name. -A numeric prefix sets MAXLEVEL (defaults to 2)." - (interactive (list (km/get-org-file-buffer) current-prefix-arg)) - (km/org-refile-to-other-file (buffer-file-name buffer) - maxlevel)) - -(defun km/get-org-file-buffer () - (get-buffer - (org-icompleting-read "Buffer: " (mapcar 'buffer-name - (org-buffer-list 'files))))) - -(defun km/org-set-refiling-buffer (&optional maxlevel) - "Choose buffer to set as sole target in `org-refile-targets'. -If `org-refile-targets' is already a local variable, restore the -global value. A numeric prefix sets MAXLEVEL (defaults to 2)." - (interactive "P") - (if (local-variable-p 'org-refile-targets) - (kill-local-variable 'org-refile-targets) - (let ((buffer-file (substring-no-properties - (buffer-file-name (km/get-org-file-buffer)))) - (maxlevel (prefix-numeric-value (or maxlevel 2)))) - (set (make-local-variable 'org-refile-targets) - `((,buffer-file :maxlevel . ,maxlevel)))))) - -(define-key km/global-org-map "w" 'org-refile-goto-last-stored) -(define-key km/org-prefix-map "w" 'km/org-refile-to-other-org-buffer) -(define-key km/org-prefix-map "i" 'km/org-refile-list-item) - -(after 'org - (define-key org-mode-map [remap org-refile] 'km/org-refile-dwim) - (add-to-list 'org-speed-commands-user '("w" . km/org-refile-dwim))) - - -;;; Links - -(add-to-list 'load-path "~/src/emacs/org-link-edit/") -(require 'org-link-edit) - -(setq org-link-search-must-match-exact-headline nil) - -(after 'org - (org-add-link-type "pmid" 'km/org-pmid-open)) - -(defvar km/org-pmid-search-url "http://www.ncbi.nlm.nih.gov/pubmed/?term=%s" - "URL to search for PMID.") - -(defun km/org-pmid-open (path) - "Search for PMID at `km/org-pmid-search-url'." - (browse-url (format km/org-pmid-search-url path))) - -(defun km/org-link-dired-jump () - "Open Dired for directory of file link at point." - (interactive) - (let ((el (org-element-lineage (org-element-context) '(link) t))) - (unless (and el (equal (org-element-property :type el) "file")) - (user-error "Not on file link")) - (dired-jump 'other-window - (expand-file-name (org-element-property :path el))))) - -(defun km/org-link-edit-slurp-link () - "Slurp trailing text into link. - - \[link\]extra -> \[\[linkextra\]\] - -After slurping, return the slurped text and move point to the -beginning of the link." - (interactive) - (cl-multiple-value-bind (beg end link desc) (org-link-edit--get-link-data) - (when (progn (goto-char end) (looking-at "[^ \t\n]+")) - (let ((slurped (match-string-no-properties 0))) - (setq link (concat link slurped) - end (match-end 0)) - (delete-region beg end) - (insert (org-make-link-string link desc)) - (goto-char beg) - slurped)))) - -(define-key km/org-prefix-map "d" 'km/org-link-dired-jump) -(define-key km/global-org-map "l" 'org-store-link) - -(define-key km/org-prefix-map "." - (defhydra hydra-org-link-edit () - "Org Link Edit" - ("j" org-link-edit-forward-slurp "forward slurp") - ("k" org-link-edit-forward-barf "forward barf") - ("u" org-link-edit-backward-slurp "backward slurp") - ("i" org-link-edit-backward-barf "backward barf") - ("l" km/org-link-edit-slurp-link "slurp link" :color blue) - ("q" nil "cancel"))) - - -;;; Export - -(after 'org - (add-to-list 'org-latex-packages-alist '("" "amsmath" t))) - -(after 'ox-latex - (add-to-list 'org-latex-classes - '("short" - "\\documentclass{short}" - ("\\section{%s}" . "\\section*{%s}") - ("\\subsection{%s}" . "\\subsection*{%s}") - ("\\subsubsection{%s}" . "\\subsubsection*{%s}") - ("\\paragraph{%s}" . "\\paragraph*{%s}") - ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))) - -(defvar km/org-md-fill-column fill-column - "Fill column for exported markdown. -This is a separate variable instead of `fill-column' to allow it -to be easily overriden.") - -(defun km/org-md--fill-string (contents) - "Use `org-ascii--fill-string' to fill ox-md paragraphs." - (org-ascii--fill-string contents km/org-md-fill-column - nil)) - -(after 'ox-md - (advice-add 'org-md-paragraph :filter-return #'km/org-md--fill-string)) - -(defun km/org-md-export-unfilled-buffer () - (interactive) - (let ((km/org-md-fill-column (point-max))) - (org-md-export-as-markdown))) - - -;;; Org Babel - -(setq org-confirm-babel-evaluate nil - org-src-fontify-natively t) - -(org-babel-do-load-languages - 'org-babel-load-languages - '((shell . t) - (python . t) - (R . t) - (emacs-lisp . t) - (latex . t))) - - -;;; Org Contacts - -(require 'org-contacts) - -(setq org-contacts-files '("~/notes/contacts.org")) - -(add-to-list 'org-capture-templates - '("a" "email address" entry (file+headline "~/notes/contacts.org" "Inbox") - " -** %(org-contacts-template-name) -:PROPERTIES: -:EMAIL: %(org-contacts-template-email) -:END:")) - - -;;; Org open file - -(defadvice org-open-file (after km/org-open-add-to-recentf activate) - (recentf-add-file path)) - -(defun km/org-open-file-at-point () - "Open file at point with `org-open-file'." - (interactive) - (if (and (derived-mode-p 'org-mode) - (org-element-lineage (org-element-context) '(link) t)) - (org-open-at-point) - (let ((file (or (and (use-region-p) - (buffer-substring-no-properties - (region-beginning) (region-end))) - (thing-at-point 'filename)))) - (if (and file (file-exists-p file)) - (org-open-file file) - (km/org-open-file))))) - -(defun km/org-open-file () - "Interactive version of `org-open-file'." - (interactive) - (org-open-file (read-file-name "Open file: " nil nil t))) - -(autoload 'magit-annex-present-files "magit-annex") -(defun km/org-open-annex-file () - "Open a git annex file with `org-open-file'." - (interactive) - (--if-let (magit-annex-present-files) - (org-open-file (magit-completing-read "Open annex file" it nil t)) - (message "No annex files found"))) - -(defun km/org-open-recent-file () - "Open a file from `recentf-list' with `org-open-file'." - (interactive) - (org-open-file (km/read-recent-file))) - -(after 'init-files - (define-key km/file-map "a" 'km/org-open-annex-file) - (define-key km/file-map "o" 'km/org-open-file) - (define-key km/file-map "p" 'km/org-open-file-at-point) - (define-key km/file-map "r" 'km/org-open-recent-file)) - -(provide 'init-org) -;;; init-org.el ends here diff --git a/lisp/init-outline.el b/lisp/init-outline.el deleted file mode 100644 index 1d21531..0000000 --- a/lisp/init-outline.el +++ /dev/null @@ -1,116 +0,0 @@ -;;; init-outline.el --- Outline mode configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(defun km/outline--parent-levels (n) - "Return list of parent levels. -4 -> (1 2 3) -1 -> nil" - (unless (> n 0) - (user-error "N must be positive")) - (let (parents) - (while (/= n 1) - (setq n (1- n)) - (push n parents)) - parents)) - -(defun km/outline-imenu-create-index () - "Generate Imenu index for headings. -This assumes that `outline-regexp' marks the characters that -indicate a heading, but not the text following it. The heading -text is taken as everything on the line after the -`outline-regexp' match (except for any leading whitespace)." - (goto-char (point-min)) - (let (path-alist ; ((level . heading)) - index) - (while (re-search-forward (concat "^" outline-regexp) nil t) - (let* ((head (replace-regexp-in-string - "\\`\\s-*" "" - (buffer-substring-no-properties (point) (point-at-eol)))) - (head-level (save-excursion (beginning-of-line) - (funcall outline-level))) - (parent-levels (km/outline--parent-levels head-level)) - (parent-heads - (delq nil (mapcar (lambda (n) (cdr (assoc n path-alist))) - parent-levels))) - (target (concat (mapconcat #'identity - (append parent-heads - (list head)) - " / ")))) - (unless (string= head "") - (push (cons target (save-excursion (beginning-of-line) (point-marker))) - index)) - (push (cons head-level head) path-alist))) - (nreverse index))) - -(defun km/outline-jump-to-heading () - "Jump to heading specified by `outline-regexp'." - (interactive) - (let ((imenu-create-index-function #'km/outline-imenu-create-index) - helm-cached-imenu-tick - helm-cached-imenu-alist - helm-cached-imenu-candidates) - (call-interactively #'helm-imenu))) - -;; Modified from https://github.com/abo-abo/hydra/wiki/Emacs -(defhydra hydra-outline-mode (:hint nil) - " - ^^Hide ^^Show ^^Move -_q_ sublevels _a_ all _u_ up -_t_ body _e_ entry _n_ next visible -_o_ other _i_ children _p_ previous visible -_c_ entry _k_ branches _f_ forward same level -_h_ leaves _s_ subtree _b_ backward same level -_d_ subtree - -" - ("q" hide-sublevels) - ("t" hide-body) - ("o" hide-other) - ("c" hide-entry) - ("h" hide-leaves) - ("d" hide-subtree) - - ("a" show-all) - ("e" show-entry) - ("i" show-children) - ("k" show-branches) - ("s" show-subtree) - - ("u" outline-up-heading) - ("n" outline-next-visible-heading) - ("p" outline-previous-visible-heading) - ("f" outline-forward-same-level) - ("b" outline-backward-same-level) - - ("l" km/outline-jump-to-heading "jump" :color blue) - ("m" outline-mark-subtree "mark" :color blue)) - -(defun km/hydra-outline-mode () - (interactive) - (unless outline-minor-mode - (outline-minor-mode)) - (hydra-outline-mode/body)) - -(global-set-key (kbd "C-c n") 'km/hydra-outline-mode) - -(provide 'init-outline) -;;; init-outline.el ends here diff --git a/lisp/init-projectile.el b/lisp/init-projectile.el deleted file mode 100644 index 4c63a9e..0000000 --- a/lisp/init-projectile.el +++ /dev/null @@ -1,322 +0,0 @@ -;;; init-projectile.el --- Projectile configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'projectile) -(require 'helm-projectile) - -(setq projectile-find-dir-includes-top-level t - projectile-completion-system 'helm - projectile-use-git-grep t) - -(projectile-register-project-type 'snakemake '("Snakefile") "snakemake -p" "") - -(defun km/projectile-switch-project (&optional arg) - "Switch to a project. - -Like `projectile-switch-project', but instead of calling -`projectile-commander' when a prefix argument ARG is given, save -something for the current project before switching. - -`projectile-switch-project-action' is set to -`km/projectile-maybe-restore-thing'. If the thing saved for the -destination project is the the window configuration, this may not -end up in the project if the buffers are now dead." - (interactive "P") - (when arg (call-interactively #'km/projectile-save-thing)) - (let ((projectile-switch-project-action 'km/projectile-maybe-restore-thing)) - (projectile-switch-project))) - -(defun km/projectile-ignore-directory-p (name) - (or (file-remote-p name) - (string-prefix-p "/tmp/" name))) - -(setq projectile-ignored-project-function #'km/projectile-ignore-directory-p) - -(defun km/projectile-open-external-terminal-in-root () - "Run `km/open-external-terminal' in project root." - (interactive) - (let ((default-directory (projectile-project-root))) - (km/open-external-terminal))) - -(defun km/projectile-view-file () - "View project file. -Interactive arguments are processed according to -`projectile-find-file'." - (interactive) - (call-interactively 'projectile-find-file) - (view-mode 1)) - -(defun km/projectile-view-file-other-window () - "View project file in other window. -Interactive arguments are processed according to -`projectile-find-file-other-window'." - (interactive) - (call-interactively 'projectile-find-file-other-window) - (view-mode 1)) - -(defun km/project-filename-at-point () - "Return file name relative to `projectile-project-root'. -In the case of multiple files marked in Dired, return the file -names separated by a space." - (let* ((el (and (derived-mode-p 'org-mode) - (org-element-lineage (org-element-context) '(link) t))) - (fname (or (and (eq (org-element-type el) 'link) - (org-element-property :path el)) - (and (derived-mode-p 'dired-mode) - (dired-get-marked-files 'nodir nil)) - (thing-at-point 'filename)))) - (when fname - (mapconcat - `(lambda (f) (file-relative-name f ,(projectile-project-root))) - (if (listp fname) fname (list fname)) - " ")))) - -(defun km/projectile-copy-project-filename-as-kill () - "Copy name of project file. -If point is on a file, copy this as the file name. Otherwise, -use the name of the current file." - (interactive) - (-when-let (fname (or (km/project-filename-at-point) - (and buffer-file-name - (file-relative-name buffer-file-name - (projectile-project-root))))) - (if (eq last-command 'kill-region) - (kill-append fname nil) - (kill-new fname)) - (message "%s" fname))) - -(defvar km/projectile-project-saved-thing nil - "Property list of saved thing for projects. -The keys are project roots (strings), so use `lax-plist-put' and -`lax-plist-get'.") - -(defun km/projectile-save-thing (thing) - "Save thing for current project. - -Thing is a character representing -- . point marker -- (b)uffer -- (f)ile -- (w)indow configuration - -- (d)elete saved thing" - (interactive (list - (let ((letters '(?. ?b ?f ?w ?d))) - (read-char-choice (concat "Save [" letters "]: ") - letters)))) - (let ((value (cl-case thing - (?. - (point-marker)) - (?b - (current-buffer)) - (?f - (buffer-file-name)) - (?w - (current-window-configuration)) - (?d nil)))) - (setq km/projectile-project-saved-thing - (lax-plist-put km/projectile-project-saved-thing - (projectile-project-root) - (cons thing value))))) - -(defun km/projectile-restore-thing () - "Restore saved thing for current project. -Return nil if there is no thing saved for the current project." - (interactive) - (-when-let* ((thing-value (lax-plist-get km/projectile-project-saved-thing - (projectile-project-root))) - (thing (car thing-value)) - (value (cdr thing-value))) - (cl-case thing - (?. - (-if-let (buf (marker-buffer value)) - (progn (switch-to-buffer buf) - (goto-char value)) - (user-error "Buffer no longer exists"))) - (?b - (if (buffer-live-p value) - (switch-to-buffer value) - (user-error "Buffer no longer exists"))) - (?f - (find-file value)) - (?w - (set-window-configuration value))) - t)) - -(defvar km/projectile-switch-fallback 'projectile-commander) - -(defun km/projectile-maybe-restore-thing () - "Try to restore thing for current project. -If there is nothing ot restore, call -`km/projectile-switch-fallback'." - (or (km/projectile-restore-thing) - (funcall km/projectile-switch-fallback))) - -(defun km/projectile-kill-buffers () - "Kill all project buffers. -Like `projectile-kill-buffers', but -- Before killing buffers, delete any saved thing for the project. -- Don't ask for confirmation to kill project buffers (but - `kill-buffer' will still ask when killing a modified buffer)." - (interactive) - (km/projectile-save-thing ?d) - (cl-letf (((symbol-function 'yes-or-no-p) (lambda (&rest args) t))) - (projectile-kill-buffers))) - -;; I'm redefining a lot of bindings, so just set the whole map here to -;; have everything in one place. - -(setq projectile-command-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "4 b") 'projectile-switch-to-buffer-other-window) - (define-key map (kbd "4 C-o") 'projectile-display-buffer) - (define-key map (kbd "4 d") 'projectile-find-dir-other-window) - (define-key map (kbd "4 f") 'projectile-find-file-other-window) - (define-key map (kbd "4 v") 'km/projectile-view-file-other-window) - (define-key map (kbd ".") 'km/projectile-copy-project-filename-as-kill) - (define-key map "!" 'projectile-run-shell-command-in-root) - (define-key map "&" 'projectile-run-async-shell-command-in-root) - (define-key map "b" 'helm-projectile-switch-to-buffer) - (define-key map "c" 'projectile-compile-project) - (define-key map "d" 'helm-projectile-find-dir) - (define-key map "e" 'km/projectile-restore-thing) - (define-key map "f" 'helm-projectile-find-file) - (define-key map "F" 'helm-projectile-find-file-in-known-projects) - (define-key map "g" 'projectile-vc) - (define-key map "i" 'projectile-ibuffer) - (define-key map "I" 'projectile-invalidate-cache) - (define-key map "k" 'km/projectile-kill-buffers) - (define-key map "l" 'projectile-project-buffers-other-buffer) - (define-key map "m" 'projectile-commander) - (define-key map "o" 'projectile-multi-occur) - (define-key map "p" 'helm-projectile-switch-project) - (define-key map "q" 'projectile-replace) - (define-key map "r" 'helm-projectile-recentf) - (define-key map "s" 'projectile-grep) - (define-key map "v" 'km/projectile-view-file) - (define-key map "w" 'km/projectile-save-thing) - map)) -(fset 'projectile-command-map projectile-command-map) - -(key-chord-define-global "jq" 'projectile-commander) -(key-chord-define-global "gp" 'km/projectile-switch-project) - -(define-prefix-command 'km/projectile-ctl-x-4-map) -(define-key ctl-x-4-map "p" 'km/projectile-ctl-x-4-map) - -(define-key km/projectile-ctl-x-4-map (kbd "C-o") - 'projectile-display-buffer) -(define-key km/projectile-ctl-x-4-map "b" - 'projectile-switch-to-buffer-other-window) -(define-key km/projectile-ctl-x-4-map "d" - 'projectile-find-dir-other-window) -(define-key km/projectile-ctl-x-4-map "f" - 'projectile-find-file-other-window) -(define-key km/projectile-ctl-x-4-map "t" - 'projectile-find-implementation-or-test-other-window) -(define-key km/projectile-ctl-x-4-map "v" - 'km/projectile-view-file-other-window) - - -;;; Commander methods - -;; Like `projectile-command-map', I'm redefining a lot of bindings, so -;; unset pre-defined methods and define everyting here. - -(setq projectile-commander-methods nil) - -(def-projectile-commander-method ?b - "Find project buffer." - (call-interactively 'helm-projectile-switch-to-buffer)) - -(def-projectile-commander-method ?c - "Run project compilation command." - (call-interactively 'projectile-compile-project)) - -(def-projectile-commander-method ?d - "Find directory in project." - (helm-projectile-find-dir)) - -(def-projectile-commander-method ?D - "Find a project directory in other window." - (call-interactively 'projectile-find-dir-other-window)) - -(def-projectile-commander-method ?e - "Restore saved thing." - (km/projectile-restore-thing)) - -(def-projectile-commander-method ?f - "Open project file." - (helm-projectile-find-file)) - -(def-projectile-commander-method ?F - "Find project file in other window." - (call-interactively 'projectile-find-file-other-window)) - -(def-projectile-commander-method ?g - "Open project root in vc-dir or magit." - (projectile-vc)) - -(def-projectile-commander-method ?i - "Open an IBuffer window showing all buffers in the current project." - (call-interactively 'projectile-ibuffer)) - -(def-projectile-commander-method ?k - "Kill all project buffers." - (call-interactively 'km/projectile-kill-buffers)) - -(def-projectile-commander-method ?o - "Display a project buffer in other window." - (call-interactively 'projectile-display-buffer)) - -(def-projectile-commander-method ?O - "Run multi-occur on project buffers." - (projectile-multi-occur)) - -(def-projectile-commander-method ?p - "Switch project." - (helm-projectile-switch-project)) - -(def-projectile-commander-method ?r - "Find recently visited file in project." - (projectile-recentf)) - -(def-projectile-commander-method ?s - "Run grep on project." - (call-interactively #'projectile-grep)) - -(def-projectile-commander-method ?v - "View project file." - (km/projectile-view-file)) - -(def-projectile-commander-method ?V - "View project file in other window." - (km/projectile-view-file-other-window)) - -(def-projectile-commander-method ?w - "Save thing." - (call-interactively #'km/projectile-save-thing)) - -(projectile-global-mode) - -(provide 'init-projectile) -;;; init-projectile.el ends here diff --git a/lisp/init-python.el b/lisp/init-python.el deleted file mode 100644 index 3af81ec..0000000 --- a/lisp/init-python.el +++ /dev/null @@ -1,319 +0,0 @@ -;;; init-python.el --- Python configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(setq python-fill-docstring-style 'pep-257-nn - python-indent-guess-indent-offset nil) - -(setq python-shell-interpreter "ipython" - python-shell-prompt-detect-enabled nil) - -(add-to-list 'interpreter-mode-alist '("python2" . python-mode)) -(add-to-list 'interpreter-mode-alist '("python3" . python-mode)) - -(add-hook 'python-mode-hook 'km/python-set-local-vars) -(add-hook 'python-mode-hook - (lambda () - (add-hook - 'post-self-insert-hook - #'km/python-indent-post-self-insert-function 'append 'local))) - -(defun km/python-set-local-vars () - (setq outline-regexp "####* ") - (setq outline-level 'km/python-outline-level) - ;; Stop semantic from taking over imenu. - (setq imenu-create-index-function #'python-imenu-create-index) - (set (make-local-variable 'compile-command) "py.test")) - -(defun km/python-outline-level () - (and (looking-at (concat "^" outline-regexp)) - (- (match-end 0) (match-beginning 0) 3))) - -(defun km/toggle-ipython-shell () - "Switch between using python and IPython for interactive shell." - (interactive) - (setq python-shell-interpreter - (if (string= python-shell-interpreter "python") "ipython" "python"))) - -(defun km/find-python-test-file-other-window (arg) - "Open test file for the current Python file in another window. -If the file does not already exist, import the original Python -file. Unless ARG is non-nil, py.test is also imported." - (interactive "P") - (let* ((py-file (file-name-nondirectory buffer-file-name)) - (test-file (concat "test_" py-file))) - (find-file-other-window test-file) - (unless (file-exists-p test-file) - (insert (format "import %s\n" (file-name-sans-extension py-file))) - (unless arg - (insert "import pytest\n"))))) - -(defun km/python-shell-send-function-or-paragraph-and-step () - "Send function or paragraph to Python shell. - -Send function if point is inside one. Otherwise, send the current -paragraph. After evaluation, step to the next code line. - -This is inspired by `ess-eval-function-or-paragraph-and-step'." - (interactive) - (if (km/python-inside-defun-p) - (progn - (python-shell-send-defun 1) - (python-nav-end-of-defun)) - (let (end) - (save-excursion - (forward-paragraph) - (setq end (point)) - (backward-paragraph) - (python-shell-send-region (point) end)) - (goto-char end))) - (km/python-next-code-line 1)) - -(defun km/python-shell-send-buffer-up-to-line () - "Send beginning of buffer to the current line to Python shell." - (interactive) - (python-shell-send-region (point-min) (line-end-position))) - -(defun km/python-inside-defun-p () - ;; I don't use `python-nav-beginning-of-defun' because it will go to - ;; the function even when the point is not inside of it. - (when (python-info-current-defun) - t)) - -(defun km/python-next-code-line (&optional arg skip-to-eob) - "This is copied nearly exactly from `ess-next-code-line'." - (interactive "p") - (or arg (setq arg 1)) - (beginning-of-line) - (let ((pos (point)) - (n 0) - (inc (if (> arg 0) 1 -1))) - (while (and (/= arg 0) (= n 0)) - (setq n (forward-line inc)); n=0 is success - (if (not (fboundp 'comment-beginning)) - (while (and (= n 0) - (looking-at "\\s-*\\($\\|\\s<\\)")) - (setq n (forward-line inc))) - (comment-beginning) - (beginning-of-line) - (forward-comment (* inc (buffer-size))) ;; as suggested in info file - ) - (if (or skip-to-eob - (not (looking-at "[ \t\n]*\\'"))) ;; don't go to eob or whatever - (setq arg (- arg inc)) - (goto-char pos) - (setq arg 0) - (forward-line 1));; stop at next empty line - (setq pos (point))) - (goto-char pos) - n)) - -(defvar km/python-shell-current-string nil) -(defun km/python-shell-send-set-string (set) - "Send previously set string to Python shell. -If a string has not been set previously or SET is non-nil, prompt -for a new string." - (interactive "P") - (when (or set (not km/python-shell-current-string)) - (let ((initial (and (use-region-p) - (buffer-substring-no-properties - (region-beginning) (region-end))))) - (setq km/python-shell-current-string (read-string "Python command: " initial)))) - (python-shell-send-string km/python-shell-current-string)) - -(defun km/python-shell--read-buffer () - (let ((buf-alist (mapcar (lambda (b) (cons (buffer-name b) b)) - (km/mode-buffers 'inferior-python-mode)))) - (if (= (length buf-alist) 1) - (cdr (car buf-alist)) - (cdr (assoc-string (completing-read "Shell buffer: " buf-alist) - buf-alist))))) - -(defun km/python-shell--find-prompt-start (move-func) - "Find first line of input prompt. - -`comint-forward-prompt' can end up at other points in input -entry, like '*': - - In [1]: for i in range(3): - ...: * print(i) - -Return point at the beginning input entry." - (let ((beg-prompt-re "\\(In \\[[0-9]+\\]:\\|>>> \\)")) - (funcall move-func) - (while (not (save-excursion - (beginning-of-line) - (looking-at-p beg-prompt-re))) - (funcall move-func)) - (point-at-bol))) - -(defun km/python-copy-last-shell-line-as-comment (&optional which-shell) - "Insert last input and output Python shell as comment. -When the current buffer is not associated with a Python shell or -WHICH-SHELL is non-nil, prompt with all Python shell buffers." - (interactive "P") - (let* ((default-shell-buffer - (get-buffer (format "*%s*" (python-shell-get-process-name t)))) - (shell-buffer (if (or which-shell (not default-shell-buffer)) - (km/python-shell--read-buffer) - default-shell-buffer)) - (start-pos (point)) - text) - (with-current-buffer shell-buffer - (save-excursion - (goto-char (point-max)) - (let* ((inhibit-field-text-motion t) - (beg (km/python-shell--find-prompt-start - (lambda () - (comint-previous-prompt 1)))) - (end (km/python-shell--find-prompt-start - ;; FIXME: If extra blank prompts exist at the end - ;; of shell, this will grab them (because - ;; `comint-next-prompt' skips over them). - (lambda () - (end-of-line) - (comint-next-prompt 1))))) - (setq text (buffer-substring-no-properties beg end))))) - (insert text) - (comment-region start-pos (point)))) - -(defun km/python-indent-post-self-insert-function () - "Adjust indentation after insert of specfic characters. -This is taken from `python-indent-post-self-insert-function'. -Unlike that function, it does not rely on `electric-indent-mode' -being turned on." - (when (and (not (bolp)) - (let ((paren-start (python-syntax-context 'paren))) - ;; Check that point is inside parens. - (when paren-start - (not - ;; Filter the case where input is happening in the same - ;; line where the open paren is. - (= (line-number-at-pos) - (line-number-at-pos paren-start))))) - ;; When content has been added before the closing paren or a - ;; comma has been inserted, it's ok to do the trick. - (or - (memq (char-after) '(?\) ?\] ?\})) - (eq (char-before) ?,))) - (save-excursion - (goto-char (line-beginning-position)) - (let ((indentation (python-indent-calculate-indentation))) - (when (< (current-indentation) indentation) - (indent-line-to indentation)))))) - -(after 'python - (define-key python-mode-map (kbd "C-c C-.") - 'km/python-shell-send-buffer-up-to-line) - (define-key python-mode-map (kbd "C-c C-b") 'python-shell-send-buffer) - ;; Rebind `python-shell-send-buffer'. - (define-key python-mode-map (kbd "C-c C-c") - 'km/python-shell-send-function-or-paragraph-and-step) - - (define-key python-mode-map (kbd "C-c C-d") 'km/python-shell-send-set-string) - - ;; Swap `python-shell-send-defun' and `python-eldoc-at-point'. - (define-key python-mode-map (kbd "C-c C-f") 'python-shell-send-defun) - (define-key python-mode-map (kbd "C-M-x") 'python-eldoc-at-point) - - (define-prefix-command 'km/python-prefix-map) - (define-key python-mode-map (kbd "C-c m") 'km/python-prefix-map) - - (define-key km/python-prefix-map "c" 'km/python-copy-last-shell-line-as-comment) - (define-key km/python-prefix-map "t" 'km/find-python-test-file-other-window)) - - -;;; Pydoc - -(defvar km/pydoc-dir "~/src/emacs/pydoc/") - -(when (file-exists-p km/pydoc-dir) - (add-to-list 'load-path km/pydoc-dir) - (autoload 'pydoc "pydoc" nil t)) - -(setq pydoc-make-method-buttons nil) - -(defvar km/pydoc-names nil - "List of names that have been sucessfully loaded by `pydoc'.") - -(defvar km/pydoc-names-file "~/.emacs.d/.pydoc-names" - "File to save `km/pydoc-names' to.") - -(add-hook 'pydoc-after-finish-hook #'km/pydoc-store-name) - -(defun km/pydoc () - "Run `pydoc', prompting with `km/pydoc-names'." - (interactive) - (let* ((default-directory "~/") - (initial-name (and (use-region-p) - (buffer-substring-no-properties - (region-beginning) - (region-end)))) - (name (completing-read "Name: " km/pydoc-names nil nil - initial-name))) - (pydoc name))) - -(defun km/pydoc-store-name () - "Store the name for the current pydoc object." - (with-current-buffer (pydoc-buffer) - (unless (eq (plist-get pydoc-info :type) 'not-found) - (cl-pushnew (substring-no-properties (car (cdr help-xref-stack-item))) - km/pydoc-names - :test #'string=)))) - -(defun km/pydoc-save-names-file (&optional file) - "Save `km/pydoc-names' to FILE. -FILE is `km/pydoc-names-file' by default." - (interactive - (list - (read-file-name (format "Save file (default %s): " - km/pydoc-names-file) - nil km/pydoc-names-file t))) - (setq file (or file km/pydoc-names-file)) - (when (file-writable-p file) - (with-temp-file file - (let (print-length) - (print (sort km/pydoc-names #'string-lessp) - (current-buffer)))))) - -(defun km/pydoc-read-names-file (&optional file) - "Read `km/pydoc-names-file' from FILE. -FILE is `km/pydoc-names-file' by default." - (interactive - (list - (read-file-name (format "Read file (default %s): " - km/pydoc-names-file) - nil km/pydoc-names-file t))) - (with-temp-buffer - (insert-file-contents (or file km/pydoc-names-file)) - (setq km/pydoc-names (read (current-buffer))))) - -(after 'pydoc - ;; Don't shadow my `ace-link' binding. - (define-key pydoc-mode-map "o" #'ace-link-help)) - -(when (file-exists-p km/pydoc-names-file) - (km/pydoc-read-names-file km/pydoc-names-file)) - -(global-set-key (kbd "C-h y") #'km/pydoc) - -(provide 'init-python) -;;; init-python.el ends here diff --git a/lisp/init-server.el b/lisp/init-server.el deleted file mode 100644 index 04be8b2..0000000 --- a/lisp/init-server.el +++ /dev/null @@ -1,46 +0,0 @@ -;;; init-server.el --- Emacs server configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(setq server-use-tcp t) -(require 'server) -(unless (server-running-p) - (server-start)) - -(let ((server (daemonp))) - (cond - ((string= server "default") - ;; Remove all mail map bindings except notmuch. - (global-set-key (kbd "C-x m") nil) - (global-set-key (kbd "C-x m n") 'notmuch) - (add-hook 'kill-emacs-hook #'km/pydoc-save-names-file) - (setq save-abbrevs 'silently - bookmark-save-flag 1)) - ((string= server "mail") - (setq mode-line-misc-info - (cons (propertize " [Mail] " 'face 'font-lock-doc-face) - mode-line-misc-info)) - (key-chord-define-global "jg" 'km/mail-map) - (setq recentf-save-file "~/.emacs.d/cache/recentf-mail") - (setq save-abbrevs nil)))) - -(provide 'init-server) -;;; init-server.el ends here diff --git a/lisp/init-snakemake.el b/lisp/init-snakemake.el deleted file mode 100644 index 09b2065..0000000 --- a/lisp/init-snakemake.el +++ /dev/null @@ -1,75 +0,0 @@ -;;; init-snakemake.el --- Snakemake mode configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(add-to-list 'load-path "~/src/emacs/snakemake-mode/") -(require 'snakemake-mode-autoloads) - -(autoload 'snakemake-compile-command "snakemake-mode") - -(setq snakemake-compile-command-options '("-p")) - -;; Although `compile-command' and `imenu-create-index-function' are -;; set when snakemake-mode is derived from Python mode, I need to -;; define them again here because I have a Python mode hook overrides -;; the Python versions. -(add-hook 'snakemake-mode-hook #'km/snakemake-set-local-vars) - -(defun km/snakemake-set-local-vars () - (set (make-local-variable 'compile-command) - (snakemake-compile-command)) - (set (make-local-variable 'imenu-create-index-function) - #'snakemake-imenu-create-index)) - -(defun km/snakemake-compile-project-file (jobs) - "Run Snakemake to produce project file at point. -The numeric prefix JOBS controls the number of jobs that -Snakemake runs (defaults to 1). If JOBS is zero, perform a dry -run. If JOBS is negative, just touch the output files." - (interactive "p") - (let* ((fname (or (km/project-filename-at-point) - (read-file-name "File: "))) - (job-flag (cond - ((> jobs 0) (format " -j%s " jobs)) - ((zerop jobs) " -n ") - (t " -t "))) - (compile-command (concat (snakemake-compile-command) job-flag - fname)) - (default-directory (projectile-project-root))) - (call-interactively 'compile))) - -(defun km/snakemake-compile-project-rule () - "Run `snakemake-compile-rule' from project root." - (interactive) - (let ((default-directory (projectile-project-root))) - (call-interactively #'snakemake-compile-rule))) - -(after 'init-external - (define-key km/compile-map "b" - 'km/snakemake-compile-project-rule) - (define-key km/compile-map "p" - 'km/snakemake-compile-project-file)) - -(after 'dired - (define-key dired-mode-map "b" 'km/snakemake-compile-project-file)) - -(provide 'init-snakemake) -;;; init-snakemake.el ends here diff --git a/lisp/init-tex.el b/lisp/init-tex.el deleted file mode 100644 index 87f949e..0000000 --- a/lisp/init-tex.el +++ /dev/null @@ -1,61 +0,0 @@ -;;; init-tex.el --- TeX configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(setq reftex-default-bibliography '("refs.bib")) - -(setq font-latex-fontify-sectioning 'color - TeX-electric-math '("$" . "$")) - -(put 'LaTeX-narrow-to-environment 'disabled nil) - -(add-to-list 'auto-mode-alist '("\\.[tT]e[xX]\\'" . latex-mode)) - -(add-hook 'LaTeX-mode-hook 'turn-on-reftex) -(add-hook 'LaTeX-mode-hook 'flyspell-mode) -(add-hook 'LaTeX-mode-hook (lambda () - (setq imenu-create-index-function - #'km/latex-imenu-create-index-function))) - -(defun km/latex-imenu-create-index-function () - ;; See `LaTeX-imenu-create-index-function'. - (TeX-update-style) - (let ((sec-re (LaTeX-outline-regexp)) - (title-re "\\*\\{0,1\\}{\\([^}]+\\)}") - entries) - (goto-char (point-min)) - (while (re-search-forward sec-re nil t) - (let ((sec (replace-regexp-in-string - "\\\\" "" (match-string-no-properties 0))) - (title (and (looking-at title-re) - (replace-regexp-in-string - "\\s-\\s-+" " " - (replace-regexp-in-string - "\n" "" (match-string-no-properties 1)))))) - (when (> (length title) 45) - (setf (substring title 21 -21) "...")) - (push (cons (if title (format "%s (%s)" title sec) sec) - (save-excursion (beginning-of-line) (point-marker))) - entries))) - (nreverse entries))) - -(provide 'init-tex) -;;; init-tex.el ends here diff --git a/lisp/init-util.el b/lisp/init-util.el deleted file mode 100644 index c085738..0000000 --- a/lisp/init-util.el +++ /dev/null @@ -1,38 +0,0 @@ -;;; init-util.el --- Utilities - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'dash) - -;; Taken from -;; http://milkbox.net/note/single-file-master-emacs-configuration/. -(defmacro after (mode &rest body) - "`eval-after-load' MODE evaluate BODY." - (declare (indent defun)) - `(eval-after-load ,mode - '(progn ,@body))) - -(defun km/mode-buffers (mode) - (--filter (with-current-buffer it (derived-mode-p mode)) - (buffer-list))) - -(provide 'init-util) -;;; init-util.el ends here diff --git a/lisp/init-view.el b/lisp/init-view.el deleted file mode 100644 index 7128a6b..0000000 --- a/lisp/init-view.el +++ /dev/null @@ -1,33 +0,0 @@ -;;; init-view.el --- View mode configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(after 'view - (define-key view-mode-map "l" 'recenter-top-bottom) - (define-key view-mode-map "f" 'forward-word) - (define-key view-mode-map "b" 'backward-word) - (define-key view-mode-map "]" 'forward-paragraph) - (define-key view-mode-map "[" 'backward-paragraph) - (define-key view-mode-map "j" 'avy-goto-subword-1)) - -(key-chord-define-global "hq" 'view-mode) - -(provide 'init-view) diff --git a/lisp/init-yas.el b/lisp/init-yas.el deleted file mode 100644 index 0d8c31b..0000000 --- a/lisp/init-yas.el +++ /dev/null @@ -1,47 +0,0 @@ -;;; init-yas.el --- Yasnippet configuration - -;; Copyright (C) 2012-2016 Kyle Meyer - -;; Author: Kyle Meyer -;; URL: https://github.com/kyleam/emacs.d - -;; 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 . - -;;; Code: - -(require 'yasnippet) - -(setq yas-fallback-behavior nil) - -(defvar km/personal-snippets - (file-name-as-directory (expand-file-name "psnippets" user-emacs-directory))) - -(when (file-exists-p km/personal-snippets) - (add-to-list 'yas-snippet-dirs km/personal-snippets)) - -(defun km/yas-with-comment (str) - (concat comment-start - (unless (s-ends-with? " " comment-start) " ") - str comment-end)) - -(define-key yas-minor-mode-map (kbd "C-c i") 'yas-expand) -;; Remove commands with 'C-c &' prefix, which conflicts with -;; `org-mark-ring-goto' binding' -(define-key yas-minor-mode-map (kbd "C-c &") nil) -(define-key yas-minor-mode-map (kbd "") nil) -(define-key yas-minor-mode-map (kbd "TAB") nil) - -(yas-global-mode) - -(provide 'init-yas) diff --git a/lisp/km-abbrev.el b/lisp/km-abbrev.el new file mode 100644 index 0000000..9d81da8 --- /dev/null +++ b/lisp/km-abbrev.el @@ -0,0 +1,64 @@ +;;; km-abbrev.el --- Abbrev mode extesions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'abbrev) + +;;;###autoload +(defun km/abbrev-add-case-global () + "Define lower abbreviation for the word before point. +Like `add-global-abbrev', but always make the abbreviation the +lower case variant of the word before point." + (interactive) + ;; Modified from `add-abbrev'. + (let* ((table global-abbrev-table) + (exp (buffer-substring-no-properties + (point) + (save-excursion (forward-word -1) (point)))) + (name (downcase exp))) + (when (or (not (abbrev-expansion name table)) + (y-or-n-p (format "%s expands to \"%s\"; redefine? " + name (abbrev-expansion name table)))) + (define-abbrev table name exp)))) + +;;;###autoload +(defun km/abbrev-inverse-add-uppercase-global () + "Define uppercase expansion for the word before point. +Like `inverse-add-global-abbrev', but always use the lower case +version of the word before point as the abbreviation and the +upper case version as the expansion." + (interactive) + ;; Modified from `inverse-add-abbrev'. + (let* ((table global-abbrev-table) + (end (point)) + (start (save-excursion (forward-word -1) (point))) + (name (downcase (buffer-substring-no-properties start end))) + (exp (upcase name))) + (when (or (not (abbrev-expansion name table)) + (y-or-n-p (format "%s expands to \"%s\"; redefine? " + name (abbrev-expansion name table)))) + (define-abbrev table name exp) + (save-excursion + (goto-char end) + (expand-abbrev))))) + +(provide 'km-abbrev) +;;; km-abbrev.el ends here diff --git a/lisp/km-ace-link.el b/lisp/km-ace-link.el new file mode 100644 index 0000000..3de1b05 --- /dev/null +++ b/lisp/km-ace-link.el @@ -0,0 +1,92 @@ +;;; km-ace-link.el --- Extensions for Ace Link + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'avy) +(require 'dash) +(require 'wid-edit) + +(declare-function dired-next-line "dired" (arg)) +(defun km/ali--dired-collect-references () + (let ((end (window-end)) + points) + (save-excursion + (goto-char (window-start)) + (while (< (point) end) + (--when-let (dired-next-line 1) + (push it points))) + (nreverse points)))) + + +(autoload 'org-open-file "org") +(declare-function dired-get-filename "dired" + (&optional localp no-error-if-not-filep)) +;;;###autoload +(defun km/ace-link-dired () + "Ace jump to files in dired buffers." + (interactive) + (avy-with km/ace-link-dired + (setq avy-action + (lambda (pt) + (goto-char pt) + (org-open-file (dired-get-filename)))) + (avy--process + (km/ali--dired-collect-references) + #'avy--overlay-post))) + +(defun km/ali--widget-collect-references () + "Collect the positions of visible widgets in buffer." + (let (candidates pt) + (save-excursion + (save-restriction + (narrow-to-region + (window-start) + (window-end)) + (goto-char (point-min)) + (setq pt (point)) + (while (progn (ignore-errors (widget-forward 1)) + (> (point) pt)) + (setq pt (point)) + (push (point) candidates)) + (nreverse candidates))))) + +(declare-function gnus-summary-widget-forward "gnus-sum" (arg)) +;;;###autoload +(defun km/ace-link-widget () + "Press a widget that is visible in the current buffer. +This can be used in place of `ace-link-gnus' and has the +advantage of working for gwene buffers in addition to normal mail +buffers because it doesn't rely on the 'gnus-string' text +property." + (interactive) + (when (eq major-mode 'gnus-summary-mode) + (gnus-summary-widget-forward 1)) + (avy-with km/ace-link-widget + (setq avy-action + (lambda (pt) + (goto-char (1+ pt)) + (widget-button-press (point)))) + (avy--process + (km/ali--widget-collect-references) + #'avy--overlay-post))) + +(provide 'km-ace-link) +;;; km-ace-link.el ends here diff --git a/lisp/km-avy.el b/lisp/km-avy.el new file mode 100644 index 0000000..ba8e058 --- /dev/null +++ b/lisp/km-avy.el @@ -0,0 +1,70 @@ +;;; km-avy.el --- Extensions for avy + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'avy) + +(declare-function occur-mode-display-occurrence "replace") +;;;###autoload +(defun km/occur-avy-goto-subword-1 () + "Like `avy-goto-subword-1', but display occurence." + (interactive) + (let (avy-all-windows) + (call-interactively #'avy-goto-subword-1)) + (occur-mode-display-occurrence)) + +(declare-function compilation-display-error "compile") +;;;###autoload +(defun km/grep-avy-goto-subword-1 () + "Like `avy-goto-subword-1', but call `compilation-display-error'." + (interactive) + (let (avy-all-windows) + (call-interactively #'avy-goto-subword-1)) + (compilation-display-error)) + +(declare-function org-agenda-do-context-action "org-agenda") +;;;###autoload +(defun km/org-agenda-avy-goto-subword-1 () + (interactive) + (let (avy-all-windows) + (call-interactively #'avy-goto-subword-1)) + (org-agenda-do-context-action)) + +(declare-function magit-diff-show-or-scroll-up "magit-diff") +;;;###autoload +(defun km/magit-avy-goto-subword-1 () + "Like `km/avy-goto-subword-1', but maybe show commit and limit to window." + (interactive) + (let (avy-all-windows) + (call-interactively #'avy-goto-subword-1)) + (when (derived-mode-p 'magit-log-mode) + (magit-diff-show-or-scroll-up))) + +(declare-function gnus-summary-scroll-up "gnus-sum" (lines)) +;;;###autoload +(defun km/gnus-avy-goto-subword-and-select () + (interactive) + (let (avy-all-windows) + (call-interactively #'avy-goto-subword-1)) + (gnus-summary-scroll-up 0)) + +(provide 'km-avy) +;;; km-avy.el ends here diff --git a/lisp/km-bib.el b/lisp/km-bib.el new file mode 100644 index 0000000..1795587 --- /dev/null +++ b/lisp/km-bib.el @@ -0,0 +1,205 @@ +;;; km-bib.el --- Bibliography configuration + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'bibtex) +(require 'dash) +(require 'org) + +(defvar km/bibtex-unimportant-title-words + '("a" "aboard" "about" "above" "absent" "across" "after" "against" + "along" "alongside" "amid" "amidst" "among" "amongst" "an" "and" + "around" "as" "aslant" "astride" "at" "athwart" "atop" + "barring" "before" "behind" "below" "beneath" "beside" "besides" "between" + "beyond" "but" "by" "despite" "down" "during" "except" "failing" + "following" "for" "from" "in" "inside" "into" "like" + "mid" "minus" "near" "next" "nor" "notwithstanding" "of" "off" + "on" "onto" "opposite" "or" "out" "outside" "over" "past" + "per" "plus" "regarding" "round" "save" "since" "so" "than" + "the" "through" "throughout" "till" "times" "to" "toward" "towards" + "under" "underneath" "unlike" "until" "up" "upon" "via" "vs." + "when" "with" "within" "without" "worth" "yet") + "Words to ignore when running `km/bibtex-use-title-case'. +These are taken from +http://lanecc.libguides.com/content.php?pid=38483&sid=295540 and +have only been modified to remove duplicates. This means that +there are some unlikely words in there, but you never know when +the next article you read will have \"athwart\" in the title.") + +(defun km/bibtex-use-title-case () + "Convert title of current BibTeX entry to title case. +Change words in `km/bibtex-unimportant-title-words' to lower +case, unless the word is the first word in the title. Capitalize +all other words unless they are protected by brackets." + (save-excursion + (bibtex-beginning-of-entry) + (let* ((bounds (bibtex-search-forward-field "title" t)) + (beg (bibtex-start-of-text-in-field bounds))) + (goto-char (1- beg)) + (while (re-search-forward "\\(\\W\\)\\(\\w+\\)\\(\\W\\)" + (bibtex-end-of-text-in-field bounds) t) + (cond + ((and (string= (match-string 1) "{") + (string= (match-string 3) "}")) + ;; Go to previous character in case '}' is within the word. + (backward-char)) + ;; Leave commands alone. + ((string= (match-string 1) "\\")) + ;; Capitalize the first word of the title. This will fail if + ;; there is a space after '{'. + ((= (match-beginning 1) beg) + (backward-word) + (capitalize-word 1)) + ;; Subword is separated by '-' or '{'. + ((or (string= (match-string 1) "-") + (string= (match-string 1) "}")) + (backward-word) + (downcase-word 1)) + (t + (backward-word) + (if (member (downcase (match-string-no-properties 2)) + km/bibtex-unimportant-title-words) + (downcase-word 1) + (capitalize-word 1)))))))) + +(defun km/bibtex-single-space-author-list () + "Convert multiple spaces in author list to single space." + (save-excursion + (bibtex-beginning-of-entry) + (let* ((bounds (bibtex-search-forward-field "author" t)) + (beg (bibtex-start-of-text-in-field bounds))) + (when bounds + (goto-char beg) + (while (re-search-forward "\\(\\s-+\\) and" + (bibtex-end-of-text-in-field bounds) t) + (replace-match "" nil nil nil 1)) + (goto-char beg) + (fill-paragraph))))) + +(defun km/bibtex-set-coding-system () + (set-buffer-file-coding-system 'utf-8)) + +(defun km/bibtex-remove-entry-space () + "Remove space in entry header. +For example, convert + + @article { + +to + + @article{" + (save-excursion + (bibtex-beginning-of-entry) + (when (looking-at "@\\w+\\(\\s-+\\)") + (replace-match "" nil nil nil 1)))) + +(defun km/bibtex-downcase-entry () + (save-excursion + (bibtex-beginning-of-entry) + (when (looking-at "^@\\([^{]+\\){") + (replace-match (downcase (match-string-no-properties 1)) + 'fixedcase nil nil 1)))) + +(defun km/bibtex-pages-use-double-hyphen () + "Use double hyphen for page range." + (save-excursion + (bibtex-beginning-of-entry) + (let ((bounds (bibtex-search-forward-field "pages" t))) + (when bounds + (goto-char (bibtex-start-of-text-in-field bounds)) + (and (re-search-forward "[^A-z0-9]*-[^A-z0-9]*" + (bibtex-end-of-text-in-field bounds) t) + (replace-match "--")))))) + +(defun km/bibtex-remove-doi-leader () + "Remove leading part (http:...) of doi URL." + (save-excursion + (bibtex-beginning-of-entry) + (let ((bounds (bibtex-search-forward-field "doi" t))) + (when bounds + (goto-char (bibtex-start-of-text-in-field bounds)) + (and (re-search-forward "http://dx.doi.org/" + (bibtex-end-of-text-in-field bounds) t) + (replace-match "")))))) + +(defun km/bibtex-downcase-keys () + "Downcase keys that are all caps." + (save-excursion + (bibtex-beginning-of-entry) + (let (case-fold-search) + (while (re-search-forward "^\\s-*\\([A-Z]+\\)\\s-*=" nil t) + (replace-match (downcase (match-string 1)) 'fixedcase + nil nil 1))))) + +(defun km/bibtex-downcase-author-and () + (save-excursion + (bibtex-beginning-of-entry) + (let ((bounds (bibtex-search-forward-field "author" t))) + (when bounds + (goto-char (bibtex-start-of-text-in-field bounds)) + (let (case-fold-search) + (while (re-search-forward "\\bAND\\b" + (bibtex-end-of-text-in-field bounds) t) + (replace-match (downcase (match-string 0)) 'fixedcase))))))) + +(defvar km/bibtex-article-fields-to-delete + '("abstract" "issn" "pubmedid" "url" "eprint" "keywords")) + +(defun km/bibtex-delete-article-fields () + (save-excursion + (when (and (bibtex-beginning-of-entry) + (looking-at bibtex-entry-maybe-empty-head) + (string= (downcase (bibtex-type-in-head)) "article")) + (dolist (f km/bibtex-article-fields-to-delete) + (let (bounds) + ;; Make sure field is removed even if it is repeated. + (while (progn (bibtex-beginning-of-entry) + (setq bounds (bibtex-search-forward-field f t))) + (goto-char (bibtex-end-of-field bounds)) + (skip-chars-backward " \t\n") + (delete-region (bibtex-start-of-field bounds) + (point)))))))) + +;;;###autoload +(defun km/browse-doi (doi) + "Open DOI in browser. +When called interactively, take the DOI from the text under +point. The link is opened using the settings of +`org-doi-server-url'." + (interactive (list (km/doi-at-point))) + (browse-url (org-link-escape-browser (concat org-doi-server-url doi)))) + +;;;###autoload +(defun km/copy-doi-as-kill () + "Copy DOI at point to kill ring." + (interactive) + (-when-let (doi (km/doi-at-point)) + (kill-new (message "%s" (concat "doi:" doi))))) + +(defun km/doi-at-point () + "Return DOI at point." + (save-excursion + (skip-chars-backward "-.A-z0-9/") + (and (looking-at "\\(doi:[ \t\n]*\\)*\\([-./A-z0-9]+[A-z0-9]\\)\\b") + (match-string-no-properties 2)))) + +(provide 'km-bib) +;;; km-bib.el ends here diff --git a/lisp/km-buffer-cleanup.el b/lisp/km-buffer-cleanup.el new file mode 100644 index 0000000..ac0b008 --- /dev/null +++ b/lisp/km-buffer-cleanup.el @@ -0,0 +1,52 @@ +;;; km-buffer-cleanup.el --- Clean up buffer on save + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'whitespace) + +(defvar-local km/prevent-cleanup nil + "If set, `km/cleanup-buffer' does not perform clean up on save.") + +(defun km/toggle-prevent-cleanup () + "Toggle state of `km/prevent-cleanup'." + (interactive) + (if km/prevent-cleanup + (progn + (message "Allowing cleanup on save") + (kill-local-variable 'whitespace-style) + (global-whitespace-mode 0) + (global-whitespace-mode 1)) + (message "Preventing cleanup on save") + (setq-local whitespace-style + '(face trailing indentation + tab-mark space-mark newline-mark)) + (global-whitespace-mode 0) + (global-whitespace-mode 1)) + (setq km/prevent-cleanup (not km/prevent-cleanup))) + +(defun km/cleanup-buffer () + (interactive) + (unless km/prevent-cleanup + (whitespace-cleanup) + (delete-trailing-whitespace))) + +(provide 'km-buffer-cleanup) +;;; km-buffer-cleanup.el ends here diff --git a/lisp/km-buffers.el b/lisp/km-buffers.el new file mode 100644 index 0000000..b2138de --- /dev/null +++ b/lisp/km-buffers.el @@ -0,0 +1,60 @@ +;;; km-buffers.el --- Buffer-related extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +;;;###autoload +(defun km/save-and-kill-buffer () + "Save current buffer and then kill it." + (interactive) + (save-buffer) + (kill-this-buffer)) + +;;;###autoload +(defun km/kill-buffer (&optional arg) + "Kill this buffer. +With single C-u, prompt for buffer to kill. With double C-u, +kill this buffer and the window." + (interactive "P") + (cond + ((not arg) + (kill-buffer)) + ((equal arg '(16)) + (kill-buffer-and-window)) + (t + (call-interactively #'kill-buffer)))) + +;;;###autoload +(defun km/save-buffers () + "Run `save-some-buffers', but don't ask to save the current buffer. +`save-some-buffers' is called interactively." + (interactive) + (let* ((base-buf (buffer-base-buffer)) + (buf (or base-buf (current-buffer))) + (buf-file (buffer-file-name buf))) + (when (and (buffer-live-p buf) + (buffer-modified-p buf) + buf-file) + (with-current-buffer buf + (save-buffer)))) + (call-interactively #'save-some-buffers)) + +(provide 'km-buffers) +;;; km-buffers.el ends here diff --git a/lisp/km-compile.el b/lisp/km-compile.el new file mode 100644 index 0000000..4812afd --- /dev/null +++ b/lisp/km-compile.el @@ -0,0 +1,94 @@ +;;; km-compile.el --- Compilation extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + + +(require 'compile) +(require 'dash) + +(defvar km/compilation-buffer-name-prefix "compilation: ") + +(defun km/compilation-name-by-directory (&optional mode) + (let ((name (if (and mode (not (equal mode "compilation"))) + (downcase mode) + (concat km/compilation-buffer-name-prefix + (abbreviate-file-name default-directory))))) + (concat "*" name "*"))) + +(defun km/compilation-buffer-p (buffer) + (with-current-buffer buffer + (and (derived-mode-p 'compilation-mode) + (string-prefix-p (concat "*" km/compilation-buffer-name-prefix) + (buffer-name))))) + +;;;###autoload +(defun km/compile-in-home-dir () + (interactive) + (let ((default-directory "~/")) + (call-interactively #'compile))) + +;;;###autoload +(defun km/compilation-recompile (&optional arg) + "Recompile buffer. +By default, use `compilation-last-buffer'. If ARG is 0, get +buffer with name given by `km/compilation-name-by-directory'. +Otherwise, if ARG is non-nil, prompt with buffers from +`km/compilation-buffer-list'." + (interactive (list (and current-prefix-arg + (prefix-numeric-value current-prefix-arg)))) + (with-current-buffer (km/compilation--get-buffer arg) + (if (derived-mode-p 'occur-mode) + (revert-buffer) + (recompile)))) + +(defun km/compilation-display-buffer (&optional arg) + "Display compilation buffer. +By default, use `compilation-last-buffer'. If ARG is 0, get +buffer with name given by `km/compilation-name-by-directory'. +Otherwise, if ARG is non-nil, prompt with buffers from +`km/compilation-buffer-list'." + (interactive (list (and current-prefix-arg + (prefix-numeric-value current-prefix-arg)))) + (display-buffer (km/compilation--get-buffer arg))) + +(defun km/compilation--get-buffer (&optional arg) + (cond + ((and (not arg) + (buffer-live-p compilation-last-buffer) + compilation-last-buffer)) + ((and (numberp arg) + (= arg 0)) + (get-buffer (km/compilation-name-by-directory))) + (t + (let ((cbufs (-map #'buffer-name (km/compilation-buffer-list))) + buf) + (cl-case (length cbufs) + (0 (user-error "No compilation buffers found")) + (1 (setq buf (car cbufs))) + (t (setq buf (completing-read "Compilation buffer: " cbufs + nil nil nil nil (car cbufs))))) + buf)))) + +(defun km/compilation-buffer-list () + (-filter #'km/compilation-buffer-p (buffer-list))) + +(provide 'km-compile) +;;; km-compile.el ends here diff --git a/lisp/km-diff.el b/lisp/km-diff.el new file mode 100644 index 0000000..69a5a64 --- /dev/null +++ b/lisp/km-diff.el @@ -0,0 +1,57 @@ +;;; km-diff.el --- Diff-related extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'dash) +(require 'diff) +(require 'ediff) +(require 'view) + +;;;###autoload +(defun km/diff () + "Run `diff' and then select buffer and turn on View mode." + (interactive) + (call-interactively #'diff) + (select-window (get-buffer-window "*Diff*")) + (view-mode 1)) + +;;;###autoload +(defun km/revert-buffer-and-view () + (interactive) + (revert-buffer) + (view-mode 1)) + +;;;###autoload +(defun km/ediff-with-other-window () + "Run `ediff' on current window's file and other window's file." + (interactive) + (let ((windows (window-list))) + (unless (= (length windows) 2) + (user-error "Function restricted to two-window frames")) + (-if-let* ((file-a (buffer-file-name + (window-buffer (car windows)))) + (file-b (buffer-file-name + (window-buffer (cadr windows))))) + (ediff file-a file-b) + (user-error "At least one buffer is not visiting a file")))) + +(provide 'km-diff) +;;; km-diff.el ends here diff --git a/lisp/km-dired.el b/lisp/km-dired.el new file mode 100644 index 0000000..bbd38e6 --- /dev/null +++ b/lisp/km-dired.el @@ -0,0 +1,121 @@ +;;; km-dired.el --- Dired extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'dired) +(require 'org) +(require 'km-util) +(require 'projectile) + +(defun km/dired-switch-to-buffer () + (interactive) + (switch-to-buffer (km/dired-completing-buffer))) + +;;;###autoload +(defun km/dired-switch-to-buffer-other-window () + (interactive) + (pop-to-buffer (km/dired-completing-buffer))) + +(defun km/dired-completing-buffer () + (completing-read "Dired buffer: " + (mapcar #'buffer-name (km/mode-buffers 'dired-mode)))) + +;;;###autoload +(defun km/org-open-dired-marked-files (&optional arg) + "Open marked files (or next ARG) with `org-open-file'." + (interactive "p") + (setq arg (and current-prefix-arg arg)) + (let* ((files (dired-get-marked-files nil arg)) + (num-files (length files))) + (when (or (< num-files 5) + (yes-or-no-p (format "Open %s files?" num-files))) + (dolist (f files) (org-open-file f))))) + +;;;###autoload +(defun km/dired-view-file-other-window () + "In Dired, view this file in another window." + (interactive) + (view-file-other-window (dired-get-file-for-visit))) + +;;;###autoload +(defun km/dired-copy-and-edit () + "Copy file and enter `wdired-mode' for completing rename." + (interactive) + (unless (derived-mode-p 'dired-mode) + (user-error "Must be in a Dired buffer")) + (let* ((fname (dired-get-filename)) + (flag "---copy---") + (new-fname (concat fname flag))) + (when (file-directory-p fname) + (user-error "File cannot be directory")) + (copy-file fname new-fname) + (dired-revert) + (wdired-change-to-wdired-mode) + (goto-char (point-min)) + (re-search-forward (format "%s\\(%s\\)" + (file-name-nondirectory fname) + flag)) + (replace-match "" t nil nil 1))) + + +;;; Copying file names + +;;;###autoload +(defun km/dired-copy-project-filename-as-kill () + "Copy names of marked project files into kill ring. +This is similar to `dired-copy-filename-as-kill', but the leading +path is always relative to `projectile-project-root'." + (interactive) + (km/dired-copy-filename-relative-to-directory + (projectile-project-root))) + +;;;###autoload +(defun km/dired-copy-relative-filename-as-kill (&optional arg) + "Copy names of marked (or next ARG) files into kill ring. +This is similar to `dired-copy-filename-as-kill', but the leading +path is always relative to the `default-directory' of the other +window." + (interactive "p") + (setq arg (and current-prefix-arg arg)) + (km/dired-copy-filename-relative-to-directory + (km/other-default-directory) arg)) + +(defun km/dired-copy-filename-relative-to-directory (directory &optional arg) + "Like `dired-copy-filename-as-kill', but the filename is always +relative to DIRECTORY." + (let* ((string + (mapconcat #'identity + (mapcar (lambda (f) (file-relative-name f directory)) + (dired-get-marked-files t arg)) + " "))) + (if (eq last-command 'kill-region) + (kill-append string nil) + (kill-new string)) + (message "%s" string))) + +(defun km/other-default-directory () + "Get `default-directory' for result of `(other-window 1)'." + (save-window-excursion + (other-window 1) + default-directory)) + +(provide 'km-dired) +;;; km-dired.el ends here diff --git a/lisp/km-editing.el b/lisp/km-editing.el new file mode 100644 index 0000000..eb93d0c --- /dev/null +++ b/lisp/km-editing.el @@ -0,0 +1,220 @@ +;;; km-editing.el --- Editing-related extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'dash) +(require 'outline) +(require 's) +(require 'select) +(require 'thingatpt) +(require 'whitespace) + +;; http://www.emacswiki.org/emacs/UnfillParagraph +;;;###autoload +(defun km/unfill-paragraph () + "Convert a multi-line paragraph to a single line of text." + (interactive) + (let ((fill-column (point-max))) + (fill-paragraph nil))) + +;;;###autoload +(defun km/fill-surrounding-indented () + "Fill current line with all surrounding lines of same indentation. +This is like `fill-individual-paragraphs', but 1) it acts only on +a single paragraph at point, not all paragraphs in a region, and +2) it doesn't treat lines with the following structure as a +special case. + + foo> This line with extra indentation starts + foo> a paragraph that continues on more lines." + (interactive) + (save-excursion + (let ((orig-point (point)) + (level (current-indentation)) + beg end) + (beginning-of-line) + (while (and (not beg) (not (bobp))) + (forward-line -1) + (when (or (/= level (current-indentation)) + (looking-at "^\\s-*$")) + (forward-line) + (setq beg (point)))) + (goto-char orig-point) + (beginning-of-line) + (while (and (not end) (not (eobp))) + (forward-line) + (when (or (/= level (current-indentation)) + (looking-at "^\\s-*$")) + (forward-line -1) + (end-of-line) + (setq end (point)))) + (fill-region (or beg (point-min)) (or end (point-max)))))) + +;;;###autoload +(defun km/reduce-to-single-spaces () + "Reduce consecutive blank lines to a single line." + (interactive) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "\\([ \t]*\n\\)\\{3,\\}" nil t) + (forward-line -1) + (delete-blank-lines)))) + +;;;###autoload +(defun km/export-wrapped-text (&optional xselect) + "Export the text in current buffer as wrapped text. + +This is useful for preparing text in emacs and then exporting to +a wrapped buffer for pasting text (e.g., into a web form). + +With an active region, restrict export to this region. If +XSELECT is non-nil, copy the region with `x-select-text'." + (interactive "P") + (let ((wrapped-buffer (get-buffer-create "*Wrapped export*"))) + (apply #'copy-to-buffer wrapped-buffer + (if (use-region-p) + (list (region-beginning) (region-end)) + (list (point-min) (point-max)))) + (with-current-buffer wrapped-buffer + (while (not (eobp)) + (forward-paragraph) + (forward-line -1) + (km/unfill-paragraph) + (forward-line 1)) + (when xselect + (x-select-text + (buffer-substring-no-properties (point-min) (point-max))))) + (pop-to-buffer wrapped-buffer))) + +;;;###autoload +(defun km/narrow-to-comment-heading () + "Narrow to the current comment heading subtree. + +Narrow the buffer from the current comment heading to the next +comment heading of the same level or, if not found, to the end of +the buffer. + +A comment is considered a heading if it is at the beginning of +the line and if it conists of 3 or more occurences of +`comment-start'. The number of `comment-start' characters is +taken to indicate the level of the heading (with 3 being the top +level). + +In the examples below, 'x' indicates the current point and '>>>' +and '<<<' mark the bounds of the narrowed region. + +--------------------------------------------------------------- + >>>;;; Level one heading + x + + ;;;; Level two heading + + <<< + ;;; Another level one heading +------------------------------eob------------------------------ + +--------------------------------------------------------------- + ;;; Level one heading + + >>>;;;; Level two heading + x + <<< + ;;;; Another level one heading +------------------------------eob------------------------------ + +--------------------------------------------------------------- + >>>;;; Level one heading + x + + ;;;; Level two heading + <<< +------------------------------eob------------------------------" + (interactive) + (unless comment-start + (user-error "Comment syntax is not defined for current buffer")) + (unless (= (length comment-start) 1) + (user-error "Buffer's comment string consists of more than one character")) + (save-excursion + (widen) + (let ((outline-regexp (concat (s-repeat 4 comment-start) "*"))) + (outline-mark-subtree) + (narrow-to-region (region-beginning) (region-end))))) + +;;;###autoload +(defun km/toggle-line-or-region-comment (beg end) + "Comment or uncomment the current line or region. +If there is an active region, act on all lines that the region +touches." + (interactive "*r") + (unless (use-region-p) + (setq beg (point) + end (point))) + (let ((bol (save-excursion (goto-char beg) + (line-beginning-position))) + (eol (save-excursion (goto-char end) + (line-end-position)))) + (unless (eq bol eol) + (comment-or-uncomment-region bol eol) + (forward-line)))) + +;; Modified from http://oremacs.com/2015/01/26/occur-dwim/. +;;;###autoload +(defun km/occur () + "Call `occur' with active region or symbol at point." + (interactive) + (--when-let (if (use-region-p) + (buffer-substring-no-properties + (region-beginning) (region-end)) + (thing-at-point 'symbol)) + (push it regexp-history)) + (call-interactively 'occur)) + + +;;; Kill map + +;;;###autoload +(defun km/kill-string-at-point () + (interactive) + (let ((string-start (nth 8 (syntax-ppss)))) + (goto-char string-start) + (kill-sexp))) + +;; Taken from prelude-core.el. +;;;###autoload +(defun km/join-next-line-with-space () + "Join current line to the next line with a space in between." + (interactive) + (delete-indentation 1)) + +(defmacro km/make-kill-thing-at-point (thing) + `(defun ,(intern (concat "km/kill-" thing "-at-point")) () + ,(format "Kill %s at point." thing) + (interactive) + (goto-char (beginning-of-thing (quote ,(make-symbol thing)))) + (,(intern (concat "kill-" thing)) 1))) + +(km/make-kill-thing-at-point "line") +(km/make-kill-thing-at-point "paragraph") +(km/make-kill-thing-at-point "sentence") +(km/make-kill-thing-at-point "word") + +(provide 'km-editing) +;;; km-editing.el ends here diff --git a/lisp/km-ess.el b/lisp/km-ess.el new file mode 100644 index 0000000..c7c2c8c --- /dev/null +++ b/lisp/km-ess.el @@ -0,0 +1,46 @@ +;;; km-ess.el --- ESS extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'ess-comp) +(require 'ess-inf) +(require 'ess-s-l) + +;;;###autoload +(defun km/ess-eval-buffer-up-to-line () + "Send up to the current line to inferior ESS process." + (interactive) + (ess-eval-region (point-min) (line-end-position) nil)) + +(defvar km/ess-dplry-pipe-key "|") + +;;;###autoload +(defun km/ess-insert-dplyr-pipe () + "Insert `km/ess-dplry-pipe' using `ess-smart-S-assign'. +Based on instructions in `ess-smart-S-assign-key', I didn't think +this would work, but it seems to so far." + (interactive) + (let ((ess-S-assign " %>% ") + (ess-smart-S-assign-key km/ess-dplry-pipe-key)) + (call-interactively #'ess-smart-S-assign))) + +(provide 'km-ess) +;;; km-ess.el ends here diff --git a/lisp/km-files.el b/lisp/km-files.el new file mode 100644 index 0000000..8c1f679 --- /dev/null +++ b/lisp/km-files.el @@ -0,0 +1,158 @@ +;;; km-files.el --- File-related extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'recentf) + +;;;###autoload +(defun km/rename-current-buffer-file () + "Rename current buffer and file it is visiting." + (interactive) + (let ((name (buffer-name)) + (filename (buffer-file-name))) + (if (not (and filename (file-exists-p filename))) + (user-error "Buffer '%s' is not visiting a file!" name) + (let ((new-name (read-file-name "New name: " filename))) + (if (get-buffer new-name) + (user-error "A buffer named '%s' already exists!" new-name) + (rename-file filename new-name 1) + (rename-buffer new-name) + (set-visited-file-name new-name) + (set-buffer-modified-p nil) + (message "File '%s' successfully renamed to '%s'" + name (file-name-nondirectory new-name))))))) + +;; https://github.com/purcell/emacs.d/blob/master/lisp/init-utils.el +;;;###autoload +(defun km/delete-this-file () + "Delete the current file, and kill the buffer." + (interactive) + (or (buffer-file-name) (user-error "No file is currently being edited")) + (when (yes-or-no-p (format "Really delete '%s'?" + (file-name-nondirectory buffer-file-name))) + (delete-file (buffer-file-name)) + (kill-this-buffer))) + +;; http://emacs-fu.blogspot.com/2013/03/editing-with-root-privileges-once-more.html +;;;###autoload +(defun km/find-file-as-root () + "Automatically edit file with root-privileges." + (interactive) + (let ((file (read-file-name "Edit as root: "))) + (unless (file-writable-p file) + (setq file (concat "/sudo:root@localhost:" file))) + (find-file file))) + +(declare-function dired-jump (&optional other-window file-name)) +;;;###autoload +(defun km/dired-jump-file-at-point () + "Run `dired-jump' on file at point." + (interactive) + (let ((file (or (and (use-region-p) + (buffer-substring-no-properties + (region-beginning) (region-end))) + (thing-at-point 'filename)))) + (if (and file (file-exists-p file)) + (dired-jump 'other-window (expand-file-name file)) + (user-error "No file at point")))) + +;;;###autoload +(defun km/touch-buffer-file () + "Run touch on `buffer-file-name'." + (interactive) + (call-process "touch" nil nil nil + (or (buffer-file-name (buffer-base-buffer)) + (user-error "Not visiting file")))) + +;;;###autoload +(defun km/write-file () + "Run `write-file'. +Use the current file name as initial input of prompt." + (interactive) + (let* ((init-file (and buffer-file-name + (file-name-nondirectory buffer-file-name))) + (new-file (read-file-name "Write file: " nil nil nil + init-file))) + (write-file new-file t))) + +;; Modified from prelude +;;;###autoload +(defun km/recentf-find-file () + "Find a file from `recentf-list'." + (interactive) + (find-file (km/read-recent-file))) + +;;;###autoload +(defun km/recentf-find-file-other-window () + "Find a file from `recentf-list' in other window." + (interactive) + (find-file-other-window (km/read-recent-file))) + +(defun km/read-recent-file () + (completing-read "Choose recent file: " recentf-list nil t)) + + +;;; Scratch files + +(defvar km/find-scratch-buffers + '((?e ".el" "Elisp") + (?g ".scm" "Guile scheme") + (?h ".hs" "Haskell") + (?m ".md" "Markdown") + (?n "" "No mode") + (?o ".org" "Org") + (?p ".py" "Python") + (?r ".r" "R") + (?s ".sh" "Shell") + (?t ".txt" "Text")) + "List of scratch buffers. +Format of each element should be (CHARACTER EXTENSION DOC). DOC +is not required.") + +;;;###autoload +(defun km/scratch-find-file (&optional pwd) + "Find scratch buffer. + +Prompt with characters from `km/find-scratch-buffers' to +determine the extension of the scratch file. + +With prefix argument PWD, find the scratch file in +`default-directory' instead of /tmp." + (interactive "P") + (switch-to-buffer (km/scratch--find-file-no-select pwd))) + +;;;###autoload +(defun km/scratch-find-file-other-window (&optional pwd) + "Like `km/find-scratch-file', but open buffer in another window." + (interactive "P") + (switch-to-buffer-other-window (km/scratch--find-file-no-select pwd))) + +(defun km/scratch--find-file-no-select (pwd) + (find-file-noselect (km/scratch--get-file-name pwd))) + +(defun km/scratch--get-file-name (pwd) + (let* ((choices (mapcar #'car km/find-scratch-buffers)) + (ch (read-char-choice (concat "[" choices "]") choices)) + (ext (cadr (assq ch km/find-scratch-buffers)))) + (concat (if pwd default-directory "/tmp/") "scratch" ext))) + +(provide 'km-files) +;;; km-files.el ends here diff --git a/lisp/km-framewin.el b/lisp/km-framewin.el new file mode 100644 index 0000000..a81f464 --- /dev/null +++ b/lisp/km-framewin.el @@ -0,0 +1,65 @@ +;;; km-framewin.el --- Frame and window extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +;;;###autoload +(defun km/clone-indirect-buffer-other-window-and-widen () + "Clone as indirect buffer and then widen." + (interactive) + (call-interactively #'clone-indirect-buffer-other-window) + (widen)) + +;; From prelude +;;;###autoload +(defun km/swap-windows () + "Swap 2 windows." + (interactive) + (if (/= (count-windows) 2) + (message "You need exactly 2 windows to do this.") + (let* ((w1 (car (window-list))) + (w2 (cadr (window-list))) + (b1 (window-buffer w1)) + (b2 (window-buffer w2)) + (s1 (window-start w1)) + (s2 (window-start w2))) + (set-window-buffer w1 b2) + (set-window-buffer w2 b1) + (set-window-start w1 s2) + (set-window-start w2 s1))) + (other-window 1)) + +;; http://www.emacswiki.org/emacs/ToggleWindowSplit +;;;###autoload +(defun km/switch-window-split () + "If the window is split vertically, split it horizontally or vice versa. +Assumes that the window is only split into two." + (interactive) + (unless (= (length (window-list)) 2) + (user-error "Can only toggle a window split in two")) + (let ((split-vertically-p (window-combined-p))) + (delete-window) + (if split-vertically-p + (split-window-horizontally) + (split-window-vertically)) + (switch-to-buffer nil))) + +(provide 'km-framewin) +;;; km-framewin.el ends here diff --git a/lisp/km-god.el b/lisp/km-god.el new file mode 100644 index 0000000..172dff0 --- /dev/null +++ b/lisp/km-god.el @@ -0,0 +1,36 @@ +;;; km-god.el --- God mode extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'god-mode) + +(defun km/god-update-cursor () + (setq cursor-type (if god-local-mode 'bar 'box))) + +(defun km/god-gnus-p () + "Return non-nil if a Gnus-related mode is enabled." + (derived-mode-p 'gnus-group-mode + 'gnus-summary-mode + 'gnus-article-mode + 'message-mode)) + +(provide 'km-god) +;;; km-god.el ends here diff --git a/lisp/km-helm.el b/lisp/km-helm.el new file mode 100644 index 0000000..9be507a --- /dev/null +++ b/lisp/km-helm.el @@ -0,0 +1,48 @@ +;;; km-helm.el --- Helm configuration + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'helm) + +;;;###autoload +(defun km/helm-display-buffer () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action #'display-buffer))) + +;;;###autoload +(defun km/helm-display-file () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action + (lambda (f) + (display-buffer (find-file-noselect f)))))) + +(autoload 'org-open-file "org") +;;;###autoload +(defun km/helm-ff-org-open-file () + "Run `org-open-file' from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action #'org-open-file))) + +(provide 'km-helm) +;;; km-helm.el ends here diff --git a/lisp/km-hydra.el b/lisp/km-hydra.el new file mode 100644 index 0000000..94e47cc --- /dev/null +++ b/lisp/km-hydra.el @@ -0,0 +1,203 @@ +;;; km-hydra.el --- Hydra definitions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'hydra) + +(defhydra hydra-org-link-edit () + "Org Link Edit" + ("j" org-link-edit-forward-slurp "forward slurp") + ("k" org-link-edit-forward-barf "forward barf") + ("u" org-link-edit-backward-slurp "backward slurp") + ("i" org-link-edit-backward-barf "backward barf") + ("l" km/org-link-edit-slurp-link "slurp link" :color blue) + ("q" nil "cancel")) + +(defhydra hydra-smerge (:hint nil) + " +_b_ keep base _d_ diff _n_ next +_m_ keep mine _e_ ediff _p_ previous +_o_ keep other _h_ refine +_a_ keep all +\n" + ("b" smerge-keep-base) + ("m" smerge-keep-mine) + ("o" smerge-keep-other) + ("a" smerge-keep-all) + ("n" smerge-next) + ("p" smerge-prev) + ("h" smerge-refine) + ("e" smerge-ediff :color blue) + ("d" (call-interactively + (pcase (read-char-choice + "< base-mine, > base-other, = mine-other" + (list ?< ?> ?=)) + (?< #'smerge-diff-base-mine) + (?> #'smerge-diff-base-other) + (?= #'smerge-diff-mine-other)))) + ("l" recenter-top-bottom "recenter") + ("u" undo "undo") + ("q" nil "quit")) + +(defhydra hydra-file-search-map (:hint nil :color blue) + " +^^Grep ^^Dired +^^------------ ^^------------------ +_f_: grep-find _d_: find-grep-dired +_g_: lgrep _D_: find-dired +_G_: grep _n_: find-name-dired +_r_: rgrep +_v_: vc-git-grep +_z_: zgrep +\n" + ("f" grep-find) + ("g" lgrep) + ("G" grep) + ("r" rgrep) + ("v" vc-git-grep) + ("z" zrgrep) + + ("d" find-grep-dired) + ("D" find-dired) + ("n" find-name-dired) + + ("q" nil "quit")) + +;; Modified from https://github.com/abo-abo/hydra/wiki/Emacs +(defhydra hydra-outline-mode (:hint nil) + " + ^^Hide ^^Show ^^Move +_q_ sublevels _a_ all _u_ up +_t_ body _e_ entry _n_ next visible +_o_ other _i_ children _p_ previous visible +_c_ entry _k_ branches _f_ forward same level +_h_ leaves _s_ subtree _b_ backward same level +_d_ subtree + +" + ("q" hide-sublevels) + ("t" hide-body) + ("o" hide-other) + ("c" hide-entry) + ("h" hide-leaves) + ("d" hide-subtree) + + ("a" show-all) + ("e" show-entry) + ("i" show-children) + ("k" show-branches) + ("s" show-subtree) + + ("u" outline-up-heading) + ("n" outline-next-visible-heading) + ("p" outline-previous-visible-heading) + ("f" outline-forward-same-level) + ("b" outline-backward-same-level) + + ("l" km/outline-jump-to-heading "jump" :color blue) + ("m" outline-mark-subtree "mark" :color blue)) + +(defun km/hydra-outline-mode () + (interactive) + (unless outline-minor-mode + (outline-minor-mode)) + (hydra-outline-mode/body)) + +(defhydra hydra-window-map (:hint nil) + " +_l_: Switch split _r_: Winner redo _o_: Scroll other +_s_: Swap _u_: Winner undo _i_: Scroll other down +\n" + ("l" km/switch-window-split) + ("s" km/swap-windows) + + ("r" winner-redo) + ("u" winner-undo) + + ("o" scroll-other-window) + ("i" scroll-other-window-down) + + ("f" make-frame "new frame" :color blue) + ("q" nil "quit")) + +;; Multiple cursors hydra is modified from +;; https://github.com/abo-abo/hydra/wiki/multiple-cursors +(defhydra hydra-multiple-cursors (:hint nil) + " + ^^Up ^^Down +_p_ Next _n_ Next +_P_ Skip _N_ Skip +_y_ Unmark _u_ Unmark + +" + ("l" mc/edit-lines "edit lines" :exit t) + ("a" mc/mark-all-like-this "mark all" :exit t) + ("n" mc/mark-next-like-this) + ("N" mc/skip-to-next-like-this) + ("u" mc/unmark-next-like-this) + ("p" mc/mark-previous-like-this) + ("P" mc/skip-to-previous-like-this) + ("y" mc/unmark-previous-like-this)) + +(defhydra hydra-kmacro (:hint nil :exit t) + " + ^^Defining ^^Applying +_?_ Query _c_ Call +_b_ Bind to key _o_ Call 2nd in ring +_e_ Edit _r_ Apply to region lines +_E_ Step edit +_N_ Name last +_l_ Use recent strokes +_s_ Start defining +_x_ To register + + ^^Ring ^^Counter +_D_ Delete ring head _+_ Add counter +_n_ Cycle next _=_ Set counter +_p_ Cycle previous _f_ Set format +_t_ Swap _i_ Insert counter +_v_ View + +" + ("+" kmacro-add-counter) + ("=" kmacro-set-counter) + ("D" kmacro-delete-ring-head) + ("f" kmacro-set-format) + ("i" kmacro-insert-counter) + ("c" kmacro-end-and-call-macro :exit nil) + ("o" kmacro-call-ring-2nd-repeat) + ("e" kmacro-edit-macro) + ("E" kmacro-step-edit-macro) + ("n" kmacro-cycle-ring-next :exit nil) + ("p" kmacro-cycle-ring-previous :exit nil) + ("t" kmacro-swap-ring) + ("v" kmacro-view-macro-repeat) + ("b" kmacro-bind-to-key) + ("l" kmacro-edit-lossage) + ("N" kmacro-name-last-macro) + ("?" kbd-macro-query) + ("r" apply-macro-to-region-lines) + ("s" kmacro-start-macro) + ("x" kmacro-to-register) + ("q" nil "quit" :hint t)) + +(provide 'km-hydra) +;;; km-hydra.el ends here diff --git a/lisp/km-magit.el b/lisp/km-magit.el new file mode 100644 index 0000000..616f975 --- /dev/null +++ b/lisp/km-magit.el @@ -0,0 +1,579 @@ +;;; km-magit.el --- Magit extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'avy) +(require 'git-rebase) +(require 'magit) +(require 'projectile) + +(defun km/magit-auto-commit () + "Commit all changes with \"auto\" commit message. +This can be useful for non-source code repos (e.g., Org mode note +files) or commiting incomplete changes that will be extended into +a proper commit." + (interactive) + (magit-run-git "commit" "--all" "--message=auto")) + +;;;###autoload +(defun km/magit-show-commit-at-point (&optional choose-project) + "Show commit point. +If there is no current project or if the prefix argument +CHOOSE-PROJECT is non-nil, prompt for the project name." + (interactive "P") + (if (save-excursion (skip-chars-backward "A-z0-9") + (looking-at "\\b[A-z0-9]\\{4,40\\}\\b")) + (let* ((hash (match-string-no-properties 0)) + (project + (and (or choose-project + (not (projectile-project-p)) + (not (magit-rev-verify (concat hash "^{commit}")))) + (completing-read "Project: " + (projectile-relevant-known-projects)))) + (default-directory (or project default-directory))) + (magit-show-commit hash (car (magit-diff-arguments)))) + (user-error "No hash found at point"))) + +;;;###autoload +(defun km/magit-commit-extend-with-file () + "Extend last commit with changes in the current file." + (interactive) + (let ((file (or (magit-current-file) + (user-error "No current file")))) + (cond ((magit-anything-staged-p) + (user-error "There are already staged changes")) + ((member file (nconc (magit-untracked-files) + (magit-modified-files))) + (magit-with-toplevel (magit-stage-file file)) + (magit-commit-extend)) + (t + (message "No changes to %s" file))))) + +;;;###autoload +(defun km/magit-commit-wip-with-file () + "Make a WIP commit for the current file. +Unlike `magit-wip-*' commands, this commit is made to the current +branch." + (interactive) + (let ((file (or (magit-current-file) + (user-error "No current file")))) + (cond ((magit-anything-staged-p) + (user-error "There are already staged changes")) + ((member file (nconc (magit-untracked-files) + (magit-modified-files))) + (magit-with-toplevel (magit-stage-file file)) + (magit-run-git "commit" (concat "--message=WIP " file))) + (t + (message "No changes to %s" file))))) + +(defun km/magit-ff-merge-upstream () + "Perform fast-forward merge of upstream branch. +\n(git merge --no-edit --ff-only )" + (interactive) + (--if-let (magit-get-tracked-branch) + (magit-merge it '("--ff-only")) + (user-error "No upstream branch"))) + +(defun km/magit-stage-file-intent (file) + "Stage FILE but not its content. +With a prefix argument or when there is no file at point, ask for +the file to be staged. Otherwise, stage the file at point +without requiring confirmation. +\n(git add -N FILE)" + ;; Modified from `magit-stage-file'. + (interactive + (let* ((atpoint (magit-section-when (file))) + (current (magit-file-relative-name)) + (choices (magit-untracked-files)) + (default (car (member (or atpoint current) choices)))) + (list (if (or current-prefix-arg (not default)) + (magit-completing-read "Stage file" choices + nil t nil nil default) + default)))) + (magit-run-git "add" "-N" file)) + +(defun km/magit-push-all () + "Push all branches." + (interactive) + (magit-run-git-async "push" "-v" + (magit-read-remote "Remote") + "--all")) + +(defun km/magit-push-head (remote &optional args) + "Push current branch to same name on remote. +\n(git push [ARGS] REMOTE HEAD)" + (interactive (list (magit-read-remote "Remote") (magit-push-arguments))) + (magit-run-git-async "push" "-v" args remote "HEAD")) + +(defun km/magit-checkout-local-tracking (remote-branch) + "Create and checkout a local tracking branch for REMOTE-BRANCH. +\n(git checkout -t REMOTE-BRANCH\)" + (interactive + (list (let ((branches (magit-list-remote-branch-names))) + (magit-completing-read + "Remote branch" branches nil t nil nil + (car (member (magit-branch-or-commit-at-point) + branches)))))) + (magit-run-git "checkout" "-t" remote-branch)) + +(defun km/magit-branch-rename (old new &optional force) + "Like `magit-branch-rename', but use old branch as initial prompt." + (interactive + (let ((branch (magit-read-local-branch "Rename branch"))) + (list branch + (magit-read-string-ns (format "Rename branch '%s' to" branch) + branch) + current-prefix-arg))) + (unless (string= old new) + (magit-run-git-no-revert "branch" (if force "-M" "-m") old new))) + +(defun km/magit-delete-previous-branch (&optional force) + "Delete previous branch. +\n(git branch -d @{-1})" + (interactive "P") + (magit-run-git "branch" (if force "-D" "-d") "@{-1}")) + +(defun km/magit-checkout-previous-branch () + "Checkout previous branch. +\n(git checkout -)" + (interactive) + (magit-run-git "checkout" "-")) + +(defun km/magit-list-recent-refs (n &optional remote) + "List N recent refs. +If REMOTE is non-nil, limit to remote refs." + (magit-git-lines + "for-each-ref" "--sort=-committerdate" "--format=%(refname:short)" + (format "--count=%s" n) + (if remote "refs/remotes" "refs/heads"))) + +(defun km/magit-checkout-recent-ref (n) + "Checkout branch from N recent refs. +Refs are sorted by committer date." + (interactive (list (or (and current-prefix-arg + (prefix-numeric-value current-prefix-arg)) + 5))) + (magit-run-git "checkout" + (magit-completing-read + "Ref" (km/magit-list-recent-refs n)))) + +(defun km/magit-checkout-track-recent-ref (n) + "Create and checkout a local tracking branch. +Listed refs are limited to N most recent, sorted by committer +date." + (interactive (list (or (and current-prefix-arg + (prefix-numeric-value current-prefix-arg)) + 5))) + (magit-run-git "checkout" "-t" + (magit-completing-read + "Ref" (km/magit-list-recent-refs n 'remote)))) + +(defun km/magit-refs-filter-recent (n) + "Limit branch list to N most recent. +Warning: I find this useful, but it's a hack that breaks +magit-section-backward and probably other things. Hit `g` to +refresh the buffer, and all should be right again." + (interactive (list (or (and current-prefix-arg + (prefix-numeric-value current-prefix-arg)) + 5))) + (unless (derived-mode-p 'magit-refs-mode) + (user-error "Not in Magit Refs mode")) + (let ((sec (magit-current-section)) + remote refs line-sec) + (when (eq (magit-section-type sec) 'branch) + (setq sec (magit-section-parent sec))) + (when (eq (magit-section-type sec) 'remote) + (setq remote (magit-section-value sec))) + (setq refs + (magit-git-lines + "for-each-ref" "--sort=-committerdate" "--format=%(refname:short)" + (format "--count=%s" n) + (if remote (format "refs/remotes/%s" remote) "refs/heads"))) + (save-excursion + (save-restriction + (narrow-to-region (magit-section-content sec) (magit-section-end sec)) + (goto-char (point-min)) + (while (and (not (eobp)) + (setq line-sec (magit-current-section)) + (eq (magit-section-type line-sec) 'branch)) + (if (member (magit-section-value line-sec) refs) + (forward-line 1) + (let ((inhibit-read-only t)) + (delete-region (magit-section-start line-sec) + (magit-section-end line-sec))))))))) + +(defun km/magit-checkout-master () + "Checkout master branch. +\n(git checkout master)" + (interactive) + (magit-run-git "checkout" "master")) + +(defun km/magit-branch-and-checkout-from-current (branch) + "Create and checkout BRANCH at current branch. +This is equivalent to running `magit-branch-and-checkout' with +START-POINT set to the current branch. +\n(git checkout -b BRANCH)" + (interactive (list (magit-read-string "Branch name"))) + (magit-run-git "checkout" "-b" branch)) + +(defun km/magit-backup-branch () + "Create a backup branch for the current branch. +\n(git branch b/)" + (interactive) + (--if-let (magit-get-current-branch) + (magit-run-git "branch" (concat "b/" it)) + (user-error "No current branch"))) + +(defun km/magit-mode-bury-all-windows (&optional kill-buffer) + "Run `magit-mode-quit-window' until no longer in Magit buffer." + (interactive "P") + (while (derived-mode-p 'magit-mode) + (magit-mode-bury-buffer kill-buffer))) + +(defun km/magit-log-select-guess-fixup-commit (&optional ntop) + "Guess commit from fixup/squash commmits. +Consider NTOP commits (default is 5) when searching for 'fixup!' +and 'squash!' titles." + (interactive (list (or (and current-prefix-arg + (prefix-numeric-value current-prefix-arg)) + 5))) + (let (ntop-end msgs commit-pts) + (save-excursion + ;; Get limit for fixup/squash search. + (goto-char (point-min)) + (setq ntop-end (line-end-position (1+ ntop))) + ;; Get fixup and squash messages. + (while (re-search-forward "[a-z0-9]+ \\(fixup!\\|squash!\\) \\(.+\\)" + ntop-end t) + (push (match-string-no-properties 2) msgs)) + (when (not msgs) + (user-error "No fixup or squash commits found")) + ;; Find earliest commit. + (goto-char (point-min)) + (dolist (msg msgs) + (when (re-search-forward (concat "[a-z0-9]+ " msg "\n") nil t) + (push (match-beginning 0) commit-pts)))) + (if commit-pts + (goto-char (apply #'max commit-pts)) + (message "No matching commits found")))) + +;;;###autoload +(defun km/magit-reset-file (rev file &optional checkout) + "Reset FILE from revision REV. + +If prefix argument CHECKOUT is non-nil, checkout FILE from REV +instead. + +\(git reset REV -- FILE) +\(git checkout REV -- FILE)" + (interactive + (let ((rev (magit-read-branch-or-commit "Revision" magit-buffer-revision))) + (list rev (magit-read-file-from-rev rev "File") current-prefix-arg))) + (magit-with-toplevel + (magit-run-git (if checkout "checkout" "reset") + rev "--" file))) + +;;;###autoload +(defun km/magit-pin-file (&optional other-rev) + "Pin this file to the current revision. + +Visit the current file and current revision with +`magit-find-file'. Position point as in the original buffer. +This may not correspond to same content if text before point has +changed since the current commit. + +If OTHER-REV is non-nil, prompt for another revision instead of +the current. If buffer is already a revision buffer, then find +the working tree copy instead. In both these cases, point may +not land in a reasonable location depending on how the content of +the file has changed." + (interactive "P") + (magit-with-toplevel + (let* ((line (+ (if (bolp) 1 0) (count-lines 1 (point)))) + (col (current-column)) + (rev (cond (other-rev + (magit-read-branch-or-commit "Find file from revision")) + ((not magit-buffer-file-name) + (or (magit-get-current-branch) + (magit-rev-parse "HEAD"))))) + (fname (file-relative-name + (or buffer-file-name + magit-buffer-file-name + (user-error "Buffer not visiting file"))))) + (if rev (magit-find-file rev fname) (find-file fname)) + (goto-char (point-min)) + (forward-line (1- line)) + (move-to-column col)))) + +;;;###autoload +(defun km/magit-revfile-reset (&optional checkout) + "Reset to revision from current revfile. +If CHECKOUT is non-nil, checkout file instead." + (interactive "P") + (unless (and magit-buffer-refname magit-buffer-file-name) + (user-error "Not in Magit revfile buffer")) + (magit-with-toplevel + (magit-run-git (if checkout "checkout" "reset") + magit-buffer-refname "--" magit-buffer-file-name))) + +;;;###autoload +(defun km/magit-find-recently-changed-file (n) + "Find a file that changed from \"HEAD~N..HEAD\". +N defaults to 20." + (interactive "p") + (unless current-prefix-arg (setq n 10)) + (magit-with-toplevel + (find-file (magit-completing-read + "File" + (magit-changed-files (format "HEAD~%s..HEAD" n)) + nil t)))) + +(defun km/magit-find-commit-file (commit) + "Find file changed in COMMIT." + (interactive (list (or (magit-branch-or-commit-at-point) + (and (derived-mode-p 'magit-revision-mode) + (car magit-refresh-args)) + (magit-read-branch-or-commit "Commit")))) + (let ((files (magit-changed-files (format "%s~..%s" commit commit)))) + (find-file + (cl-case (length files) + (0 (user-error "No changed files in %s" commit)) + (1 (car files)) + (t (magit-completing-read "File" files nil t)))))) + +(defun km/magit-insert-staged-file (&optional no-directory) + "Select staged file to insert. + +This is useful for referring to file names in commit messages. +By default, the path for the file name is relative to the top +directory of the repository. Remove the directory component from +the file name if NO-DIRECTORY is non-nil. + +If there are no staged files, look instead at files that changed +in HEAD. These rules will usually offer the files of interest +while commiting, but this is not the case if you are amending a +commit with the \"--only\" flag and have staged files (i.e., this +command will still offer the staged files)." + (interactive "P") + (magit-with-toplevel + (let* ((files (or (magit-staged-files) + (magit-changed-files "HEAD^..HEAD"))) + (file (cl-case (length files) + (1 (car files)) + (0 (error "No files found")) + (t + (completing-read "Staged file: " files nil t))))) + (insert (if no-directory (file-name-nondirectory file) file))))) + +(defun km/magit-shorten-hash (hash &optional n) + (magit-rev-parse (format "--short=%s" (or n (magit-abbrev-length))) hash)) + +;;;###autoload +(defun km/magit-shorten-hash-at-point (&optional n) + "Shorten hash at point to N characters. + +N defaults to `magit-abbrev-length'. If the commit belongs to +the current repo and the hash is ambiguous, the hash is extended +as needed. + +To explicitly set the hash length, use a numeric prefix +argument." + (interactive (list (or (and current-prefix-arg + (prefix-numeric-value current-prefix-arg)) + (magit-abbrev-length)))) + (cond + ((< n 4) + (user-error "Hash must be at least 4 characters")) + ((>= n 40) + (user-error "Full hashes are 40 characters")) + ((> n 30) + (message "That doesn't seem incredibly useful, but OK"))) + (let ((offset (- (skip-chars-backward "A-z0-9")))) + (if (looking-at "\\b[A-z0-9]\\{5,40\\}\\b") + (let ((hash-len (- (match-end 0) (match-beginning 0))) + (hash (match-string 0))) + (when (< hash-len n) + (user-error "Desired hash length is greater than current")) + (replace-match (or (km/magit-shorten-hash hash n) + ;; We're not in a repo. + (substring hash 0 n)) + 'fixedcase) + (when (< offset n) + (skip-chars-backward "A-z0-9") + (goto-char (+ (point) offset)))) + (goto-char (+ (point) offset)) + (user-error "No hash found at point")))) + +(defvar km/magit-copy-hook + '(km/magit-copy-commit-summary-from-header + km/magit-copy-commit-message + km/magit-copy-region-commits + km/magit-copy-region-hunk + km/magit-copy-hunk) + "Functions tried by `km/magit-copy-as-kill'. +These will be given one argument (the current prefix value) and +should succeed by copying and returning non-nil or fail by +returning nil.") + +;;;###autoload +(defun km/magit-copy-commit-summary (commit) + "Copy a citation for the COMMIT at point. +Format the reference as ', (, )'. If there +is no commit at point or with a prefix argument, prompt for +COMMIT." + (interactive + (let ((atpoint (or (and magit-blame-mode (magit-blame-chunk-get :hash)) + (magit-branch-or-commit-at-point) + (magit-tag-at-point)))) + (list (or (and (not current-prefix-arg) atpoint) + (magit-read-branch-or-commit "Commit" atpoint))))) + (if (magit-rev-verify (concat commit "^{commit}")) + (kill-new (message + ;; Using `magit-git-string' instead of + ;; `magit-rev-format' to pass --date flag. + (magit-git-string "show" "-s" "--date=short" + "--format=%h (%s, %ad)" + commit "--"))) + (user-error "%s does not exist" commit))) + +(defun km/magit-copy-commit-summary-from-header (&optional arg) + (magit-section-when headers + (km/magit-copy-commit-summary (car magit-refresh-args)))) + +(defun km/magit-copy-region-commits (&optional arg) + (--when-let (magit-region-values 'commit) + (deactivate-mark) + (kill-new + (mapconcat #'identity it + (if arg (read-string "Separator: ") ", "))))) + +(defun km/magit-copy-commit-message (&optional arg) + (magit-section-when message + (kill-new (replace-regexp-in-string + "^ " "" + (buffer-substring-no-properties (magit-section-start it) + (magit-section-end it)))))) + +(defun km/magit-copy-region-hunk (&optional no-column) + (when (magit-section-internal-region-p) + (magit-section-when hunk + (deactivate-mark) + (let ((text (buffer-substring-no-properties + (region-beginning) (region-end)))) + (kill-new (if no-column + (replace-regexp-in-string "^[ \\+\\-]" "" text) + text)))))) + +(defun km/magit-copy-hunk (&optional arg) + (magit-section-when hunk + (let ((start (save-excursion (goto-char (magit-section-start it)) + (1+ (point-at-eol))))) + (kill-new (buffer-substring-no-properties + start (magit-section-end it)))))) + +(defun km/magit-copy-as-kill () + "Try `km/magit-copy-hook' before calling `magit-copy-as-kill'. +With a prefix argument of -1, always call `magit-copy-section-value' +Otherwise, the current prefix argument is passed to each hook +function." + (interactive) + (or (unless (= (prefix-numeric-value current-prefix-arg) -1) + (run-hook-with-args-until-success + 'km/magit-copy-hook current-prefix-arg)) + (magit-copy-section-value))) + +(defun km/magit-rev-ancestor-p (rev-a rev-b) + "Report whether REV-A is the ancestor of REV-B. +Use the revision at point as REV-B. With prefix argument or if +there is no revision at point, prompt for the revision. Always +prompt for REV-A." + (interactive + (let* ((atpoint (or (and magit-blame-mode (magit-blame-chunk-get :hash)) + (magit-branch-or-commit-at-point) + (magit-tag-at-point))) + (commit (or (and (not current-prefix-arg) atpoint) + (magit-read-branch-or-commit "Descendant" atpoint)))) + (list (magit-read-other-branch-or-commit + (format "Test if ancestor of %s" commit) commit) + commit))) + (message "%s is %san ancestor of %s" rev-a + (if (magit-git-success "merge-base" "--is-ancestor" + rev-a rev-b) + "" "NOT ") + rev-b)) + +(defun km/magit-refs-toggle-tags () + "Toggle showing tags in `magit-refs-mode'. +This only affects the current buffer and is useful if you do not +show tags by default." + (interactive) + (if (memq 'magit-insert-tags magit-refs-sections-hook) + (remove-hook 'magit-refs-sections-hook 'magit-insert-tags t) + (add-hook 'magit-refs-sections-hook 'magit-insert-tags t t)) + (magit-refresh-buffer)) + +(defun km/magit-log-flip-revs () + "Swap revisions in log range." + (interactive) + (let ((range (caar magit-refresh-args))) + (if (and range + (derived-mode-p 'magit-log-mode) + (string-match magit-range-re range)) + (progn + (setf (caar magit-refresh-args) + (concat (match-string 3 range) + (match-string 2 range) + (match-string 1 range))) + (magit-refresh)) + (user-error "No range to swap")))) + +(defun km/magit-flip-revs () + (interactive) + (cond ((derived-mode-p 'magit-diff-mode) + (call-interactively #'magit-diff-flip-revs)) + ((derived-mode-p 'magit-log-mode) + (call-interactively #'km/magit-log-flip-revs)))) + +(defun km/magit-diff-visit-file (&optional prev-rev other-window) + "Like `magit-diff-visit-file', but with the option to visit REV^. + +If prefix argument PREV-REV is non-nil, visit file for REV^ +instead of REV. If not in `magit-revision-mode', the prefix +argument has no effect. + +OTHER-WINDOW corresponds to `magit-diff-visit-file's OTHER-WINDOW +argument. Interactively, this can be accessed using the command +`km/magit-diff-visit-file-other-window'." + (interactive "P") + (let ((magit-refresh-args (if (and prev-rev + (derived-mode-p 'magit-revision-mode)) + (cons (concat (car magit-refresh-args) "^") + (cdr magit-refresh-args)) + magit-refresh-args)) + (current-prefix-arg (and other-window (list 4)))) + (call-interactively #'magit-diff-visit-file))) + +(defun km/magit-diff-visit-file-other-window (&optional prev-rev) + (interactive "P") + (km/magit-diff-visit-file prev-rev t)) + +(provide 'km-magit) +;;; km-magit.el ends here diff --git a/lisp/km-mail.el b/lisp/km-mail.el new file mode 100644 index 0000000..9a3a3ab --- /dev/null +++ b/lisp/km-mail.el @@ -0,0 +1,150 @@ +;;; km-mail.el --- Mail-related extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'dash) +(require 'gnus) +(require 'gnus-group) +(require 'gnus-topic) +(require 'gnus-sum) +(require 'notmuch) +(require 'shr) + +;; http://www.emacswiki.org/emacs/GnusSync +(defun km/gnus-grace-exit-before-kill-emacs () + (if (and (fboundp 'gnus-alive-p) + (gnus-alive-p)) + (let ((noninteractive t)) + (gnus-group-exit)))) + + +(defun km/gnus-group-sort-by-topic (info1 info2) + "Sort alphabetically by group topic. +This allows groups to be ordered by topics even when topic mode +is off." + (require 'gnus-topic) + (string< (gnus-group-topic (gnus-info-group info1)) + (gnus-group-topic (gnus-info-group info2)))) + +(defun km/gnus-summary-set-current-article () + (unless gnus-summary-buffer + (user-error "No summary buffer")) + (with-current-buffer gnus-summary-buffer + (save-window-excursion (gnus-summary-select-article)))) + +(defun km/gnus--last-message-link () + (with-current-buffer gnus-article-buffer + (save-excursion + (goto-char (point-max)) + (widget-forward -1) + (--when-let (or (get-text-property (point) 'gnus-string) + (get-text-property (point) 'shr-url)) + (kill-new it))))) + +(defun km/gnus--gmane-link (&optional perma) + (with-current-buffer gnus-original-article-buffer + (-when-let* ((blink (message-field-value "Archived-At")) + (link (or (and (string-match "\\`<\\(.*\\)>\\'" blink) + (match-string 1 blink)) + blink))) + (if perma + link + (replace-regexp-in-string "\\`http://permalink\.gmane\.org/" + "http://thread.gmane.org/" + link))))) + +(defun km/gnus-copy-gmane-link-as-kill (&optional perma) + (interactive "P") + (km/gnus-summary-set-current-article) + (with-current-buffer gnus-original-article-buffer + (--when-let (km/gnus--gmane-link perma) + (kill-new (message it))))) + +(defun km/gnus-copy-message-link (follow) + "Copy link for current message. +If it has an \"Archived-At\" header, use that. Otherwise, get +the link from the last widget in the buffer. With prefix +argument FOLLOW, follow link instead of copying it." + (interactive "P") + (km/gnus-summary-set-current-article) + (with-current-buffer gnus-original-article-buffer + (save-excursion + (--when-let (or (km/gnus--gmane-link) + (km/gnus--last-message-link)) + (funcall (if follow + #'browse-url + (lambda (s) (kill-new (message s)))) + it))))) + +(defun km/gnus-open-github-patch () + "Open patch from github email. +A new buffer with the patch contents is opened in another window." + (interactive) + (km/gnus-summary-set-current-article) + (let ((bufname (generate-new-buffer-name "*gnus-github-patch*")) + url) + (with-current-buffer gnus-original-article-buffer + (save-excursion + (goto-char (point-min)) + (if (re-search-forward "https://github.com/.*\\.patch") + (setq url (match-string-no-properties 0)) + (user-error "No patch found")))) + (with-current-buffer (get-buffer-create bufname) + (url-insert-file-contents url) + (diff-mode) + (view-mode 1)) + (pop-to-buffer bufname))) + +(defun km/gnus-summary-catchup (&optional no-next) + "Mark all articles as read. +Don't ask for confirmation. With prefix argument NO-NEXT, exit +to group buffer instead of moving to next group." + (interactive "P") + (let ((gnus-auto-select-next (unless no-next 'quietly))) + (gnus-summary-catchup-and-exit nil t))) + +(defun km/shr-browse-url-and-goto-next () + "Run `shr-browse-url' followed by `shr-next-link'." + (interactive) + (shr-browse-url) + (shr-next-link)) + + +;;; Message mode + +(defun km/message-confirm-sender () + "Stop sending message from the wrong address." + (unless (yes-or-no-p (format "Send message from %s?" + (message-field-value "From"))) + (user-error "Not sending message"))) + + +;;; Notmuch + +(require 'notmuch) + +;;;###autoload +(defun km/notmuch-show-copy-message-id-as-kill () + (interactive) + (kill-new (message "%s" (notmuch-show-get-message-id)))) + +(provide 'km-mail) +;;; km-mail.el ends here diff --git a/lisp/km-org.el b/lisp/km-org.el new file mode 100644 index 0000000..fcf82ed --- /dev/null +++ b/lisp/km-org.el @@ -0,0 +1,583 @@ +;;; km-org.el --- Org mode extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'dash) +(require 'org) +(require 'org-agenda) +(require 'org-link-edit) +(require 'ox-ascii) +(require 's) + +;;;###autoload +(defun km/org-tree-to-indirect-buffer (&optional arg) + "Run `org-tree-to-indirect-buffer', keeping previous buffer. +By default, `org-tree-to-indirect-buffer' deletes the previous +indirect buffer when making a new one to avoid accumulating +buffers, which can be overriden by a C-u prefix. Reverse this +behavior so that the prefix must be given in order to delete the +previous indirect buffer. If the argument is a number, which has +a different meaning, it is left untouched." + (interactive "P") + (unless (numberp arg) + (setq arg (not arg))) + (org-tree-to-indirect-buffer arg)) + +;;;###autoload +(defun km/org-tree-to-indirect-buffer-current-window (&optional arg) + "Create indirect buffer and narrow to subtree in this window. +Before running `org-tree-to-indirect-buffer', set +`org-indirect-buffer-display' to `current-window'." + (interactive "P") + (let ((org-indirect-buffer-display 'current-window)) + (km/org-tree-to-indirect-buffer arg))) + +;;;###autoload +(defun km/org-clone-and-shift-by-repeater () + "Clone current subtree, shifting new timestamp by repeater. +The repeater is removed from the original subtree." + (interactive) + (save-excursion + (org-back-to-heading) + (let ((repeater + (and (re-search-forward + ;; Regexp taken from `org-clone-subtree-with-time-shift'. + "<[^<>\n]+ +\\([.+]?\\+[0-9]+[hdwmy]\\)" + (save-excursion (org-end-of-subtree)) t) + (match-string-no-properties 1)))) + (unless repeater + (user-error "Subtree does not have repeater")) + (org-clone-subtree-with-time-shift 0 repeater)))) + +;;;###autoload +(defun km/org-delete-checked-items () + "Delete checked items. + +If the element at point is not a plain list, search the parent +elements for a plain list, stopping when the first plain list or +headline is found. + +After deleting checked items, move to the first item of the list. +If there are no items of the list remaining, move to the parent +heading." + (interactive) + (let* ((el (or (org-element-lineage (org-element-context) '(plain-list) t) + (user-error "Point is not within a plain list"))) + (beg (org-element-property :begin el)) + ;; Check maximum point because, if narrowed to a heading, + ;; org-element can return a point beyond this. + (end (min (org-element-property :end el) (point-max))) + (struct (org-element-property :structure el)) + (list-level (org-list-get-ind beg struct)) + (deleted-count 0) + (text (buffer-substring beg end)) + new-text) + (with-temp-buffer + (insert text) + (let ((offset (1- beg)) + (pmax (point-max)) + level box bpos epos) + (dolist (item (reverse struct)) + (setq level (nth 1 item) + box (nth 4 item) + bpos (- (nth 0 item) offset) + ;; Minimum check here is for the same reason as + ;; above with `end'. This only comes into play for + ;; the last item. + epos (min (- (nth 6 item) offset) pmax)) + (when (and (= list-level level) + (string= box "[X]")) + (delete-region bpos epos) + (setq deleted-count (1+ deleted-count))))) + (setq new-text (buffer-string))) + (if (= deleted-count 0) + (message "No checked boxes found") + (delete-region beg end) + (goto-char beg) + (insert new-text) + (goto-char beg) + (unless (eq (car (org-element-at-point)) 'plain-list) + (outline-previous-heading)) + (org-update-checkbox-count-maybe) + (message "Deleted %s item(s)" deleted-count)))) + +(defmacro km/org--save-pos-on-sort (&rest body) + "Try to return to the orginal position after sorting. + +Sorting doesn't play well with `save-restriction' or markers, so +just put the point where it was relative to the original heading. +This may not actually be the same tree if there are redundant +headings. + +This relies on point being placed at the heading that was sorted, +as `org-sort-entries' does." + `(let ((starting-pos (point))) + (org-back-to-heading t) + (let ((heading-line (buffer-substring-no-properties + (point-at-bol) (point-at-eol))) + (chars-after-heading (- starting-pos (point)))) + ,@body + (search-forward heading-line) + (beginning-of-line) + (goto-char (+ (point) chars-after-heading))))) + +;;;###autoload +(defun km/org-sort-parent (arg) + "Sort on parent heading ARG levels up. +After sorting, return point to its previous location under the +current heading." + (interactive "p") + (km/org--save-pos-on-sort + (outline-up-heading arg) + (call-interactively #'org-sort))) + +(defun km/org--prop-sort-args () + "Return `org-sort-entries' arguments based on \"SORT\" property." + (when (save-excursion (org-goto-first-child)) + (let ((prop (org-entry-get nil "sort" 'inherit))) + (when prop + (let* ((current-level (org-current-level)) + (sort-prop (s-split " by " prop)) + (levels (mapcar #'string-to-number (s-split nil (car sort-prop)))) + (sorting-type (cadr sort-prop)) + sorting-func) + (if sorting-type + (progn + (setq sorting-type (read sorting-type)) + (cond + ((characterp sorting-type)) + ((fboundp sorting-type) + (setq sorting-func sorting-type + sorting-type ?f)) + (t + (user-error "Invalid sorting type: %s" sorting-type)))) + (setq sorting-type ?a)) + (when (or (equal levels (list 0)) + (memq current-level levels)) + (list nil sorting-type sorting-func))))))) + +(defun km/org-maybe-sort () + "Sort current heading based on \"SORT\" property. + +Property value should have the format \"LEVELS by TYPE\", where +LEVELS specifies the level of heading to sort and TYPE is the +sorting type. + +If LEVELS is a space-seperated list of positive integers, only +sort heading if it is at one of these levels. If LEVELS is zero +or a non-numeric string, sort heading regardless of its level. +If LEVELS is a negative number, do not sort. (Notice that there +is only support for sorting subheadings in a tree, not top-level +headings.) + +If TYPE is a character, pass it as the SORTING-TYPE argument to +`org-sort-entries'. If TYPE is the name of a bound function, +pass it as the GETKEY-FUNC argument to `org-sort-entries' (with +?f as the SORTING-TYPE value). If \"by TYPE\" is omitted from +the property value, sort alphabetically. + +For example + + 2 by ?a Sort alphabetically if level 2 heading. + 2 Same as above. + + t Sort heading alphabetically. + all Same as above. + + 1 by func Sort heading using function if level 1 heading. + + -1 Don't sort. Useful for overriding parent value." + (let ((sort-args (km/org--prop-sort-args))) + (when sort-args + (apply #'org-sort-entries sort-args)))) + +;;;###autoload +(defun km/org-maybe-sort-buffer-headings () + "Call `km/org-maybe-sort' on buffer headings." + (interactive) + (org-map-entries #'km/org-maybe-sort)) + +;;;###autoload +(defun km/org-maybe-sort-parent () + "Sort parent heading based on \"SORT\" property. +See `km/org-maybe-sort' for details of property value format." + (let (heading-pos sort-args) + (save-excursion + (and (org-up-heading-safe) + (setq heading-pos (point) + sort-args (km/org--prop-sort-args)))) + (when sort-args + (km/org--save-pos-on-sort + (goto-char heading-pos) + (apply #'org-sort-entries sort-args))))) + +(defun km/org-sort-heading-ignoring-articles () + "Sort alphabetically, but ignore any leading articles." + (let* ((ignored-words '("a" "an" "the")) + (heading (org-no-properties + (org-get-heading 'no-tags 'no-todo))) + (heading-words (split-string heading))) + (when (member (downcase (car heading-words)) + ignored-words) + (setq heading-words (cdr heading-words))) + (mapconcat #'identity heading-words " "))) + +;;;###autoload +(defun km/org-remove-title-leader () + "Remove leader from Org heading title. + +Convert + + * TODO leader: Rest of title :tag: + +to + + * TODO Rest of title :tag:" + (interactive) + (save-excursion + (let ((regex (format "^%s\\(?:%s \\)?\\(?:%s \\)?\\(.*: \\)\\w+" + org-outline-regexp org-todo-regexp + org-priority-regexp))) + (org-back-to-heading) + (when (re-search-forward regex (point-at-eol) t) + (replace-match "" nil nil nil 4) + (org-set-tags nil t))))) + +(defun km/org-add-blank-before-heading () + "Add a blank line before Org headings in buffer." + (interactive) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "[^\n]\n\\*" nil t) + (when (org-at-heading-p) + (beginning-of-line) + (open-line 1))))) + +(autoload 'km/reduce-to-single-spaces "km-editing") +;;;###autoload +(defun km/org-normalize-spaces () + "Reduce to single spaces and add space before headings." + (interactive) + (km/reduce-to-single-spaces) + (km/org-add-blank-before-heading)) + +;;;###autoload +(defun km/org-switch-to-buffer-other-window (&optional arg) + (interactive "P") + (cl-letf (((symbol-function 'org-pop-to-buffer-same-window) + (lambda (buffer-or-name &rest args) + (funcall #'pop-to-buffer buffer-or-name)))) + (org-switchb arg))) + +;;;###autoload +(defun km/org-open-at-point-stay () + "Like `org-open-at-point', but stay on heading. +This variant is convient to use in `org-speed-commands-user' +because remaining on the heading allows additional commands to be +called through the speed command interface." + (interactive) + (unless (org-at-heading-p) + (user-error "Not at heading")) + (save-excursion + (call-interactively #'org-open-at-point))) + + +;;; Agenda + + +(defvar km/org-agenda-file-directory nil) + +;;;###autoload +(defun km/org-agenda-cd-and-read-dir-locals () + (setq default-directory "~/notes/") + (hack-local-variables)) + +;;;###autoload +(defun km/org-agenda-store-current-span () + "Store the current span value in `org-agenda-span'. +This allows the view to persist when the agenda buffer is +killed." + (when org-agenda-current-span + (setq org-agenda-span org-agenda-current-span))) + +;;;###autoload +(defun km/org-agenda-add-or-remove-file (file) + "Add or remove link to FILE in `km/org-agenda-file-directory'. +If a link for FILE does not exist, create it. Otherwise, remove +it. Like `org-agenda-file-to-front', this results in FILE being +displayed in the agenda." + (interactive (list (cl-case major-mode + (org-mode (buffer-file-name)) + (dired-mode (dired-get-filename)) + (org-agenda-mode (ignore-errors (save-window-excursion + (org-agenda-goto) + (buffer-file-name)))) + (t (read-file-name "Link file: "))))) + (let ((agenda-file (expand-file-name (file-name-nondirectory file) + km/org-agenda-file-directory))) + (if (file-equal-p (file-truename agenda-file) file) + (progn + (when (called-interactively-p) (message "Deleting %s" agenda-file)) + (delete-file agenda-file)) + (when (called-interactively-p) (message "Adding %s" agenda-file)) + (make-symbolic-link file agenda-file)))) + +;;;###autoload +(defun km/org-open-default-notes-file-inbox () + "Open \"Inbox\" heading of `org-default-notes-file'." + (interactive) + (find-file org-default-notes-file) + (goto-char (org-find-exact-headline-in-buffer "Inbox" nil t)) + (recenter-top-bottom 0) + (show-children)) + +;;;###autoload +(defun km/org-goto-agenda-heading () + "Jump to heading in agenda files." + (interactive) + (let ((org-refile-targets + '((org-agenda-files :maxlevel . 3) + (org-agenda-text-search-extra-files :maxlevel . 3)))) + (org-refile '(4)))) + + +;;; Refiling + +(defvar km/org-agenda-refile-targets + '((nil :maxlevel . 3) + (org-agenda-files :maxlevel . 2) + (org-agenda-text-search-extra-files :maxlevel . 2))) + +(defun km/org-refile-verify-target () + "Exclude DONE state from refile targets." + (not (member (nth 2 (org-heading-components)) org-done-keywords))) + +(defvar km/org-refile-list-item-tag "bref" + "Tag marking heading with list that can be refiled to.") + +(defun km/org-refile-list-item (&optional copy) + "Refile list item to a heading. + +Consider targets to be headings with the tag +`km/org-refile-list-item-tag' in any file listed in +`org-refile-targets'. + +The item is dropped directly under the heading, after any +planning information or property drawers. No attempt is made to +make sure that it is part of any previous list. + +With prefix argument COPY, the item is not deleted from the +original list." + (interactive "P") + (unless (org-at-item-p) + (user-error "Not at an item")) + (let* ((beg (save-excursion (beginning-of-line) (point-marker))) + (end (save-excursion + (goto-char + (nth 6 (assoc (marker-position beg) (org-list-struct)))) + (point-marker))) + (item (buffer-substring-no-properties beg end)) + (ftargets (mapcar #'car org-refile-targets)) + (org-refile-targets (mapcar + (lambda (f) + (cons f (cons :tag km/org-refile-list-item-tag))) + ftargets)) + (loc (org-refile-get-location "Bullet heading")) + (fname (nth 1 loc)) + (heading-pos (nth 3 loc))) + (with-current-buffer (or (find-buffer-visiting fname) + (find-file-noselect fname)) + (org-with-wide-buffer + (goto-char heading-pos) + (forward-line) + (while (and (not (eobp)) + (memq (org-element-type (org-element-at-point)) + '(planning property-drawer node-property))) + (forward-line)) + (insert item) + (org-update-checkbox-count-maybe))) + (goto-char beg) + (unless copy + (delete-region beg end) + (org-update-checkbox-count-maybe)))) + +(defvar km/org-refile-dwim-maxlevel 2) + +(defun km/org-refile-dwim () + "Rebind `org-refile-targets' if next window is an Org buffer. +A target is determined by `km/org-refile-dwim-target-file'." + (interactive) + (let* ((dwim-target (km/org-refile-dwim-target-file)) + (org-refile-targets (if dwim-target + `((nil + :maxlevel . ,km/org-refile-dwim-maxlevel) + (dwim-target + :maxlevel . ,km/org-refile-dwim-maxlevel)) + org-refile-targets))) + (call-interactively #'org-refile))) + +(defun km/org-refile-dwim-target-file () + "Return next window that is an Org buffer." + (let* ((from-buffer (current-buffer)) + (other-win (get-window-with-predicate + (lambda (w) + (with-current-buffer (window-buffer w) + (and (derived-mode-p 'org-mode) + (not (eq from-buffer (current-buffer))))))))) + (and other-win + (buffer-file-name (window-buffer other-win))))) + +(defun km/org-refile-to-other-file (file &optional maxlevel) + "Refile with `org-refile-targets' set to FILE. +A numeric prefix sets MAXLEVEL (defaults to 2)." + (interactive "fFile: \nP") + (let* ((maxlevel (prefix-numeric-value (or maxlevel 2))) + (file (substring-no-properties file)) + (org-refile-targets `((,file :maxlevel . ,maxlevel)))) + (org-refile))) + +(defun km/org-refile-to-other-org-buffer (buffer &optional maxlevel) + "Refile with `org-refile-targets' set to BUFFER file name. +A numeric prefix sets MAXLEVEL (defaults to 2)." + (interactive (list (km/get-org-file-buffer) current-prefix-arg)) + (km/org-refile-to-other-file (buffer-file-name buffer) + maxlevel)) + +(defun km/get-org-file-buffer () + (get-buffer + (org-icompleting-read "Buffer: " (mapcar 'buffer-name + (org-buffer-list 'files))))) + +(defun km/org-set-refiling-buffer (&optional maxlevel) + "Choose buffer to set as sole target in `org-refile-targets'. +If `org-refile-targets' is already a local variable, restore the +global value. A numeric prefix sets MAXLEVEL (defaults to 2)." + (interactive "P") + (if (local-variable-p 'org-refile-targets) + (kill-local-variable 'org-refile-targets) + (let ((buffer-file (substring-no-properties + (buffer-file-name (km/get-org-file-buffer)))) + (maxlevel (prefix-numeric-value (or maxlevel 2)))) + (set (make-local-variable 'org-refile-targets) + `((,buffer-file :maxlevel . ,maxlevel)))))) + + +;;; Links + +(defvar km/org-pmid-search-url "http://www.ncbi.nlm.nih.gov/pubmed/?term=%s" + "URL to search for PMID.") + +;;;###autoload +(defun km/org-pmid-open (path) + "Search for PMID at `km/org-pmid-search-url'." + (browse-url (format km/org-pmid-search-url path))) + +(defun km/org-link-dired-jump () + "Open Dired for directory of file link at point." + (interactive) + (let ((el (org-element-lineage (org-element-context) '(link) t))) + (unless (and el (equal (org-element-property :type el) "file")) + (user-error "Not on file link")) + (dired-jump 'other-window + (expand-file-name (org-element-property :path el))))) + +(defun km/org-link-edit-slurp-link () + "Slurp trailing text into link. + + \[link\]extra -> \[\[linkextra\]\] + +After slurping, return the slurped text and move point to the +beginning of the link." + (interactive) + (cl-multiple-value-bind (beg end link desc) (org-link-edit--get-link-data) + (when (progn (goto-char end) (looking-at "[^ \t\n]+")) + (let ((slurped (match-string-no-properties 0))) + (setq link (concat link slurped) + end (match-end 0)) + (delete-region beg end) + (insert (org-make-link-string link desc)) + (goto-char beg) + slurped)))) + + +;;; Export + +(defvar km/org-md-fill-column fill-column + "Fill column for exported markdown. +This is a separate variable instead of `fill-column' to allow it +to be easily overriden.") + + +;;;###autoload +(defun km/org-md-fill-string (contents) + "Use `org-ascii--fill-string' to fill ox-md paragraphs." + (org-ascii--fill-string contents km/org-md-fill-column + nil)) + +(defun km/org-md-export-unfilled-buffer () + (interactive) + (let ((km/org-md-fill-column (point-max))) + (org-md-export-as-markdown))) + + +;;; Org open file + +;;;###autoload +(defun km/org-open-file-at-point () + "Open file at point with `org-open-file'." + (interactive) + (if (and (derived-mode-p 'org-mode) + (org-element-lineage (org-element-context) '(link) t)) + (org-open-at-point) + (let ((file (or (and (use-region-p) + (buffer-substring-no-properties + (region-beginning) (region-end))) + (thing-at-point 'filename)))) + (if (and file (file-exists-p file)) + (org-open-file file) + (km/org-open-file))))) + +;;;###autoload +(defun km/org-open-file () + "Interactive version of `org-open-file'." + (interactive) + (org-open-file (read-file-name "Open file: " nil nil t))) + +(autoload 'magit-annex-present-files "magit-annex") +(autoload 'magit-completing-read "magit-utils") +;;;###autoload +(defun km/org-open-annex-file () + "Open a git annex file with `org-open-file'." + (interactive) + (--if-let (magit-annex-present-files) + (org-open-file (magit-completing-read "Open annex file" it nil t)) + (message "No annex files found"))) + +(autoload 'km/read-recent-file "km-files") +;;;###autoload +(defun km/org-open-recent-file () + "Open a file from `recentf-list' with `org-open-file'." + (interactive) + (org-open-file (km/read-recent-file))) + +(provide 'km-org) +;;; km-org.el ends here diff --git a/lisp/km-outline.el b/lisp/km-outline.el new file mode 100644 index 0000000..45560fd --- /dev/null +++ b/lisp/km-outline.el @@ -0,0 +1,78 @@ +;;; init-outline.el --- Outline mode extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'outline) + +(defun km/outline--parent-levels (n) + "Return list of parent levels. +4 -> (1 2 3) +1 -> nil" + (unless (> n 0) + (user-error "N must be positive")) + (let (parents) + (while (/= n 1) + (setq n (1- n)) + (push n parents)) + parents)) + +(defun km/outline-imenu-create-index () + "Generate Imenu index for headings. +This assumes that `outline-regexp' marks the characters that +indicate a heading, but not the text following it. The heading +text is taken as everything on the line after the +`outline-regexp' match (except for any leading whitespace)." + (goto-char (point-min)) + (let (path-alist ; ((level . heading)) + index) + (while (re-search-forward (concat "^" outline-regexp) nil t) + (let* ((head (replace-regexp-in-string + "\\`\\s-*" "" + (buffer-substring-no-properties (point) (point-at-eol)))) + (head-level (save-excursion (beginning-of-line) + (funcall outline-level))) + (parent-levels (km/outline--parent-levels head-level)) + (parent-heads + (delq nil (mapcar (lambda (n) (cdr (assoc n path-alist))) + parent-levels))) + (target (concat (mapconcat #'identity + (append parent-heads + (list head)) + " / ")))) + (unless (string= head "") + (push (cons target (save-excursion (beginning-of-line) (point-marker))) + index)) + (push (cons head-level head) path-alist))) + (nreverse index))) + +(declare-function helm-imenu "helm-imenu") +;;;###autoload +(defun km/outline-jump-to-heading () + "Jump to heading specified by `outline-regexp'." + (interactive) + (let ((imenu-create-index-function #'km/outline-imenu-create-index) + helm-cached-imenu-tick + helm-cached-imenu-alist + helm-cached-imenu-candidates) + (call-interactively #'helm-imenu))) + +(provide 'km-outline) +;;; km-outline.el ends here diff --git a/lisp/km-projectile.el b/lisp/km-projectile.el new file mode 100644 index 0000000..27b4075 --- /dev/null +++ b/lisp/km-projectile.el @@ -0,0 +1,182 @@ +;;; km-projectile.el --- Projectile configuration + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'dired) +(require 'projectile) +(require 'org-element) + +;;;###autoload +(defun km/projectile-switch-project (&optional arg) + "Switch to a project. + +Like `projectile-switch-project', but instead of calling +`projectile-commander' when a prefix argument ARG is given, save +something for the current project before switching. + +`projectile-switch-project-action' is set to +`km/projectile-maybe-restore-thing'. If the thing saved for the +destination project is the the window configuration, this may not +end up in the project if the buffers are now dead." + (interactive "P") + (when arg (call-interactively #'km/projectile-save-thing)) + (let ((projectile-switch-project-action 'km/projectile-maybe-restore-thing)) + (projectile-switch-project))) + +(declare-function km/open-external-terminal "km-shell") +;;;###autoload +(defun km/projectile-open-external-terminal-in-root () + "Run `km/open-external-terminal' in project root." + (interactive) + (let ((default-directory (projectile-project-root))) + (km/open-external-terminal))) + +;;;###autoload +(defun km/projectile-view-file () + "View project file. +Interactive arguments are processed according to +`projectile-find-file'." + (interactive) + (call-interactively #'projectile-find-file) + (view-mode 1)) + +;;;###autoload +(defun km/projectile-view-file-other-window () + "View project file in other window. +Interactive arguments are processed according to +`projectile-find-file-other-window'." + (interactive) + (call-interactively 'projectile-find-file-other-window) + (view-mode 1)) + +(defun km/project-filename-at-point () + "Return file name relative to `projectile-project-root'. +In the case of multiple files marked in Dired, return the file +names separated by a space." + (let* ((el (and (derived-mode-p 'org-mode) + (org-element-lineage (org-element-context) '(link) t))) + (fname (or (and (eq (org-element-type el) 'link) + (org-element-property :path el)) + (and (derived-mode-p 'dired-mode) + (dired-get-marked-files 'nodir nil)) + (thing-at-point 'filename)))) + (when fname + (mapconcat + `(lambda (f) (file-relative-name f ,(projectile-project-root))) + (if (listp fname) fname (list fname)) + " ")))) + +;;;###autoload +(defun km/projectile-copy-project-filename-as-kill () + "Copy name of project file. +If point is on a file, copy this as the file name. Otherwise, +use the name of the current file." + (interactive) + (-when-let (fname (or (km/project-filename-at-point) + (and buffer-file-name + (file-relative-name buffer-file-name + (projectile-project-root))))) + (if (eq last-command 'kill-region) + (kill-append fname nil) + (kill-new fname)) + (message "%s" fname))) + +(defvar km/projectile-project-saved-thing nil + "Property list of saved thing for projects. +The keys are project roots (strings), so use `lax-plist-put' and +`lax-plist-get'.") + +;;;###autoload +(defun km/projectile-save-thing (thing) + "Save thing for current project. + +Thing is a character representing +- . point marker +- (b)uffer +- (f)ile +- (w)indow configuration + +- (d)elete saved thing" + (interactive (list + (let ((letters '(?. ?b ?f ?w ?d))) + (read-char-choice (concat "Save [" letters "]: ") + letters)))) + (let ((value (cl-case thing + (?. + (point-marker)) + (?b + (current-buffer)) + (?f + (buffer-file-name)) + (?w + (current-window-configuration)) + (?d nil)))) + (setq km/projectile-project-saved-thing + (lax-plist-put km/projectile-project-saved-thing + (projectile-project-root) + (cons thing value))))) + +(defun km/projectile-restore-thing () + "Restore saved thing for current project. +Return nil if there is no thing saved for the current project." + (interactive) + (-when-let* ((thing-value (lax-plist-get km/projectile-project-saved-thing + (projectile-project-root))) + (thing (car thing-value)) + (value (cdr thing-value))) + (cl-case thing + (?. + (-if-let (buf (marker-buffer value)) + (progn (switch-to-buffer buf) + (goto-char value)) + (user-error "Buffer no longer exists"))) + (?b + (if (buffer-live-p value) + (switch-to-buffer value) + (user-error "Buffer no longer exists"))) + (?f + (find-file value)) + (?w + (set-window-configuration value))) + t)) + +(defvar km/projectile-switch-fallback 'projectile-commander) + +(defun km/projectile-maybe-restore-thing () + "Try to restore thing for current project. +If there is nothing ot restore, call +`km/projectile-switch-fallback'." + (or (km/projectile-restore-thing) + (funcall km/projectile-switch-fallback))) + +(defun km/projectile-kill-buffers () + "Kill all project buffers. +Like `projectile-kill-buffers', but +- Before killing buffers, delete any saved thing for the project. +- Don't ask for confirmation to kill project buffers (but + `kill-buffer' will still ask when killing a modified buffer)." + (interactive) + (km/projectile-save-thing ?d) + (cl-letf (((symbol-function 'yes-or-no-p) (lambda (&rest args) t))) + (projectile-kill-buffers))) + +(provide 'km-projectile) +;;; km-projectile.el ends here diff --git a/lisp/km-python.el b/lisp/km-python.el new file mode 100644 index 0000000..dd11b75 --- /dev/null +++ b/lisp/km-python.el @@ -0,0 +1,267 @@ +;;; km-python.el --- Python extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'python) +(require 'km-util) + +;;;###autoload +(defun km/toggle-ipython-shell () + "Switch between using python and IPython for interactive shell." + (interactive) + (setq python-shell-interpreter + (if (string= python-shell-interpreter "python") "ipython" "python"))) + +;;;###autoload +(defun km/find-python-test-file-other-window (arg) + "Open test file for the current Python file in another window. +If the file does not already exist, import the original Python +file. Unless ARG is non-nil, py.test is also imported." + (interactive "P") + (let* ((py-file (file-name-nondirectory buffer-file-name)) + (test-file (concat "test_" py-file))) + (find-file-other-window test-file) + (unless (file-exists-p test-file) + (insert (format "import %s\n" (file-name-sans-extension py-file))) + (unless arg + (insert "import pytest\n"))))) + +;;;###autoload +(defun km/python-shell-send-function-or-paragraph-and-step () + "Send function or paragraph to Python shell. + +Send function if point is inside one. Otherwise, send the current +paragraph. After evaluation, step to the next code line. + +This is inspired by `ess-eval-function-or-paragraph-and-step'." + (interactive) + (if (km/python-inside-defun-p) + (progn + (python-shell-send-defun 1) + (python-nav-end-of-defun)) + (let (end) + (save-excursion + (forward-paragraph) + (setq end (point)) + (backward-paragraph) + (python-shell-send-region (point) end)) + (goto-char end))) + (km/python-next-code-line 1)) + +;;;###autoload +(defun km/python-shell-send-buffer-up-to-line () + "Send beginning of buffer to the current line to Python shell." + (interactive) + (python-shell-send-region (point-min) (line-end-position))) + +(defun km/python-inside-defun-p () + ;; I don't use `python-nav-beginning-of-defun' because it will go to + ;; the function even when the point is not inside of it. + (when (python-info-current-defun) + t)) + +(defun km/python-next-code-line (&optional arg skip-to-eob) + "This is copied nearly exactly from `ess-next-code-line'." + (interactive "p") + (or arg (setq arg 1)) + (beginning-of-line) + (let ((pos (point)) + (n 0) + (inc (if (> arg 0) 1 -1))) + (while (and (/= arg 0) (= n 0)) + (setq n (forward-line inc)); n=0 is success + (if (not (fboundp 'comment-beginning)) + (while (and (= n 0) + (looking-at "\\s-*\\($\\|\\s<\\)")) + (setq n (forward-line inc))) + (comment-beginning) + (beginning-of-line) + (forward-comment (* inc (buffer-size))) ;; as suggested in info file + ) + (if (or skip-to-eob + (not (looking-at "[ \t\n]*\\'"))) ;; don't go to eob or whatever + (setq arg (- arg inc)) + (goto-char pos) + (setq arg 0) + (forward-line 1));; stop at next empty line + (setq pos (point))) + (goto-char pos) + n)) + +(defvar km/python-shell-current-string nil) + +;;;###autoload +(defun km/python-shell-send-set-string (set) + "Send previously set string to Python shell. +If a string has not been set previously or SET is non-nil, prompt +for a new string." + (interactive "P") + (when (or set (not km/python-shell-current-string)) + (let ((initial (and (use-region-p) + (buffer-substring-no-properties + (region-beginning) (region-end))))) + (setq km/python-shell-current-string (read-string "Python command: " initial)))) + (python-shell-send-string km/python-shell-current-string)) + +(defun km/python-shell--read-buffer () + (let ((buf-alist (mapcar (lambda (b) (cons (buffer-name b) b)) + (km/mode-buffers 'inferior-python-mode)))) + (if (= (length buf-alist) 1) + (cdr (car buf-alist)) + (cdr (assoc-string (completing-read "Shell buffer: " buf-alist) + buf-alist))))) + +(defun km/python-shell--find-prompt-start (move-func) + "Find first line of input prompt. + +`comint-forward-prompt' can end up at other points in input +entry, like '*': + + In [1]: for i in range(3): + ...: * print(i) + +Return point at the beginning input entry." + (let ((beg-prompt-re "\\(In \\[[0-9]+\\]:\\|>>> \\)")) + (funcall move-func) + (while (not (save-excursion + (beginning-of-line) + (looking-at-p beg-prompt-re))) + (funcall move-func)) + (point-at-bol))) + +;;;###autoload +(defun km/python-copy-last-shell-line-as-comment (&optional which-shell) + "Insert last input and output Python shell as comment. +When the current buffer is not associated with a Python shell or +WHICH-SHELL is non-nil, prompt with all Python shell buffers." + (interactive "P") + (let* ((default-shell-buffer + (get-buffer (format "*%s*" (python-shell-get-process-name t)))) + (shell-buffer (if (or which-shell (not default-shell-buffer)) + (km/python-shell--read-buffer) + default-shell-buffer)) + (start-pos (point)) + text) + (with-current-buffer shell-buffer + (save-excursion + (goto-char (point-max)) + (let* ((inhibit-field-text-motion t) + (beg (km/python-shell--find-prompt-start + (lambda () + (comint-previous-prompt 1)))) + (end (km/python-shell--find-prompt-start + ;; FIXME: If extra blank prompts exist at the end + ;; of shell, this will grab them (because + ;; `comint-next-prompt' skips over them). + (lambda () + (end-of-line) + (comint-next-prompt 1))))) + (setq text (buffer-substring-no-properties beg end))))) + (insert text) + (comment-region start-pos (point)))) + +(defun km/python-indent-post-self-insert-function () + "Adjust indentation after insert of specfic characters. +This is taken from `python-indent-post-self-insert-function'. +Unlike that function, it does not rely on `electric-indent-mode' +being turned on." + (when (and (not (bolp)) + (let ((paren-start (python-syntax-context 'paren))) + ;; Check that point is inside parens. + (when paren-start + (not + ;; Filter the case where input is happening in the same + ;; line where the open paren is. + (= (line-number-at-pos) + (line-number-at-pos paren-start))))) + ;; When content has been added before the closing paren or a + ;; comma has been inserted, it's ok to do the trick. + (or + (memq (char-after) '(?\) ?\] ?\})) + (eq (char-before) ?,))) + (save-excursion + (goto-char (line-beginning-position)) + (let ((indentation (python-indent-calculate-indentation))) + (when (< (current-indentation) indentation) + (indent-line-to indentation)))))) + + +;;; Pydoc + +(require 'pydoc) + +(defvar km/pydoc-names nil + "List of names that have been sucessfully loaded by `pydoc'.") + +(defvar km/pydoc-names-file "~/.emacs.d/.pydoc-names" + "File to save `km/pydoc-names' to.") + +;;;###autoload +(defun km/pydoc () + "Run `pydoc', prompting with `km/pydoc-names'." + (interactive) + (let* ((default-directory "~/") + (initial-name (and (use-region-p) + (buffer-substring-no-properties + (region-beginning) + (region-end)))) + (name (completing-read "Name: " km/pydoc-names nil nil + initial-name))) + (pydoc name))) + +(defun km/pydoc-store-name () + "Store the name for the current pydoc object." + (with-current-buffer (pydoc-buffer) + (unless (eq (plist-get pydoc-info :type) 'not-found) + (cl-pushnew (substring-no-properties (car (cdr help-xref-stack-item))) + km/pydoc-names + :test #'string=)))) + +(defun km/pydoc-save-names-file (&optional file) + "Save `km/pydoc-names' to FILE. +FILE is `km/pydoc-names-file' by default." + (interactive + (list + (read-file-name (format "Save file (default %s): " + km/pydoc-names-file) + nil km/pydoc-names-file t))) + (setq file (or file km/pydoc-names-file)) + (when (file-writable-p file) + (with-temp-file file + (let (print-length) + (print (sort km/pydoc-names #'string-lessp) + (current-buffer)))))) + +;;;###autoload +(defun km/pydoc-read-names-file (&optional file) + "Read `km/pydoc-names-file' from FILE. +FILE is `km/pydoc-names-file' by default." + (interactive + (list + (read-file-name (format "Read file (default %s): " + km/pydoc-names-file) + nil km/pydoc-names-file t))) + (with-temp-buffer + (insert-file-contents (or file km/pydoc-names-file)) + (setq km/pydoc-names (read (current-buffer))))) + +(provide 'km-python) +;;; km-python.el ends here diff --git a/lisp/km-shell.el b/lisp/km-shell.el new file mode 100644 index 0000000..fe9d25d --- /dev/null +++ b/lisp/km-shell.el @@ -0,0 +1,117 @@ +;;; km-shell.el --- Shell-related extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'comint) +(require 'dash) +(require 'term) + +(defvar km/terminal "urxvt") + +;;;###autoload +(defun km/open-external-terminal () + (interactive) + (start-process "ext-term" nil km/terminal)) + +;;;###autoload +(defun km/zsh-ansi-term (&optional directory name) + "Open an ansi-term buffer running ZSH in DIRECTORY. + +If a terminal for DIRECTORY already exists, switch to that +buffer. If the current buffer is a terminal for DIRECTORY, +create an additional terminal. + +By default, DIRECTORY is `default-directory'. + +With a numeric prefix argument 0, prompt the user with existing +ZSH terminal directories. + +With a C-u prefix argument, set DIRECTORY to the user home +directory. + +With any other non-nil value, prompt for a directory. + +If NAME is non-nil, use *NAME* for the buffer name instead of +*zsh: DIRECTORY*. If that buffer already exists, it will be +grabbed regardless of whether its default-directory matches +DIRECTORY." + (interactive (km/zsh-ansi-term--args)) + (let* ((dir (abbreviate-file-name directory)) + (name (or name (concat "zsh: " dir))) + (full-name (concat "*" name "*")) + (default-directory dir)) + (pop-to-buffer-same-window + (cond + ((and (not (string= (km/zsh-ansi-term-directory) dir)) + (get-buffer full-name))) + (t + (cl-letf (((symbol-function 'switch-to-buffer) + (lambda (b &rest _) (get-buffer b)))) + (ansi-term "zsh" name))))) + (comint-goto-process-mark))) + +;;;###autoload +(defun km/zsh-toggle-ansi-term-home (&optional other-window) + (interactive "P") + (if (string= "*zsh*" (buffer-name)) + (bury-buffer) + (let ((display-buffer-overriding-action + (and other-window '(nil (inhibit-same-window . t))))) + (km/zsh-ansi-term "~/" "zsh")))) + +;;;###autoload +(defun km/zsh-ansi-term-other-window (&optional directory) + (interactive (km/zsh-ansi-term--args)) + (let ((display-buffer-overriding-action + '(nil (inhibit-same-window . t)))) + (km/zsh-ansi-term directory))) + +(defun km/zsh-ansi-term--args () + (list (cond + ((not current-prefix-arg) + default-directory) + ((= (prefix-numeric-value current-prefix-arg) 4) + "~/") + ((= (prefix-numeric-value current-prefix-arg) 0) + (let ((dirs (km/zsh-ansi-term-current-directories))) + (cl-case (length dirs) + (0 (user-error "No ZSH buffers found")) + (1 (car dirs)) + (t (completing-read "Directory: " dirs + nil nil nil nil (car dirs)))))) + (t + (read-directory-name "Directory: "))))) + +(defun km/zsh-ansi-term-directory (&optional buffer) + "Return directory name for ZSH terminal in BUFFER. +BUFFER defaults to current buffer." + (with-current-buffer (or buffer (current-buffer)) + (let ((bname (buffer-name))) + (and (derived-mode-p 'term-mode) + (string-match "^\\*zsh: \\(.*\\)\\*\\(<[0-9]+>\\)*$" + bname) + (match-string 1 bname))))) + +(defun km/zsh-ansi-term-current-directories () + (-distinct (-keep #'km/zsh-ansi-term-directory (buffer-list)))) + +(provide 'km-shell) +;;; km-shell.el ends here diff --git a/lisp/km-snakemake.el b/lisp/km-snakemake.el new file mode 100644 index 0000000..01fa7f1 --- /dev/null +++ b/lisp/km-snakemake.el @@ -0,0 +1,53 @@ +;;; km-snakemake.el --- Extensions for Snakemake mode + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'projectile) +(require 'km-projectile) +(require 'snakemake-mode) + +;;;###autoload +(defun km/snakemake-compile-project-file (jobs) + "Run Snakemake to produce project file at point. +The numeric prefix JOBS controls the number of jobs that +Snakemake runs (defaults to 1). If JOBS is zero, perform a dry +run. If JOBS is negative, just touch the output files." + (interactive "p") + (let* ((fname (or (km/project-filename-at-point) + (read-file-name "File: "))) + (job-flag (cond + ((> jobs 0) (format " -j%s " jobs)) + ((zerop jobs) " -n ") + (t " -t "))) + (compile-command (concat (snakemake-compile-command) job-flag + fname)) + (default-directory (projectile-project-root))) + (call-interactively 'compile))) + +;;;###autoload +(defun km/snakemake-compile-project-rule () + "Run `snakemake-compile-rule' from project root." + (interactive) + (let ((default-directory (projectile-project-root))) + (call-interactively #'snakemake-compile-rule))) + +(provide 'km-snakemake) +;;; km-snakemake.el ends here diff --git a/lisp/km-tex.el b/lisp/km-tex.el new file mode 100644 index 0000000..15447d3 --- /dev/null +++ b/lisp/km-tex.el @@ -0,0 +1,49 @@ +;;; km-tex.el --- TeX extensions + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'tex) +(require 'latex) + +(defun km/latex-imenu-create-index-function () + ;; See `LaTeX-imenu-create-index-function'. + (TeX-update-style) + (let ((sec-re (LaTeX-outline-regexp)) + (title-re "\\*\\{0,1\\}{\\([^}]+\\)}") + entries) + (goto-char (point-min)) + (while (re-search-forward sec-re nil t) + (let ((sec (replace-regexp-in-string + "\\\\" "" (match-string-no-properties 0))) + (title (and (looking-at title-re) + (replace-regexp-in-string + "\\s-\\s-+" " " + (replace-regexp-in-string + "\n" "" (match-string-no-properties 1)))))) + (when (> (length title) 45) + (setf (substring title 21 -21) "...")) + (push (cons (if title (format "%s (%s)" title sec) sec) + (save-excursion (beginning-of-line) (point-marker))) + entries))) + (nreverse entries))) + +(provide 'km-tex) +;;; km-tex.el ends here diff --git a/lisp/km-theme.el b/lisp/km-theme.el new file mode 100644 index 0000000..4eeb799 --- /dev/null +++ b/lisp/km-theme.el @@ -0,0 +1,85 @@ +;;; km-theme.el --- Tweaks to the stekene light theme + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(load-theme 'stekene-light t) + +(custom-theme-set-faces + 'stekene-light + '(default ((t (:background "white")))) + '(outline-1 ((t (:inherit font-lock-function-name-face)))) + '(outline-2 ((t (:inherit font-lock-doc-face)))) + '(outline-3 ((t (:inherit font-lock-constant-face)))) + '(org-todo ((t (:foreground "#ba2727" :background "gray85")))) + '(org-done ((t (:foreground "#557755" :background "gray85")))) + '(org-date ((t (:inherit font-lock-builtin-face)))) + '(org-upcoming-deadline ((t (:inherit font-lock-constant-face)))) + '(org-warning ((t (:inherit font-lock-string-face)))) + '(org-agenda-date-today ((t (:weight semi-bold)))) + '(org-agenda-date-weekend ((t (:inherit org-agenda-date)))) + '(org-agenda-structure ((t (:foreground "gray40")))) + '(org-agenda-date ((t (:inherit default)))) + '(org-scheduled ((t (:inherit org-scheduled-today)))) + '(org-agenda-calendar-event ((t (:inherit font-lock-function-name-face)))) + '(gnus-group-mail-1-empty ((t (:inherit font-lock-string-face)))) + '(gnus-group-mail-2-empty ((t (:inherit font-lock-function-name-face)))) + '(gnus-group-mail-3-empty ((t (:inherit font-lock-type-face)))) + '(gnus-group-mail-1 ((t (:inherit gnus-group-mail-1-empty :weight semi-bold)))) + '(gnus-group-mail-2 ((t (:inherit gnus-group-mail-2-empty :weight semi-bold)))) + '(gnus-group-mail-3 ((t (:inherit gnus-group-mail-3-empty :weight semi-bold)))) + '(gnus-subject ((t (:inherit font-lock-doc-face)))) + '(gnus-button ((t (:inherit default)))) + '(gnus-cite-1 ((t (:inherit font-lock-function-name-face)))) + '(gnus-cite-2 ((t (:inherit font-lock-string-face)))) + '(gnus-header-name ((t (:inherit font-lock-constant-face)))) + '(gnus-header-content ((t (:inherit default)))) + '(magit-section-highlight ((t (:background "white")))) + '(magit-diff-hunk-heading-highlight ((t (:foreground "gray30" :background "grey80")))) + '(magit-diff-hunk-heading ((t (:foreground "gray30" :background "grey90")))) + '(magit-diff-added-highlight ((t (:foreground "DarkGreen" :background "grey98")))) + '(magit-diff-added ((t (:foreground "DarkGreen" :background "grey98")))) + '(magit-diff-removed-highlight ((t (:foreground "#aa2222" :background "grey98")))) + '(magit-diff-removed ((t (:foreground "#aa2222" :background "grey98")))) + '(magit-diff-context-highlight ((t (:foreground "grey50" :background "grey98")))) + '(magit-diff-context ((t (:foreground "grey50" :background "grey98")))) + '(diff-added ((t (:foreground "DarkGreen" :background "grey98")))) + '(diff-removed ((t (:foreground "#aa2222" :background "grey98")))) + '(diff-context ((t (:foreground "grey50" :background "grey98")))) + '(term-color-black ((t (:inherit default)))) + '(term-color-red ((t (:inherit font-lock-string-face)))) + '(term-color-green ((t (:foreground "#3cb371")))) + '(term-color-yellow ((t (:inherit font-lock-doc-face)))) + '(term-color-blue ((t (:inherit font-lock-function-name-face)))) + '(term-color-magenta ((t (:foreground "#8b008b")))) + '(term-color-cyan ((t (:foreground "#008b8b")))) + ;; Darken a bit (was gray40). + '(font-lock-keyword-face ((t (:foreground "gray33"))))) + +(custom-set-faces + '(Info-quoted ((t (:inherit default)))) + '(git-timemachine-minibuffer-detail-face ((t (:inherit font-lock-doc-face)))) + '(dired-subtree-depth-1-face ((t (:background "gray80")))) + '(dired-subtree-depth-2-face ((t (:background "gray90")))) + '(dired-subtree-depth-3-face ((t (:background "gray95")))) + '(whitespace-indentation ((t (:background "gray90"))))) + +(provide 'km-theme) +;;; km-theme.el ends here diff --git a/lisp/km-util.el b/lisp/km-util.el new file mode 100644 index 0000000..8957862 --- /dev/null +++ b/lisp/km-util.el @@ -0,0 +1,38 @@ +;;; km-util.el --- Utilities + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'dash) + +;; Taken from +;; http://milkbox.net/note/single-file-master-emacs-configuration/. +(defmacro after (mode &rest body) + "`eval-after-load' MODE evaluate BODY." + (declare (indent defun)) + `(eval-after-load ,mode + '(progn ,@body))) + +(defun km/mode-buffers (mode) + (--filter (with-current-buffer it (derived-mode-p mode)) + (buffer-list))) + +(provide 'km-util) +;;; km-util.el ends here diff --git a/lisp/km-webjump.el b/lisp/km-webjump.el new file mode 100644 index 0000000..94b3842 --- /dev/null +++ b/lisp/km-webjump.el @@ -0,0 +1,48 @@ +;;; km-webjump.el --- Extensions for webjump + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'webjump) + +(defun km/webjump-read-string (prompt) + "Like `webjump-read-string', but set default." + (let* ((default (if (use-region-p) + (buffer-substring-no-properties + (region-beginning) (region-end)) + (thing-at-point 'symbol))) + (prompt (if default + (format "%s (%s): " prompt default) + (concat prompt ": "))) + (input (read-string prompt nil nil default))) + (unless (webjump-null-or-blank-string-p input) + (substring-no-properties input)))) + +;;;###autoload +(defun km/webjump () + "Run`webjump' with symbol at point or region as default query. +This affects only sites in the `simple-query' format." + (interactive) + (cl-letf (((symbol-function 'webjump-read-string) #'km/webjump-read-string)) + (call-interactively #'webjump))) + +(provide 'km-webjump) +;;; km-webjump.el ends here diff --git a/lisp/setkey.el b/lisp/setkey.el new file mode 100644 index 0000000..e5d3ec1 --- /dev/null +++ b/lisp/setkey.el @@ -0,0 +1,48 @@ +;;; setkey.el --- Set a temporary key binding + +;; Copyright (C) 2012-2016 Kyle Meyer + +;; Author: Kyle Meyer +;; URL: https://github.com/kyleam/emacs.d + +;; 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 . + +;;; Code: + +(defvar setkey-command nil) +(defvar setkey-last-call-time nil) +(defvar setkey-seconds-timeout 600) + +;;;###autoload +(defun setkey-call () + "Call `setkey-command'. +When `setkey-command' is nil or the time since the last call +has exceeded `setkey-seconds-timeout', read the command to +call." + (interactive) + (when (or (not setkey-command) + (> (- (float-time) setkey-last-call-time) + setkey-seconds-timeout)) + (setq setkey-command (read-command "Command: " setkey-command))) + (setq setkey-last-call-time (float-time)) + (call-interactively setkey-command)) + +(defun setkey-reset () + "Reset `setkey-call' command." + (interactive) + (setq setkey-command nil + setkey-last-call-time nil)) + +(provide 'setkey) +;;; setkey.el ends here -- cgit v1.2.3