;;; init.el --- My Emacs init -*- lexical-binding: t -*- ;;; Commentary: ;;; Code: (eval-and-compile (require 'cask "~/projects/ext/cask/cask.el") (cask-initialize)) (eval-when-compile (require 'cl) (require 'dash) (require 'esh-io) (require 'esh-proc) (require 'fill-column-indicator) (require 'magit) (require 'yasnippet) (require 'noflet)) (load (concat user-emacs-directory "init2")) ;;;; Autoloads (autoload 'jabber-connect "jabber" nil t) (autoload 'moz-minor-mode "moz" nil t) (autoload 'notifications-notify "notifications") (autoload 'php-mode "php-mode" nil t) (autoload 'po-mode "po-mode" nil t) (autoload 'pony-mode "pony-mode" nil t) (autoload 'sawfish-mode "sawfish" nil t) (autoload 'server-running-p "server") (autoload 'tagedit-mode "tagedit" nil t) (autoload 'tern-mode "tern" nil t) (autoload 'w3m-bookmark-view "w3m" nil t) (autoload 'w3m-goto-url "w3m" nil t) (autoload 'xmodmap-mode "xmodmap-mode" nil t) ;;;; Macros (defmacro auto-init (library) "Load a file for LIBRARY after loading the library. The loaded file should be `LIBRARY-init', either `.el' or `.elc' will do." `(with-eval-after-load ',library (load ,(concat (if (symbolp library) (symbol-name library) library) "-init")))) ;; http://www.lunaryorn.com/2013/06/25/introducing-with-eval-after-load/ (defmacro stante-after (feature &rest forms) "After FEATURE is loaded, evaluate FORMS. FEATURE may be an unquoted feature symbol or a file name, see `eval-after-load'." (declare (indent 1) (debug t)) `(,(if (or (not byte-compile-current-file) (if (symbolp feature) (require feature nil :noerror) (load feature :no-message :no-error))) `progn (message "stante-after: cannot find %s" feature) 'with-no-warnings) (with-eval-after-load ',feature ,@forms))) (defmacro oni:add-hooks (hook &rest functions) "Add to HOOK each function in FUNCTIONS." (declare (indent 1)) `(progn ,@(mapcar (lambda (func) `(add-hook ,hook ,func)) functions))) (defmacro oni:add-function-to-hooks (func &rest hooks) "Add FUNCTION to each hook in HOOKS." (declare (indent 1)) `(progn ,@(mapcar (lambda (hook) `(add-hook ,hook ,func)) hooks))) (defmacro oni:email (user at host dot com) "Turn arguments into an email address. The resulting email address will look like: USER@HOST.COM, AT and DOT are intentionally being skipped." (ignore at dot) (concat (symbol-name user) "@" (symbol-name host) "." (symbol-name com))) (defmacro oni:eval-after-init (&rest body) "Defer execution of BODY until after Emacs init." (declare (indent 0)) `(add-hook 'emacs-startup-hook #'(lambda () ,@body))) (defmacro oni:link-modes (mode1 mode2) "Whenever MODE1 is started, also start MODE2. Same for stopping. If INVERSE is specified, make sure MODE2 is turned off whenever MODE1 is enabled and vice-versa." (let* ((mode1-name (symbol-name mode1)) (mode2-name (symbol-name mode2)) (function-name (intern (concat "toggle-" mode2-name "-by-" mode1-name)))) `(progn (defun ,function-name () ,(concat "Toggle `" mode2-name "' according to the variable `" mode1-name "'.") (,mode2 (or ,mode1 -1))) (add-hook ',(intern (concat mode1-name "-hook")) #',function-name)))) (defmacro oni:exclude-modes (mode1 mode2) "Whenever MODE1 is started, stop MODE2. Switch for stopping." (let* ((mode1-name (symbol-name mode1)) (mode2-name (symbol-name mode2)) (function-name (intern (concat "toggle-" mode2-name "-inverse-of-" mode1-name)))) `(progn (defvar ,mode1) (defun ,function-name () ,(concat "Toggle `" mode2-name "' according to the inverse of `" mode1-name "'.") (,mode2 (or (not ,mode1) -1))) (add-hook ',(intern (concat mode1-name "-hook")) #',function-name)))) ;;;;; Vacuous (defvar elnode-do-init) (defvar eshell-prompt-regexp) (defvar gnus-init-file) (defvar sql-product) (defvar sql-prompt-regexp) (defvar whitespace-style) (defvar *jabber-connected*) (defvar *jabber-current-status*) ;;;; Functions (defun oni:add-import-from (package import) (interactive (list (completing-read "From package: " (oni:collect-from-imports)) (read-string "Import: "))) (save-excursion (goto-char (point-min)) (search-forward (concat "from " package " import (")) (insert "\n " import ",") (oni:sort-imports))) (defun oni:after-save-func () "Function for `after-save-hook'." (executable-make-buffer-file-executable-if-script-p) (let* ((dom-dir (locate-dominating-file (buffer-file-name) "Makefile")) (TAGSp (not (string= "" (shell-command-to-string (concat "grep \"^TAGS:\" " dom-dir "Makefile")))))) (when (and dom-dir TAGSp) (shell-command (concat "make -C " dom-dir " TAGS >/dev/null 2>&1"))))) (defun oni:augment-sql-prompt () "Add the MariaDB prompt to the `sql-prompt-regexp'." (if (eq sql-product 'mysql) (setq sql-prompt-regexp (rx (and line-start (or "mysql" (and "MariaDB [" (1+ nonl) "]")) "> "))))) (defun oni:before-save-func () "Function for `before-save-hook'." (if (eq major-mode 'html-mode) (oni:replace-html-special-chars)) (if (not (eq major-mode 'markdown-mode)) (delete-trailing-whitespace))) (defun oni:c-mode-func () "Function for `c-mode-hook'." (local-set-key [f9] 'compile)) (defun oni:change-number-at-point (change-func) "Use CHANGE-FUNC to change the number at `point'." (let ((num (number-to-string (funcall change-func (number-at-point)))) (bounds (bounds-of-thing-at-point 'word))) (save-excursion (delete-region (car bounds) (cdr bounds)) (insert num)))) (defun oni:change-prev-case (num dir) (let ((regfunc (if (eq dir 'up) 'upcase-region 'downcase-region)) (wordfunc (if (eq dir 'up) 'upcase-word 'downcase-word))) (if (> num 1) (funcall regfunc (point) (- (point) num)) (funcall wordfunc -1)))) (defun oni:collect-from-imports () (let (results) (save-excursion (goto-char (point-min)) (while (re-search-forward "from \\(.+\\) import" nil :noerror) (push (buffer-substring-no-properties (match-beginning 1) (match-end 1)) results))) results)) (defun oni:current-jabber-status () "Return a string representing the current jabber status." (or (and (not *jabber-connected*) "Offline") (and (not (string= *jabber-current-status* "")) *jabber-current-status*) "Online")) (defun oni:decrease-number-at-point () "Take the number at `point' and replace it with it decreased by 1." (interactive) (oni:change-number-at-point #'1-)) (defun oni:diary-display-func () "Function for `diary-display-hook'." (diary-fancy-display)) (defun oni:downcase-prev (num) (interactive "p") (oni:change-prev-case num 'down)) (defun oni:enable (functions) "Set the `disabled' property for each item in FUNCTIONS to nil." (mapc #'(lambda (f) (put f 'disabled nil)) functions)) (defun oni:eshell-handle-url () (save-excursion (goto-char eshell-last-output-start) (while (re-search-forward "https?://[^ \n]+" eshell-last-output-end :noerror) (make-button (match-beginning 0) (match-end 0) 'action (lambda (button) (browse-url (button-label button))))))) (defun oni:eshell-prompt () "Show a pretty shell prompt." (concat (if (not (looking-back "\n")) "\n") (oni:shorten-dir (abbreviate-file-name (eshell/pwd))) " > ")) (defun oni:go-mode-func () "Function for `go-mode-hook'." (setq indent-tabs-mode nil)) (defun oni:haskell-mode-func () "Function for `haskell-mode-hook'." (turn-on-haskell-indentation)) (defun oni:increase-number-at-point () "Take the number at `point' and replace it with it increased by 1." (interactive) (oni:change-number-at-point #'1+)) (defun indent-defun () "Indent the current defun." (interactive) (save-excursion (mark-defun) (indent-region (region-beginning) (region-end)))) (defun oni:eshell-C-d () "Either call `delete-char' interactively or quit." (interactive) (condition-case err (call-interactively #'delete-char) (error (if (and (eq (car err) 'end-of-buffer) (looking-back eshell-prompt-regexp)) (kill-buffer) (signal (car err) (cdr err)))))) (defun oni:level (lst) "Reduce a 2-level list LST to a flat list." (let ((lsts (mapcar (lambda (l) (if (listp l) l (list l))) lst))) (apply #'append lsts))) (defun oni:locally-enable-double-spaces () "Specify that two spaces end a sentence in the current buffer." (setq-local sentence-end-double-space t)) (defun oni:lua-mode-func() "Function for `lua-mode-hook'." (local-unset-key (kbd ")")) (local-unset-key (kbd "]")) (local-unset-key (kbd "}"))) (defun oni:make-import-multiline (from-point to-point) (interactive (list (line-beginning-position) (line-end-position))) (goto-char from-point) (search-forward "import" to-point) (insert " (\n") (delete-horizontal-space) (let ((imports-start (point)) imports-end) (while (search-forward "," to-point :noeror) (insert "\n") (delete-horizontal-space)) (end-of-line) (insert ",\n") (setf imports-end (point)) (insert ")") (python-indent-shift-right imports-start imports-end) (forward-line -1) (oni:sort-imports))) (defun oni:make-readable () "Make non-programming buffers a little more readable." (setq line-spacing .2)) (defun oni:markdown-mode-func () "Function for `markdown-mode-hook'." (setq-local whitespace-style '(face trailing))) (defun oni:maybe-fci-mode () "Turn on `fci-mode' if there is a filename for the buffer." (when (buffer-file-name) (fci-mode))) (defun oni:maybe-prettify-symbols-mode (&optional arg) "See of `prettify-symbols-mode' is bound and call it if so." (when (fboundp 'prettify-symbols-mode) (prettify-symbols-mode arg))) (defun oni:mini-fix-timestamp-string (date-string) "A minimal version of Xah Lee's `fix-timestamp-string'. Turn DATE-STRING into something else that can be worked with in code. Found at http://xahlee.org/emacs/elisp_parse_time.html" (setq date-string (replace-regexp-in-string "Jan" "01" date-string) date-string (replace-regexp-in-string "Feb" "02" date-string) date-string (replace-regexp-in-string "Mar" "03" date-string) date-string (replace-regexp-in-string "Apr" "04" date-string) date-string (replace-regexp-in-string "May" "05" date-string) date-string (replace-regexp-in-string "Jun" "06" date-string) date-string (replace-regexp-in-string "Jul" "07" date-string) date-string (replace-regexp-in-string "Aug" "08" date-string) date-string (replace-regexp-in-string "Sep" "09" date-string) date-string (replace-regexp-in-string "Oct" "10" date-string) date-string (replace-regexp-in-string "Nov" "11" date-string) date-string (replace-regexp-in-string "Dec" "12" date-string)) (string-match "^\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{4\\}\\)$" date-string) (format "%s-%s-%s" (match-string 3 date-string) (match-string 2 date-string) (match-string 1 date-string))) (defun oni:mode-line-current-song () "Extract current song information from a path. EMMS only shows me the absolute path of a song, this function extracts the parts I want to know about." (let ((song (emms-track-name (emms-playlist-current-selected-track)))) (if (string-match "\\([^/]+\\)/\\([0-9]\\{4\\}\\) - \\(.+\\)/\\([0-9]\\{2,3\\}\\) - \\(.+\\)\\..\\{3,4\\}$" song) (let ((band (substring song (match-beginning 1) (match-end 1))) (title (substring song (match-beginning 5) (match-end 5)))) (format "[%s - %s]" band title)) song))) (defun oni:move-beginning-of-dwim () "Move to beginning of line either after indentation or before." (interactive) (let ((start (point))) (back-to-indentation) (if (= start (point)) (move-beginning-of-line 1)))) (defun oni:move-end-of-dwim () "Move to end of line, either before any comments or after." (interactive) (let ((start (point)) (eolpos (line-end-position))) (beginning-of-line) (if (and comment-start (not (looking-at (regexp-quote comment-start))) (comment-search-forward eolpos t)) (progn (search-backward-regexp (concat "[^ \t" comment-start "]")) (forward-char) (when (or (bolp) (= start (point))) (end-of-line))) (end-of-line)))) (defun oni:myepisodes-formatter (plist) "Format RSS items from MyEpisodes as org tasks. PLIST contains all the pertinent information." (let ((str (plist-get plist :title))) (string-match "^\\[ \\([^\]]+\\) \\]\\[ \\([^\]]+\\) \\]\\[ \\([^\]]+\\) \\]\\[ \\([^\]]+\\) \\]$" str) (let* ((title (match-string 1 str)) (episode (match-string 2 str)) (name (match-string 3 str)) (date (oni:mini-fix-timestamp-string (match-string 4 str)))) (format "* ACQUIRE %s %s - %s \n SCHEDULED: <%s>" title episode name date)))) (defun oni:php-mode-func () "Function for `php-mode-hook'." (c-set-offset 'arglist-intro '+) (c-set-offset 'arglist-close '0) (setq-local fci-rule-column 80)) (defun oni:prog-mode-func () "Function for `prog-mode-hook'." (setq-local comment-auto-fill-only-comments t)) (defun oni:python-mode-func () "Function for `python-mode-hook'." (local-set-key (kbd "C->") 'python-indent-shift-right) (local-set-key (kbd "C-<") 'python-indent-shift-left) (set (make-local-variable 'electric-indent-chars) nil) (setq-local fci-rule-column 79) (setq-local fill-column 72) (setq-local whitespace-style '(tab-mark))) (defun oni:raise-ansi-term (arg) "Create or show an `ansi-term' buffer." (interactive "P") (let ((buffer (get-buffer "*ansi-term*"))) (if (and buffer (not arg)) (switch-to-buffer buffer) (ansi-term (getenv "SHELL"))))) (defun oni:raise-eshell () "Start or switch back to `eshell'. Also change directories to current working directory." (interactive) (let ((dir (file-name-directory (or (buffer-file-name) "~/"))) (hasfile (not (eq (buffer-file-name) nil))) (started (and (boundp 'eshell-buffer-name) eshell-buffer-name (buffer-live-p (get-buffer eshell-buffer-name))))) (eshell) (when (and hasfile (eq eshell-process-list nil)) (eshell/cd dir) (when started (eshell-reset))))) (defun oni:raise-scratch (&optional mode) "Show the *scratch* buffer. If called with a universal argument, ask the user which mode to use. If MODE is not nil, open a new buffer with the name *MODE-scratch* and load MODE as its major mode." (interactive (list (if current-prefix-arg (read-string "Mode: ") nil))) (let* ((bname (if mode (concat "*" mode "-scratch*") "*scratch*")) (buffer (get-buffer bname)) (mode-sym (intern (concat mode "-mode")))) (unless buffer (setq buffer (generate-new-buffer bname)) (with-current-buffer buffer (when (fboundp mode-sym) (funcall mode-sym)))) (select-window (display-buffer buffer)))) (defun oni:reload-buffer () "Reload current buffer." (interactive) (revert-buffer nil t nil)) (defun oni:replace-html-special-chars () "Replace special characters with HTML escaped entities." (oni:replace-occurrences "é" "é")) (defun oni:replace-occurrences (from to) "Replace all occurrences of FROM with TO in the current buffer." (save-excursion (goto-char (point-min)) (while (search-forward from nil t) (replace-match to)))) (defun oni:request-pull () "Start a mail to request pulling from a git repository." (interactive) (let* ((default-directory (expand-file-name (or (locate-dominating-file default-directory ".git") (magit-read-top-dir nil)))) (refs (magit-list-interesting-refs magit-uninteresting-refs)) (from (cdr (assoc (completing-read "From: " refs) refs))) (url (read-from-minibuffer "Pull URL: ")) (to (symbol-name (read-from-minibuffer "Up to (HEAD): " nil nil t nil "HEAD"))) (patchp (and current-prefix-arg (listp current-prefix-arg)))) (message "Requesting pull for %s from %s to %s at %s with%s patch" default-directory from to url (if patchp "" "out")) (compose-mail nil (concat "Requesting pull for " (file-name-base (directory-file-name default-directory)))) (save-excursion (goto-char (point-max)) (insert (shell-command-to-string (concat "git --git-dir='" default-directory ".git' --work-tree='" default-directory "' request-pull " (when patchp "-p ") from " " url " " to)))))) (defun oni:reset-default-directory () "Reset `default-directory' to HOME." (setq default-directory (getenv "HOME"))) (defun oni:scroll-down-or-prev-page (arg) "Either scroll down or go to the previous page. Depending on the value of `buffer-narrowed-p'." (interactive "^P") (if (buffer-narrowed-p) (let ((scroll-error-top-bottom nil)) (condition-case nil (scroll-down-command arg) (beginning-of-buffer (narrow-to-page -1) (goto-char (point-min))))) (scroll-down-command arg))) (defun oni:scroll-up-or-next-page (arg) "Either scroll up or go to the next page. Depending on the value of `buffer-narrowed-p'." (interactive "^P") (if (buffer-narrowed-p) (let ((scroll-error-top-bottom nil)) (condition-case nil (scroll-up-command arg) (end-of-buffer (narrow-to-page 1) (goto-char (point-min))))) (scroll-up-command arg))) (defun oni:self-insert-dwim () "Execute self insert, but when the region is active call self insert at the end of the region and at the beginning." (interactive) (if (region-active-p) (let ((electric-pair-mode nil) (beginning (region-beginning)) (end (region-end))) (goto-char end) (self-insert-command 1) (save-excursion (goto-char beginning) (self-insert-command 1))) (self-insert-command 1))) (defun oni:set-emacs-lisp-keys () "Set some keys for `emacs-lisp-mode'." (local-set-key (kbd "C-.") 'find-function)) (defun oni:set-emacs-lisp-symbols () "Set a few extra UTF-8 symbols for use in emacs-lisp." (when (boundp 'prettify-symbols-alist) (setq prettify-symbols-alist (append prettify-symbols-alist '(("<=" . ?≤) (">=" . ?≥) ("sqrt" . ?√)))))) (defun oni:set-ispell-local-en-dict () "Set `ispell-local-dictionary' to en." (setq ispell-local-dictionary "en")) (defun oni:set-keys-for-dired () "Set some keybindings for `dired'." (local-set-key (kbd "E") #'wdired-change-to-wdired-mode)) (defun oni:set-keys-for-eshell () "Set some keybindings for `eshell'." (local-set-key (kbd "C-d") #'oni:eshell-C-d)) (defun oni:set-keys-for-hy () "Set some keybindings for `hy-mode'." (local-set-key (kbd "{") #'paredit-open-curly) (local-set-key (kbd "}") #'paredit-close-curly)) (defun oni:set-keys-for-jabber-chat () "Set certain keys for `jabber-chat-mode'." (local-set-key (kbd "M-!") #'shell-command-with-command)) (defun oni:set-keys-for-tagedit () "Set some keybindings for `tagedit-mode'." (local-set-key (kbd "M-k") #'tagedit-kill-attribute)) (defun oni:set-python-imenu-function () "Set the `imenu-create-index-function' variable. For `python-mode' I prefer `python-imenu-create-flat-index'." (setq imenu-create-index-function #'python-imenu-create-flat-index)) (defun oni:set-python-symbols () "Set a few extra UTF-8 symbols for use in python." (when (boundp 'prettify-symbols-alist) (setq prettify-symbols-alist '(("lambda" . ?λ) ("<=" . ?≤) (">=" . ?≥) ("!=" . ?≠))))) (defun oni:set-tab-maybe-toggle-outline () "Wrap the current function mapped to `TAB'." (let ((func (or (lookup-key (current-local-map) (kbd "TAB")) (lookup-key (current-global-map) (kbd "TAB"))))) (local-set-key (kbd "TAB") (lambda () (interactive) (if (outline-on-heading-p) (if (outline-invisible-p (line-end-position)) (show-entry) (hide-entry)) (call-interactively func)))))) (let (setp) (defun oni:set-theme (frame) "Try to set the theme for the current (first) frame." (ignore frame) (unless setp ;; (load-theme 'yoshi t) (load-theme 'monokai t) ;; (smt/enable) ;; (require 'my-smt) ;; (smt/set-theme 'my-smt) ;; (set-face-attribute 'mode-line nil :box nil) ;; (set-face-attribute 'mode-line-inactive nil :box nil) ))) (if (daemonp) (add-hook 'after-make-frame-functions (lambda (frame) (noflet ((display-graphic-p (&optional display) t)) (oni:set-theme frame)))) (oni:eval-after-init (oni:set-theme nil))) (defun oni:shell-command-with-command (command &optional output-buffer) "Print both COMMAND and the output into OUTPUT-BUFFER." (interactive (list (read-shell-command "Shell command: " nil nil) current-prefix-arg)) (when output-buffer (insert "`" command "':\n")) (shell-command command output-buffer)) (defun oni:shorten-dir (dir) "Shorten a directory, (almost) like fish does it." (while (string-match "\\(\\.?[^./]\\)[^/]+/" dir) (setq dir (replace-match "\\1/" nil nil dir))) dir) (defun oni:show-buffer-position () "Show the position in the current buffer." (interactive) (message (format "%d:%d" (line-number-at-pos) (current-column)))) (defun oni:show-org-index () "Show the index of my org files." (interactive) (find-file "~/documents/org/index.org")) (defun oni:sort-imports () "Sort python multiline imports using `()'." (interactive) (save-excursion (sort-lines nil (1+ (search-backward "(")) (1- (search-forward ")"))))) (defun oni:split-window-interactive (dir) "Split windows in direction DIR. Can also delete or switch to another window." (interactive (list (read-char "Direction (h,v,q,d,o): "))) (case dir ((?v) (split-window-vertically)) ((?h) (split-window-horizontally)) ((?q) (delete-other-windows)) ((?d) (delete-window)) ((?o) (other-window 1)))) (defun oni:split-window-interactively (window) "Ask for a direction and split WINDOW that way. If no direction is given, don't split." (let ((dir (read-char "Direction (h,v): "))) (case dir ((?v) (split-window-vertically)) ((?h) (split-window-horizontally)) (t window)))) (defun oni:start-python-test-mail-server () "Run the python test mailserver." (interactive) (start-process "python-test-mail-server" "*py-mail-server*" "python" "-m" "smtpd" "-n" "-c" "DebuggingServer" "localhost:1025")) (defun oni:stop-python-test-mail-server () "Stop the python test mailserver." (interactive) (kill-process "python-test-mail-server")) (defun oni:switch-to-other-buffer () "Switch to the most recently viewed buffer." (interactive) (switch-to-buffer (other-buffer))) (defun oni:term-mode-func () "Function for `term-mode-hook'." (setq truncate-lines nil)) (defun oni:turn-on-compilation-shell-for-pony () "Turn on option `compilation-shell-minor-mode' for `pony-minor-mode'." (add-hook 'pony-minor-mode-hook 'compilation-shell-minor-mode nil t)) (defun oni:upcase-prev (num) (interactive "p") (oni:change-prev-case num 'up)) (defun oni:vala-mode-func () "Function for `vala-mode-hook'." (setq indent-tabs-mode nil)) (defun oni:write-file-func () "Function for `write-file-hooks'." (time-stamp)) (defun oni:yas-minor-mode-func () "Function for `yas-minor-mode-hook'." (define-key yas-minor-mode-map (kbd "TAB") nil) (define-key yas-minor-mode-map [(tab)] nil) (define-key yas-minor-mode-map (kbd "C-\\") 'yas-expand)) ;;;; Modes ;; Copied from electric.el, modified from `electric-indent-local-mode'. (define-minor-mode oni:electric-pair-local-mode "Toggle `electric-pair-mode' only in this buffer." :variable (buffer-local-value 'electric-pair-mode (current-buffer)) (cond ((eq electric-pair-mode (default-value 'electric-pair-mode)) (kill-local-variable 'electric-pair-mode)) ((not (default-value 'electric-pair-mode)) ;; Locally enabled, but globally disabled. (electric-pair-mode 1) ; Setup the hooks. (setq-default electric-pair-mode nil) ; But keep it globally disabled. ))) ;;;; Tests (stante-after ert (ert-deftest oni:add-import-from () (with-temp-buffer (python-mode) (insert "from myaethon2.core.administration.models import ( Client, Contact, Individual, Location, )") (oni:add-import-from "myaethon2.core.administration.models" "Debtor") (should (equal (buffer-substring-no-properties (point-min) (point-max)) "from myaethon2.core.administration.models import ( Client, Contact, Debtor, Individual, Location, )")))) (ert-deftest oni:collect-from-imports () (with-temp-buffer (python-mode) (insert "import calendar as cal import datetime from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required from django.core.context_processors import csrf from django.core.exceptions import PermissionDenied from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.urlresolvers import reverse from django.db import transaction from django.db.models import Q from django.shortcuts import get_object_or_404, render from django.utils import formats from django.utils.translation import ugettext as _ from myaethon2.core.business_units import BU from myaethon2.core.forms import MultiFormWrapper from myaethon2.core.models import AUser, Service from myaethon2.core.planning.models import Booking, JOB_TYPES from myaethon2.core.util import simplify_timedelta from myaethon2.core.views import SearchAndSortListView from myaethon2.jobs import forms, status from myaethon2.jobs.models import Assignment, Job, JobGroup from myaethon2.jobs.util import JobFactory from myaethon2.workers.models import Worker from myaethon2.export import Exporter, XLSGenerator from django.http import ( HttpResponseForbidden, HttpResponseNotAllowed, HttpResponseRedirect, ) from django.views.generic import ( CreateView, DeleteView, DetailView, ListView, UpdateView, ) from myaethon2.core.administration.models import ( Client, Contact, Debtor, Individual, Location, ) from myaethon2.core.decorators import ( json_response, protect_with, with_help_text, )") (should (equal (sort (oni:collect-from-imports) #'string-lessp) '("django.conf" "django.contrib" "django.contrib.auth.decorators" "django.core.context_processors" "django.core.exceptions" "django.core.paginator" "django.core.urlresolvers" "django.db" "django.db.models" "django.http" "django.shortcuts" "django.utils" "django.utils.translation" "django.views.generic" "myaethon2.core.administration.models" "myaethon2.core.business_units" "myaethon2.core.decorators" "myaethon2.core.forms" "myaethon2.core.models" "myaethon2.core.planning.models" "myaethon2.core.util" "myaethon2.core.views" "myaethon2.export" "myaethon2.jobs" "myaethon2.jobs.models" "myaethon2.jobs.util" "myaethon2.workers.models"))))) (ert-deftest oni:make-import-multiline () (with-temp-buffer (python-mode) (insert "from myaethon2.core.administration.models import Contact, Individual, Client, Location") (oni:make-import-multiline (line-beginning-position) (line-end-position)) (should (equal (buffer-substring-no-properties (point-min) (point-max)) "from myaethon2.core.administration.models import ( Client, Contact, Individual, Location, )"))))) ;;;; Unconditional settings (eval '(setq inhibit-startup-echo-area-message "slash")) ;;;; Module-specific settings (auto-init appt) (auto-init avandu) (stante-after auto-complete (add-to-list 'ac-modes 'slime-repl-mode) (setq ac-auto-show-menu nil ac-use-quick-help nil)) (stante-after "bindings" (setq mode-line-default-help-echo "")) (stante-after browse-url (setq browse-url-browser-function 'browse-url-generic) (setq browse-url-generic-program "conkeror")) (stante-after cc-vars (setq-default c-basic-offset 4) (setq c-offsets-alist '((statement-block-intro . +) (knr-argdecl-intro . 5) (substatement-open . +) (substatement-label . 0) (label . 0) (statement-case-open . +) (statement-cont . +) (arglist-intro . +) (arglist-close . 0) (inline-open . 0) (brace-list-open . +) (topmost-intro-cont first c-lineup-topmost-intro-cont c-lineup-gnu-DEFUN-intro-cont)))) (stante-after colemak-evil (define-key evil-insert-state-map (kbd "C-g") #'evil-normal-state) (define-key evil-replace-state-map (kbd "C-g") #'evil-normal-state) (define-key evil-visual-state-map (kbd "C-g") #'evil-normal-state)) (stante-after compile (setq compilation-scroll-output t)) (stante-after custom (setq custom-theme-directory "~/.emacs.d/themes")) (stante-after desktop (setq desktop-restore-frames nil) (setq desktop-load-locked-desktop t) (setq desktop-clear-preserve-buffers (append (list (rx (and bol (or (and "+" (1+ nonl)) "dailies" "work" "tasks" "org" (or "bookmarks.org" "contacts.org")) eol)) (rx (or "*ielm*" "*-jabber-roster-*" "*eshell*" "*ansi-term*" "*slime-repl sbcl*" "*slime-events*")) (rx (and "*" (or "magit" "scratch-") (1+ nonl) "*")) (rx (or "irc.freenode.net:6667" (and "#" (1+ nonl))))) desktop-clear-preserve-buffers)) (setq desktop-files-not-to-save (rx (or (regexp "\\(^/[^/:]*:\\|(ftp)$\\)") (and "/" (or "dailies" "tasks" "org" "bookmarks.org" "contacts.org" "work") eol)))) (add-to-list 'desktop-globals-to-clear 'desktop-dirname)) (stante-after dired (add-hook 'dired-mode-hook #'oni:set-keys-for-dired)) (stante-after eap (setq eap-music-library "/mnt/music") (setq eap-playlist-library "~/music/playlists")) (stante-after ediff-wind (setq ediff-window-setup-function 'ediff-setup-windows-plain)) (stante-after eltuki (setq eltuki-blog-dir "~/documents/blog")) (stante-after em-prompt (setq eshell-highlight-prompt nil) (setq eshell-prompt-function 'oni:eshell-prompt) (setq eshell-prompt-regexp "^[~/].* > ")) (stante-after em-term (add-to-list 'eshell-visual-commands "unison")) (stante-after emms (emms-minimalistic) (emms-default-players) (require 'emms-player-mpd) (require 'emms-mode-line)) (stante-after emms-mode-line (setq emms-mode-line-mode-line-function 'oni:mode-line-current-song) (emms-mode-line 1)) (stante-after emms-player-mpd (add-to-list 'emms-player-list 'emms-player-mpd) (setq emms-player-mpd-music-directory "/mnt/music/mp3")) (stante-after erc (setq erc-hide-list '("PART")) (setq erc-nick "ryuslash")) (stante-after erc-join (setq erc-autojoin-channels-alist '(("freenode.net" "#ninthfloor" "#emacs" "#dispass")))) (stante-after erc-stamp (setq erc-insert-timestamp-function 'erc-insert-timestamp-left) (setq erc-timestamp-format "[%H:%M] ") (setq erc-timestamp-only-if-changed-flag nil)) (stante-after esh-mode (add-to-list 'eshell-output-filter-functions #'oni:eshell-handle-url) (add-to-list 'eshell-output-filter-functions #'eshell-truncate-buffer)) (stante-after evil (mapc (lambda (mode) (evil-set-initial-state mode 'emacs)) '(jabber-roster-mode grep-mode avandu-overview-mode avandu-article-mode gnus-summary-mode gnus-article-mode gnus-group-mode magit-status-mode magit-key-mode sql-interactive-mode Info-mode jabber-chat-mode diff-mode prodigy-mode calculator-mode messages-buffer-mode)) (setq evilnc-hotkey-comment-operator ",") (require 'evil-nerd-commenter)) (stante-after eww (setq eww-download-path ; Don't go to ~/Downloads "~/downloads/")) (stante-after files (setq-default require-final-newline t) (setq auto-mode-case-fold nil) (setq auto-save-file-name-transforms `((".*" "~/.local/share/emacs/autosave/" t))) (setq backup-directory-alist `((".*" . "~/.local/share/emacs/backup/"))) (setq auto-mode-alist (append '(("/PKGBUILD$" . sh-mode) (".install$" . sh-mode) ("\\.jl$" . sawfish-mode) ("\\.js\\(on\\)?$" . js2-mode) ("\\.m\\(ark\\)?d\\(?:o?wn\\)?$" . markdown-mode) ("\\.php[345]?$" . php-mode) ("\\.po\\'\\|\\.po\\." . po-mode) ("\\.tm?pl$" . html-mode) ("^\\.Xmodmap$" . xmodmap-mode)) auto-mode-alist))) (stante-after fill-column-indicator (setq fci-rule-column 73)) (stante-after fiplr (add-to-list 'fiplr-root-markers ".emacs.desktop") (push "*.pyc" (cadr (assoc 'files fiplr-ignored-globs))) (push "*.cache" (cadr (assoc 'files fiplr-ignored-globs))) (push "*.elc" (cadr (assoc 'files fiplr-ignored-globs))) (push ".emacs.desktop*" (cadr (assoc 'files fiplr-ignored-globs))) (push ".cask" (cadr (assoc 'directories fiplr-ignored-globs))) (push "migrations" (cadr (assoc 'directories fiplr-ignored-globs))) (push "vendor-lisp" (cadr (assoc 'directories fiplr-ignored-globs)))) (stante-after flycheck (mapc (lambda (c) (delq c flycheck-checkers)) '(python-pylint python-pyflakes)) (setf flycheck-highlighting-mode 'columns) (require 'flycheck-commit-check)) (stante-after geiser-repl (setq geiser-repl-history-filename "~/.emacs.d/geiser-history")) (stante-after gnutls (add-to-list 'gnutls-trustfiles (expand-file-name "~/ssl_20130810/sub.class1.server.ca.pem"))) (stante-after grep (add-to-list 'grep-find-ignored-directories "migrations") (add-to-list 'grep-find-ignored-directories "vendor") (add-to-list 'grep-find-ignored-files "TAGS")) (stante-after help-at-pt (setq help-at-pt-display-when-idle t)) (stante-after ido (setq ido-ignore-buffers (list "^\\` " "^irc\\." "^\\#" "^\\*Customize Option:" (rx (or "*-jabber-roster-*" "*Messages*" "*fsm-debug*" "*magit-process*" "*magit-edit-log*" "*Backtrace*" "*Ibuffer*")))) (setq ido-auto-merge-work-directories-length -1) (setq ido-default-buffer-method 'pop-to-buffer) (setq ido-max-window-height 1) (setq ido-save-directory-list-file nil)) (stante-after imenu (setq imenu-auto-rescan t)) (stante-after jabber (load "jabber-init")) (stante-after jedi (setq jedi:tooltip-method nil)) (stante-after jit-lock (setq jit-lock-defer-time 0.2)) (stante-after magit (setq magit-repo-dirs '("~/projects/")) (setq magit-diff-refine-hunk 'all)) (stante-after message (setq message-send-mail-function 'message-send-mail-with-sendmail) (setq message-sendmail-extra-arguments '("-a" "ryuslash"))) (stante-after minibuf-eldef (setq minibuffer-eldef-shorten-default t)) (stante-after mouse (setq mouse-yank-at-point t)) (stante-after org (require 'org-init) (setq org-special-ctrl-a/e t)) (stante-after org2blog (setq org2blog/wp-blog-alist '(("ryublog" :url "https://ryuslash.org/blog/xmlrpc.php" :username "ryuslash")))) (stante-after "paragraphs" (setq sentence-end-double-space nil)) (stante-after php-mode (setq-default php-mode-warn-if-mumamo-off nil) (setq php-function-call-face 'font-lock-function-name-face) (setq php-mode-force-pear t)) (stante-after prodigy (prodigy-define-service :name "Python test mailserver" :command "python" :args '("-m" "smtpd" "-n" "-c" "DebuggingServer" "localhost:1025") :tags '(work mail) :kill-process-buffer-on-stop t) (prodigy-define-service :name "Picturefix mollie-bank" :command "bundle" :args '("exec" "mollie-bank") :cwd "~/projects/work/photension/picturefix" :path '("~/.rbenv/shims") :tags '(work) :kill-signal 'sigkill) (prodigy-define-service :name "Picturefix" :command "bundle" :args '("exec" "rails" "s") :cwd "~/projects/work/photension/picturefix" :path '("~/.rbenv/shims") :tags '(work) :kill-signal 'sigkill) (prodigy-define-service :name "Picturefix sidekiq" :command "bundle" :args '("exec" "sidekiq") :cwd "~/projects/work/photension/picturefix" :path '("~/.rbenv/shims") :tags '(work) :kill-signal 'sigkill)) (stante-after python-environment (setcar python-environment-virtualenv "virtualenv2")) (stante-after scheme (require 'ac-geiser)) (stante-after sendmail (setq send-mail-function 'sendmail-send-it) (setq sendmail-program "/usr/bin/msmtp")) (stante-after simple (setq read-mail-command 'gnus) (define-key special-mode-map "z" #'kill-this-buffer)) (with-eval-after-load 'slime (setq slime-lisp-implementations '((sbcl ("sbcl" "--noinform") :coding-system utf-8-unix) (clisp ("clisp") :coding-system utf-8-unix)) slime-default-lisp 'sbcl)) (stante-after smex (setq smex-key-advice-ignore-menu-bar t) (setq smex-save-file "~/.emacs.d/smex-items")) (stante-after "startup" (setq inhibit-default-init t) (setq inhibit-startup-message t) (setq initial-major-mode 'emacs-lisp-mode) (setq initial-scratch-message nil)) (stante-after tern (require 'tern-auto-complete) (tern-ac-setup)) (stante-after time-stamp (setq time-stamp-active t) (setq time-stamp-format "%04y-%02m-%02d %02H:%02M:%02S (%u)")) (stante-after type-break (setq type-break-good-rest-interval (* 60 10)) (setq type-break-interval (* 60 50)) (setq type-break-keystroke-threshold '(nil . nil))) (stante-after uniquify (setq uniquify-buffer-name-style 'post-forward)) (stante-after w3m (setq w3m-fill-column 72)) (stante-after "window" (setq split-height-threshold 40) (add-to-list 'display-buffer-alist '("^\\*\\(?:.+-\\)?scratch\\*$" display-buffer-same-window)) (add-to-list 'display-buffer-alist '("^\\*magit: .*\\*$" display-buffer-same-window))) (stante-after woman (setq woman-fill-column 72)) (stante-after yasnippet (setq yas-fallback-behavior nil) (setq yas-prompt-functions '(yas-ido-prompt))) ;;;; Hooks (add-hook 'after-save-hook 'oni:after-save-func t) (add-hook 'before-save-hook 'oni:before-save-func) (add-hook 'comint-mode-hook #'oni:turn-on-compilation-shell-for-pony) (add-hook 'css-mode-hook #'rainbow-mode) (add-hook 'diary-display-hook 'oni:diary-display-func) (add-hook 'git-commit-mode-hook #'oni:set-ispell-local-en-dict) (add-hook 'haskell-mode-hook 'oni:haskell-mode-func) (add-hook 'js-mode-hook #'moz-minor-mode) (add-hook 'outline-minor-mode-hook #'oni:set-tab-maybe-toggle-outline) (add-hook 'slime-mode-hook #'set-up-slime-ac) (add-hook 'sql-interactive-mode-hook #'oni:augment-sql-prompt) (add-hook 'term-mode-hook 'oni:term-mode-func) (add-hook 'vala-mode-hook #'oni:vala-mode-func) (add-hook 'write-file-hooks 'oni:write-file-func) (add-hook 'yas-global-mode-hook 'oni:yas-minor-mode-func) (add-hook 'scheme-mode-hook (lambda () (setq ac-sources '(ac-source-geiser)))) (add-hook 'java-mode-hook #'oni:electric-pair-local-mode) (oni:add-function-to-hooks #'flycheck-mode 'perl-mode-hook 'rst-mode-hook 'rust-mode-hook 'sh-mode-hook 'git-commit-mode-hook) (oni:add-function-to-hooks #'oni:make-readable 'Info-mode-hook 'gnus-article-mode-hook 'gnus-group-mode-hook 'org-agenda-mode-hook) (oni:add-function-to-hooks #'paredit-mode 'clojure-mode-hook 'geiser-repl-mode-hook 'sawfish-mode-hook 'scheme-mode-hook) (oni:add-function-to-hooks #'hl-sexp-mode 'clojure-mode-hook 'geiser-repl-mode-hook 'sawfish-mode-hook 'scheme-mode-hook) (oni:add-hooks 'c-mode-hook #'oni:c-mode-func #'oni:electric-pair-local-mode) (oni:add-hooks 'emacs-lisp-mode-hook (lambda () (setf ac-sources '(ac-source-emacs-lisp-features ac-source-functions ac-source-variables ac-source-symbols))) #'oni:locally-enable-double-spaces #'oni:set-emacs-lisp-symbols #'paredit-mode #'flycheck-mode #'eldoc-mode #'oni:set-emacs-lisp-keys #'hl-sexp-mode) (oni:add-hooks 'eshell-mode-hook #'buffer-disable-undo #'oni:set-keys-for-eshell #'eshell-fringe-status-mode) (oni:add-hooks 'gnus-summary-mode-hook #'oni:make-readable (lambda () (local-set-key (kbd "M-d") (lambda () (interactive) (gnus-summary-delete-article) (gnus-summary-next-subject 1))))) (oni:add-hooks 'go-mode-hook #'oni:go-mode-func #'flycheck-mode) (oni:add-hooks 'html-mode-hook #'flycheck-mode #'oni:maybe-fci-mode #'tagedit-mode #'turn-off-flyspell #'turn-off-auto-fill) (oni:add-hooks 'hy-mode-hook #'paredit-mode #'oni:set-keys-for-hy #'hl-sexp-mode) (oni:add-hooks 'ielm-mode-hook #'paredit-mode #'eldoc-mode #'oni:set-emacs-lisp-keys #'hl-sexp-mode) (oni:add-hooks 'jabber-chat-mode-hook #'oni:set-keys-for-jabber-chat #'oni:make-readable #'oni:reset-default-directory) (oni:add-hooks 'js2-mode-hook #'tern-mode #'moz-minor-mode #'oni:electric-pair-local-mode) (oni:add-hooks 'lisp-mode-hook (lambda () (setf ac-sources '(ac-source-slime-simple))) #'oni:set-emacs-lisp-symbols #'paredit-mode #'hl-sexp-mode) (oni:add-hooks 'lua-mode-hook #'oni:lua-mode-func #'flycheck-mode #'oni:electric-pair-local-mode) (oni:add-hooks 'markdown-mode-hook #'whitespace-mode #'oni:markdown-mode-func) (oni:add-hooks 'php-mode-hook #'oni:php-mode-func #'flycheck-mode) (oni:add-hooks 'prog-mode-hook #'oni:prog-mode-func #'oni:maybe-fci-mode #'rainbow-delimiters-mode #'oni:maybe-prettify-symbols-mode) (oni:add-hooks 'python-mode-hook (lambda () (setq ac-sources '(ac-source-jedi-direct))) #'oni:set-python-symbols #'flycheck-mode #'whitespace-mode #'oni:python-mode-func #'oni:set-python-imenu-function #'jedi:setup #'subword-mode #'oni:electric-pair-local-mode) (oni:add-hooks 'ruby-mode-hook #'flycheck-mode #'oni:electric-pair-local-mode) (oni:add-hooks 'slime-repl-mode-hook #'paredit-mode #'set-up-slime-ac #'hl-sexp-mode) (oni:add-hooks 'tagedit-mode-hook #'tagedit-add-experimental-features #'tagedit-add-paredit-like-keybindings #'oni:set-keys-for-tagedit) (oni:add-hooks 'texinfo-mode-hook #'flycheck-mode #'outline-minor-mode) (oni:add-hooks 'text-mode-hook #'auto-fill-mode #'flyspell-mode #'oni:make-readable) ;;;; Keybindings (global-set-key (kbd "'") 'oni:self-insert-dwim) (global-set-key (kbd "") 'oni:raise-scratch) (global-set-key (kbd "") 'gnus) (global-set-key (kbd "") 'git-project-show-files) (global-set-key (kbd "") #'oni:reload-buffer) (global-set-key (kbd "") 'magit-status) (global-set-key (kbd "") #'oni:raise-eshell) (global-set-key (kbd "") 'oni:show-org-index) (global-set-key (kbd "") 'oni:scroll-up-or-next-page) (global-set-key (kbd "") 'oni:scroll-down-or-prev-page) (global-set-key (kbd "C-. C-.") #'oni:switch-to-other-buffer) (global-set-key (kbd "C-<") 'indent-shift-left) (global-set-key (kbd "C->") 'indent-shift-right) (global-set-key (kbd "C-M-4") 'split-window-vertically) (global-set-key (kbd "C-M-SPC") 'er/expand-region) (global-set-key (kbd "C-M-d") 'kill-word) (global-set-key (kbd "C-M-w") 'backward-kill-word) (global-set-key (kbd "C-M-x") 'smex-major-mode-commands) (global-set-key (kbd "C-M-z") 'indent-defun) (global-set-key (kbd "C-S-k") 'kill-whole-line) (global-set-key (kbd "C-c +") #'oni:increase-number-at-point) (global-set-key (kbd "C-c -") #'oni:decrease-number-at-point) (global-set-key (kbd "C-c Q") #'delete-other-windows) (global-set-key (kbd "C-c R") #'delete-window) (global-set-key (kbd "C-c S") #'split-window-right) (global-set-key (kbd "C-c a") 'org-agenda) (global-set-key (kbd "C-c c") 'org-capture) (global-set-key (kbd "C-c d c") 'desktop-clear) (global-set-key (kbd "C-c d d") 'desktop-registry-change-desktop) (global-set-key (kbd "C-c d k") #'desktop-registry-remove-desktop) (global-set-key (kbd "C-c d s") 'desktop-save-in-desktop-dir) (global-set-key (kbd "C-c g b") 'magit-checkout) (global-set-key (kbd "C-c g f") 'magit-fetch) (global-set-key (kbd "C-c g i") 'magit-init) (global-set-key (kbd "C-c g s") 'magit-status) (global-set-key (kbd "C-c h r") 'hypo-region) (global-set-key (kbd "C-c i p") 'identica-update-status-interactive) (global-set-key (kbd "C-c m") 'gnus) (global-set-key (kbd "C-c p") 'oni:show-buffer-position) (global-set-key (kbd "C-c s") #'split-window-below) (global-set-key (kbd "C-c t") 'oni:raise-ansi-term) (global-set-key (kbd "C-c u") #'upcase-transient-mode) (global-set-key (kbd "C-c w d") 'oni:downcase-prev) (global-set-key (kbd "C-c w u") 'oni:upcase-prev) (global-set-key (kbd "C-e") 'oni:move-end-of-dwim) (global-set-key (kbd "C-x 4 f") #'fiplr-find-file-other-window) (global-set-key (kbd "C-x 5 f") #'fiplr-find-file-other-frame) (global-set-key (kbd "C-x 8 e") "€") (global-set-key (kbd "C-x C-b") 'ibuffer) (global-set-key (kbd "C-x f") 'fiplr-find-file) (global-set-key (kbd "M-0") 'delete-window) (global-set-key (kbd "M-1") 'delete-other-windows) (global-set-key (kbd "M-2") 'split-window-below) (global-set-key (kbd "M-3") 'split-window-right) (global-set-key (kbd "M-4") 'split-window-horizontally) (global-set-key (kbd "M-n") 'idomenu) (global-set-key (kbd "M-o") 'other-window) (global-set-key (kbd "M-x") 'smex) (global-set-key (kbd "\"") 'oni:self-insert-dwim) (global-set-key [remap move-beginning-of-line] #'oni:move-beginning-of-dwim) ;;;; Misc modes (oni:enable '(downcase-region narrow-to-page narrow-to-region scroll-left upcase-region)) (oni:link-modes outline-minor-mode persistent-outline-mode) (oni:exclude-modes magit-blame-mode fci-mode) (oni:eval-after-init (require 'auto-complete-config) (ac-config-default) (ido-ubiquitous-mode) (smex-initialize) (global-diff-hl-mode) (mode-icons-mode) (desktop-registry-auto-register) (yas-global-mode) (require 'popwin) (popwin-mode) (evil-mode) (require 'colemak-evil)) (when (equal system-name "drd") (load "eap-autoloads")) (auto-insert-mode) (electric-indent-mode -1) (ido-mode 1) (minibuffer-electric-default-mode) (savehist-mode) (show-paren-mode) (winner-mode) (help-at-pt-set-timer) (windmove-default-keybindings) (load system-name :noerror) ;;;; End (provide 'init) ;;; init.el ends here