From 17153036984ee29eaab904bc7e4e6c1bada53f43 Mon Sep 17 00:00:00 2001 From: Levi Olson Date: Mon, 11 Mar 2019 11:25:09 -0500 Subject: [PATCH] Elfeed; Jabber; Hyperspace; Updates to Mu4e; Cleanup unused packages --- init.el | 778 +++++++++++++++++++++++++++++---- init.elc | Bin 27589 -> 44276 bytes init.org | 961 ++++++++++++++++++++++++++++++++++------- scripts/expand-mime.sh | 27 ++ 4 files changed, 1502 insertions(+), 264 deletions(-) create mode 100644 scripts/expand-mime.sh diff --git a/init.el b/init.el index 6097bc7..d8bf476 100644 --- a/init.el +++ b/init.el @@ -33,29 +33,30 @@ doom-themes ein eldoc-eval + elfeed + elfeed-org elpy expand-region fic-mode + flycheck gitignore-mode go-mode go-playground gorepl-mode - flycheck iedit ivy ivy-hydra + jabber json-mode magit material-theme multiple-cursors - nnir-est projectile - py-autopep8 rainbow-delimiters shrink-path tide typescript-mode - use-package + ;; use-package web-mode which-key)) @@ -127,7 +128,7 @@ (put 'upcase-region 'disabled nil) (put 'downcase-region 'disabled nil) -(setq inhibit-splash-screen nil +(setq inhibit-splash-screen t fancy-splash-image "~/.emacs.d/public/emacs-logo.png" fancy-splash-image-file "~/.emacs.d/public/emacs-logo.png") @@ -142,7 +143,8 @@ (menu-bar-mode 0) (scroll-bar-mode 0) (tool-bar-mode 0) - +(setq auth-sources '("~/.authinfo.gpg")) +(set-default 'truncate-lines t) ;; (load-theme 'doom-city-lights t) ;; (load-theme 'doom-dracula t) @@ -239,6 +241,251 @@ (provide 'openhab-mode) +;;; hyperspace.el --- Get there from here -*- lexical-binding: t; -*- + +;; Copyright (C) 2017-2019 Ian Eure + +;; Author: Ian Eure +;; URL: https://github.com/ieure/hyperspace-el +;; Version: 0.8.4 +;; Package-Requires: ((emacs "25") (s "1.12.0")) +;; Keywords: tools, convenience + +;; 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: + +;; Hyperspace is a way to get nearly anywhere from wherever you are, +;; whether that's within Emacs or on the web. It's somewhere in +;; between Quicksilver and keyword URLs, giving you a single, +;; consistent interface to get directly where you want to go. It’s +;; for things that you use often, but not often enough to justify a +;; dedicated binding. +;; +;; When you enter Hyperspace, it prompts you where to go: +;; +;; HS: +;; +;; This prompt expects a keyword and a query. The keyword picks where +;; you want to go, and the remainder of the input is an optional +;; argument which can be used to further search or direct you within +;; that space. +;; +;; Some concrete examples: +;; +;; | *If you enter* | *then Hyperspace* | +;; |------------------+----------------------------------------------------------| +;; | "el" | opens info node "(elisp)Top" | +;; | "el eval-region" | searches for "eval-region" in the elisp Info index | +;; | "bb" | shows all BBDB entries | +;; | "bb kenneth" | shows all BBDB entries with a name matching "kenneth" | +;; | "ddg foo" | searches DuckDuckGo for "foo" using browse-url | +;; | "wp foo" | searches Wikipedia for "foo" using browse-url | +;; + +;;; Code: + +(require 'subr-x) +(require 's) + +;; Action helpers + +(defun hyperspace-action->browse-url-pattern (pattern query) + "Browse a URL former from PATTERN and QUERY." + (browse-url (format pattern query))) + +(defun hyperspace-action->info (node &optional query) + "Open an Info buffer for NODE. + + If QUERY is present, look it up in the index." + (info node) + (when query + (Info-index query))) + +;; Package definitions + +(defvar hyperspace-history nil + "History of Hyperspace actions.") + +(defgroup hyperspace nil + "Getting there from here" + :prefix "hyperspace-" + :group 'applications) + +(defcustom hyperspace-actions + '(("ddg" . "https://duckduckgo.com/?q=%s") + ("dis" . "https://duckduckgo.com/?q=%s&iax=images&ia=images") + ("wp" . "https://en.wikipedia.org/wiki/%s") + ("g" . "https://www.google.com/search?q=%s") + ("gi" . "https://www.google.com/search?tbm=isch&q=%s") + ("gm" . "https://www.google.com/maps/search/%s") + ("yt" . "https://www.youtube.com/results?search_query=%s") + ("clp" . "https://portland.craigslist.org/search/sss?query=%s") + ("eb" . "https://www.ebay.com/sch/i.html?_nkw=%s") + ("nf" . "https://www.netflix.com/search?q=%s") + ("sh" . (lambda (query) (interactive) (shell-command query))) + ("imdb" . "https://www.imdb.com/find?q=peter+jackson&s=all") + ("bb" . bbdb-search-name) + ("el" . (apply-partially #'hyperspace-action->info "(elisp)Top")) + ("av" . apropos-variable) + ("ac" . apropos-command) + ("af" . (lambda (query) (apropos-command query t)))) + + "Where Hyperspace should send you. + + Hyperspace actions are a cons of (KEYWORD . DISPATCHER). When + Hyperspace is invoked, the keyword is extracted from the user + input and looked up in this alist. The remainder of the + string is passed to the dispatcher as its QUERY argument. + + DISPATCHER can be a function which performs the action. + + DISPATCHER can also be an expression which returns a function + to perform the action. + + Finally, DISPATCHER can be a string with a URL pattern containing + '%s'. The '%s' will be replaced with the query, and the URL browsed." + + :group 'hyperspace + :type '(alist :key-type (string :tag "Keyword") + :value-type (choice + (function :tag "Function") + (string :tag "URL Pattern") + (sexp :tag "Expression")))) + +(defcustom hyperspace-default-action + (caar hyperspace-actions) + "A place to go if you don't specify one." + :group 'hyperspace + :type `(radio + ,@(mapcar (lambda (action) (list 'const (car action))) hyperspace-actions))) + +(defcustom hyperspace-max-region-size 256 + "Maximum size of a region to consider for a Hyperspace query. + + If the region is active when Hyperspace is invoked, it's used + as the default query, unless it's more than this number of + characters." + :group 'hyperspace + :type 'integer) + + + +(defun hyperspace--cleanup (text) + "Clean TEXT so it can be used for a Hyperspace query." + (save-match-data + (string-trim + (replace-regexp-in-string (rx (1+ (or blank "\n"))) " " text)))) + +(defun hyperspace--initial-text () + "Return the initial text. + + This is whatever's in the active region, but cleaned up." + (when (use-region-p) + (let* ((start (region-beginning)) + (end (region-end)) + (size (- end start))) + (when (<= size hyperspace-max-region-size) + (hyperspace--cleanup + (buffer-substring-no-properties start end)))))) + +(defun hyperspace--initial (initial-text) + "Turn INITIAL-TEXT into INITIAL-CONTENTS for reading." + (when initial-text (cons (concat " " initial-text) 1))) + +(defun hyperspace--process-input (text) + "Process TEXT into an actionable keyword and query." + (let ((kw-text (s-split-up-to "\\s-+" text 1))) + (if (assoc (car kw-text) hyperspace-actions) + kw-text + (list hyperspace-default-action text)))) + +(defun hyperspace--query () + "Ask the user for the Hyperspace action and query. + + Returns (KEYWORD . QUERY). + + If the region isn't active, the user is prompted for the + action and query. + + If the region is active, its text is used as the initial value + for the query, and the user enters the action. + + If a prefix argument is specified and the region is active, + `HYPERSPACE-DEFAULT-ACTION' is chosen without prompting." + + (let ((initial (hyperspace--initial-text))) + (if (and initial current-prefix-arg) + (list hyperspace-default-action initial) + (hyperspace--process-input + (read-from-minibuffer "HS: " (hyperspace--initial initial) nil nil + 'hyperspace-history))))) + +(defun hyperspace--evalable-p (form) + "Can FORM be evaluated?" + (and (listp form) + (or (functionp (car form)) + (subrp (car form))))) + +(defun hyperspace--dispatch (action &optional query) + "Execute ACTION, with optional QUERY argument." + (pcase action + ((pred functionp) (funcall action query)) + ((pred hyperspace--evalable-p) (funcall (eval action) query)) + ((pred stringp) (hyperspace-action->browse-url-pattern action query)) + (_ (error "Unknown action")))) + +;;;###autoload +(defun hyperspace (keyword &optional query) + "Execute action for keyword KEYWORD, with optional QUERY." + (interactive (hyperspace--query)) + (let ((action (cdr (assoc keyword hyperspace-actions)))) + (hyperspace--dispatch (or action hyperspace-default-action) query))) + +;;;###autoload +(defun hyperspace-enter (&optional query) + "Enter Hyperspace, sending QUERY to the default action. + + If the region is active, use that as the query for + ‘hyperspace-default-action’. Otherwise, prompt the user." + (interactive (list (hyperspace--initial-text))) + (hyperspace + hyperspace-default-action + (or query + (read-from-minibuffer + (format "HS: %s " hyperspace-default-action) nil nil + 'hyperspace-history)))) + +;; Minor mode + +(defvar hyperspace-minor-mode-map + (let ((kmap (make-sparse-keymap))) + (define-key kmap (kbd "H-SPC") #'hyperspace) + (define-key kmap (kbd "") #'hyperspace-enter) + kmap)) + +;;;###autoload +(define-minor-mode hyperspace-minor-mode + "Global (universal) minor mode to jump from here to there." + nil nil hyperspace-minor-mode-map + :group 'hyperspace + :global t) + +(provide 'hyperspace) + +;;; hyperspace.el ends here + (require 'which-key) (which-key-setup-minibuffer) (which-key-mode) @@ -267,7 +514,7 @@ (defun dired-mode-setup () "Will run as hook for `dired-mode'." - (dired-hide-details-mode 1)) + (dired-hide-details-mode nil)) (add-hook 'dired-mode-hook 'dired-mode-setup) (require 'ivy-hydra) @@ -312,76 +559,111 @@ (require 'mu4e) ;; default -(setq mu4e-maildir "~/Mail" - mu4e-mu-binary "/usr/local/bin/mu" - mu4e-get-mail-command "offlineimap" ;; Allow updating with the "U" command - mu4e-sent-messages-behavior 'delete ;; Delete sent messages - mu4e-view-show-images t ;; attempt to show images - mu4e-view-image-max-width 400 ;; max image size - message-kill-buffer-on-exit t ;; don't keep messages around - mu4e-use-fancy-chars t ;; use 'fancy' chars - mu4e-update-interval 300 ;; 5 mins +(setq mu4e-maildir "~/Mail" + mu4e-mu-binary "/usr/local/bin/mu" + mu4e-change-filenames-when-moving t ;; Rename files when moving (required by mbsync) + mu4e-compose-in-new-frame t ;; New compose gets new frame + mu4e-context-policy 'pick-first + mu4e-get-mail-command "mbsync -a" ;; MBSYNC is the mail cmd + mu4e-html2text-command "/usr/local/bin/w3m -T text/html" ;; HTML to text command + mu4e-sent-messages-behavior 'delete ;; Delete sent messages + mu4e-update-interval 300 ;; 5 mins + mu4e-use-fancy-chars t ;; use 'fancy' chars + mu4e-user-mail-address-list '("lolson@eaglecrk.com" + "lolson@vlocity.com" + "olson.levi@gmail.com") + mu4e-view-show-images t ;; attempt to show images + mu4e-view-image-max-width 400 ;; max image size + + message-citation-line-format "On %a %d %b %Y at %R, %f wrote:\n" ;; customize the reply-quote-string + message-citation-line-function 'message-insert-formatted-citation-line ;; choose to use the formatted string + message-kill-buffer-on-exit t ;; don't keep messages around + + send-mail-function 'smtpmail-send-it ;; Default email send function + smtpmail-default-smtp-server "smtp.gmail.com" + smtpmail-smtp-service 587 ) +(defun mdmail-send-buffer () + (interactive) + (shell-command-on-region (point-min) (point-max) "mdmail")) + (setq mu4e-contexts - `( ,(make-mu4e-context - :name "Vlocity" - :enter-func (lambda () (mu4e-message "Entering Vlocity")) - :leave-func (lambda () (mu4e-message "Leaving Vlocity")) - ;; we match based on the contact-fields of the message - :match-func (lambda (msg) - (when msg - (string= (mu4e-message-field msg :maildir) "/Vlocity"))) - :vars '( ( user-mail-address . "lolson@vlocity.com" ) - ( smtpmail-mail-address . "lolson@vlocity.com" ) - ( user-full-name . "Levi Olson" ) - ( mu4e-compose-signature . - (concat - "--\n" - "Levi Olson\n" - "Senior UI Developer")) - ( mu4e-sent-folder . "/Vlocity/[Gmail].Sent Mail" ) - ( mu4e-drafts-folder . "/Vlocity/[Gmail].Drafts" ) - ( mu4e-trash-folder . "/Vlocity/[Gmail].Trash" ) - ( mu4e-maildir-shortcuts . (("/Vlocity/INBOX" . ?i) - ("/Vlocity/[Gmail].Sent Mail" . ?s) - ("/Vlocity/[Gmail].Trash" . ?t) - ("/Vlocity/[Gmail].All Mail" . ?a))))) - ,(make-mu4e-context - :name "Gmail" - :enter-func (lambda () (mu4e-message "Entering Gmail")) - :leave-func (lambda () (mu4e-message "Leaving Gmail")) - ;; this matches maildir /Arkham and its sub-directories - :match-func (lambda (msg) - (when msg - (string= (mu4e-message-field msg :maildir) "/Gmail"))) - :vars '( ( user-mail-address . "olson.levi@gmail.com" ) - ( smtpmail-mail-address . "olson.levi@gmail.com" ) - ( user-full-name . "Levi Olson" ) - ( mu4e-compose-signature . - (concat - "--\n" - "Levi\n")) - ( mu4e-sent-folder . "/Gmail/[Gmail].Sent Mail" ) - ( mu4e-drafts-folder . "/Gmail/[Gmail].Drafts" ) - ( mu4e-trash-folder . "/Gmail/[Gmail].Trash" ) - ( mu4e-maildir-shortcuts . (("/Gmail/INBOX" . ?i) - ("/Gmail/[Gmail].Sent Mail" . ?s) - ("/Gmail/[Gmail].Trash" . ?t) - ("/Gmail/[Gmail].All Mail" . ?a)) - ))))) - -;; (defcustom smtpmail-smtp-user nil -;; "User name to use when looking up credentials in the authinfo file. -;; If non-nil, only consider credentials for the specified user." -;; :version "24.1" -;; :type '(choice (const nil) string) -;; :group 'smtpmail) - - - -;; How to handle HTML emails -;; (setq mu4e-html2text-command "textutil -stdin -format html -convert txt -stdout") + `( + ;; ,(make-mu4e-context + ;; :name "Vlocity" + ;; :enter-func (lambda () (mu4e-message "Entering Vlocity")) + ;; :leave-func (lambda () (mu4e-message "Leaving Vlocity")) + ;; ;; we match based on the contact-fields of the message + ;; :match-func (lambda (msg) + ;; (when msg + ;; (string= (mu4e-message-field msg :maildir) "/Vlocity"))) + ;; :vars '( ( user-mail-address . "lolson@vlocity.com" ) + ;; ( smtpmail-mail-address . "lolson@vlocity.com" ) + ;; ( smtpmail-smtp-user . "lolson@vlocity.com" ) + ;; ( smtpmail-smtp-server . "smtp.gmail.com" ) + ;; ( user-full-name . "Levi Olson" ) + ;; ( mu4e-compose-signature . + ;; (concat + ;; "Levi Olson\n" + ;; "Senior UI Developer")) + ;; ( mu4e-sent-folder . "/Vlocity/[Gmail].Sent Mail" ) + ;; ( mu4e-drafts-folder . "/Vlocity/[Gmail].Drafts" ) + ;; ( mu4e-trash-folder . "/Vlocity/[Gmail].Trash" ) + ;; ( mu4e-maildir-shortcuts . (("/Vlocity/INBOX" . ?i) + ;; ("/Vlocity/[Gmail].Sent Mail" . ?s) + ;; ("/Vlocity/[Gmail].Trash" . ?t) + ;; ("/Vlocity/[Gmail].All Mail" . ?a))))) + ,(make-mu4e-context + :name "EagleCreek" + :enter-func (lambda () (mu4e-message "Entering EagleCreek")) + :leave-func (lambda () (mu4e-message "Leaving EagleCreek")) + ;; we match based on the contact-fields of the message + :match-func (lambda (msg) + (when msg + (string= (mu4e-message-field msg :maildir) "/eaglecrk"))) + :vars '( ( user-mail-address . "lolson@eaglecrk.com" ) + ( smtpmail-mail-address . "lolson@eaglecrk.com" ) + ( smtpmail-smtp-user . "lolson@eaglecrk.com" ) + ( smtpmail-smtp-server . "smtp.office365.com" ) + ( user-full-name . "Levi Olson" ) + ;; ( mu4e-compose-signature . + ;; (concat + ;; "Levi Olson\n" + ;; "Eagle Creek Software Services\n" + ;; "Senior Application Developer Consultant\n")) + ( mu4e-sent-folder . "/eaglecrk/Sent Items" ) + ( mu4e-drafts-folder . "/eaglecrk/Drafts" ) + ( mu4e-trash-folder . "/eaglecrk/Deleted Items" ) + ( mu4e-maildir-shortcuts . (("/eaglecrk/Inbox" . ?i) + ("/eaglecrk/Sent Items" . ?s) + ("/eaglecrk/Deleted Items" . ?t) + ("/eaglecrk/Archive" . ?a))))) + ;; ,(make-mu4e-context + ;; :name "Gmail" + ;; :enter-func (lambda () (mu4e-message "Entering Gmail")) + ;; :leave-func (lambda () (mu4e-message "Leaving Gmail")) + ;; ;; this matches maildir /Arkham and its sub-directories + ;; :match-func (lambda (msg) + ;; (when msg + ;; (string= (mu4e-message-field msg :maildir) "/Gmail"))) + ;; :vars '( ( user-mail-address . "olson.levi@gmail.com" ) + ;; ( smtpmail-mail-address . "olson.levi@gmail.com" ) + ;; ( smtpmail-smtp-user . "olson.levi@gmail.com" ) + ;; ( smtpmail-smtp-server . "smtp.gmail.com" ) + ;; ( user-full-name . "Levi Olson" ) + ;; ( mu4e-compose-signature . + ;; (concat + ;; "Levi\n")) + ;; ( mu4e-sent-folder . "/Gmail/[Gmail].Sent Mail" ) + ;; ( mu4e-drafts-folder . "/Gmail/[Gmail].Drafts" ) + ;; ( mu4e-trash-folder . "/Gmail/[Gmail].Trash" ) + ;; ( mu4e-maildir-shortcuts . (("/Gmail/INBOX" . ?i) + ;; ("/Gmail/[Gmail].Sent Mail" . ?s) + ;; ("/Gmail/[Gmail].Trash" . ?t) + ;; ("/Gmail/[Gmail].All Mail" . ?a)) + ;; ))) + )) ;; Add option to view HTML in browser (add-to-list 'mu4e-headers-actions @@ -389,6 +671,41 @@ (add-to-list 'mu4e-view-actions '("in browser" . mu4e-action-view-in-browser) t) + + +(defun my-message-current-line-cited-p () + "Indicate whether the line at point is a cited line." + (save-match-data + (string-match (concat "^" message-cite-prefix-regexp) + (buffer-substring (line-beginning-position) (line-end-position))))) + +(defun my-message-says-attachment-p () + "Return t if the message suggests there can be an attachment." + (save-excursion + (goto-char (point-min)) + (save-match-data + (let (search-result) + (while + (and (setq search-result (re-search-forward "\\(attach\\|pdf\\|file\\)" nil t)) + (my-message-current-line-cited-p))) + search-result)))) + +(defun my-message-has-attachment-p () + "Return t if the message has an attachment." + (save-excursion + (goto-char (point-min)) + (save-match-data + (re-search-forward "<#part" nil t)))) + +(defun my-message-pre-send-check-attachment () + (when (and (my-message-says-attachment-p) + (not (my-message-has-attachment-p))) + (unless + (y-or-n-p "No attachment. Send anyway?") + (error "It seems that an attachment is needed, but none was found. Aborting sending.")))) + +(add-hook 'message-send-hook 'my-message-pre-send-check-attachment) + (require 'projectile) (require 'counsel-projectile) @@ -398,6 +715,219 @@ projectile-completion-system 'ivy) (counsel-projectile-mode) +;;; notify.el --- notification front-end + +;; Copyright (C) 2008 Mark A. Hershberger + +;; Original Author: Mark A. Hershberger +;; Modified by Andrey Kotlarski +;; Modified by Andrew Gwozdziewycz +;; Modified by Aidan Gauland October 2011 +;; Modified by Olivier Sirven November 2013 +;; Keywords: extensions, convenience, lisp + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; This provides a single function, `notify', that will produce a notify +;; pop-up via D-Bus, libnotify, simple message or growl. +;; To use, just put (autoload 'notify "notify" "Notify TITLE, BODY.") +;; in your init file. You may override default chosen notification +;; method by assigning `notify-method' to one of 'notify-via-dbus +;; 'notify-via-libnotify or 'notify-via-message +;;; Code: + +(defvar notify-defaults (list :app "Emacs" :icon "emacs" :timeout 5000 + :urgency "low" + :category "emacs.message") + "Notification settings' defaults. +May be overridden with key-value additional arguments to `notify'.") +(defvar notify-delay '(0 5 0) + "Minimum time allowed between notifications in time format.") +(defvar notify-last-notification '(0 0 0) "Time of last notification.") +(defvar notify-method 'notify-via-growl "Notification method among +'notify-via-dbus, 'notify-via-libnotify, 'notify-via-message or +'notify-via-growl") + +;; determine notification method unless already set +;; prefer growl > D-Bus > libnotify > message +(cond + ((null notify-method) + (setq notify-method + (cond + ((executable-find "growlnotify") 'notify-via-growl) + ((and (require 'dbus nil t) + (dbus-ping :session "org.freedesktop.Notifications")) + (defvar notify-id 0 "Current D-Bus notification id.") + 'notify-via-dbus) + ((executable-find "notify-send") 'notify-via-libnotify) + (t 'notify-via-message)))) + ((eq notify-method 'notify-via-dbus) ;housekeeping for pre-chosen DBus + (if (and (require 'dbus nil t) + (dbus-ping :session "org.freedesktop.Notifications")) + (defvar notify-id 0 "Current D-Bus notification id.") + (setq notify-method (if (executable-find "notify-send") + 'notify-via-libnotify + 'notify-via-message)))) + ((and (eq notify-method 'notify-via-libnotify) + (not (executable-find "notify-send"))) ;housekeeping for pre-chosen libnotify + (setq notify-method + (if (and (require 'dbus nil t) + (dbus-ping :session "org.freedesktop.Notifications")) + (progn + (defvar notify-id 0 "Current D-Bus notification id.") + 'notify-via-dbus) + 'notify-via-message))) + ((and (eq notify-method 'notify-via-growl) + (not (executable-find "growlnotify"))) + (setq notify-method 'notify-via-message))) + +(defun notify-via-dbus (title body) + "Send notification with TITLE, BODY `D-Bus'." + (dbus-call-method :session "org.freedesktop.Notifications" + "/org/freedesktop/Notifications" + "org.freedesktop.Notifications" "Notify" + (get 'notify-defaults :app) + (setq notify-id (+ notify-id 1)) + (get 'notify-defaults :icon) title body '(:array) + '(:array :signature "{sv}") ':int32 + (get 'notify-defaults :timeout))) + +(defun notify-via-libnotify (title body) + "Notify with TITLE, BODY via `libnotify'." + (call-process "notify-send" nil 0 nil + title body "-t" + (number-to-string (get 'notify-defaults :timeout)) + "-i" (get 'notify-defaults :icon) + "-u" (get 'notify-defaults :urgency) + "-c" (get 'notify-defaults :category))) + +(defun notify-via-message (title body) + "Notify TITLE, BODY with a simple message." + (message "%s: %s" title body)) + +(defun notify-via-growl (title body) + "Notify TITLE, BODY with a growl" + (call-process "growlnotify" nil 0 nil + "-a" (get 'notify-defaults :app) + "-n" (get 'notify-defaults :category) + "-t" (notify-via-growl-stringify title) + "-m" (notify-via-growl-stringify body))) + +(defun notify-via-growl-stringify (thing) + (cond ((null thing) "") + ((stringp thing) thing) + (t (format "%s" thing)))) + +(defun keywords-to-properties (symbol args &optional defaults) + "Add to SYMBOL's property list key-values from ARGS and DEFAULTS." + (when (consp defaults) + (keywords-to-properties symbol defaults)) + (while args + (put symbol (car args) (cadr args)) + (setq args (cddr args)))) + + +;;;###autoload +(defun notify (title body &rest args) + "Notify TITLE, BODY via `notify-method'. +ARGS may be amongst :timeout, :icon, :urgency, :app and :category." + (when (time-less-p notify-delay + (time-since notify-last-notification)) + (or (eq notify-method 'notify-via-message) + (keywords-to-properties 'notify-defaults args + notify-defaults)) + (setq notify-last-notification (current-time)) + (funcall notify-method title body))) + +(provide 'notify) + +;;; notify.el ends here + +(require 'jabber) + +(setq jabber-history-enabled t + jabber-use-global-history nil + jabber-backlog-number 40 + jabber-backlog-days 30 + jabber-alert-presence-message-function (lambda (_who _oldstatus _newstatus _statustext) nil) + ) + +(setq jabber-account-list '( + ;; ("olson.levi@gmail.com" + ;; (:network-server . "talk.google.com") + ;; (:connection-type . ssl)) + ("lolson@vlocity.com" + (:network-server . "talk.google.com") + (:connection-type . ssl)) + )) + +(defvar my-chat-prompt "[%t] %n>\n" "Customized chat prompt") +(when (featurep 'jabber) + (setq + jabber-chat-foreign-prompt-format my-chat-prompt + jabber-chat-local-prompt-format my-chat-prompt + jabber-groupchat-prompt-format my-chat-prompt + jabber-muc-private-foreign-prompt-format "[%t] %g/%n>\n" + ) + ) + +(defun notify-jabber-notify (from buf text _proposed-alert) + "(jabber.el hook) Notify of new Jabber chat messages via notify.el" + (when (or jabber-message-alert-same-buffer + (not (memq (selected-window) (get-buffer-window-list buf)))) + (if (jabber-muc-sender-p from) + (notify (format "(PM) %s" + (jabber-jid-displayname (jabber-jid-user from))) + (format "%s: %s" (jabber-jid-resource from) text))) + (notify (format "%s" (jabber-jid-displayname from)) + text))) + +;; (add-hook 'jabber-alert-message-hooks 'notify-jabber-notify) + + +;; (require 'autosmiley) +;; (add-hook 'jabber-chat-mode-hook 'autosmiley-mode) + + +(defun jabber () + (interactive) + (jabber-connect-all) + (switch-to-buffer "*-jabber-roster-*")) + +(defun hyperspace-action->mu4e (&optional query) + "Search mu4e with QUERY. + + If QUERY is unspecified, use the first bookmark in variable + ‘mu4e-bookmarks’ and update mail and index." + + (mu4e-headers-search (or query (caar mu4e-bookmarks))) + (unless query + (mu4e-update-mail-and-index nil))) +(add-to-list 'hyperspace-actions '("m4" . hyperspace-action->mu4e)) + +(defun hyperspace-action->elfeed (&optional query) + "Load elfeed, optionally searching for QUERY." + (elfeed) + (if query + (elfeed-search-set-filter query) + (elfeed-search-fetch nil))) +(add-to-list 'hyperspace-actions '("lf" . hyperspace-action->elfeed)) + (require 'rainbow-delimiters) (global-flycheck-mode) @@ -525,6 +1055,29 @@ (shell . t) (emacs-lisp . t))) +(setq org-todo-keywords + '((sequence "TODO(t)" "|" "DONE(d)") + (sequence "BUG(b)" "|" "INPROGRESS(i)" "FIXED(f)") + (sequence "|" "CANCELED(c)") + (sequence "|" "NEEDCLARIFICATION(n)") + (sequence "|" "PROVIDEUPDATE(p)") + (sequence "|" "WAITING(w)") + )) + +(setq org-agenda-files + '("~/Dropbox/Org/todo.org" "~/Dropbox/Org/archive.org")) +(setq org-refile-targets + '((nil :maxlevel . 1) + (org-agenda-files :maxlevel . 1))) + +(add-hook 'focus-in-hook + (lambda () (progn + (setq org-tags-column (- 5 (frame-width)))) (org-align-all-tags))) + +(add-hook 'focus-out-hook + (lambda () (progn + (setq org-tags-column (- 5 (frame-width)))) (org-align-all-tags))) + (defvar org-src-tab-acts-natively) (setq org-src-tab-acts-natively t) ;; (setenv "NODE_PATH" @@ -532,7 +1085,7 @@ (defvar org-confirm-babel-evaluate) -(defun my-org-confirm-babel-evaluate (lang body) +(defun my-org-confirm-babel-evaluate (lang _body) "Execute certain languages without confirming. Takes LANG to allow and BODY to execute." (not (or (string= lang "js") @@ -553,11 +1106,38 @@ "\n" "#+END_SRC"))) +;;store org-mode links to messages +(require 'org-mu4e) +;;store link to message if in header view, not to header query +(setq org-mu4e-link-query-in-headers-mode nil) + +(setq org-capture-templates + '(("t" "todo" entry (file+headline "~/todo.org" "Tasks") + "* TODO [#A] %?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n%a\n"))) + +(elfeed-org) +(setq rmh-elfeed-org-files (list "~/Dropbox/Org/elfeed.org")) + +(defun leo/elfeed-search (arg) + "Search for ARG in feed." + (interactive) + (elfeed-search-set-filter arg)) + +(define-key elfeed-search-mode-map "a" (lambda () (interactive) (leo/elfeed-search ""))) +(define-key elfeed-search-mode-map "e" (lambda () (interactive) (leo/elfeed-search "+emacs"))) +(define-key elfeed-search-mode-map "d" (lambda () (interactive) (leo/elfeed-search "+daily"))) +(define-key elfeed-search-mode-map "x" (lambda () (interactive) (leo/elfeed-search "xkcd"))) + (defun find-user-init-file () "Edit the `~/.emacs.d/init.org' file." (interactive) (find-file "~/.emacs.d/init.org")) +(defun find-todo-file () + "Edit the `~/todo.org' file." + (interactive) + (find-file "~/Dropbox/Org/todo.org")) + (defun load-user-init-file () "LO: Reload the `~/.emacs.d/init.elc' file." (interactive) @@ -730,33 +1310,39 @@ (local-set-key (kbd "C-c C-c") 'compile)) (require 'company) -(add-hook 'comint-mode-hook (lambda () (local-set-key (kbd "C-l") 'clear-comint))) +(add-hook 'comint-mode-hook (lambda () (local-set-key (kbd "c-l") 'clear-comint))) (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode) (add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode) (add-hook 'c-mode-common-hook 'c-setup) (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode)) (defvar company-active-map (make-keymap) - "Company Mode keymap.") + "company mode keymap.") (defvar custom-bindings (make-keymap) - "A keymap of custom bindings.") - -(define-key global-map (kbd "M-p") 'jump-to-previous-like-this) -(define-key global-map (kbd "M-n") 'jump-to-next-like-this) -(define-key global-map (kbd "M-") 'switch-to-next-buffer) -(define-key global-map (kbd "M-")'delete-backward-to-boundary) -(define-key global-map (kbd "C-")'delete-backward-to-boundary) - -(global-set-key (kbd "C-S-") 'mc/mark-next-like-this) -(global-set-key (kbd "C->") 'mc/mark-next-like-this-symbol) -(global-set-key (kbd "C-S-") 'mc/mark-previous-like-this) -(global-set-key (kbd "C-<") 'mc/mark-previous-like-this) -(global-set-key (kbd "C-c C->") 'mc/mark-all-like-this) -(global-set-key "%" 'match-paren) -(global-set-key (kbd "C-x .") 'dash-at-point) -(global-set-key (kbd "C-x ,") 'dash-at-point-with-docset) -(global-set-key (kbd "C-s") (lambda () (interactive) (swiper (format "%s" (thing-at-point 'symbol))))) -(global-set-key (kbd "M-m") 'mu4e) + "a keymap of custom bindings.") + +(define-key custom-bindings (kbd "M-p") 'jump-to-previous-like-this) +(define-key custom-bindings (kbd "M-n") 'jump-to-next-like-this) +(define-key custom-bindings (kbd "M-") 'switch-to-next-buffer) +(define-key custom-bindings (kbd "M-")'delete-backward-to-boundary) +(define-key custom-bindings (kbd "C-")'delete-backward-to-boundary) +(define-key custom-bindings (kbd "C-}") 'mc/mark-next-like-this) +(define-key custom-bindings (kbd "C-)") 'mc/unmark-next-like-this) +(define-key custom-bindings (kbd "C-{") 'mc/mark-previous-like-this) +(define-key custom-bindings (kbd "C-(") 'mc/unmark-previous-like-this) +(define-key custom-bindings (kbd "C-'") 'mc-hide-unmatched-lines-mode) +(define-key custom-bindings (kbd "C-c 1") 'mc/insert-numbers) +(define-key custom-bindings (kbd "C-c s") 'mc/sort-regions) +(define-key custom-bindings "%" 'match-paren) +(define-key custom-bindings (kbd "C-x .") 'dash-at-point) +(define-key custom-bindings (kbd "C-x ,") 'dash-at-point-with-docset) +(define-key custom-bindings (kbd "C-s") (lambda () (interactive) (swiper (format "%s" (thing-at-point 'symbol))))) +(define-key custom-bindings (kbd "C-x C-l m") 'mu4e) +(define-key custom-bindings (kbd "C-x C-o t") 'find-todo-file) +(define-key custom-bindings (kbd "C-x C-l j") 'jabber) +(define-key custom-bindings (kbd "C-x C-l f") 'elfeed) +(define-key custom-bindings (kbd "C-x C-l a") 'org-agenda) +(define-key custom-bindings (kbd "M-SPC") #'hyperspace) ;; (dolist (n (number-sequence 1 9)) ;; (global-set-key (kbd (concat "M-" (int-to-string n))) ;; (lambda () (interactive) (switch-shell n)))) @@ -812,7 +1398,7 @@ ;; How tall the mode-line should be (only respected in GUI Emacs). -(setq doom-modeline-height 35) +(setq doom-modeline-height 30) ;; How wide the mode-line bar should be (only respected in GUI Emacs). (setq doom-modeline-bar-width 4) diff --git a/init.elc b/init.elc index 656cbdd646b9c3886ff0ea0033a63561624a2174..7952997ec6afc0f93527c40da355ce665995826c 100644 GIT binary patch literal 44276 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 aPC_X*f!LRI46;0kiccgDpaoxk_x}JmA#myd delta 4616 zcma)AU2q#$6_#u{Bu?TagHeY3-b!gFqFHC}-u;o1xY&u~SV^3u&3`Ntucei}@oLxA zu1%FfMJZ6|50JSX(lCW4FdYi%w9M=b%<#g%(1*Oi(4hp%Tb~#hVCeJ-2F|^!U1{YM z;u*>Je)pX3o_o&G-cS1AZqM8ACcim#X2~^NRB70H%`BseQ+B<&Y0tTPP}Quv9?~ln zqk>Eu9j@tR7vaK?R2b^--@ku9Ix}y&U}YKL1{}8Ip|WFpx@li4KP;eG16ht9^IkUU zuIbn=GW5D-8g=B&>o$_e5Pdwka}qf3;&P?9hVB&l;k0FdFOlrpVl6=oWJZ~d?6Zw3aF9({knOid$_ql@V@^)~#tH(wxB zl(y0T^sM%eZTweX55|5mol5qqMDb5;zMBM#JPW7^JBg&Vtq8k_N;pwbu$!ob16aml zYZ+shurS3iD&~ztD!$BsC}^zmBX5E&0GC=kO&GxpcN|;_&KsT;_JETUNe%a5$&s`Y zA<@nZArKFdI&BE>5IKeb4~g-RF(8s-L^2S`F(Mg= z|6FMH+NR~t@2mGD3H{>%Z#|wy655^Jg@%x((8n)t@f*W;dYgkR;!m)U(@EUexz)> zx0EIvNddPA#S92b&nIfDBuZF*RZNdFkMxleQE_?#-lbM&Fb6d(y(+g*6{1(6RaGr4 z0z)*Yx7NfVq(N*NxNA{@Il>I1N@-e267ggLu&A8^>_jI9HYXFz@>C*-9fps_0O(J) z{#c^f?l+j9=0;}{VN8M5*%k{+V`Ox$%jo>Qj4pH;U0h+*1j0+96^y46LC#=UDkaiI zw&8RleUyEo*!VN-oMF90l1?)4G7ozuF>tJuNgsneD-6{+IvbL=2+Hl0R1)dq&A7GE zWnB#e4tAf5Pv_%PGn{tj=}N>HM76-9vN#H#8G3v4?CLqg6Kc9?(Mw|wQtjYhc4VAt z)iQ0vtm%sxR52{WGmwX5QA9)(>CAZgW9KZVYPTrKrX4(+Zj z$-rUQo=|nH3S5gr?S1!79bp-q9s*wvO~NqpsL)1n)8STpk=x636trA!bq;0t zS;Nxz^I25*^>?DidGsu1gL+QP7|B z)INHEzH#&sT7PtA3sX>KXUTRAOK3|bq2^TRAIzWAx5x6daCmT#dotZr)$MXaD9`J4 zmp(PNbqy1W#uJ{4=lS!Kk7*q{4d#m>*>R!AWMOQ0bL=&4HBrA z@EOh#xO~Lu@?pXXheCr-q}D9}PdS3&>Dm@6p*N21Anp+Av(N#wOQzXUNx&h&T?-Gv zP`NOKvBo1DvHlNgbvaTAPbpOKhU=)dm04 z?wwwLps_jGhZRK@sXqMiLzpEEuHJUy#yVUBm}hSV9HbFo`sURP-TCBS>F=L_ zFVYX1-%4SXNdDKq@JHK0JbZjO$`5MYw!7y?YP8bi2VjgXQZD%LneN=?wK zaSXVWHW&8_%)wm;YX&qcm@YH}zi<1S?HRcH#6|`#{ z;sSOg(FuZ78|9+tU^je-!((x{DdNK%ACKcAKEmONZn((dquuZ^4j=D^L0m&XC%WNK zPiIRS|6(o7g#m| z2t=k+6J|}j!hS8Gx4!$v_LheWqIbb)ps7Q`X`$kL)}BVS@?P3}b!$#i`M9Rn7X%xw zKfy8=48eonQ3SV9n{}+fX=#zZ^6IV!C3zZ65u|EZ%ojRcb<2u8DY + ;; URL: https://github.com/ieure/hyperspace-el + ;; Version: 0.8.4 + ;; Package-Requires: ((emacs "25") (s "1.12.0")) + ;; Keywords: tools, convenience + + ;; 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: + + ;; Hyperspace is a way to get nearly anywhere from wherever you are, + ;; whether that's within Emacs or on the web. It's somewhere in + ;; between Quicksilver and keyword URLs, giving you a single, + ;; consistent interface to get directly where you want to go. It’s + ;; for things that you use often, but not often enough to justify a + ;; dedicated binding. + ;; + ;; When you enter Hyperspace, it prompts you where to go: + ;; + ;; HS: + ;; + ;; This prompt expects a keyword and a query. The keyword picks where + ;; you want to go, and the remainder of the input is an optional + ;; argument which can be used to further search or direct you within + ;; that space. + ;; + ;; Some concrete examples: + ;; + ;; | *If you enter* | *then Hyperspace* | + ;; |------------------+----------------------------------------------------------| + ;; | "el" | opens info node "(elisp)Top" | + ;; | "el eval-region" | searches for "eval-region" in the elisp Info index | + ;; | "bb" | shows all BBDB entries | + ;; | "bb kenneth" | shows all BBDB entries with a name matching "kenneth" | + ;; | "ddg foo" | searches DuckDuckGo for "foo" using browse-url | + ;; | "wp foo" | searches Wikipedia for "foo" using browse-url | + ;; + + ;;; Code: + + (require 'subr-x) + (require 's) + + ;; Action helpers + + (defun hyperspace-action->browse-url-pattern (pattern query) + "Browse a URL former from PATTERN and QUERY." + (browse-url (format pattern query))) + + (defun hyperspace-action->info (node &optional query) + "Open an Info buffer for NODE. + + If QUERY is present, look it up in the index." + (info node) + (when query + (Info-index query))) + + ;; Package definitions + + (defvar hyperspace-history nil + "History of Hyperspace actions.") + + (defgroup hyperspace nil + "Getting there from here" + :prefix "hyperspace-" + :group 'applications) + + (defcustom hyperspace-actions + '(("ddg" . "https://duckduckgo.com/?q=%s") + ("dis" . "https://duckduckgo.com/?q=%s&iax=images&ia=images") + ("wp" . "https://en.wikipedia.org/wiki/%s") + ("g" . "https://www.google.com/search?q=%s") + ("gi" . "https://www.google.com/search?tbm=isch&q=%s") + ("gm" . "https://www.google.com/maps/search/%s") + ("yt" . "https://www.youtube.com/results?search_query=%s") + ("clp" . "https://portland.craigslist.org/search/sss?query=%s") + ("eb" . "https://www.ebay.com/sch/i.html?_nkw=%s") + ("nf" . "https://www.netflix.com/search?q=%s") + ("sh" . (lambda (query) (interactive) (shell-command query))) + ("imdb" . "https://www.imdb.com/find?q=peter+jackson&s=all") + ("bb" . bbdb-search-name) + ("el" . (apply-partially #'hyperspace-action->info "(elisp)Top")) + ("av" . apropos-variable) + ("ac" . apropos-command) + ("af" . (lambda (query) (apropos-command query t)))) + + "Where Hyperspace should send you. + + Hyperspace actions are a cons of (KEYWORD . DISPATCHER). When + Hyperspace is invoked, the keyword is extracted from the user + input and looked up in this alist. The remainder of the + string is passed to the dispatcher as its QUERY argument. + + DISPATCHER can be a function which performs the action. + + DISPATCHER can also be an expression which returns a function + to perform the action. + + Finally, DISPATCHER can be a string with a URL pattern containing + '%s'. The '%s' will be replaced with the query, and the URL browsed." + + :group 'hyperspace + :type '(alist :key-type (string :tag "Keyword") + :value-type (choice + (function :tag "Function") + (string :tag "URL Pattern") + (sexp :tag "Expression")))) + + (defcustom hyperspace-default-action + (caar hyperspace-actions) + "A place to go if you don't specify one." + :group 'hyperspace + :type `(radio + ,@(mapcar (lambda (action) (list 'const (car action))) hyperspace-actions))) + + (defcustom hyperspace-max-region-size 256 + "Maximum size of a region to consider for a Hyperspace query. + + If the region is active when Hyperspace is invoked, it's used + as the default query, unless it's more than this number of + characters." + :group 'hyperspace + :type 'integer) + + + + (defun hyperspace--cleanup (text) + "Clean TEXT so it can be used for a Hyperspace query." + (save-match-data + (string-trim + (replace-regexp-in-string (rx (1+ (or blank "\n"))) " " text)))) + + (defun hyperspace--initial-text () + "Return the initial text. + + This is whatever's in the active region, but cleaned up." + (when (use-region-p) + (let* ((start (region-beginning)) + (end (region-end)) + (size (- end start))) + (when (<= size hyperspace-max-region-size) + (hyperspace--cleanup + (buffer-substring-no-properties start end)))))) + + (defun hyperspace--initial (initial-text) + "Turn INITIAL-TEXT into INITIAL-CONTENTS for reading." + (when initial-text (cons (concat " " initial-text) 1))) + + (defun hyperspace--process-input (text) + "Process TEXT into an actionable keyword and query." + (let ((kw-text (s-split-up-to "\\s-+" text 1))) + (if (assoc (car kw-text) hyperspace-actions) + kw-text + (list hyperspace-default-action text)))) + + (defun hyperspace--query () + "Ask the user for the Hyperspace action and query. + + Returns (KEYWORD . QUERY). + + If the region isn't active, the user is prompted for the + action and query. + + If the region is active, its text is used as the initial value + for the query, and the user enters the action. + + If a prefix argument is specified and the region is active, + `HYPERSPACE-DEFAULT-ACTION' is chosen without prompting." + + (let ((initial (hyperspace--initial-text))) + (if (and initial current-prefix-arg) + (list hyperspace-default-action initial) + (hyperspace--process-input + (read-from-minibuffer "HS: " (hyperspace--initial initial) nil nil + 'hyperspace-history))))) + + (defun hyperspace--evalable-p (form) + "Can FORM be evaluated?" + (and (listp form) + (or (functionp (car form)) + (subrp (car form))))) + + (defun hyperspace--dispatch (action &optional query) + "Execute ACTION, with optional QUERY argument." + (pcase action + ((pred functionp) (funcall action query)) + ((pred hyperspace--evalable-p) (funcall (eval action) query)) + ((pred stringp) (hyperspace-action->browse-url-pattern action query)) + (_ (error "Unknown action")))) + + ;;;###autoload + (defun hyperspace (keyword &optional query) + "Execute action for keyword KEYWORD, with optional QUERY." + (interactive (hyperspace--query)) + (let ((action (cdr (assoc keyword hyperspace-actions)))) + (hyperspace--dispatch (or action hyperspace-default-action) query))) + + ;;;###autoload + (defun hyperspace-enter (&optional query) + "Enter Hyperspace, sending QUERY to the default action. + + If the region is active, use that as the query for + ‘hyperspace-default-action’. Otherwise, prompt the user." + (interactive (list (hyperspace--initial-text))) + (hyperspace + hyperspace-default-action + (or query + (read-from-minibuffer + (format "HS: %s " hyperspace-default-action) nil nil + 'hyperspace-history)))) + + ;; Minor mode + + (defvar hyperspace-minor-mode-map + (let ((kmap (make-sparse-keymap))) + (define-key kmap (kbd "H-SPC") #'hyperspace) + (define-key kmap (kbd "") #'hyperspace-enter) + kmap)) + + ;;;###autoload + (define-minor-mode hyperspace-minor-mode + "Global (universal) minor mode to jump from here to there." + nil nil hyperspace-minor-mode-map + :group 'hyperspace + :global t) + + (provide 'hyperspace) + + ;;; hyperspace.el ends here +#+END_SRC + ** Tools *** General #+BEGIN_SRC emacs-lisp :results silent @@ -320,7 +571,7 @@ #+BEGIN_SRC emacs-lisp :results silent (defun dired-mode-setup () "Will run as hook for `dired-mode'." - (dired-hide-details-mode 1)) + (dired-hide-details-mode nil)) (add-hook 'dired-mode-hook 'dired-mode-setup) #+END_SRC *** Ivy @@ -368,88 +619,158 @@ #+END_SRC *** Mu4e -#+BEGIN_SRC emacs-lisp :results silent - (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu/mu4e") - (require 'mu4e) - - ;; default - (setq mu4e-maildir "~/Mail" - mu4e-mu-binary "/usr/local/bin/mu" - mu4e-get-mail-command "offlineimap" ;; Allow updating with the "U" command - mu4e-sent-messages-behavior 'delete ;; Delete sent messages - mu4e-view-show-images t ;; attempt to show images - mu4e-view-image-max-width 400 ;; max image size - message-kill-buffer-on-exit t ;; don't keep messages around - mu4e-use-fancy-chars t ;; use 'fancy' chars - mu4e-update-interval 300 ;; 5 mins - ) - - (setq mu4e-contexts - `( ,(make-mu4e-context - :name "Vlocity" - :enter-func (lambda () (mu4e-message "Entering Vlocity")) - :leave-func (lambda () (mu4e-message "Leaving Vlocity")) - ;; we match based on the contact-fields of the message - :match-func (lambda (msg) - (when msg - (string= (mu4e-message-field msg :maildir) "/Vlocity"))) - :vars '( ( user-mail-address . "lolson@vlocity.com" ) - ( smtpmail-mail-address . "lolson@vlocity.com" ) - ( user-full-name . "Levi Olson" ) - ( mu4e-compose-signature . - (concat - "--\n" - "Levi Olson\n" - "Senior UI Developer")) - ( mu4e-sent-folder . "/Vlocity/[Gmail].Sent Mail" ) - ( mu4e-drafts-folder . "/Vlocity/[Gmail].Drafts" ) - ( mu4e-trash-folder . "/Vlocity/[Gmail].Trash" ) - ( mu4e-maildir-shortcuts . (("/Vlocity/INBOX" . ?i) - ("/Vlocity/[Gmail].Sent Mail" . ?s) - ("/Vlocity/[Gmail].Trash" . ?t) - ("/Vlocity/[Gmail].All Mail" . ?a))))) - ,(make-mu4e-context - :name "Gmail" - :enter-func (lambda () (mu4e-message "Entering Gmail")) - :leave-func (lambda () (mu4e-message "Leaving Gmail")) - ;; this matches maildir /Arkham and its sub-directories - :match-func (lambda (msg) - (when msg - (string= (mu4e-message-field msg :maildir) "/Gmail"))) - :vars '( ( user-mail-address . "olson.levi@gmail.com" ) - ( smtpmail-mail-address . "olson.levi@gmail.com" ) - ( user-full-name . "Levi Olson" ) - ( mu4e-compose-signature . - (concat - "--\n" - "Levi\n")) - ( mu4e-sent-folder . "/Gmail/[Gmail].Sent Mail" ) - ( mu4e-drafts-folder . "/Gmail/[Gmail].Drafts" ) - ( mu4e-trash-folder . "/Gmail/[Gmail].Trash" ) - ( mu4e-maildir-shortcuts . (("/Gmail/INBOX" . ?i) - ("/Gmail/[Gmail].Sent Mail" . ?s) - ("/Gmail/[Gmail].Trash" . ?t) - ("/Gmail/[Gmail].All Mail" . ?a)) - ))))) - - ;; (defcustom smtpmail-smtp-user nil - ;; "User name to use when looking up credentials in the authinfo file. - ;; If non-nil, only consider credentials for the specified user." - ;; :version "24.1" - ;; :type '(choice (const nil) string) - ;; :group 'smtpmail) - - - - ;; How to handle HTML emails - ;; (setq mu4e-html2text-command "textutil -stdin -format html -convert txt -stdout") - - ;; Add option to view HTML in browser - (add-to-list 'mu4e-headers-actions - '("in browser" . mu4e-action-view-in-browser) t) - (add-to-list 'mu4e-view-actions - '("in browser" . mu4e-action-view-in-browser) t) -#+END_SRC + #+BEGIN_SRC emacs-lisp :results silent + (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu/mu4e") + (require 'mu4e) + + ;; default + (setq mu4e-maildir "~/Mail" + mu4e-mu-binary "/usr/local/bin/mu" + mu4e-change-filenames-when-moving t ;; Rename files when moving (required by mbsync) + mu4e-compose-in-new-frame t ;; New compose gets new frame + mu4e-context-policy 'pick-first + mu4e-get-mail-command "mbsync -a" ;; MBSYNC is the mail cmd + mu4e-html2text-command "/usr/local/bin/w3m -T text/html" ;; HTML to text command + mu4e-sent-messages-behavior 'delete ;; Delete sent messages + mu4e-update-interval 300 ;; 5 mins + mu4e-use-fancy-chars t ;; use 'fancy' chars + mu4e-user-mail-address-list '("lolson@eaglecrk.com" + "lolson@vlocity.com" + "olson.levi@gmail.com") + mu4e-view-show-images t ;; attempt to show images + mu4e-view-image-max-width 400 ;; max image size + + message-citation-line-format "On %a %d %b %Y at %R, %f wrote:\n" ;; customize the reply-quote-string + message-citation-line-function 'message-insert-formatted-citation-line ;; choose to use the formatted string + message-kill-buffer-on-exit t ;; don't keep messages around + + send-mail-function 'smtpmail-send-it ;; Default email send function + smtpmail-default-smtp-server "smtp.gmail.com" + smtpmail-smtp-service 587 + ) + + (defun mdmail-send-buffer () + (interactive) + (shell-command-on-region (point-min) (point-max) "mdmail")) + + (setq mu4e-contexts + `( + ;; ,(make-mu4e-context + ;; :name "Vlocity" + ;; :enter-func (lambda () (mu4e-message "Entering Vlocity")) + ;; :leave-func (lambda () (mu4e-message "Leaving Vlocity")) + ;; ;; we match based on the contact-fields of the message + ;; :match-func (lambda (msg) + ;; (when msg + ;; (string= (mu4e-message-field msg :maildir) "/Vlocity"))) + ;; :vars '( ( user-mail-address . "lolson@vlocity.com" ) + ;; ( smtpmail-mail-address . "lolson@vlocity.com" ) + ;; ( smtpmail-smtp-user . "lolson@vlocity.com" ) + ;; ( smtpmail-smtp-server . "smtp.gmail.com" ) + ;; ( user-full-name . "Levi Olson" ) + ;; ( mu4e-compose-signature . + ;; (concat + ;; "Levi Olson\n" + ;; "Senior UI Developer")) + ;; ( mu4e-sent-folder . "/Vlocity/[Gmail].Sent Mail" ) + ;; ( mu4e-drafts-folder . "/Vlocity/[Gmail].Drafts" ) + ;; ( mu4e-trash-folder . "/Vlocity/[Gmail].Trash" ) + ;; ( mu4e-maildir-shortcuts . (("/Vlocity/INBOX" . ?i) + ;; ("/Vlocity/[Gmail].Sent Mail" . ?s) + ;; ("/Vlocity/[Gmail].Trash" . ?t) + ;; ("/Vlocity/[Gmail].All Mail" . ?a))))) + ,(make-mu4e-context + :name "EagleCreek" + :enter-func (lambda () (mu4e-message "Entering EagleCreek")) + :leave-func (lambda () (mu4e-message "Leaving EagleCreek")) + ;; we match based on the contact-fields of the message + :match-func (lambda (msg) + (when msg + (string= (mu4e-message-field msg :maildir) "/eaglecrk"))) + :vars '( ( user-mail-address . "lolson@eaglecrk.com" ) + ( smtpmail-mail-address . "lolson@eaglecrk.com" ) + ( smtpmail-smtp-user . "lolson@eaglecrk.com" ) + ( smtpmail-smtp-server . "smtp.office365.com" ) + ( user-full-name . "Levi Olson" ) + ;; ( mu4e-compose-signature . + ;; (concat + ;; "Levi Olson\n" + ;; "Eagle Creek Software Services\n" + ;; "Senior Application Developer Consultant\n")) + ( mu4e-sent-folder . "/eaglecrk/Sent Items" ) + ( mu4e-drafts-folder . "/eaglecrk/Drafts" ) + ( mu4e-trash-folder . "/eaglecrk/Deleted Items" ) + ( mu4e-maildir-shortcuts . (("/eaglecrk/Inbox" . ?i) + ("/eaglecrk/Sent Items" . ?s) + ("/eaglecrk/Deleted Items" . ?t) + ("/eaglecrk/Archive" . ?a))))) + ;; ,(make-mu4e-context + ;; :name "Gmail" + ;; :enter-func (lambda () (mu4e-message "Entering Gmail")) + ;; :leave-func (lambda () (mu4e-message "Leaving Gmail")) + ;; ;; this matches maildir /Arkham and its sub-directories + ;; :match-func (lambda (msg) + ;; (when msg + ;; (string= (mu4e-message-field msg :maildir) "/Gmail"))) + ;; :vars '( ( user-mail-address . "olson.levi@gmail.com" ) + ;; ( smtpmail-mail-address . "olson.levi@gmail.com" ) + ;; ( smtpmail-smtp-user . "olson.levi@gmail.com" ) + ;; ( smtpmail-smtp-server . "smtp.gmail.com" ) + ;; ( user-full-name . "Levi Olson" ) + ;; ( mu4e-compose-signature . + ;; (concat + ;; "Levi\n")) + ;; ( mu4e-sent-folder . "/Gmail/[Gmail].Sent Mail" ) + ;; ( mu4e-drafts-folder . "/Gmail/[Gmail].Drafts" ) + ;; ( mu4e-trash-folder . "/Gmail/[Gmail].Trash" ) + ;; ( mu4e-maildir-shortcuts . (("/Gmail/INBOX" . ?i) + ;; ("/Gmail/[Gmail].Sent Mail" . ?s) + ;; ("/Gmail/[Gmail].Trash" . ?t) + ;; ("/Gmail/[Gmail].All Mail" . ?a)) + ;; ))) + )) + + ;; Add option to view HTML in browser + (add-to-list 'mu4e-headers-actions + '("in browser" . mu4e-action-view-in-browser) t) + (add-to-list 'mu4e-view-actions + '("in browser" . mu4e-action-view-in-browser) t) + + + + (defun my-message-current-line-cited-p () + "Indicate whether the line at point is a cited line." + (save-match-data + (string-match (concat "^" message-cite-prefix-regexp) + (buffer-substring (line-beginning-position) (line-end-position))))) + + (defun my-message-says-attachment-p () + "Return t if the message suggests there can be an attachment." + (save-excursion + (goto-char (point-min)) + (save-match-data + (let (search-result) + (while + (and (setq search-result (re-search-forward "\\(attach\\|pdf\\|file\\)" nil t)) + (my-message-current-line-cited-p))) + search-result)))) + + (defun my-message-has-attachment-p () + "Return t if the message has an attachment." + (save-excursion + (goto-char (point-min)) + (save-match-data + (re-search-forward "<#part" nil t)))) + + (defun my-message-pre-send-check-attachment () + (when (and (my-message-says-attachment-p) + (not (my-message-has-attachment-p))) + (unless + (y-or-n-p "No attachment. Send anyway?") + (error "It seems that an attachment is needed, but none was found. Aborting sending.")))) + + (add-hook 'message-send-hook 'my-message-pre-send-check-attachment) + #+END_SRC *** Projectile #+BEGIN_SRC emacs-lisp :results silent (require 'projectile) @@ -461,7 +782,251 @@ projectile-completion-system 'ivy) (counsel-projectile-mode) #+END_SRC +*** Notify + #+BEGIN_SRC emacs-lisp :results silent + ;;; notify.el --- notification front-end + + ;; Copyright (C) 2008 Mark A. Hershberger + + ;; Original Author: Mark A. Hershberger + ;; Modified by Andrey Kotlarski + ;; Modified by Andrew Gwozdziewycz + ;; Modified by Aidan Gauland October 2011 + ;; Modified by Olivier Sirven November 2013 + ;; Keywords: extensions, convenience, lisp + + ;; This file 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 2, or (at your option) + ;; any later version. + + ;; This file 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 GNU Emacs; see the file COPYING. If not, write to + ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + ;; Boston, MA 02111-1307, USA. + + ;;; Commentary: + + ;; This provides a single function, `notify', that will produce a notify + ;; pop-up via D-Bus, libnotify, simple message or growl. + ;; To use, just put (autoload 'notify "notify" "Notify TITLE, BODY.") + ;; in your init file. You may override default chosen notification + ;; method by assigning `notify-method' to one of 'notify-via-dbus + ;; 'notify-via-libnotify or 'notify-via-message + ;;; Code: + + (defvar notify-defaults (list :app "Emacs" :icon "emacs" :timeout 5000 + :urgency "low" + :category "emacs.message") + "Notification settings' defaults. + May be overridden with key-value additional arguments to `notify'.") + (defvar notify-delay '(0 5 0) + "Minimum time allowed between notifications in time format.") + (defvar notify-last-notification '(0 0 0) "Time of last notification.") + (defvar notify-method 'notify-via-growl "Notification method among + 'notify-via-dbus, 'notify-via-libnotify, 'notify-via-message or + 'notify-via-growl") + + ;; determine notification method unless already set + ;; prefer growl > D-Bus > libnotify > message + (cond + ((null notify-method) + (setq notify-method + (cond + ((executable-find "growlnotify") 'notify-via-growl) + ((and (require 'dbus nil t) + (dbus-ping :session "org.freedesktop.Notifications")) + (defvar notify-id 0 "Current D-Bus notification id.") + 'notify-via-dbus) + ((executable-find "notify-send") 'notify-via-libnotify) + (t 'notify-via-message)))) + ((eq notify-method 'notify-via-dbus) ;housekeeping for pre-chosen DBus + (if (and (require 'dbus nil t) + (dbus-ping :session "org.freedesktop.Notifications")) + (defvar notify-id 0 "Current D-Bus notification id.") + (setq notify-method (if (executable-find "notify-send") + 'notify-via-libnotify + 'notify-via-message)))) + ((and (eq notify-method 'notify-via-libnotify) + (not (executable-find "notify-send"))) ;housekeeping for pre-chosen libnotify + (setq notify-method + (if (and (require 'dbus nil t) + (dbus-ping :session "org.freedesktop.Notifications")) + (progn + (defvar notify-id 0 "Current D-Bus notification id.") + 'notify-via-dbus) + 'notify-via-message))) + ((and (eq notify-method 'notify-via-growl) + (not (executable-find "growlnotify"))) + (setq notify-method 'notify-via-message))) + + (defun notify-via-dbus (title body) + "Send notification with TITLE, BODY `D-Bus'." + (dbus-call-method :session "org.freedesktop.Notifications" + "/org/freedesktop/Notifications" + "org.freedesktop.Notifications" "Notify" + (get 'notify-defaults :app) + (setq notify-id (+ notify-id 1)) + (get 'notify-defaults :icon) title body '(:array) + '(:array :signature "{sv}") ':int32 + (get 'notify-defaults :timeout))) + + (defun notify-via-libnotify (title body) + "Notify with TITLE, BODY via `libnotify'." + (call-process "notify-send" nil 0 nil + title body "-t" + (number-to-string (get 'notify-defaults :timeout)) + "-i" (get 'notify-defaults :icon) + "-u" (get 'notify-defaults :urgency) + "-c" (get 'notify-defaults :category))) + + (defun notify-via-message (title body) + "Notify TITLE, BODY with a simple message." + (message "%s: %s" title body)) + + (defun notify-via-growl (title body) + "Notify TITLE, BODY with a growl" + (call-process "growlnotify" nil 0 nil + "-a" (get 'notify-defaults :app) + "-n" (get 'notify-defaults :category) + "-t" (notify-via-growl-stringify title) + "-m" (notify-via-growl-stringify body))) + + (defun notify-via-growl-stringify (thing) + (cond ((null thing) "") + ((stringp thing) thing) + (t (format "%s" thing)))) + + (defun keywords-to-properties (symbol args &optional defaults) + "Add to SYMBOL's property list key-values from ARGS and DEFAULTS." + (when (consp defaults) + (keywords-to-properties symbol defaults)) + (while args + (put symbol (car args) (cadr args)) + (setq args (cddr args)))) + + + ;;;###autoload + (defun notify (title body &rest args) + "Notify TITLE, BODY via `notify-method'. + ARGS may be amongst :timeout, :icon, :urgency, :app and :category." + (when (time-less-p notify-delay + (time-since notify-last-notification)) + (or (eq notify-method 'notify-via-message) + (keywords-to-properties 'notify-defaults args + notify-defaults)) + (setq notify-last-notification (current-time)) + (funcall notify-method title body))) + + (provide 'notify) + + ;;; notify.el ends here + #+END_SRC +*** Jabber + #+BEGIN_SRC emacs-lisp :results silent + (require 'jabber) + + (setq jabber-history-enabled t + jabber-use-global-history nil + jabber-backlog-number 40 + jabber-backlog-days 30 + jabber-alert-presence-message-function (lambda (_who _oldstatus _newstatus _statustext) nil) + ) + + (setq jabber-account-list '( + ;; ("olson.levi@gmail.com" + ;; (:network-server . "talk.google.com") + ;; (:connection-type . ssl)) + ("lolson@vlocity.com" + (:network-server . "talk.google.com") + (:connection-type . ssl)) + )) + + (defvar my-chat-prompt "[%t] %n>\n" "Customized chat prompt") + (when (featurep 'jabber) + (setq + jabber-chat-foreign-prompt-format my-chat-prompt + jabber-chat-local-prompt-format my-chat-prompt + jabber-groupchat-prompt-format my-chat-prompt + jabber-muc-private-foreign-prompt-format "[%t] %g/%n>\n" + ) + ) + + (defun notify-jabber-notify (from buf text _proposed-alert) + "(jabber.el hook) Notify of new Jabber chat messages via notify.el" + (when (or jabber-message-alert-same-buffer + (not (memq (selected-window) (get-buffer-window-list buf)))) + (if (jabber-muc-sender-p from) + (notify (format "(PM) %s" + (jabber-jid-displayname (jabber-jid-user from))) + (format "%s: %s" (jabber-jid-resource from) text))) + (notify (format "%s" (jabber-jid-displayname from)) + text))) + + ;; (add-hook 'jabber-alert-message-hooks 'notify-jabber-notify) + + ;; (require 'autosmiley) + ;; (add-hook 'jabber-chat-mode-hook 'autosmiley-mode) + + + (defun jabber () + (interactive) + (jabber-connect-all) + (switch-to-buffer "*-jabber-roster-*")) + #+END_SRC +*** Terminal-Notifier + #+BEGIN_SRC emacs-lisp :results silent :tangle no + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Terminal notifier + ;; requires 'brew install terminal-notifier' + ;; stolen from erc-notifier + + (defvar terminal-notifier-command (executable-find "terminal-notifier") "The path to terminal-notifier.") + + ; (terminal-notifier-notify "Emacs notification" "Something amusing happened") + + (defun terminal-notifier-notify (title message) + "Show a message with + terminal-notifier-command + ." + (start-process "terminal-notifier" + "terminal-notifier" + terminal-notifier-command + "-title" title + "-message" message + "-activate" "org.gnu.Emacs")) + + (defun timed-notification (time msg) + (interactive "sNotification when (e.g: 2 minutes, 60 seconds, 3 days): \nsMessage: ") + (run-at-time time nil (lambda (msg) (terminal-notifier-notify "Emacs" msg)) msg)) + #+END_SRC +*** Hyperspace +#+BEGIN_SRC emacs-lisp :results silent + (defun hyperspace-action->mu4e (&optional query) + "Search mu4e with QUERY. + + If QUERY is unspecified, use the first bookmark in variable + ‘mu4e-bookmarks’ and update mail and index." + + (mu4e-headers-search (or query (caar mu4e-bookmarks))) + (unless query + (mu4e-update-mail-and-index nil))) + (add-to-list 'hyperspace-actions '("m4" . hyperspace-action->mu4e)) + + (defun hyperspace-action->elfeed (&optional query) + "Load elfeed, optionally searching for QUERY." + (elfeed) + (if query + (elfeed-search-set-filter query) + (elfeed-search-fetch nil))) + (add-to-list 'hyperspace-actions '("lf" . hyperspace-action->elfeed)) +#+END_SRC ** Development Specific *** General #+BEGIN_SRC emacs-lisp :results silent @@ -607,6 +1172,29 @@ (shell . t) (emacs-lisp . t))) + (setq org-todo-keywords + '((sequence "TODO(t)" "|" "DONE(d)") + (sequence "BUG(b)" "|" "INPROGRESS(i)" "FIXED(f)") + (sequence "|" "CANCELED(c)") + (sequence "|" "NEEDCLARIFICATION(n)") + (sequence "|" "PROVIDEUPDATE(p)") + (sequence "|" "WAITING(w)") + )) + + (setq org-agenda-files + '("~/Dropbox/Org/todo.org" "~/Dropbox/Org/archive.org")) + (setq org-refile-targets + '((nil :maxlevel . 1) + (org-agenda-files :maxlevel . 1))) + + (add-hook 'focus-in-hook + (lambda () (progn + (setq org-tags-column (- 5 (frame-width)))) (org-align-all-tags))) + + (add-hook 'focus-out-hook + (lambda () (progn + (setq org-tags-column (- 5 (frame-width)))) (org-align-all-tags))) + (defvar org-src-tab-acts-natively) (setq org-src-tab-acts-natively t) ;; (setenv "NODE_PATH" @@ -614,7 +1202,7 @@ (defvar org-confirm-babel-evaluate) - (defun my-org-confirm-babel-evaluate (lang body) + (defun my-org-confirm-babel-evaluate (lang _body) "Execute certain languages without confirming. Takes LANG to allow and BODY to execute." (not (or (string= lang "js") @@ -635,6 +1223,32 @@ "\n" "#+END_SRC"))) #+END_SRC +**** Mu4e + #+BEGIN_SRC emacs-lisp :results silent + ;;store org-mode links to messages + (require 'org-mu4e) + ;;store link to message if in header view, not to header query + (setq org-mu4e-link-query-in-headers-mode nil) + + (setq org-capture-templates + '(("t" "todo" entry (file+headline "~/todo.org" "Tasks") + "* TODO [#A] %?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n%a\n"))) + #+END_SRC +**** ElFeed + #+BEGIN_SRC emacs-lisp :results silent + (elfeed-org) + (setq rmh-elfeed-org-files (list "~/Dropbox/Org/elfeed.org")) + + (defun leo/elfeed-search (arg) + "Search for ARG in feed." + (interactive) + (elfeed-search-set-filter arg)) + + (define-key elfeed-search-mode-map "a" (lambda () (interactive) (leo/elfeed-search ""))) + (define-key elfeed-search-mode-map "e" (lambda () (interactive) (leo/elfeed-search "+emacs"))) + (define-key elfeed-search-mode-map "d" (lambda () (interactive) (leo/elfeed-search "+daily"))) + (define-key elfeed-search-mode-map "x" (lambda () (interactive) (leo/elfeed-search "xkcd"))) + #+End_SRC ** Functions #+BEGIN_SRC emacs-lisp :results silent (defun find-user-init-file () @@ -642,6 +1256,11 @@ (interactive) (find-file "~/.emacs.d/init.org")) + (defun find-todo-file () + "Edit the `~/todo.org' file." + (interactive) + (find-file "~/Dropbox/Org/todo.org")) + (defun load-user-init-file () "LO: Reload the `~/.emacs.d/init.elc' file." (interactive) @@ -815,35 +1434,41 @@ #+END_SRC ** Bindings - #+BEGIN_SRC emacs-lisp :results silent + #+begin_src emacs-lisp :results silent (require 'company) - (add-hook 'comint-mode-hook (lambda () (local-set-key (kbd "C-l") 'clear-comint))) + (add-hook 'comint-mode-hook (lambda () (local-set-key (kbd "c-l") 'clear-comint))) (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode) (add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode) (add-hook 'c-mode-common-hook 'c-setup) (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode)) (defvar company-active-map (make-keymap) - "Company Mode keymap.") + "company mode keymap.") (defvar custom-bindings (make-keymap) - "A keymap of custom bindings.") - - (define-key global-map (kbd "M-p") 'jump-to-previous-like-this) - (define-key global-map (kbd "M-n") 'jump-to-next-like-this) - (define-key global-map (kbd "M-") 'switch-to-next-buffer) - (define-key global-map (kbd "M-")'delete-backward-to-boundary) - (define-key global-map (kbd "C-")'delete-backward-to-boundary) - - (global-set-key (kbd "C-S-") 'mc/mark-next-like-this) - (global-set-key (kbd "C->") 'mc/mark-next-like-this-symbol) - (global-set-key (kbd "C-S-") 'mc/mark-previous-like-this) - (global-set-key (kbd "C-<") 'mc/mark-previous-like-this) - (global-set-key (kbd "C-c C->") 'mc/mark-all-like-this) - (global-set-key "%" 'match-paren) - (global-set-key (kbd "C-x .") 'dash-at-point) - (global-set-key (kbd "C-x ,") 'dash-at-point-with-docset) - (global-set-key (kbd "C-s") (lambda () (interactive) (swiper (format "%s" (thing-at-point 'symbol))))) - (global-set-key (kbd "M-m") 'mu4e) + "a keymap of custom bindings.") + + (define-key custom-bindings (kbd "M-p") 'jump-to-previous-like-this) + (define-key custom-bindings (kbd "M-n") 'jump-to-next-like-this) + (define-key custom-bindings (kbd "M-") 'switch-to-next-buffer) + (define-key custom-bindings (kbd "M-")'delete-backward-to-boundary) + (define-key custom-bindings (kbd "C-")'delete-backward-to-boundary) + (define-key custom-bindings (kbd "C-}") 'mc/mark-next-like-this) + (define-key custom-bindings (kbd "C-)") 'mc/unmark-next-like-this) + (define-key custom-bindings (kbd "C-{") 'mc/mark-previous-like-this) + (define-key custom-bindings (kbd "C-(") 'mc/unmark-previous-like-this) + (define-key custom-bindings (kbd "C-'") 'mc-hide-unmatched-lines-mode) + (define-key custom-bindings (kbd "C-c 1") 'mc/insert-numbers) + (define-key custom-bindings (kbd "C-c s") 'mc/sort-regions) + (define-key custom-bindings "%" 'match-paren) + (define-key custom-bindings (kbd "C-x .") 'dash-at-point) + (define-key custom-bindings (kbd "C-x ,") 'dash-at-point-with-docset) + (define-key custom-bindings (kbd "C-s") (lambda () (interactive) (swiper (format "%s" (thing-at-point 'symbol))))) + (define-key custom-bindings (kbd "C-x C-l m") 'mu4e) + (define-key custom-bindings (kbd "C-x C-o t") 'find-todo-file) + (define-key custom-bindings (kbd "C-x C-l j") 'jabber) + (define-key custom-bindings (kbd "C-x C-l f") 'elfeed) + (define-key custom-bindings (kbd "C-x C-l a") 'org-agenda) + (define-key custom-bindings (kbd "M-SPC") #'hyperspace) ;; (dolist (n (number-sequence 1 9)) ;; (global-set-key (kbd (concat "M-" (int-to-string n))) ;; (lambda () (interactive) (switch-shell n)))) @@ -899,72 +1524,72 @@ #+END_SRC *** Doom Modeline -#+BEGIN_SRC emacs-lisp :results silent - (require 'doom-modeline) - (doom-modeline-mode 1) + #+BEGIN_SRC emacs-lisp :results silent + (require 'doom-modeline) + (doom-modeline-mode 1) - ;; How tall the mode-line should be (only respected in GUI Emacs). - (setq doom-modeline-height 35) + ;; How tall the mode-line should be (only respected in GUI Emacs). + (setq doom-modeline-height 30) - ;; How wide the mode-line bar should be (only respected in GUI Emacs). - (setq doom-modeline-bar-width 4) + ;; How wide the mode-line bar should be (only respected in GUI Emacs). + (setq doom-modeline-bar-width 4) - ;; Determines the style used by `doom-modeline-buffer-file-name'. - ;; - ;; Given ~/Projects/FOSS/emacs/lisp/comint.el - ;; truncate-upto-project => ~/P/F/emacs/lisp/comint.el - ;; truncate-from-project => ~/Projects/FOSS/emacs/l/comint.el - ;; truncate-with-project => emacs/l/comint.el - ;; truncate-except-project => ~/P/F/emacs/l/comint.el - ;; truncate-upto-root => ~/P/F/e/lisp/comint.el - ;; truncate-all => ~/P/F/e/l/comint.el - ;; relative-from-project => emacs/lisp/comint.el - ;; relative-to-project => lisp/comint.el - ;; file-name => comint.el - ;; buffer-name => comint.el<2> (uniquify buffer name) - ;; - ;; If you are expereicing the laggy issue, especially while editing remote files - ;; with tramp, please try `file-name' style. - ;; Please refer to https://github.com/bbatsov/projectile/issues/657. - (setq doom-modeline-buffer-file-name-style 'truncate-upto-project) + ;; Determines the style used by `doom-modeline-buffer-file-name'. + ;; + ;; Given ~/Projects/FOSS/emacs/lisp/comint.el + ;; truncate-upto-project => ~/P/F/emacs/lisp/comint.el + ;; truncate-from-project => ~/Projects/FOSS/emacs/l/comint.el + ;; truncate-with-project => emacs/l/comint.el + ;; truncate-except-project => ~/P/F/emacs/l/comint.el + ;; truncate-upto-root => ~/P/F/e/lisp/comint.el + ;; truncate-all => ~/P/F/e/l/comint.el + ;; relative-from-project => emacs/lisp/comint.el + ;; relative-to-project => lisp/comint.el + ;; file-name => comint.el + ;; buffer-name => comint.el<2> (uniquify buffer name) + ;; + ;; If you are expereicing the laggy issue, especially while editing remote files + ;; with tramp, please try `file-name' style. + ;; Please refer to https://github.com/bbatsov/projectile/issues/657. + (setq doom-modeline-buffer-file-name-style 'truncate-upto-project) - ;; What executable of Python will be used (if nil nothing will be showed). - (setq doom-modeline-python-executable "python") + ;; What executable of Python will be used (if nil nothing will be showed). + (setq doom-modeline-python-executable "python") - ;; Whether show `all-the-icons' or not (if nil nothing will be showed). - (setq doom-modeline-icon t) + ;; Whether show `all-the-icons' or not (if nil nothing will be showed). + (setq doom-modeline-icon t) - ;; Whether show the icon for major mode. It respects `doom-modeline-icon'. - (setq doom-modeline-major-mode-icon t) + ;; Whether show the icon for major mode. It respects `doom-modeline-icon'. + (setq doom-modeline-major-mode-icon t) - ;; Display color icons for `major-mode'. It respects `all-the-icons-color-icons'. - (setq doom-modeline-major-mode-color-icon nil) + ;; Display color icons for `major-mode'. It respects `all-the-icons-color-icons'. + (setq doom-modeline-major-mode-color-icon nil) - ;; Whether display minor modes or not. Non-nil to display in mode-line. - (setq doom-modeline-minor-modes nil) + ;; Whether display minor modes or not. Non-nil to display in mode-line. + (setq doom-modeline-minor-modes nil) - ;; If non-nil, a word count will be added to the selection-info modeline segment. - (setq doom-modeline-enable-word-count nil) + ;; If non-nil, a word count will be added to the selection-info modeline segment. + (setq doom-modeline-enable-word-count nil) - ;; If non-nil, only display one number for checker information if applicable. - (setq doom-modeline-checker-simple-format t) + ;; If non-nil, only display one number for checker information if applicable. + (setq doom-modeline-checker-simple-format t) - ;; Whether display perspective name or not. Non-nil to display in mode-line. - (setq doom-modeline-persp-name t) + ;; Whether display perspective name or not. Non-nil to display in mode-line. + (setq doom-modeline-persp-name t) - ;; Whether display `lsp' state or not. Non-nil to display in mode-line. - (setq doom-modeline-lsp t) + ;; Whether display `lsp' state or not. Non-nil to display in mode-line. + (setq doom-modeline-lsp t) - ;; Whether display github notifications or not. Requires `ghub` package. - (setq doom-modeline-github nil) + ;; Whether display github notifications or not. Requires `ghub` package. + (setq doom-modeline-github nil) - ;; The interval of checking github. - (setq doom-modeline-github-interval (* 30 60)) + ;; The interval of checking github. + (setq doom-modeline-github-interval (* 30 60)) - ;; Whether display environment version or not. - (setq doom-modeline-env-version t) + ;; Whether display environment version or not. + (setq doom-modeline-env-version t) - ;; Whether display mu4e notifications or not. Requires `mu4e-alert' package. - (setq doom-modeline-mu4e t) -#+END_SRC + ;; Whether display mu4e notifications or not. Requires `mu4e-alert' package. + (setq doom-modeline-mu4e t) + #+END_SRC diff --git a/scripts/expand-mime.sh b/scripts/expand-mime.sh new file mode 100644 index 0000000..6c375ef --- /dev/null +++ b/scripts/expand-mime.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +TEXT=`cat` + +DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +PLAIN=$(echo "$TEXT" | pandoc -f markdown -t plain ) +HTML=$(echo "$TEXT" | pandoc -f markdown -t html ) +# CSS=$(cat $DIR/mime.css ) + +# + +cat < +$PLAIN +<#part type=text/html> + + + + + + $HTML + + +<#/multipart> +EOF