;; -*- lexical-binding: t; -*- ;;;; Startup ;; Do not merge echo-area-message sexp (setq inhibit-startup-echo-area-message "alan") (setq inhibit-startup-screen t initial-scratch-message "" initial-major-mode 'text-mode user-mail-address "alan@alanpearce.co.uk" user-full-name "Alan Pearce" custom-file "~/.emacs.d/custom.el") (load custom-file :noerror) ;;; Allow lisps to use a common setup. I don't know why they don't have some lispy mode as their parent, but this is close enough (defcustom lisp-common-mode-hook nil "Hook run when entering any Lisp mode." :type 'hook :group 'lisp) ;;;; Environment & Location (defun env/get-location () "Return the physical location of the system, or `nil' if unknown" (if (executable-find "netctl") (catch 'found (mapc (lambda (line) (cond ((string-prefix-p "* home" line) (throw 'found 'home)) ((string-prefix-p "* work" line) (throw 'found 'work)))) (process-lines "netctl" "list")) nil))) (defun env/get-system-type () "Return the type of computer that is running" (cond ((string-prefix-p "prefect" system-name) 'desktop) ((string-prefix-p "server" system-name) 'server) ((string-prefix-p "sheldon" system-name) 'laptop))) (defvar env/location (env/get-location) "The type of location the system is located in Values: `work', `home'") (defvar env/system-type (env/get-system-type) "The type of computer Emacs is running on Values: `desktop', `server', `laptop'") (defun env/recheck-location () (interactive) (setq env/location (env/get-location))) (defvar *init-file* (when user-init-file (expand-file-name "init.el" (file-name-directory (file-truename user-init-file)))) "Where the emacs init file really is, passing through symlinks.") ;;;; Package Management (eval-and-compile (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") ("marmalade" . "http://marmalade-repo.org/packages/") ("melpa" . "http://melpa.milkbox.net/packages/") ("org" . "http://orgmode.org/elpa/"))) (package-initialize)) (when (not package-archive-contents) (package-refresh-contents)) (add-to-list 'load-path (expand-file-name "elisp/" user-emacs-directory)) (mapc (lambda (package-name) (unless (package-installed-p package-name) (package-install package-name))) '(bind-key diminish use-package)) (require 'use-package) ;;;; Style (use-package linum :defer t :config (setq linum-format " %4d ")) (use-package highlight-symbol :disabled t :config (setq highlight-symbol-idle-delay 0.2)) (use-package whitespace :defer t :config (setq whitespace-style '(face space tabs trailing newline empty space-after-tab tab-mark space-before-tab indentation indentation::space indentation::tabs))) (global-font-lock-mode t) ;; Allow font-lock-mode to do background parsing (setq jit-lock-stealth-time 1 jit-lock-stealth-load 100 jit-lock-chunk-size 1000 jit-lock-defer-time 0.01) (use-package solarized-theme :ensure t :config (load-theme 'solarized-light t)) (when (or (display-graphic-p) (daemonp)) (defun use-variable-fonts () (interactive) (variable-pitch-mode) (setq cursor-type 'bar)) (cond ((eq window-system 'w32) (let* ((font-size 10) (font-list (font-family-list)) (mono-face (cond ((member "Liberation Mono" font-list) "Liberation Mono") ((member "Liberation Sans Mono" font-list) "Liberation Sans Mono") ((member "Consolas" font-list) "Consolas"))) (variable-face "Segoe UI") (default-font (concat mono-face "-" (number-to-string font-size)))) (when mono-face (set-face-font 'default default-font) (set-face-font 'fixed-pitch default-font)) (when variable-face (set-face-font 'variable-pitch (concat variable-face "-" (number-to-string (1+ font-size))))))) ((eq window-system 'ns) (let* ((font-size 14) (font-list (font-family-list)) (mono-face (cond ((member "Droid Sans Mono" font-list) "Droid Sans Mono"))) (variable-face "Helvetica Neue") (default-font (concat mono-face "-" (number-to-string font-size)))) (when mono-face (set-face-font 'default default-font) (set-face-font 'fixed-pitch default-font)))))) (with-elapsed-timer "Setting up font styles" (let* ((font-height (face-attribute 'default :height)) (small-font-height (max 1 (floor (* .917 font-height))))) (mapc (lambda (item) (put (car item) 'customized-face (cadr item)) (face-spec-set (car item) (cadr item))) `((linum ((t (:height ,small-font-height :foreground unspecified :inherit fringe :overline nil :slant normal)))) (vertical-border ((t (:foreground unspecified :background unspecified :inherit file-name-shadow)))) (font-lock-comment-face ((t (:slant normal)))) (font-lock-doc-face ((t (:slant normal)))) (popup-face ((t (:background unspecified :foreground unspecified :inherit linum :height ,font-height)))) (popup-scroll-bar-foreground-face ((t (:background unspecified :inherit region)))) (popup-scroll-bar-background-face ((t (:background unspecified :inherit popup-face)))) (ac-completion-face ((t (:background unspecified :foreground unspecified :inherit popup-face)))) (ac-candidate-face ((t (:background unspecified :foreground unspecified :inherit linum :height ,font-height)))) (ac-selection-face ((t (:background unspecified :foreground unspecified :inherit font-lock-variable-name-face :inverse-video t)))) (ac-candidate-mouse-face ((t (:background unspecified :foreground unspecified :inherit region)))) (ac-dabbrev-menu-face ((t (:background unspecified :foreground unspecified :inherit popup-face)))) (ac-dabbrev-selection-face ((t (:background unspecified :foreground unspecified :inherit ac-selection-face)))) (flymake-warnline ((t (:background unspecified :foreground unspecified :inherit font-lock-preprocessor-face)))) (org-table ((t (:inherit 'fixed-pitch)))) (org-formula ((t (:foreground "Firebrick" :inherit 'fixed-pitch)))) (org-done ((t (:weight normal :strike-through t)))) (org-headline-done ((t (:strike-through t)))))))) ;;;; Autosaves & Backups (let ((backup-dir (expand-file-name "~/.emacs.d/backups/"))) (unless (file-directory-p backup-dir) (make-directory backup-dir)) (setq backup-directory-alist `((".*" . ,backup-dir)) auto-save-file-name-transforms `((".*" ,temporary-file-directory t)) backup-by-copying-when-linked t backup-by-copying-when-mismatch t)) ;;;; Buffers (use-package ibuffer :bind (("C-x C-b" . ibuffer)) :config (progn (setq ibuffer-saved-filter-groups (quote (("default" ("org" (mode . org-mode)) ("emacs" (mode . emacs-lisp-mode)) ("zsh" (filename . "/zsh")) ("server" (filename . "/su:root@server")))))) ;; Human-readable base-2 size column (define-ibuffer-column size-h (:name "Size" :inline t) (cond ((> (buffer-size) 1024) (format "%7.2fK" (/ (buffer-size) 1024.0))) ((> (buffer-size) 1048576) (format "%7.2fM" (/ (buffer-size) 1048576.0))) (t (format "%8d" (buffer-size))))) (setq ibuffer-formats '((mark modified read-only " " (name 18 18 :left :elide) " " (size-h 9 -1 :right) " " (mode 16 16 :left :elide) " " filename-and-process))))) (use-package uniquify :config (progn (setq uniquify-buffer-name-style 'reverse uniquify-separator "/" uniquify-after-kill-buffer-p t uniquify-ignore-buffers-re "^\\*"))) ;;;; Communication (use-package erc :defer t :config (progn (setq erc-user-full-name "Alan Pearce" erc-email-userid "alan@alanpearce.co.uk" erc-echo-notice-in-minibuffer t erc-keywords '("alanpearce" "lethalrocks") erc-autojoin-channels-alist '(("freenode.net" "#emacs" "##freebsd" "#bufferbloat" "#openwrt" "#lojban" "#zfs" "#introverts") ("what.cd" "#what.cd") ("beusergroup.co.uk" "#be"))) (add-to-list 'erc-modules 'scrolltobottom) (add-to-list 'erc-modules 'autojoin) (add-to-list 'erc-modules 'match))) (setq message-send-mail-function 'smtpmail-send-it) (setq smtpmail-smtp-server "external.home" smtpmail-smtp-service 587) (use-package mu4e :if (and (eq env/location 'home) (eq env/system-type 'desktop)) :load-path "/usr/share/emacs/site-lisp/mu4e" :commands (mu4e) :config (progn (setq mu4e-get-mail-command "true" mu4e-update-interval 300 mu4e-sent-folder "/alanpearce/Sent" mu4e-drafts-folder "/alanpearce/Drafts" mu4e-refile-folder "/alanpearce/Archive") (bind-key "q" #'bury-buffer mu4e-main-mode-map) (bind-key "d" #'mu4e-headers-mark-for-delete mu4e-headers-mode-map))) ;;;; Completion (setq completion-styles '(basic initials partial-completion substring) completion-ignore-case t) (use-package smart-tab :ensure t :init (global-smart-tab-mode) :config (progn (nconc smart-tab-completion-functions-alist '((php-mode . php-complete-function))) (diminish 'smart-tab-mode ""))) (use-package company :ensure t :commands (company-mode) :bind (("C-" . company-complete)) :init (progn (add-hook 'prog-mode-hook #'company-mode) (setq company-idle-delay .3 company-begin-commands '(self-insert-command)))) ;;;; Dates & Times (use-package calendar :defer t :config (progn (setq calendar-week-start-day 1) (calendar-set-date-style 'iso))) (defun insert-date (prefix) "Insert the current date. With prefix-argument, use British format. With two prefix arguments, write out the day and month name." (interactive "P") (let ((format (cond ((not prefix) "%Y-%m-%d") ((equal prefix '(4)) "%d/%m/%Y") ((equal prefix '(16)) "%A, %d %B %Y")))) (insert (format-time-string format)))) ;;;; Directory browsing (use-package dired :defer t :config (progn (bind-key "" #'dired-find-file dired-mode-map) (bind-key "^" (lambda () (interactive) (find-alternate-file "..")) dired-mode-map) (setq dired-dwim-target t dired-recursive-copies 'top dired-recursive-deletes 'top dired-bind-jump nil) (put 'dired-find-alternate-file 'disabled nil))) (use-package dired+ :defer t :ensure t :config (diredp-toggle-find-file-reuse-dir 1)) ;;;; Documentation (add-to-list 'Info-default-directory-list (concat user-emacs-directory "info")) (use-package which-func :init (which-function-mode) :config (setq which-func-modes t)) ;;;; Files (prefer-coding-system 'utf-8-auto-unix) (set-default-coding-systems 'utf-8-auto-unix) (setq-default buffer-file-coding-system 'utf-8-auto-unix) (global-auto-revert-mode 1) (add-hook 'before-save-hook #'delete-trailing-whitespace) (defun rename-current-buffer-file () "Renames current buffer and file it is visiting." (interactive) (let ((name (buffer-name)) (filename (buffer-file-name))) (if (not (and filename (file-exists-p filename))) (error "Buffer '%s' is not visiting a file!" name) (let ((new-name (read-file-name "New name: " filename))) (if (get-buffer new-name) (error "A buffer named '%s' already exists!" new-name) (cond ((vc-backend filename) (vc-rename-file filename new-name)) (t (rename-file filename new-name t) (rename-buffer new-name) (set-visited-file-name new-name) (set-buffer-modified-p nil) (message "File '%s' successfully renamed to '%s'" name (file-name-nondirectory new-name))))))))) (defun delete-current-buffer-file () "Removes file connected to current buffer and kills buffer." (interactive) (let ((filename (buffer-file-name)) (buffer (current-buffer))) (if (not (and filename (file-exists-p filename))) (ido-kill-buffer) (when (yes-or-no-p "Are you sure you want to remove this file? ") (if (vc-backend filename) (vc-delete-file filename) (delete-file filename) (kill-buffer buffer) (message "File '%s' successfully removed" filename)))))) (use-package recentf :init (progn (setq recentf-auto-cleanup 'never recentf-save-file (expand-file-name "recentf" user-emacs-directory)) (recentf-mode 1))) (use-package saveplace :config (progn (setq-default save-place t) (setq save-place-file (expand-file-name ".saveplace" user-emacs-directory)))) (use-package tramp :defer t :config (progn (setq tramp-default-method (if (eq system-type 'windows-nt) "plinkx" "ssh") tramp-default-user-alist '(("\\`su\\(do\\)?\\'" nil "root") (nil nil "alan")) tramp-backup-directory-alist backup-directory-alist backup-enable-predicate (lambda (name) (and (normal-backup-enable-predicate name) (not (let ((method (file-remote-p name 'method))) (when (stringp method) (member method '("su" "sudo"))))))) tramp-shell-prompt-pattern "\\(?:^\\| \\)[^#$%>\n]*#?[#$%>›] *\\(\\[[0-9;]*[a-zA-Z] *\\)*") (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" (concat "/" tramp-default-method ":%h:"))) (add-to-list 'tramp-default-proxies-alist '((regexp-quote (system-name)) nil nil)) (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil)) (add-to-list 'tramp-default-proxies-alist '("router" nil nil)))) (use-package tramp-sh :defer t :config (progn (add-to-list 'tramp-remote-path "/usr/local/sbin") (add-to-list 'tramp-remote-path "~/bin"))) (use-package ediff :defer t :config (progn (setq ediff-split-window-function 'split-window-horizontally ediff-window-setup-function 'ediff-setup-windows-plain))) ;;;; Indentation (setq-default tab-width 4 indent-tabs-mode t) (setq tab-stop-list ;; (mapcar (lambda (x) ;; (* 4 x)) ;; (number-sequence 1 (/ 120 4))) '(4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 112 116 120) tab-always-indent 'complete) (use-package auto-indent-mode :ensure t :commands (auto-indent-minor-mode auto-indent-mode) :config (progn (setq auto-indent-key-for-end-of-line-then-newline "" auto-indent-key-for-end-of-line-insert-char-then-newline "" auto-indent-blank-lines-on-move nil auto-indent-assign-indent-level 4 auto-indent-backward-delete-char-behavior nil auto-indent-delete-trailing-whitespace-on-save-file t auto-indent-mode-untabify-on-yank-or-paste nil) (auto-indent-global-mode) (defun lisp-auto-indent-mode () (set (make-local-variable 'auto-indent-assign-indent-level) 2)) (add-hook 'lisp-common-mode-hook #'lisp-auto-indent-mode))) (use-package smart-tabs-mode :ensure t :commands (smart-tabs-mode smart-tabs-mode-enable smart-tabs-advice) :config (progn (smart-tabs-insinuate 'c 'javascript 'cperl 'python 'ruby) (add-hook 'php-mode-hook #'smart-tabs-mode-enable))) ;;;; Keybindings (when (eq system-type 'darwin) (set-keyboard-coding-system nil) (setq mac-option-modifier 'meta mac-right-option-modifier 'left mac-control-modifier 'control mac-right-control-modifier 'left mac-command-modifier 'super mac-right-command-modifier 'left mac-function-modifier 'hyper)) (unbind-key "") (bind-key "" #'compile) (bind-key "" #'kmacro-start-macro-or-insert-counter) (bind-key "" #'kmacro-end-or-call-macro) (bind-key "" #'execute-extended-command) (unbind-key "C-z") (bind-key "C-" #'other-window) (bind-key "C-x C-r" #'revert-buffer) (bind-key "C-x C-j" #'delete-indentation) (unbind-key "C-x C-c") (bind-key "C-c i" #'ucs-insert) (bind-key "M-/" #'hippie-expand) (unbind-key "s-h") (unbind-key "s-n") (unbind-key "s-p") (unbind-key "s-w") (bind-key "s-x" (define-prefix-command 'super-x-map)) (set-register ?e `(file . ,*init-file*)) ;; Enable narrowing functions C-x n (put 'narrow-to-defun 'disabled nil) (put 'narrow-to-page 'disabled nil) (put 'narrow-to-region 'disabled nil) ;;;; Minibuffer (setq enable-recursive-minibuffers t) (minibuffer-depth-indicate-mode t) (if (daemonp) (defalias 'exit-emacs #'delete-frame) (defalias 'exit-emacs #'save-buffers-kill-emacs)) (use-package lacarte :bind (("M-`" . lacarte-execute-menu-command))) (use-package helm-config :ensure helm :bind (("C-x i" . helm-imenu)) :config (setq helm-idle-delay .1 helm-input-idle-delay .1)) (use-package ido :bind (("C-x b" . ido-switch-buffer)) :init (progn (setq ido-save-directory-list-file (expand-file-name "ido-state" user-emacs-directory)) (bind-key* "C-x C-f" #'ido-find-file) (ido-mode 1)) :config (progn (setq ido-decorations (quote ("\n›" "" "\n " "\n …" "[" "]" " [No match]" " [Matched]" " [Not readable]" " [Too big]" " [Confirm]")) ido-auto-merge-delay-time 99999 ido-enable-flex-matching t ido-use-virtual-buffers t) (ido-init-completion-maps) (defun ido-manual-merge () (interactive) (ido-initiate-auto-merge (current-buffer))) (bind-key "C-c C-s" #'ido-manual-merge ido-file-dir-completion-map))) (use-package smex :ensure t :bind (("M-x" . smex) ("" . smex) ("" . smex) ("M-X" . smex-major-mode-commands) ("C-c M-x" . execute-extended-command)) :config (progn (setq smex-key-advice-ignore-menu-bar t smex-auto-update nil) (defun smex-update-after-load (unused) (if (boundp 'smex-cache) (smex-update))) (add-hook 'after-load-functions 'smex-update-after-load)) :init (progn (setq smex-history-length 100 smex-save-file (concat user-emacs-directory "smex-items")) (smex-initialize))) ;;;; Modeline (column-number-mode t) (size-indication-mode t) ;;;; Modes ;;;; systemd files (add-to-list 'auto-mode-alist '("\\.service\\'" . conf-mode)) (add-to-list 'auto-mode-alist '("\\.target\\'" . conf-mode)) (add-to-list 'auto-mode-alist '("\\.socket\\'" . conf-mode)) (use-package xrdb-mode :mode (("\\.Xdefaults\\'" . xrdb-mode) ("\\.Xresources\\'" . xrdb-mode))) (use-package haskell-mode :ensure t :mode (("\\.hs\\'" . haskell-mode) ("xmobarrc\\'" . haskell-mode))) (use-package nginx-mode :ensure t :mode (("nginx.conf" . nginx-mode))) (use-package lua-mode :ensure t :mode (("\\.lua\\'" . lua-mode))) (use-package puppet-mode :mode (("\\.pp\\'" . puppet-mode)) :config (progn (add-hook 'puppet-mode-hook #'autopair-mode))) (use-package ruby-mode :mode (("\\.rb\\'" . ruby-mode) ("\\.cap\\'" . ruby-mode))) ;;;; Planning (use-package org :ensure org-plus-contrib :bind (("C-c C-a" . org-agenda-list) ("C-c a" . org-agenda) ("C-c l" . org-store-link) ("C-c r" . org-remember) ("C-c b" . org-iswitchb)) :config (progn (setq org-directory "~/org" org-agenda-files `(,org-directory) org-default-notes-file (concat org-directory "/notes") ;; Fewer asterisks, doesn't change indentation org-hide-leading-stars t org-startup-indented t ;; ‘Remember’: new items at top org-reverse-note-order t org-modules '(org-habit org-checklist) ;; Add time done to ‘done’ tasks org-log-done 'time ;; Allow refiling into any org file org-refile-targets '((org-agenda-files :maxlevel . 3)) org-list-allow-alphabetical t org-pretty-entities t org-table-duration-custom-format 'seconds org-export-have-math t org-blank-before-new-entry '((heading . t) (plain-list-item . auto)) org-fontify-done-headline t org-replace-disputed-keys t org-todo-keywords '((sequence "TODO" "STARTED" "|" "DONE") (sequence "TOLEARN" "LEARNING" "LEARNED" "|" "MASTERED") (sequence "|" "CANCELLED"))) (set-register ?o `(file . ,(expand-file-name "organiser.org" org-directory))) (defadvice org-clock-in (after wicked activate) "Mark STARTED when clocked in" (save-excursion (catch 'exit (org-back-to-heading t) (if (looking-at org-outline-regexp) (goto-char (1- (match-end 0)))) (if (looking-at (concat " +" org-todo-regexp "\\( +\\|[ \t]*$\\)")) (org-todo "STARTED"))))))) (use-package org-journal :config (progn (setq org-journal-date-format "%A, %d %B %Y"))) ;;;; Programming (use-package cedet :disabled t :config (progn (semantic-load-enable-code-helpers) (global-semantic-idle-completions-mode t) (global-semantic-highlight-func-mode t) (global-semantic-show-unmatched-syntax-mode t) (global-semantic-decoration-mode t))) ;;;; Projects (use-package projectile :ensure t :bind (("C-c C-f" . projectile-find-file) ("s-x s-f" . projectile-find-file)) :commands (projectile-global-mode)) (use-package project-persist :ensure t :commands (project-persist-mode) :bind (("C-c P d" . project-persist-delete) ("C-c P f" . project-persist-find) ("C-c P k" . project-persist-close) ("C-c P n" . project-persist-create) ("C-c P s" . project-persist-save)) :config (progn (project-persist-mode t) (setq project-persist-auto-save-global t) (add-hook 'project-persist-before-load-hook #'kill-all-buffers) (defun emacs-process-p (pid) "If pid is the process ID of an emacs process, return t, else nil. Also returns nil if pid is nil." (when pid (let ((attributes (process-attributes pid)) (cmd)) (dolist (attr attributes) (if (string= "comm" (car attr)) (setq cmd (cdr attr)))) (if (and cmd (or (string= "emacs" cmd) (string= "emacs.exe" cmd))) t)))) (defadvice desktop-owner (after pry-from-cold-dead-hands activate) "Don't allow dead emacsen to own the desktop file." (when (not (emacs-process-p ad-return-value)) (setq ad-return-value nil))) (defun load-project-desktop () "Load the project's desktop if available" (ignore-errors (let ((default-directory project-persist-current-project-settings-dir)) (desktop-read)))) (defun kill-all-buffers () "Kill all file-based buffers." (interactive) (mapc (lambda (buf) (when (buffer-file-name buf) (when (and (buffer-modified-p buf) (y-or-n-p (format "Buffer %s is modified - save it?" (buffer-name buf)))) (save-some-buffers nil buf)) (set-buffer-modified-p nil) (kill-buffer buf))) (buffer-list))) (add-hook 'project-persist-after-close-hook (lambda () (kill-all-buffers) (projectile-global-mode -1))) (add-hook 'project-persist-after-load-hook (lambda () (setq default-directory (pp/settings-get 'root-dir)) (load-project-desktop) (projectile-global-mode 1))) (add-hook 'project-persist-after-save-hook (lambda () (message (format "Saving project desktop (%s)" project-persist-current-project-settings-dir)) (desktop-save project-persist-current-project-settings-dir))) )) (use-package vc :config (progn (setq vc-follow-symlinks t))) (use-package diff-hl :ensure t :init (global-diff-hl-mode)) (use-package magit :commands (magit-status) :bind (("C-x g" . magit-status) ("s-G" . magit-status)) :init (add-hook 'magit-mode-hook #'magit-load-config-extensions)) ;;;; Spelling (setq ispell-program-name "aspell" ispell-dictionary "british") ;; (setq ispell-process-directory (expand-file-name "~/")) ;; If aspell is too slow ;; If it is still too slow, use ‘ultra’ instead of ‘fast’ ;; (setq ispell-extra-args '(" --sug-mode=fast")) (use-package ispell :bind (("" . ispell-word))) ;;;; Scripting (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) (use-package sh-script :defer t :config (setq sh-shell-file "/usr/bin/env zsh")) (use-package ntcmd :mode (("\\`.cmd\\'" . ntcmd-mode) ("\\`.bat\\'" . ntcmd-mode))) ;;;; Shells & REPLs (use-package eshell :bind ("C-c s" . eshell) :defer t :config (progn (setq eshell-directory-name "~/.emacs.d/eshell") (use-package em-smart :init (progn (setq eshell-where-to-jump 'begin eshell-review-quick-commands nil eshell-smart-space-goes-to-end t) (eshell-smart-initialize))))) (autoload #'eshell/cd "em-dirs") (defun eshell-goto-current-dir (&optional arg) (interactive "P") (let ((dir default-directory)) (eshell arg) (eshell/cd dir))) (bind-key "C-c S" #'eshell-goto-current-dir) (use-package shell :defer t :config (define-key shell-mode-map (kbd "C-d") 'comint-delchar-or-eof-or-kill-buffer)) (use-package multi-term :ensure t :if (not (eq system-type 'windows-nt)) :bind ("C-`" . multi-term-dedicated-toggle)) (defun comint-delchar-or-eof-or-kill-buffer (arg) (interactive "p") (if (null (get-buffer-process (current-buffer))) (kill-buffer) (comint-delchar-or-maybe-eof arg))) ;;;; Text editing ;; Enable upcase and downcase-region (put 'upcase-region 'disabled nil) (put 'downcase-region 'disabled nil) (setq sentence-end-double-space nil line-move-visual nil) (setq x-select-enable-clipboard t) (if (functionp 'x-cut-buffer-or-selection-value) (setq interprogram-paste-function 'x-cut-buffer-or-selection-value)) ;; replace highlighted text rather than just inserting at point (delete-selection-mode t) (show-paren-mode t) (bind-key "S-SPC" #'set-mark-command) (use-package subword :init (global-subword-mode t)) (use-package misc :bind (("M-z" . zap-up-to-char) ("M-Z" . zap-to-char))) (use-package ap-functions :commands (ap/remove-extra-cr ap/byte-compile-get-dest) :bind (("C-x r M-w" . copy-rectangle) ("M-!" . shell-execute))) (when (boundp 'x-select-request-type) (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))) (use-package ace-jump-mode :ensure t :bind (("C-c SPC" . ace-jump-mode)) :config (progn (ace-jump-mode-enable-mark-sync) (setq ace-jump-word-mode-use-query-char nil ace-jump-mode-scope 'window))) (use-package autopair :ensure t :commands (autopair-mode autopair-on) :init (progn (add-hook 'prog-mode-hook #'autopair-on) (defun autopair-off () (autopair-mode -1)) (add-hook 'lisp-common-mode-hook #'autopair-off) (setq autopair-blink nil autopair-skip-whitespace nil))) (use-package expand-region :ensure t :bind ("C-M-SPC" . er/expand-region)) (use-package goto-chg :bind ("C-x SPC" . goto-last-change)) (use-package multiple-cursors :ensure t :config (progn (bind-key "C-." #'mc/mark-next-like-this) (bind-key "C-," #'mc/mark-previous-like-this) (bind-key "M-" #'mc/mark-all-like-this-dwim) (bind-key "C-" #'mc/mark-more-like-this-extended) (bind-key "C-S-L" #'mc/edit-lines))) (use-package eldoc :defer t :config (progn (eldoc-add-command 'paredit-backward-delete 'paredit-close-round))) (use-package paredit :ensure t :commands (paredit-mode) :init (progn (add-hook 'lisp-common-mode-hook #'enable-paredit-mode) (put #'paredit-forward-delete 'delete-selection 'supersede) (put #'paredit-backward-delete 'delete-selection 'supersede) (add-hook 'minibuffer-setup-hook #'conditionally-enable-paredit-mode) (defun conditionally-enable-paredit-mode () "enable paredit-mode during eval-expression" (if (eq this-command 'eval-expression) (paredit-mode 1))))) (use-package shuffle-lines :bind (("C-S-" . move-line-up) ("C-S-" . move-line-down))) (use-package smart-forward :bind (("C-M-u" . smart-up) ("C-M-d" . smart-down) ("C-M-p" . smart-backward) ("C-M-n" . smart-forward))) (use-package undo-tree :ensure t :config (progn (global-undo-tree-mode) ;; Keep region when undoing in region (defadvice undo-tree-undo (around keep-region activate) (if (use-region-p) (let ((m (set-marker (make-marker) (mark))) (p (set-marker (make-marker) (point)))) ad-do-it (goto-char p) (set-mark m) (set-marker p nil) (set-marker m nil)) ad-do-it))) :diminish undo-tree-mode) ;;;; Lisps (defun ap/lisp-setup () (run-hooks 'lisp-common-mode-hook) (setq indent-tabs-mode nil) (local-set-key (kbd "RET") #'paredit-newline)) (defun set-common-lisp-indentation () (set (make-local-variable 'lisp-indent-function) #'common-lisp-indent-function)) (add-hook 'emacs-lisp-mode-hook #'ap/lisp-setup) (add-hook 'emacs-lisp-mode-hook #'turn-on-eldoc-mode) (add-hook 'scheme-mode-hook #'ap/lisp-setup) (add-hook 'lisp-mode-hook #'ap/lisp-setup) (add-hook 'lisp-mode-hook #'set-common-lisp-indentation) (use-package bytecomp :defer t :config (setq byte-compile-dest-file-function #'ap/byte-compile-get-dest)) (use-package elisp-slime-nav :commands elisp-slime-nav-mode :diminish elisp-slime-nav-mode) (use-package geiser :commands (geiser-mode geiser run-geiser run-racket)) (use-package redshank :ensure t :defer t :init (progn (add-hook 'lisp-common-mode-hook #'turn-on-redshank-mode))) (use-package geiser-base :defer t :config (use-package quack)) (use-package slime :ensure t :commands (slime) :config (progn (let ((ql-slime-helper (expand-file-name "~/quicklisp/slime-helper.el"))) (if (file-exists-p ql-slime-helper) (load ql-slime-helper)) (slime-setup)) (setq inferior-lisp-program (executable-find "sbcl")))) (defun imenu-elisp-sections () (setq imenu-prev-index-position-function nil) (add-to-list 'imenu-generic-expression '("Sections" "^;;;; \\(.+\\)$" 1) t) (add-to-list 'imenu-generic-expression '("Packages" "^(use-package\\s-+\\(\\(\\sw\\|\\s_\\)+\\)$" 1) t)) (defun init-narrow-to-section () (interactive) (save-excursion (beginning-of-line) (unless (looking-at "^;;;;") (re-search-backward "^;;;;" nil t)) (push-mark) (forward-line) (re-search-forward "^;;;;" nil t) (forward-line -1) (narrow-to-region (region-beginning) (region-end)))) (defun init-imenu (p) (interactive "P") (find-file-existing *init-file*) (widen) (helm-imenu) (if p (init-narrow-to-section))) (add-hook 'emacs-lisp-mode-hook 'imenu-elisp-sections) (defun eval-and-replace () "Replace the preceding sexp with its value." (interactive) (backward-kill-sexp) (condition-case nil (prin1 (eval (read (current-kill 0))) (current-buffer)) (error (message "Invalid expression") (insert (current-kill 0))))) (bind-key "C-c e" #'eval-and-replace) ;;;; Programming (use-package auto-async-byte-compile :ensure t :init (add-hook 'emacs-lisp-mode-hook #'auto-async-byte-compile-mode)) (use-package cc-mode :defer t :init (progn (add-hook 'c-mode-common-hook (lambda () ;; (use-package mic-paren ;; :init (paren-toggle-open-paren-context 1)) (electric-indent-mode 1)))) :config (progn (setq c-default-style '((java-mode . "java") (awk-mode . "awk") (other . "k&r")) c-basic-offset 4) (c-set-offset 'case-label '+) (c-set-offset 'arglist-cont-nonempty #'ap/c-lineup-arglist) (defun ap/c-lineup-arglist (langelem) "Line up the current argument line under the first argument. As a special case, if the indented line is inside a brace block construct, the indentation is 0 only. This is intended as a \"DWIM\" measure in cases like macros that contains statement blocks, e.g: A_VERY_LONG_MACRO_NAME ({ some (code, with + long, lines * in[it]); }); <--> c-basic-offset Works with: arglist-cont-nonempty, arglist-close." (save-excursion (let ((indent-pos (point))) (if (c-block-in-arglist-dwim (c-langelem-2nd-pos c-syntactic-element)) 0 ;; Normal case. Indent to the token after the arglist open paren. (goto-char (c-langelem-2nd-pos c-syntactic-element)) (if (and c-special-brace-lists (c-looking-at-special-brace-list)) ;; Skip a special brace list opener like "({". (progn (c-forward-token-2) (forward-char)) (forward-char)) (let ((arglist-content-start (point))) (c-forward-syntactic-ws) (when (< (point) indent-pos) (goto-char arglist-content-start) (skip-chars-forward " \t")) (vector (if (or (looking-at ",") (looking-at ")")) (- (current-column) 2) (current-column)))))))))) (use-package quickrun :defer t :ensure t) ;;;; Web Development (use-package moz :load-path "moz-repl" :commands (moz-firefox-reload moz-reload-on-save-mode moz-minor-mode) :config (progn (define-minor-mode moz-reload-on-save-mode "Moz Reload On Save Minor Mode" nil " Reload" nil (if moz-reload-on-save-mode ;; Edit hook buffer-locally. (add-hook 'after-save-hook 'moz-firefox-reload nil t) (remove-hook 'after-save-hook 'moz-firefox-reload t))) (defun moz-firefox-reload () "Reload Firefox" (interactive) (comint-send-string (inferior-moz-process) "BrowserReload();")))) (use-package skewer-mode :ensure t :defer t :init (progn (add-hook 'js2-mode-hook #'skewer-mode) (add-hook 'html-mode-hook #'skewer-html-mode) (add-hook 'css-mode-hook #'skewer-css-mode))) (use-package js2-mode :ensure t :mode ("\\.js\\'" . js2-mode) :config (progn (defun ap/javascript-setup () (if (featurep 'moz) (moz-minor-mode)) (autopair-mode -1) (auto-indent-mode -1)) (add-hook 'js2-mode-hook #'ap/javascript-setup) (setq js2-basic-offset 4 js2-global-externs '("$")))) (use-package mustache-mode :ensure t :mode (("\\.mustache" . mustache-mode) ("\\.mt\\'" . mustache-mode) ("\\.template\\'" . mustache-mode))) (use-package jinja2-mode :ensure t :mode (("\\.j2\\'" . jinja2-mode))) (use-package php-mode :mode ("\\.php\\'" . php-mode) :config (progn (bind-key "C-h C-f" #'php-search-documentation php-mode-map) (unbind-key "C-c C-f" php-mode-map) (unbind-key "C-." php-mode-map) (if (eq env/location 'home) (setq php-manual-url "http://docs.home/manual/en/")) (add-hook 'php-mode-hook (lambda () (set (make-local-variable 'eldoc-documentation-function) #'my-php-eldoc-function))) (use-package eldoc-php :config (turn-on-eldoc-mode)))) (use-package sgml-mode :defer t :config (setq sgml-basic-offset 4)) (use-package emmet-mode :ensure t :config (progn (if (functionp 'web-mode) (add-hook 'web-mode-hook #'emmet-mode)))) (use-package web-mode :defer t :mode (("/views/.*\\.php\\'" . web-mode) ("/templates/.*\\.php\\'" . web-mode)) :config (setq web-mode-code-indent-offset 4 web-mode-css-indent-offset 4 web-mode-markup-indent-offset 4)) ;;;; Windows & Frames (setq frame-title-format '("" (:eval (capitalize invocation-name)) ": " (:eval (if (buffer-file-name) (abbreviate-file-name (buffer-file-name)) "%b")))) (setq scroll-conservatively 100 ; Keep the cursor position when scrolling scroll-preserve-screen-position nil) (when menu-bar-mode (menu-bar-mode -1)) (when scroll-bar-mode (scroll-bar-mode -1) (tooltip-mode -1) (tool-bar-mode -1)) (winner-mode 1) (use-package windmove :bind (("S-" . windmove-left) ("S-" . windmove-right) ("S-" . windmove-up) ("S-" . windmove-down)))