;;; oni.el --- Functions for emacs ;; Copyright (C) 2012 Tom Willemse ;; Author: Tom Willemse ;; Keywords: local ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; ;;; Code: (autoload 'notifications-notify "notifications") (autoload 'jabber-send-message "jabber-chat") (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." (concat (symbol-name user) "@" (symbol-name host) "." (symbol-name com))) (defun oni:after-save-func () "Function for `after-save-hook'." (oni:compile-el) (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:appt-display-window-and-jabber (min-to-app new-time appt-msg) "Send a message to my phone jabber account." (jabber-send-message (car jabber-connections) "phone@ryuslash.org" nil (format "%s%s (in %s minutes)" new-time appt-msg min-to-app) nil) (appt-disp-window min-to-app new-time appt-msg)) ;; (jabber-send-message (car jabber-connections) ;; "aethon@muc.ryuslash.org" nil "Hi, I'm a programmatic message; this ;; upens up possibilities :)" "groupchat") (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) (local-set-key "\C-j" 'oni:newline-and-indent)) (defun oni:close-client-window () "Close a client's frames." (interactive) (server-save-buffers-kill-terminal nil)) (defun oni:color-for (object) "Generate a hex color by taking the first 6 characters of OBJECT's MD5 sum." (format "#%s" (substring (md5 object) 0 6))) (defun oni:compile-el () "Compile the current buffer file if it is an .el file." (let* ((full-file-name (buffer-file-name)) (file-name (file-name-nondirectory full-file-name)) (suffix (file-name-extension file-name))) (if (and (not (string-equal file-name ".dir-locals.el")) (string-equal suffix "el")) (byte-compile-file full-file-name)))) (defun oni:css-mode-func () "Function for `css-mode-hook'." (local-set-key "\C-j" 'oni:newline-and-indent) (rainbow-mode)) (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:diary-display-func () "Function for `diary-display-hook'." (diary-fancy-display)) (defun oni:emacs-lisp-mode-func () "Function for `emacs-lisp-mode-hook'." (eldoc-mode)) (defun oni:emacs-startup-func () "Function for `emacs-init-hook'." (require 'auto-complete-config) (ac-config-default)) (defun oni:go-mode-func () "Function for `go-mode-hook'." (setq indent-tabs-mode nil) (local-set-key "\C-j" 'oni:newline-and-indent)) (defun oni:gtags-mode-func () "Function for `gtags-mode-hook'." (local-set-key "\M-," 'gtags-find-tag) (local-set-key "\M-." 'gtags-find-rtag)) (defun oni:haskell-mode-func () "Function for `haskell-mode-hook'." (turn-on-haskell-indentation)) (defun oni:html-mode-func () "Function for `html-mode-hook'." (yas-minor-mode) (fci-mode) (flycheck-mode)) (defun oni:indent-shift-left (start end &optional count) "Rigidly indent region. Region is from START to END. Move COUNT number of spaces if it is non-nil otherwise use `tab-width'." (interactive (if mark-active (list (region-beginning) (region-end) current-prefix-arg) (list (line-beginning-position) (line-end-position) current-prefix-arg))) (if count (setq count (prefix-numeric-value count)) (setq count tab-width)) (when (> count 0) (let ((deactivate-mark nil)) (save-excursion (goto-char start) (while (< (point) end) (if (and (< (current-indentation) count) (not (looking-at "[ \t]*$"))) (error "Can't shift all lines enough")) (forward-line)) (indent-rigidly start end (- count)))))) (defun oni:indent-shift-right (start end &optional count) "Indent region between START and END rigidly to the right. If COUNT has been specified indent by that much, otherwise look at `tab-width'." (interactive (if mark-active (list (region-beginning) (region-end) current-prefix-arg) (list (line-beginning-position) (line-end-position) current-prefix-arg))) (let ((deactivate-mark nil)) (if count (setq count (prefix-numeric-value count)) (setq count tab-width)) (indent-rigidly start end count))) (defun oni:jabber-alert-message-func (from buffer text title) (notifications-notify :title title :body text)) (defun oni:jabber-chat-mode-func () "Function for `jabber-chat-mode-hook'." (visual-line-mode)) (defun oni:jabber-roster-mode-func () "Function for `jabber-roster-mode-hook'." (setq mode-line-format (list (propertize " %m" 'face 'mode-line-buffer-id)))) (defun oni:java-mode-func () "Function for `java-mode-hook'." (local-set-key "\C-j" 'oni:newline-and-indent)) (defun oni:js-mode-func () "Function for `js-mode-hook'." (rainbow-delimiters-mode) (local-set-key "\C-j" 'oni:newline-and-indent)) (defun oni:js2-mode-func () "Function for `js2-mode-hook'." (oni:prog-mode-func) (oni:js-mode-func) (local-set-key (kbd "") #'slime-js-reload) (slime-js-minor-mode)) (defun oni:kill-region-or-backward-char () "Either `kill-region' or `backward-delete-char-untabify'." (interactive) (if (region-active-p) (kill-region (region-beginning) (region-end)) (backward-delete-char-untabify 1))) (defun oni:kill-region-or-forward-char () "Either `kill-region' or `delete-forward-char'." (interactive) (if (region-active-p) (kill-region (region-beginning) (region-end)) (delete-forward-char 1))) (defun oni:kill-region-or-line () "Either `kill-region' or `kill-line'." (interactive) (if (region-active-p) (kill-region (region-beginning) (region-end)) (kill-line))) (defun oni:lua-mode-func() "Function for `lua-mode-hook'." (local-unset-key (kbd ")")) (local-unset-key (kbd "]")) (local-unset-key (kbd "}")) (flycheck-mode)) (defun oni:magit-log-edit-mode-func () "Function for `magit-log-edit-mode-hook'." (auto-fill-mode) (font-lock-add-keywords nil '(("\\`\\(.\\{,50\\}\\)\\(.*\\)\n?\\(.*\\)$" (1 'git-commit-summary-face) (2 'git-commit-overlong-summary-face) (3 'git-commit-nonempty-second-line-face)) ("`\\([^']+\\)'" 1 font-lock-constant-face)) t)) (defun oni:markdown-mode-func () "Function for `markdown-mode-hook'." (setq-local comment-auto-fill-only-comments nil) (setq-local whitespace-style '(face trailing)) (auto-fill-mode) (whitespace-mode)) (defun oni:message-mode-func () "Function for `message-mode-hook'." (setq-local comment-auto-fill-only-comments nil) (auto-fill-mode) (flyspell-mode)) (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:move-beginning-of-dwim () "Move to beginning of line either after indentation or before." (interactive) (let ((start (point))) (back-to-indentation) (if (= start (point)) (beginning-of-line)))) (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 (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:newline-and-indent () "`newline-and-indent', but with a twist. When dealing with braces, add another line and indent that too." (interactive) (if (and (not (or (= (point) (point-max)) (= (point) (point-min)))) (or (and (char-equal (char-before) ?{) (char-equal (char-after) ?})) (and (char-equal (char-before) ?\() (char-equal (char-after) ?\))))) (save-excursion (newline-and-indent))) (newline-and-indent)) (defun oni:org-mode-func () "Function for `org-mode-hook'." (auto-fill-mode) (yas-minor-mode) (set (make-local-variable 'comment-auto-fill-only-comments) nil)) (defun oni:php-mode-func () "Function for `php-mode-hook'." (local-set-key "\C-j" 'oni:newline-and-indent) (c-set-offset 'arglist-intro '+) (c-set-offset 'arglist-close '0) (rainbow-delimiters-mode) (setq-local fci-rule-column 80) (flycheck-mode)) (defun oni:prog-mode-func () "Function for `prog-mode-hook'." (rainbow-delimiters-mode) (fci-mode) (yas-minor-mode) (auto-fill-mode)) (defun oni:python-mode-func () "Function for `python-mode-hook'." (flycheck-mode) (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) (rainbow-delimiters-mode) (setq fci-rule-column 79 fill-column 72) (setq-local whitespace-style '(tab-mark)) (fci-mode) (whitespace-mode)) (defun oni:rainbow-mode-init () "Initialization function for rainbow-mode." (diminish 'rainbow-mode)) (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-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: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:rst-mode-func () "Function for `rst-mode-hook'." (auto-fill-mode)) (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: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:smex-init () "Initialization function for smex." (global-set-key (kbd "M-x") 'smex) (global-set-key (kbd "C-M-x") 'smex-major-mode-commands)) (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:term-mode-func () "Function for `term-mode-hook'." (setq truncate-lines nil)) (defun oni:texinfo-mode-func () "Function for `texinfo-mode-hook'." (setq-local comment-auto-fill-only-comments nil) (auto-fill-mode)) (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)) (defun oni:yasnippet-init () "Initialization function for yasnippet." (diminish 'yas-minor-mode)) (defvar oni:auto-save-name-transforms `((".*" ,temporary-file-directory t)) "Place all auto-save files in `temporary-file-directory'.") (defvar oni:backup-directory-alist `((".*" . ,temporary-file-directory)) "Palce all backup files in `temporary-file-directory'.") (defvar oni:mailbox-map '("top" ("menu" ("ryulash.org" . "ryuslash") ("ninthfloor" . "ninthfloor") ("gmail" . "gmail") ("aethon" . "aethon"))) "A mailbox map for use with `tmm-prompt'.") (provide 'oni) ;;; oni.el ends here