diff options
| -rw-r--r-- | emacs/init.el | 929 | ||||
| -rw-r--r-- | emacs/settings/defuns.el | 62 | ||||
| -rw-r--r-- | emacs/settings/init-calendar.el | 46 | ||||
| -rw-r--r-- | emacs/settings/init-edit.el | 89 | ||||
| -rw-r--r-- | emacs/settings/init-evil.el | 157 | ||||
| -rw-r--r-- | emacs/settings/init-general.el | 133 | ||||
| -rw-r--r-- | emacs/settings/init-lang.el | 193 | ||||
| -rw-r--r-- | emacs/settings/init-magit.el | 56 | ||||
| -rw-r--r-- | emacs/settings/init-misc.el | 34 | ||||
| -rw-r--r-- | emacs/settings/init-mu4e.el | 261 | ||||
| -rw-r--r-- | emacs/settings/init-project.el | 119 |
11 files changed, 1177 insertions, 902 deletions
diff --git a/emacs/init.el b/emacs/init.el index e4d5214..c1fda4e 100644 --- a/emacs/init.el +++ b/emacs/init.el @@ -33,6 +33,8 @@ ;;; Code: +;;; Preface + ;; I'm sticking with the major version I'm using, deal with it. (unless (>= emacs-major-version 24) (error "Don't be a cheap bastard and upgrade to at least GNU Emacs 24")) @@ -45,195 +47,36 @@ (add-hook 'after-init-hook (lambda () (setq gc-cons-threshold mssola-initial-gc-cons-threshold))) -;; Initialize 'package -(require 'package) - -(add-to-list 'package-archives - '("melpa" . "http://melpa.milkbox.net/packages/") t) -(add-to-list 'package-archives - '("melpa-stable" . "https://stable.melpa.org/packages/") t) - -(package-initialize) - -;; UTF-8 *always*. -(prefer-coding-system 'utf-8) -(set-default-coding-systems 'utf-8) -(set-terminal-coding-system 'utf-8) -(set-keyboard-coding-system 'utf-8) -(set-language-environment 'utf-8) +;;; User name and email. -;; User name and email. (setq user-full-name "Miquel Sabaté Solà" user-mail-address "mikisabate@gmail.com") -;; Calendar -(defvar calendar-week-start-day 1) -(global-set-key (kbd "C-c c") 'calendar) - -(defun mssola-evil-calendar () - "Define the bindings for the calendar in evil mode." - - (evil-set-initial-state 'calendar-mode 'normal) - (evil-define-key 'normal calendar-mode-map - "j" 'calendar-forward-week - "k" 'calendar-backward-week - "b" 'calendar-backward-day - "h" 'calendar-backward-day - "l" 'calendar-forward-day - "w" 'calendar-forward-day - "\C-h" 'evil-window-left - "\C-l" 'evil-window-right - "\C-j" 'evil-window-down - "\C-k" 'evil-window-up - "\C-n" 'calendar-scroll-left-three-months - "\C-p" 'calendar-scroll-right-three-months)) - -;; No welcome screen -(setq-default inhibit-startup-message t) - -;; Enable y/n answers -(fset 'yes-or-no-p 'y-or-n-p) - -;; The frame title is "<login>: <path>". If we are not editing a file, then the -;; name of the buffer is displayed (e.g. "mssola: *scratch*"). -(setq frame-title-format - '((:eval - (concat (user-real-login-name) ": " - (if (buffer-file-name) - (abbreviate-file-name (buffer-file-name)) - "%b"))))) - -;; Emacs modes typically provide a standard means to change the indentation -;; width (e.g. c-basic-offset). Moreover, even though I prefer tabs over space, -;; for most coding conventions this is not the case (e.g. ruby). For this -;; reason, I will disable them by default and enabled them back for each -;; specific case (e.g. C). I'm also using the smart-tabs-mode package, see -;; below in the languages section. -(setq-default indent-tabs-mode nil) -(setq-default tab-width 4) - -;; Maximum 80 columns. -(setq-default fill-column 80) -(setq-default auto-fill-function 'do-auto-fill) - -;; Delete the selection with a keypress. -(delete-selection-mode t) - -;; Revert buffers automatically when underlying files are changed externally -(global-auto-revert-mode t) - -;; Do not break lines -(set-default 'truncate-lines t) - -;; No backups -(setq-default make-backup-files nil) -(setq-default auto-save-default nil) - -;; Nice scrolling -(setq scroll-margin 0 - scroll-conservatively 100000 - scroll-preserve-screen-position 1) - -;; Remove whitespaces at the end of line -(add-hook 'before-save-hook 'delete-trailing-whitespace) +;;; Lisp directories. -;; Graphical interface tweaks -(menu-bar-mode -1) -(when (fboundp 'set-scroll-bar-mode) - (set-scroll-bar-mode nil)) -(when (fboundp 'tool-bar-mode) - (tool-bar-mode -1)) - -;; Lines and columns -(line-number-mode 1) -(column-number-mode 1) - -;; Cursor -(blink-cursor-mode 0) -(global-hl-line-mode -1) -(show-paren-mode 1) - -;; Follow symlinks -(setq vc-follow-symlinks t) - -;; Default theme -(load-theme 'soria t) - -;; Default font - -(set-frame-font "Droid Sans Mono Dotted for Powerline-10") -(add-to-list 'default-frame-alist '(font . "Droid Sans Mono Dotted for Powerline-10")) - -; Emacs in daemon mode does not like `set-face-attribute` because this is only -; applied if there is a frame in place, which doesn't happen when starting the -; daemon. Thus, we should call that after the frame has been created (e.g. by -; emacsclient). -; See: https://lists.gnu.org/archive/html/help-gnu-emacs/2015-03/msg00016.html -(add-hook 'after-make-frame-functions-hook - (lambda () - (set-face-attribute 'default t :font "Droid Sans Mono Dotted for Powerline-10"))) - -;; Disable C-z. It will later on be picked up by Evil's config as the escape -;; sequence. This is here to make sure that it will be disabled even if Evil -;; cannot be loaded due to some error. -(global-unset-key (kbd "C-z")) - -;; Disable all the Fn keys. -(dotimes (i 12) - (global-unset-key (kbd (format "<f%d>" (+ i 1))))) - -;; Let flyspell be performant. -(defvar flyspell-issue-message-flag nil) - -;; Custom functions - -(defun mssola-view-emacs-latest-news () - "Allow users to fetch the latest Emacs' NEWS file." - (interactive) - - (url-copy-file - "http://git.savannah.gnu.org/cgit/emacs.git/plain/etc/NEWS" - "/tmp/emacs-news" t) - - (find-file-read-only "/tmp/emacs-news" t)) - -(defun mssola-cleanup-workspace () - "Cleanup the current workspace by killing all buffers and windows. -The user will end up in the *scratch* buffer." - - (interactive) - (when (y-or-n-p "Are you sure that you want to cleanup your workspace? ") - (delete-other-windows) - (switch-to-buffer "*scratch*") +(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory)) +(add-to-list 'load-path (expand-file-name "settings" user-emacs-directory)) - ; Code taken from 'crux-kill-other-buffers in the crux package by - ; @bbatsov. It's the same but the confirmation has been removed since we are - ; already asking for permission before. - (seq-each - #'kill-buffer - (delete (current-buffer) (seq-filter #'buffer-file-name (buffer-list)))))) +;;; Basic configuration -(global-set-key (kbd "C-c a") 'mssola-cleanup-workspace) +(require 'init-general) +(require 'init-calendar) +(require 'defuns) -(defun emacs-init-time () - "Redefine the `emacs-init-time' function so it is more detailed. -Idea taken from @purcell." +;;; Custom Lisp - (interactive) - (let ((init-time - (float-time (time-subtract after-init-time before-init-time)))) - (message "%.3fs" init-time))) +(require 'g) -; Kill Emacs. Sometimes useful when you have a server-client setup and you want -; to *really* evaluate everything from scratch. -(global-set-key (kbd "C-c k") 'kill-emacs) +;;; Initialize package and use-package. -;;; My lisp directory +(require 'package) -(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory)) -(require 'g) +(add-to-list 'package-archives + '("melpa" . "http://melpa.milkbox.net/packages/") t) +(add-to-list 'package-archives + '("melpa-stable" . "https://stable.melpa.org/packages/") t) -;;; Packages +(package-initialize) ;; I'm using use-package to handle my installed packages. I don't know if it's ;; the best option or what because I haven't tested all the package managers @@ -243,735 +86,17 @@ Idea taken from @purcell." (package-refresh-contents) (package-install 'use-package)) -(require 'use-package) -(setq use-package-verbose t) - -;; Ayo silver! -(use-package ag - :ensure t - :config - (add-hook 'ag-mode-hook - (lambda () - (define-key ag-mode-map (kbd "n") 'evil-search-next) - (define-key ag-mode-map (kbd "N") 'evil-search-previous) - (define-key ag-mode-map (kbd "gg") 'evil-goto-first-line))) - (setq ag-reuse-buffers t) - (setq ag-reuse-window t)) - -;; Flycheck - -(use-package let-alist - :ensure t) - -(use-package flycheck - :ensure t - :config - (add-hook 'after-init-hook 'global-flycheck-mode) - - ;; Only show the errors buffer if it isn't there and if I'm saving the - ;; buffer. - (setq flycheck-emacs-lisp-load-path 'inherit) - (setq flycheck-check-syntax-automatically '(mode-enabled save)) - (setq flycheck-display-errors-function - #'flycheck-display-error-messages-unless-error-list)) - -;; Let's increase fonts with C-+ and C-- - -(use-package default-text-scale - :ensure t - :config - (global-set-key (kbd "C-+") 'default-text-scale-increase) - (global-set-key (kbd "C--") 'default-text-scale-decrease)) - -;; Projectile & Helm - -(use-package projectile - :ensure t - :config - (projectile-global-mode +1) - (setq projectile-mode-line - '(:eval (format " %s" (projectile-project-name))))) - -(use-package helm - :ensure t - :config - (setq projectile-completion-system 'helm) - - ; Allow the search pattern to be on the header. Taken from this Reddit thread: - ; https://www.reddit.com/r/emacs/comments/3asbyn/new_and_very_useful_helm_feature_enter_search/ - (setq helm-echo-input-in-header-line t) - - (defun helm-hide-minibuffer-maybe () - "Hide the minibuffer if we are in a Helm session" - - (when (with-helm-buffer helm-echo-input-in-header-line) - (let ((ov (make-overlay (point-min) (point-max) nil nil t))) - (overlay-put ov 'window (selected-window)) - (overlay-put ov 'face (let ((bg-color (face-background 'default nil))) - `(:background ,bg-color :foreground ,bg-color))) - (setq-local cursor-type nil)))) - - (add-hook 'helm-minibuffer-set-up-hook 'helm-hide-minibuffer-maybe) - (setq helm-split-window-in-side-p t) - - ; Preview files with tab - (define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action) - - ; Show available options - (define-key helm-map (kbd "C-a") 'helm-select-action) - - ; Some vim-like bindings - (define-key helm-map (kbd "C-j") 'helm-next-line) - (define-key helm-map (kbd "C-k") 'helm-previous-line) - - (use-package helm-ag - :ensure t)) - -(use-package helm-projectile - :ensure t - :config - (helm-projectile-on)) - -(defun mssola-helm-ag () - "Call the right ag command for helm-ag." - - (interactive) - - (condition-case nil - (helm-ag-project-root) - (error (helm-ag)))) - -(defun mssola-find-file () - "Call the proper Helm function for finding files." - - (interactive) - - (condition-case nil - (helm-projectile-find-file) - (error - (helm-find-files nil)))) - -;; Rising from the deep flames of Hell: my Evil configuration. - -(defun mssola-evil () - "Configure evil mode." - - ; We can safely remap <C-u> because the counting will be handled a-la Vim. - (define-key evil-normal-state-map (kbd "C-u") 'evil-scroll-up) - - ; Make window navigation easier. - (define-key evil-normal-state-map (kbd "C-j") 'evil-window-down) - (define-key evil-normal-state-map (kbd "C-k") 'evil-window-up) - (define-key evil-normal-state-map (kbd "C-l") 'evil-window-right) - (define-key evil-normal-state-map (kbd "C-h") 'evil-window-left) - - ; The window navigation tweaks effectively wipe out the help prefix, which - ; is bad. Fortunately we can workaround this by providing "M-h" as the new - ; help prefix. This prefix is only used in emacs mode to mark lines, which is - ; something already handled by Evil. - (define-key global-map (kbd "M-h") 'help-command) - (fset 'help-command help-map) - - ; Helm + Projectile shortcuts. - (define-key evil-normal-state-map (kbd "C-p") 'mssola-find-file) - (define-key evil-normal-state-map (kbd "M-p") 'helm-projectile-switch-project) - - ; I use the Super key in combination with j & k to move around i3. Let's unset - ; M- combos for these two fellows for whenever I misstype them. - (global-unset-key (kbd "M-j")) - (global-unset-key (kbd "M-k")) - - ; both this function and the subsequent lines about [escape] are taken from - ; @aaronbieber configuration. - (defun minibuffer-keyboard-quit () - "Abort recursive edit. -In Delete Selection mode, if the mark is active, just deactivate it; -then it takes a second \\[keyboard-quit] to abort the minibuffer." - (interactive) - (if (and delete-selection-mode transient-mark-mode mark-active) - (setq deactivate-mark t) - (when (get-buffer "*Completions*") (delete-windows-on "*Completions*")) - (abort-recursive-edit))) - - ;; Make escape quit everything, whenever possible. - (define-key evil-normal-state-map [escape] 'keyboard-quit) - (define-key evil-visual-state-map [escape] 'keyboard-quit) - (define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit) - (define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit) - (define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit) - (define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit) - (define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit) - - ; I store macros on the <q> register for convenience, so I used to use the - ; <C-q> combo to execute this macro in Vim. In Emacs though, this combo is - ; reserved to a rather useful function, and I'd like to keep it that way. So, - ; now the mapping is set to <C-2> (mnemonic: where the @ symbol is). Moreover, - ; it's applied as many times as specified by the numeric prefix argument. - (define-key evil-normal-state-map (kbd "C-2") - (lambda (n) - (interactive "p") - (evil-execute-macro n "@q"))) - - ; C-s: switch to normal mode and save the buffer. I know :) - (define-key evil-normal-state-map (kbd "C-s") 'save-buffer) - (define-key evil-insert-state-map (kbd "C-s") - (lambda () (interactive) (save-buffer) (evil-force-normal-state)))) - -(use-package evil - :ensure t - :config - (add-hook 'evil-mode-hook 'mssola-evil) - (evil-mode 1) - - ;; C-z is unused and it's close to my beloved C-c. Since I don't want to mess - ;; with one of the most sacred Emacs prefixes, I'm moving to C-z. - (define-key key-translation-map (kbd "C-z") [escape]) - (define-key evil-operator-state-map (kbd "C-z") 'keyboard-quit) - (set-quit-char "C-z") - - ; Setup the calendar for evil mode. - (eval-after-load "calendar" - '(progn (mssola-evil-calendar))) - - ;; Use the proper initial evil state for the following modes. - (evil-set-initial-state 'help-mode 'normal) - (evil-set-initial-state 'debugger-mode 'normal) - (evil-set-initial-state 'describe-mode 'normal) - (evil-set-initial-state 'Buffer-menu-mode 'normal) - - (use-package evil-leader - :ensure t - :config - (global-evil-leader-mode) - (evil-leader/set-leader ",") - (setq evil-leader/in-all-states 1) - (evil-leader/set-key - "," 'back-to-indentation - "a" 'mssola-helm-ag - "c" 'delete-window - "k" 'kill-buffer-and-window - "v" 'split-window-right - "V" (lambda () (interactive) (split-window-right) (other-window 1)) - "f" 'flycheck-list-errors - "e" 'eval-last-sexp - "b" 'view-buffer - "o" 'browse-url-at-point)) - - (use-package evil-surround - :ensure t - :config - (global-evil-surround-mode 1)) - - (use-package evil-args - :ensure t - :config - ; Configuration taken from the documentation of evil-args. +;;; And load the configuration for all the packages being used. - ;; Bind evil-args text objects - (define-key evil-inner-text-objects-map "a" 'evil-inner-arg) - (define-key evil-outer-text-objects-map "a" 'evil-outer-arg) - - ;; Bind evil-forward/backward-args - (define-key evil-normal-state-map "L" 'evil-forward-arg) - (define-key evil-normal-state-map "H" 'evil-backward-arg) - (define-key evil-motion-state-map "L" 'evil-forward-arg) - (define-key evil-motion-state-map "H" 'evil-backward-arg)) - - (use-package evil-numbers - :ensure t - :config - (define-key evil-normal-state-map (kbd "C-a") 'evil-numbers/inc-at-pt) - (define-key evil-normal-state-map (kbd "C-c -") 'evil-numbers/dec-at-pt))) - -(use-package magit - :ensure t - :config - - (evil-leader/set-key "s" 'magit-status) - - ; Proper initial states. - (add-hook 'git-commit-mode-hook 'evil-insert-state) - (evil-set-initial-state 'magit-log-edit-mode 'insert) - - ; When showing the status, hide the usually-redundant "branch" section and - ; show the rest. - (add-hook 'magit-section-set-visibility-hook - '(lambda (section) - (if (string= (magit-section-type section) "branch") - 'hide - 'show))) - - (use-package evil-magit - :ensure t - :config - - ; The magit + evil-magit combo messes up some chords, let's fix this. - (evil-define-key 'normal magit-mode-map - "\C-h" 'evil-window-left - "\C-l" 'evil-window-right - "\C-j" 'evil-window-down - "\C-k" 'evil-window-up - "\M-p" 'helm-projectile-switch-project))) - -;; Install a set of useful functions from @bbatsov. The bindings are following -;; Emacs style instead of being more Vim-like on purpose (I don't want to put -;; too many things into my leader and these shortcuts look sensible to me). -(use-package crux - :ensure t - :bind (("C-c d" . crux-delete-file-and-buffer) - ("C-c r" . crux-rename-file-and-buffer) - ("C-c i" . crux-find-user-init-file) - ("C-c o" . crux-open-with))) - -;; We will add this manually to the desired modes. -(use-package rainbow-delimiters - :ensure t) - -;; Disable all mouse interactions -(use-package disable-mouse - :ensure t - :config - (global-disable-mouse-mode)) - -;; Shows a popup with the available commands for the current chords. Helpful -;; when you don't remember a specific combination. -(use-package which-key - :ensure t) - -; Even though this is not entirely needed because Evil already covers most of -; it, sometimes it's convenient to use it. -(use-package expand-region - :ensure t - :config - - ; Set C-e as the expand command (mnemonic: expand). This command will - ; supercede the evil binding for "move one line", which I never use anyways. - (global-set-key (kbd "\C-e") 'er/expand-region) - (with-eval-after-load "evil" - '(progn - (define-key evil-normal-state-map (kbd "\C-e") 'er/expand-region) - (define-key evil-visual-state-map (kbd "\C-e") 'er/expand-region)))) - -; Directly taken from: https://github.com/magnars/expand-region.el. This way we -; can also expand the region into paragraphs and pages in text mode. -(defun er/add-text-mode-expansions () - (make-variable-buffer-local 'er/try-expand-list) - (setq er/try-expand-list (append - er/try-expand-list - '(mark-paragraph - mark-page)))) - -(add-hook 'text-mode-hook 'er/add-text-mode-expansions) +(require 'init-project) +(require 'init-edit) +(require 'init-misc) +(require 'init-evil) +(require 'init-magit) +(require 'init-mu4e) +(require 'init-lang) ;; TODO: dired evil -;; mu4e - -;; TODO: sending emails is a bit screwed in my current setup... - -;; TODO: refile -;;; http://comments.gmane.org/gmane.mail.mu.general/631 -;;; https://www.djcbsoftware.nl/code/mu/mu4e/Refiling-messages.html -;;; https://www.djcbsoftware.nl/code/mu/mu4e/Smart-refiling.html -;;; http://www.djcbsoftware.nl/code/mu/mu4e/Retrieval-and-indexing.html - -;; TODO: evil commands such as C-h, C-l -;; TODO: remove whole thread ? -;; TODO: signature below quote -;; TODO: gmail flags -;; TODO: msmtp - -; I'm using an RPM that I've built on OBS which installs mu4e globally. See: -; https://build.opensuse.org/package/show/home:mssola/mu -(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e") -(require 'mu4e) - -(when (featurep 'mu4e) - ; Define a proper shortcut. - (global-set-key (kbd "C-c m") 'mu4e) - - ;; Diferent SMTP options that will be used for each context. - - (setq message-send-mail-function 'smtpmail-send-it - mu4e-maildir (expand-file-name "~/.mail") - starttls-use-gnutls t) - - (defun mssola-gmail-smtp () - (require 'smtpmail) - - (setq smtpmail-starttls-credentials - '(("smtp.gmail.com" 587 nil nil)) - smtpmail-auth-credentials - (expand-file-name "~/.authinfo.gpg") - smtpmail-default-smtp-server "smtp.gmail.com" - smtpmail-smtp-server "smtp.gmail.com" - smtpmail-smtp-service 587)) - - (defun mssola-suse-smtp () - (require 'smtpmail) - - (setq smtpmail-starttls-credentials - '(("smtp.novell.com" 25 nil nil)) - smtpmail-auth-credentials - (expand-file-name "~/.authinfo.gpg") - smtpmail-default-smtp-server "smtp.novell.com" - smtpmail-smtp-server "smtp.novell.com" - smtpmail-smtp-service 25)) - - ;; Define the different accounts that I'm using. This is only available since - ;; mu 0.9.16. - - (setq mu4e-contexts - `( - ;; GMail - ,(make-mu4e-context - :name "gmail" - :enter-func (lambda () - (mu4e-message "Switching to gmail.com") - (mssola-gmail-smtp)) - :match-func (lambda (msg) - (when msg - (mu4e-message-contact-field-matches - msg :to "mikisabate@gmail.com"))) - :vars '( - (user-mail-address . "mikisabate@gmail.com") - (user-full-name . "Miquel Sabaté Solà") - (mu4e-reply-to-address . "mikisabate@gmail.com") - (mu4e-drafts-folder . "/gmail/Drafts") - (mu4e-sent-folder . "/gmail/Sent") - (mu4e-trash-folder . "/gmail/Trash"))) - - ;; suse.com - ,(make-mu4e-context - :name "comsuse" - :enter-func (lambda () - (mu4e-message "Switching to suse.com") - (mssola-suse-smtp)) - :match-func (lambda (msg) - (when msg - (mu4e-message-contact-field-matches - msg :to "msabate@suse.com"))) - :vars '( - (user-mail-address . "msabate@suse.com") - (user-full-name . "Miquel Sabaté Solà") - (mu4e-reply-to-address . "msabate@suse.com") - (mu4e-drafts-folder . "/susecom/Drafts") - (mu4e-sent-folder . "/susecom/Sent") - (mu4e-trash-folder . "/susecom/Trash"))) - - ;; suse.de - ,(make-mu4e-context - :name "desuse" - :enter-func (lambda () - (mu4e-message "Switching to suse.de") - (mssola-suse-smtp)) - :match-func (lambda (msg) - (when msg - (mu4e-message-contact-field-matches - msg :to "msabate@suse.de"))) - :vars '( - (user-mail-address . "msabate@suse.de") - (user-full-name . "Miquel Sabaté Solà") - (mu4e-reply-to-address . "msabate@suse.de") - (mu4e-drafts-folder . "/susede/Drafts") - (mu4e-sent-folder . "/susede/Sent") - (mu4e-trash-folder . "/susede/Trash"))))) - - ; If mu4e cannot figure things out, ask me. - (setq mu4e-context-policy 'ask) - (setq mu4e-compose-context-policy 'ask) - - ; Fill the `mu4e-user-mail-address-list' variable with the contexts. - (setq mu4e-user-mail-address-list - (delq nil - (mapcar (lambda (context) - (when (mu4e-context-vars context) - (cdr (assq 'user-mail-address - (mu4e-context-vars context))))) - mu4e-contexts))) - - ; Signature. - (setq mu4e-compose-signature - (concat - "Miquel Sabaté Solà\n" - "PGP: 4096R / 1BA5 3C7A C93D CA2A CFDF DA97 96BE 8C6F D89D 6565\n")) - - ; Sign outgoing emails - (add-hook 'message-send-hook 'mml-secure-message-sign-pgpmime) - - ; Don't save message to Sent Messages, mbsync will take care of that. - (setq mu4e-sent-messages-behavior 'delete) - - ;; To avoid UID clashes. See: - ;; http://pragmaticemacs.com/emacs/fixing-duplicate-uid-errors-when-using-mbsync-and-mu4e/ - (setq mu4e-change-filenames-when-moving t) - - ; Miscellaneous settings. - (setq mu4e-html2text-command "w3m -T text/html" - mu4e-attachment-dir "~/Downloads" - mu4e-headers-date-format "%Y-%m-%d %H:%M" - message-citation-line-format "%N @ %Y-%m-%d %H:%M %Z:\n" - message-citation-line-function 'message-insert-formatted-citation-line - message-kill-buffer-on-exit t - mu4e-get-mail-command "mbsync -aqV" - mu4e-update-interval 120 - mu4e-compose-dont-reply-to-self t - mu4e-headers-skip-duplicates t - mu4e-headers-include-related t - mu4e-headers-auto-update t) - - ;; Setting up shortcuts that can be composed with mu4e commands. For - ;; example, in this setup, pressin `ji` will jump to the inbox, and `ma` - ;; will move an email to the `All Mail` folder. - (setq mu4e-maildir-shortcuts - '( ("/INBOX" . ?i) - ("/Sent" . ?s) - ("/Trash" . ?t) - ("/Drafts" . ?d))) - - ;; The headers to show in the headers list a pair of a field and its width, - ;; with `nil' meaning 'unlimited' (better only use that for the last field. - ;; These are the defaults: - (setq mu4e-headers-fields - '( (:date . 18) - (:mailing-list . 15) - (:from-or-to . 20) - (:subject . nil))) - - ; Show images - (setq mu4e-view-show-images t - mu4e-view-image-max-width 800) - - ; Use imagemagick, if available - (when (fboundp 'imagemagick-register-types) - (imagemagick-register-types)) - - ; Webkit support for viewing HTML emails. See the following discussion: - ; https://groups.google.com/forum/#!topic/mu-discuss/JqHEGycEyKI - (defun my-mu4e-action-view-with-xwidget (msg) - "View the body of the message inside xwidget-webkit." - (unless (fboundp 'xwidget-webkit-browse-url) - (mu4e-error "No xwidget support available")) - (let* ((html (mu4e-message-field msg :body-html)) - (txt (mu4e-message-field msg :body-txt)) - (tmpfile (format "%s%x.html" temporary-file-directory (random t)))) - (unless (or html txt) - (mu4e-error "No body part for this message")) - (with-temp-buffer - ;; simplistic -- but note that it's only an example... - (insert (or html (concat "<pre>" txt "</pre>"))) - (write-file tmpfile) - (xwidget-webkit-browse-url (concat "file://" tmpfile) t)))) - - (add-to-list 'mu4e-view-actions - '("xViewXWidget" . my-mu4e-action-view-with-xwidget) t) - - ; Spell check - (add-hook 'mu4e-compose-mode-hook - (lambda () - "My settings for message composition." - (set-fill-column 99999) - (flyspell-mode))) - - ; Restore some of my evil config. - ; TODO: doesn't work - (dolist (imap '(mu4e-main-mode-map mu4e-headers-mode-map mu4e-view-mode-map)) - (evil-define-key 'evil-mu4e-state imap - "\C-h" 'evil-window-left - "\C-l" 'evil-window-right - "\C-j" 'evil-window-down - "\C-k" 'evil-window-up - "\M-p" 'helm-projectile-switch-project)) - - ; Desktop notifications - (use-package mu4e-alert - :ensure t - :config - - (mu4e-alert-set-default-style 'libnotify) - (add-hook 'after-init-hook #'mu4e-alert-enable-notifications) - (add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display) - (setq mu4e-alert-interesting-mail-query - "flag:unread AND NOT flag:trashed") - (setq mu4e-alert-email-notification-types '(count))) - - ; Evil mode in mu4e - (use-package evil-mu4e - :ensure t - :config)) - -(setq mu4e-update-interval 120) - -;;; Programming languages. - -(defun warnings-mode-hook () - "Hook for enabling the warning face on strings with a warning prefix." - - (font-lock-add-keywords nil - '(("\\(XXX\\|FIXME\\|TODO\\|HACK\\|NOTE\\)" - 1 font-lock-warning-face prepend)))) - -;; Text mode. -(add-hook 'text-mode-hook (lambda () (flyspell-mode 1))) - -;; Emacs Lisp mode -(add-hook 'emacs-lisp-mode-hook 'eldoc-mode) -(add-hook 'emacs-lisp-mode-hook 'warnings-mode-hook) -(add-hook 'emacs-lisp-mode-hook #'rainbow-delimiters-mode) - -;; C-common (it includes languages with a similar syntax of C). -(add-hook 'c-mode-common-hook 'warnings-mode-hook) -(add-hook 'c-mode-common-hook (lambda() (flyspell-prog-mode))) - -;; C -(add-hook 'c-mode-hook - (lambda () (setq indent-tabs-mode t))) - -;; C++ -(add-hook 'c++-mode-hook - (lambda () (setq indent-tabs-mode t))) - -(defun mssola-go-mode () - "My configuration for Go mode." - - ; Use goimports instead of go-fmt - (setq gofmt-command "goimports") - - ; Call Gofmt before saving - (add-hook 'before-save-hook 'gofmt-before-save) - - ; Integration flycheck with Go - (add-to-list 'load-path - (concat (getenv "GOPATH") "/src/github.com/dougm/goflymake")) - (require 'go-flycheck) - - (evil-leader/set-key - "." 'godef-jump-other-window) - - (setq indent-tabs-mode t) - (flyspell-prog-mode) - - ; eldoc support - (use-package go-eldoc - :ensure t - :config - (require 'go-eldoc)) - - (use-package go-add-tags - :ensure t - :config - (evil-leader/set-key "t" 'go-add-tags))) - -;; Go -(use-package go-mode - :ensure t - :pin melpa-stable - :config - - (add-hook 'go-mode-hook 'warnings-mode-hook) - (add-hook 'go-mode-hook 'go-eldoc-setup) - (add-hook 'go-mode-hook 'mssola-go-mode)) - -;; Ruby mode -(add-hook 'ruby-mode-hook 'warnings-mode-hook) - -;; Smart tabs -(use-package smart-tabs-mode - :ensure t - :config - (smart-tabs-add-language-support golang go-mode-hook - ((c-indent-line . c-basic-offset) - (c-indent-region . c-basic-offset))) - (smart-tabs-insinuate 'c 'c++ 'golang)) - -;; Markdown mode with preview mode in the browser. -(use-package markdown-mode - :ensure t - :config - - ; This is the one that I got from openSUSE. - (custom-set-variables - '(markdown-command "/usr/bin/markdown-calibre")) - - ; Preview mode does its things through websockets, so it's a requirement. - ; After that, we can safely require it. - (use-package websocket - :ensure t - :config - (use-package markdown-preview-mode - :ensure t))) - -; Slim -(use-package slim-mode - :ensure t) - -; YAML -(use-package yaml-mode - :ensure t - :config - - (add-hook 'yaml-mode-hook 'warnings-mode-hook)) - -; SASS/SCSS -(use-package scss-mode - :ensure t - :config - - (setq scss-compile-at-save nil) - (add-hook 'yaml-mode-hook 'warnings-mode-hook)) - -; Apply purple function identifiers to all these languages. -(dolist (lang-hook '(ruby-mode-hook - php-mode-hook - perl-mode-hook - emacs-lisp-mode-hook)) - (add-hook lang-hook 'soria-purple-identifiers)) - -;; YASnippet -(use-package yasnippet - :ensure t - :config - (yas-reload-all) - (add-hook 'prog-mode-hook #'yas-minor-mode)) - -;; CMake -(use-package cmake-mode - :ensure t - :config - - (setq auto-mode-alist - (append - '(("CMakeLists\\.txt\\'" . cmake-mode)) - '(("\\.cmake\\'" . cmake-mode)) - auto-mode-alist)) - - (use-package cmake-font-lock - :ensure t - :config - - (add-hook 'cmake-mode-hook 'cmake-font-lock-activate) - (add-hook 'yaml-mode-hook 'warnings-mode-hook))) - -(use-package dockerfile-mode - :ensure t - :config - - (add-to-list 'auto-mode-alist '("Dockerfile\\'" . dockerfile-mode))) - -(use-package terraform-mode - :ensure t - :config - - (terraform-format-on-save-mode)) - -(use-package hcl-mode - :ensure t) - -(use-package salt-mode - :ensure t) - -(use-package php-mode - :ensure t) - (provide 'init) ;;; init.el ends here diff --git a/emacs/settings/defuns.el b/emacs/settings/defuns.el new file mode 100644 index 0000000..4de9573 --- /dev/null +++ b/emacs/settings/defuns.el @@ -0,0 +1,62 @@ +;;; defuns.el --- Small utility functions + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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 <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; Small utility functions + +;;; Code: + +(defun mssola-view-emacs-latest-news () + "Allow users to fetch the latest Emacs' NEWS file." + (interactive) + + (url-copy-file + "http://git.savannah.gnu.org/cgit/emacs.git/plain/etc/NEWS" + "/tmp/emacs-news" t) + + (find-file-read-only "/tmp/emacs-news" t)) + +(defun mssola-cleanup-workspace () + "Cleanup the current workspace by killing all buffers and windows. +The user will end up in the *scratch* buffer." + + (interactive) + (when (y-or-n-p "Are you sure that you want to cleanup your workspace? ") + (delete-other-windows) + (switch-to-buffer "*scratch*") + + ; Code taken from 'crux-kill-other-buffers in the crux package by + ; @bbatsov. It's the same but the confirmation has been removed since we are + ; already asking for permission before. + (seq-each + #'kill-buffer + (delete (current-buffer) (seq-filter #'buffer-file-name (buffer-list)))))) + +(global-set-key (kbd "C-c a") 'mssola-cleanup-workspace) + +(defun emacs-init-time () + "Redefine the `emacs-init-time' function so it is more detailed. +Idea taken from @purcell." + + (interactive) + (let ((init-time + (float-time (time-subtract after-init-time before-init-time)))) + (message "%.3fs" init-time))) + +(provide 'defuns) +;;; defuns.el ends here diff --git a/emacs/settings/init-calendar.el b/emacs/settings/init-calendar.el new file mode 100644 index 0000000..a5b80c5 --- /dev/null +++ b/emacs/settings/init-calendar.el @@ -0,0 +1,46 @@ +;;; init-calendar.el --- Configuration for Calendar mode + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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 <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; Set Monday as the initial day and set Evil bindings. + +;;; Code: + +(defvar calendar-week-start-day 1) +(global-set-key (kbd "C-c c") 'calendar) + +(eval-after-load "evil" + '(progn + (evil-set-initial-state 'calendar-mode 'normal) + (evil-define-key 'normal calendar-mode-map + "j" 'calendar-forward-week + "k" 'calendar-backward-week + "b" 'calendar-backward-day + "h" 'calendar-backward-day + "l" 'calendar-forward-day + "w" 'calendar-forward-day + "q" 'calendar-exit + "\C-h" 'evil-window-left + "\C-l" 'evil-window-right + "\C-j" 'evil-window-down + "\C-k" 'evil-window-up + "\C-n" 'calendar-scroll-left-three-months + "\C-p" 'calendar-scroll-right-three-months))) + +(provide 'init-calendar) +;;; init-calendar.el ends here diff --git a/emacs/settings/init-edit.el b/emacs/settings/init-edit.el new file mode 100644 index 0000000..5d8dd7f --- /dev/null +++ b/emacs/settings/init-edit.el @@ -0,0 +1,89 @@ +;;; init-edit.el --- Packages for improving the editing experience + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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. + +;;; Commentary: +;; +;; Set of packages for improving the editing experience. + +;;; Code: + +(require 'use-package) + +;; Flycheck + +(use-package let-alist + :ensure t) + +(use-package flycheck + :ensure t + :config + (add-hook 'after-init-hook 'global-flycheck-mode) + + ;; Only show the errors buffer if it isn't there and if I'm saving the + ;; buffer. + (setq flycheck-emacs-lisp-load-path 'inherit) + (setq flycheck-check-syntax-automatically '(mode-enabled save)) + (setq flycheck-display-errors-function + #'flycheck-display-error-messages-unless-error-list)) + +;; Let's increase fonts with C-+ and C-- + +(use-package default-text-scale + :ensure t + :config + (global-set-key (kbd "C-+") 'default-text-scale-increase) + (global-set-key (kbd "C--") 'default-text-scale-decrease)) + +;; We will add this manually to the desired modes. +(use-package rainbow-delimiters + :ensure t) + +;; Disable all mouse interactions +(use-package disable-mouse + :ensure t + :config + (global-disable-mouse-mode)) + +;; Shows a popup with the available commands for the current chords. Helpful +;; when you don't remember a specific combination. +(use-package which-key + :ensure t) + +; Even though this is not entirely needed because Evil already covers most of +; it, sometimes it's convenient to use it. +(use-package expand-region + :ensure t + :config + + ; Set C-e as the expand command (mnemonic: expand). This command will + ; supercede the evil binding for "move one line", which I never use anyways. + (global-set-key (kbd "\C-e") 'er/expand-region) + (eval-after-load 'evil + '(progn + (define-key evil-normal-state-map (kbd "\C-e") 'er/expand-region) + (define-key evil-visual-state-map (kbd "\C-e") 'er/expand-region)))) + +; Directly taken from: https://github.com/magnars/expand-region.el. This way we +; can also expand the region into paragraphs and pages in text mode. +(defun er/add-text-mode-expansions () + (make-variable-buffer-local 'er/try-expand-list) + (setq er/try-expand-list (append + er/try-expand-list + '(mark-paragraph + mark-page)))) + +(add-hook 'text-mode-hook 'er/add-text-mode-expansions) + +(provide 'init-edit) +;;; init-edit.el ends here diff --git a/emacs/settings/init-evil.el b/emacs/settings/init-evil.el new file mode 100644 index 0000000..d0e8e91 --- /dev/null +++ b/emacs/settings/init-evil.el @@ -0,0 +1,157 @@ +;;; init-evil.el --- Evil configuration + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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 <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; All the configuration related to Evil mode. This file tackles *only* Evil +;; itself, not the configuration from other packages that provide evil bindings. + +;;; Code: + +(require 'use-package) + +(defun mssola-evil () + "Configure evil mode." + + ; We can safely remap <C-u> because the counting will be handled a-la Vim. + (define-key evil-normal-state-map (kbd "C-u") 'evil-scroll-up) + + ; Make window navigation easier. + (define-key evil-normal-state-map (kbd "C-j") 'evil-window-down) + (define-key evil-normal-state-map (kbd "C-k") 'evil-window-up) + (define-key evil-normal-state-map (kbd "C-l") 'evil-window-right) + (define-key evil-normal-state-map (kbd "C-h") 'evil-window-left) + + ; The window navigation tweaks effectively wipe out the help prefix, which + ; is bad. Fortunately we can workaround this by providing "M-h" as the new + ; help prefix. This prefix is only used in emacs mode to mark lines, which is + ; something already handled by Evil. + (define-key global-map (kbd "M-h") 'help-command) + (fset 'help-command help-map) + + ; Helm + Projectile shortcuts. + (eval-after-load 'helm-projectile + '(progn + (define-key evil-normal-state-map (kbd "M-p") + 'helm-projectile-switch-project))) + + ; I use the Super key in combination with j & k to move around i3. Let's unset + ; M- combos for these two fellows for whenever I misstype them. + (global-unset-key (kbd "M-j")) + (global-unset-key (kbd "M-k")) + + ; both this function and the subsequent lines about [escape] are taken from + ; @aaronbieber configuration. + (defun minibuffer-keyboard-quit () + "Abort recursive edit. +In Delete Selection mode, if the mark is active, just deactivate it; +then it takes a second \\[keyboard-quit] to abort the minibuffer." + (interactive) + (if (and delete-selection-mode transient-mark-mode mark-active) + (setq deactivate-mark t) + (when (get-buffer "*Completions*") (delete-windows-on "*Completions*")) + (abort-recursive-edit))) + + ;; Make escape quit everything, whenever possible. + (define-key evil-normal-state-map [escape] 'keyboard-quit) + (define-key evil-visual-state-map [escape] 'keyboard-quit) + (define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit) + (define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit) + (define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit) + (define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit) + (define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit) + + ; I store macros on the <q> register for convenience, so I used to use the + ; <C-q> combo to execute this macro in Vim. In Emacs though, this combo is + ; reserved to a rather useful function, and I'd like to keep it that way. So, + ; now the mapping is set to <C-2> (mnemonic: where the @ symbol is). Moreover, + ; it's applied as many times as specified by the numeric prefix argument. + (define-key evil-normal-state-map (kbd "C-2") + (lambda (n) + (interactive "p") + (evil-execute-macro n "@q"))) + + ; C-s: switch to normal mode and save the buffer. I know :) + (define-key evil-normal-state-map (kbd "C-s") 'save-buffer) + (define-key evil-insert-state-map (kbd "C-s") + (lambda () (interactive) (save-buffer) (evil-force-normal-state)))) + +(use-package evil + :ensure t + :config + + (add-hook 'evil-mode-hook 'mssola-evil) + (evil-mode 1) + + ;; C-z is unused and it's close to my beloved C-c. Since I don't want to mess + ;; with one of the most sacred Emacs prefixes, I'm moving to C-z. + (define-key key-translation-map (kbd "C-z") [escape]) + (define-key evil-operator-state-map (kbd "C-z") 'keyboard-quit) + (set-quit-char "C-z") + + ;; Use the proper initial evil state for the following modes. + (evil-set-initial-state 'help-mode 'normal) + (evil-set-initial-state 'debugger-mode 'normal) + (evil-set-initial-state 'describe-mode 'normal) + (evil-set-initial-state 'Buffer-menu-mode 'normal) + + (use-package evil-leader + :ensure t + :config + (global-evil-leader-mode) + (evil-leader/set-leader ",") + (setq evil-leader/in-all-states 1) + (evil-leader/set-key + "," 'back-to-indentation + "c" 'delete-window + "k" 'kill-buffer-and-window + "v" 'split-window-right + "V" (lambda () (interactive) (split-window-right) (other-window 1)) + "f" 'flycheck-list-errors + "e" 'eval-last-sexp + "b" 'view-buffer + "o" 'browse-url-at-point)) + + (use-package evil-surround + :ensure t + :config + (global-evil-surround-mode 1)) + + (use-package evil-args + :ensure t + :config + ; Configuration taken from the documentation of evil-args. + + ;; Bind evil-args text objects + (define-key evil-inner-text-objects-map "a" 'evil-inner-arg) + (define-key evil-outer-text-objects-map "a" 'evil-outer-arg) + + ;; Bind evil-forward/backward-args + (define-key evil-normal-state-map "L" 'evil-forward-arg) + (define-key evil-normal-state-map "H" 'evil-backward-arg) + (define-key evil-motion-state-map "L" 'evil-forward-arg) + (define-key evil-motion-state-map "H" 'evil-backward-arg)) + + (use-package evil-numbers + :ensure t + :config + (define-key evil-normal-state-map (kbd "C-a") 'evil-numbers/inc-at-pt) + (define-key evil-normal-state-map (kbd "C-c -") 'evil-numbers/dec-at-pt))) + + +(provide 'init-evil) +;;; init-evil.el ends here diff --git a/emacs/settings/init-general.el b/emacs/settings/init-general.el new file mode 100644 index 0000000..33d8d67 --- /dev/null +++ b/emacs/settings/init-general.el @@ -0,0 +1,133 @@ +;;; init-general.el --- Basic general configuration + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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 <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; General stuff like better defaults, appearance, basic editing, etc. + +;;; Code: + +;; UTF-8 *always*. +(prefer-coding-system 'utf-8) +(set-default-coding-systems 'utf-8) +(set-terminal-coding-system 'utf-8) +(set-keyboard-coding-system 'utf-8) +(set-language-environment 'utf-8) + +;; No welcome screen +(setq-default inhibit-startup-message t) + +;; Enable y/n answers +(fset 'yes-or-no-p 'y-or-n-p) + +;; The frame title is "<login>: <path>". If we are not editing a file, then the +;; name of the buffer is displayed (e.g. "mssola: *scratch*"). +(setq frame-title-format + '((:eval + (concat (user-real-login-name) ": " + (if (buffer-file-name) + (abbreviate-file-name (buffer-file-name)) + "%b"))))) + +;; Emacs modes typically provide a standard means to change the indentation +;; width (e.g. c-basic-offset). Moreover, even though I prefer tabs over space, +;; for most coding conventions this is not the case (e.g. ruby). For this +;; reason, I will disable them by default and enabled them back for each +;; specific case (e.g. C). I'm also using the smart-tabs-mode package, see +;; below in the languages section. +(setq-default indent-tabs-mode nil) +(setq-default tab-width 4) + +;; Maximum 80 columns. +(setq-default fill-column 80) +(setq-default auto-fill-function 'do-auto-fill) + +;; Delete the selection with a keypress. +(delete-selection-mode t) + +;; Revert buffers automatically when underlying files are changed externally +(global-auto-revert-mode t) + +;; Do not break lines +(set-default 'truncate-lines t) + +;; No backups +(setq-default make-backup-files nil) +(setq-default auto-save-default nil) + +;; Nice scrolling +(setq scroll-margin 0 + scroll-conservatively 100000 + scroll-preserve-screen-position 1) + +;; Remove whitespaces at the end of line +(add-hook 'before-save-hook 'delete-trailing-whitespace) + +;; Graphical interface tweaks +(menu-bar-mode -1) +(when (fboundp 'set-scroll-bar-mode) + (set-scroll-bar-mode nil)) +(when (fboundp 'tool-bar-mode) + (tool-bar-mode -1)) + +;; Lines and columns +(line-number-mode 1) +(column-number-mode 1) + +;; Cursor +(blink-cursor-mode 0) +(global-hl-line-mode -1) +(show-paren-mode 1) + +;; Follow symlinks +(setq vc-follow-symlinks t) + +;; Default font + +(set-frame-font "Droid Sans Mono Dotted for Powerline-10") +(add-to-list 'default-frame-alist '(font . "Droid Sans Mono Dotted for Powerline-10")) + +; Emacs in daemon mode does not like `set-face-attribute` because this is only +; applied if there is a frame in place, which doesn't happen when starting the +; daemon. Thus, we should call that after the frame has been created (e.g. by +; emacsclient). +; See: https://lists.gnu.org/archive/html/help-gnu-emacs/2015-03/msg00016.html +(add-hook 'after-make-frame-functions-hook + (lambda () + (set-face-attribute 'default t :font "Droid Sans Mono Dotted for Powerline-10"))) + +;; Disable C-z. It will later on be picked up by Evil's config as the escape +;; sequence. This is here to make sure that it will be disabled even if Evil +;; cannot be loaded due to some error. +(global-unset-key (kbd "C-z")) + +;; Disable all the Fn keys. +(dotimes (i 12) + (global-unset-key (kbd (format "<f%d>" (+ i 1))))) + +;; Let flyspell be performant. +(defvar flyspell-issue-message-flag nil) + +; Kill Emacs. Sometimes useful when you have a server-client setup and you want +; to *really* evaluate everything from scratch. +(global-set-key (kbd "C-c k") 'kill-emacs) + +;; Default theme +(load-theme 'soria t) + +(provide 'init-general) +;;; init-general.el ends here diff --git a/emacs/settings/init-lang.el b/emacs/settings/init-lang.el new file mode 100644 index 0000000..e53b969 --- /dev/null +++ b/emacs/settings/init-lang.el @@ -0,0 +1,193 @@ +;;; init-lang.el --- Settings for all programming languages + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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. + +;;; Commentary: +;; +;; Settings for all programming languages. + +;;; Code: + +(require 'use-package) + +(defun warnings-mode-hook () + "Hook for enabling the warning face on strings with a warning prefix." + + (font-lock-add-keywords nil + '(("\\(XXX\\|FIXME\\|TODO\\|HACK\\|NOTE\\)" + 1 font-lock-warning-face prepend)))) + +;; Text mode. +(add-hook 'text-mode-hook (lambda () (flyspell-mode 1))) + +;; Emacs Lisp mode +(add-hook 'emacs-lisp-mode-hook 'eldoc-mode) +(add-hook 'emacs-lisp-mode-hook 'warnings-mode-hook) +(add-hook 'emacs-lisp-mode-hook #'rainbow-delimiters-mode) + +;; C-common (it includes languages with a similar syntax of C). +(add-hook 'c-mode-common-hook 'warnings-mode-hook) +(add-hook 'c-mode-common-hook (lambda() (flyspell-prog-mode))) + +;; C +(add-hook 'c-mode-hook + (lambda () (setq indent-tabs-mode t))) + +;; C++ +(add-hook 'c++-mode-hook + (lambda () (setq indent-tabs-mode t))) + +;; Ruby mode +(add-hook 'ruby-mode-hook 'warnings-mode-hook) + +(defun mssola-go-mode () + "My configuration for Go mode." + + ; Use goimports instead of go-fmt + (setq gofmt-command "goimports") + + ; Call Gofmt before saving + (add-hook 'before-save-hook 'gofmt-before-save) + + ; Integration flycheck with Go + (add-to-list 'load-path + (concat (getenv "GOPATH") "/src/github.com/dougm/goflymake")) + (require 'go-flycheck) + + (evil-leader/set-key + "." 'godef-jump-other-window) + + (setq indent-tabs-mode t) + (flyspell-prog-mode) + + ; eldoc support + (use-package go-eldoc + :ensure t + :config + (require 'go-eldoc)) + + (use-package go-add-tags + :ensure t + :config + (evil-leader/set-key "t" 'go-add-tags))) + +;; Go +(use-package go-mode + :ensure t + :pin melpa-stable + :config + + (add-hook 'go-mode-hook 'warnings-mode-hook) + (add-hook 'go-mode-hook 'go-eldoc-setup) + (add-hook 'go-mode-hook 'mssola-go-mode)) + +;; Smart tabs +(use-package smart-tabs-mode + :ensure t + :config + (smart-tabs-add-language-support golang go-mode-hook + ((c-indent-line . c-basic-offset) + (c-indent-region . c-basic-offset))) + (smart-tabs-insinuate 'c 'c++ 'golang)) + +;; Markdown mode with preview mode in the browser. +(use-package markdown-mode + :ensure t + :config + + ; This is the one that I got from openSUSE. + (custom-set-variables + '(markdown-command "/usr/bin/markdown-calibre")) + + ; Preview mode does its things through websockets, so it's a requirement. + ; After that, we can safely require it. + (use-package websocket + :ensure t + :config + (use-package markdown-preview-mode + :ensure t))) + +; Slim +(use-package slim-mode + :ensure t) + +; YAML +(use-package yaml-mode + :ensure t + :config + + (add-hook 'yaml-mode-hook 'warnings-mode-hook)) + +; SASS/SCSS +(use-package scss-mode + :ensure t + :config + + (setq scss-compile-at-save nil) + (add-hook 'yaml-mode-hook 'warnings-mode-hook)) + +; Apply purple function identifiers to all these languages. +(dolist (lang-hook '(ruby-mode-hook + php-mode-hook + perl-mode-hook + emacs-lisp-mode-hook)) + (add-hook lang-hook 'soria-purple-identifiers)) + +;; YASnippet +(use-package yasnippet + :ensure t + :config + (yas-reload-all) + (add-hook 'prog-mode-hook #'yas-minor-mode)) + +;; CMake +(use-package cmake-mode + :ensure t + :config + + (setq auto-mode-alist + (append + '(("CMakeLists\\.txt\\'" . cmake-mode)) + '(("\\.cmake\\'" . cmake-mode)) + auto-mode-alist)) + + (use-package cmake-font-lock + :ensure t + :config + + (add-hook 'cmake-mode-hook 'cmake-font-lock-activate) + (add-hook 'yaml-mode-hook 'warnings-mode-hook))) + +(use-package dockerfile-mode + :ensure t + :config + + (add-to-list 'auto-mode-alist '("Dockerfile\\'" . dockerfile-mode))) + +(use-package terraform-mode + :ensure t + :config + + (terraform-format-on-save-mode)) + +(use-package hcl-mode + :ensure t) + +(use-package salt-mode + :ensure t) + +(use-package php-mode + :ensure t) + +(provide 'init-lang) +;;; init-lang.el ends here diff --git a/emacs/settings/init-magit.el b/emacs/settings/init-magit.el new file mode 100644 index 0000000..7ddbc3c --- /dev/null +++ b/emacs/settings/init-magit.el @@ -0,0 +1,56 @@ +;;; init-magit.el --- Magit configuration + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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. + +;;; Commentary: +;; +;; Magit configuration + +;;; Code: + +(require 'use-package) + +(use-package magit + :ensure t + :config + + ; Proper initial states. + (add-hook 'git-commit-mode-hook 'evil-insert-state) + (evil-set-initial-state 'magit-log-edit-mode 'insert) + + ; When showing the status, hide the usually-redundant "branch" section and + ; show the rest. + (add-hook 'magit-section-set-visibility-hook + '(lambda (section) + (if (string= (magit-section-type section) "branch") + 'hide + 'show))) + + (eval-after-load 'evil-leader + '(progn + (evil-leader/set-key "s" 'magit-status) + + (use-package evil-magit + :ensure t + :config + + ; The magit + evil-magit combo messes up some chords, let's fix this. + (evil-define-key 'normal magit-mode-map + "\C-h" 'evil-window-left + "\C-l" 'evil-window-right + "\C-j" 'evil-window-down + "\C-k" 'evil-window-up + "\M-p" 'helm-projectile-switch-project))))) + +(provide 'init-magit) +;;; init-magit.el ends here diff --git a/emacs/settings/init-misc.el b/emacs/settings/init-misc.el new file mode 100644 index 0000000..1a2a64f --- /dev/null +++ b/emacs/settings/init-misc.el @@ -0,0 +1,34 @@ +;;; init-misc.el --- Miscellaneous packages + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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. + +;;; Commentary: +;; +;; Set of packages for improving the editing experience. + +;;; Code: + +(require 'use-package) + +;; Install a set of useful functions from @bbatsov. The bindings are following +;; Emacs style instead of being more Vim-like on purpose (I don't want to put +;; too many things into my leader and these shortcuts look sensible to me). +(use-package crux + :ensure t + :bind (("C-c d" . crux-delete-file-and-buffer) + ("C-c r" . crux-rename-file-and-buffer) + ("C-c i" . crux-find-user-init-file) + ("C-c o" . crux-open-with))) + +(provide 'init-misc) +;;; init-misc.el ends here diff --git a/emacs/settings/init-mu4e.el b/emacs/settings/init-mu4e.el new file mode 100644 index 0000000..b008af5 --- /dev/null +++ b/emacs/settings/init-mu4e.el @@ -0,0 +1,261 @@ +;;; init-mu4e.el --- Configuration for mu4e + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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. + +;;; Commentary: +;; +;; Configuration for mu4e. This configuration makes quite some assumptions. +;; Read the `emacs/README.md` file as provided in my `dotfiles` project to +;; get more details. + +;;; Code: + +;; TODO: sending emails is a bit screwed in my current setup... + +;; TODO: refile +;;; http://comments.gmane.org/gmane.mail.mu.general/631 +;;; https://www.djcbsoftware.nl/code/mu/mu4e/Refiling-messages.html +;;; https://www.djcbsoftware.nl/code/mu/mu4e/Smart-refiling.html +;;; http://www.djcbsoftware.nl/code/mu/mu4e/Retrieval-and-indexing.html + +;; TODO: evil commands such as C-h, C-l +;; TODO: remove whole thread ? +;; TODO: signature below quote +;; TODO: gmail flags +;; TODO: msmtp + +; I'm using an RPM that I've built on OBS which installs mu4e globally. See: +; https://build.opensuse.org/package/show/home:mssola/mu +(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e") +(require 'mu4e) + +(when (featurep 'mu4e) + ; Define a proper shortcut. + (global-set-key (kbd "C-c m") 'mu4e) + + ;; Diferent SMTP options that will be used for each context. + + (setq message-send-mail-function 'smtpmail-send-it + mu4e-maildir (expand-file-name "~/.mail") + starttls-use-gnutls t) + + (defun mssola-gmail-smtp () + (require 'smtpmail) + + (setq smtpmail-starttls-credentials + '(("smtp.gmail.com" 587 nil nil)) + smtpmail-auth-credentials + (expand-file-name "~/.authinfo.gpg") + smtpmail-default-smtp-server "smtp.gmail.com" + smtpmail-smtp-server "smtp.gmail.com" + smtpmail-smtp-service 587)) + + (defun mssola-suse-smtp () + (require 'smtpmail) + + (setq smtpmail-starttls-credentials + '(("smtp.novell.com" 25 nil nil)) + smtpmail-auth-credentials + (expand-file-name "~/.authinfo.gpg") + smtpmail-default-smtp-server "smtp.novell.com" + smtpmail-smtp-server "smtp.novell.com" + smtpmail-smtp-service 25)) + + ;; Define the different accounts that I'm using. This is only available since + ;; mu 0.9.16. + + (setq mu4e-contexts + `( + ;; GMail + ,(make-mu4e-context + :name "gmail" + :enter-func (lambda () + (mu4e-message "Switching to gmail.com") + (mssola-gmail-smtp)) + :match-func (lambda (msg) + (when msg + (mu4e-message-contact-field-matches + msg :to "mikisabate@gmail.com"))) + :vars '( + (user-mail-address . "mikisabate@gmail.com") + (user-full-name . "Miquel Sabaté Solà") + (mu4e-reply-to-address . "mikisabate@gmail.com") + (mu4e-drafts-folder . "/gmail/Drafts") + (mu4e-sent-folder . "/gmail/Sent") + (mu4e-trash-folder . "/gmail/Trash"))) + + ;; suse.com + ,(make-mu4e-context + :name "comsuse" + :enter-func (lambda () + (mu4e-message "Switching to suse.com") + (mssola-suse-smtp)) + :match-func (lambda (msg) + (when msg + (mu4e-message-contact-field-matches + msg :to "msabate@suse.com"))) + :vars '( + (user-mail-address . "msabate@suse.com") + (user-full-name . "Miquel Sabaté Solà") + (mu4e-reply-to-address . "msabate@suse.com") + (mu4e-drafts-folder . "/susecom/Drafts") + (mu4e-sent-folder . "/susecom/Sent") + (mu4e-trash-folder . "/susecom/Trash"))) + + ;; suse.de + ,(make-mu4e-context + :name "desuse" + :enter-func (lambda () + (mu4e-message "Switching to suse.de") + (mssola-suse-smtp)) + :match-func (lambda (msg) + (when msg + (mu4e-message-contact-field-matches + msg :to "msabate@suse.de"))) + :vars '( + (user-mail-address . "msabate@suse.de") + (user-full-name . "Miquel Sabaté Solà") + (mu4e-reply-to-address . "msabate@suse.de") + (mu4e-drafts-folder . "/susede/Drafts") + (mu4e-sent-folder . "/susede/Sent") + (mu4e-trash-folder . "/susede/Trash"))))) + + ; If mu4e cannot figure things out, ask me. + (setq mu4e-context-policy 'ask) + (setq mu4e-compose-context-policy 'ask) + + ; Fill the `mu4e-user-mail-address-list' variable with the contexts. + (setq mu4e-user-mail-address-list + (delq nil + (mapcar (lambda (context) + (when (mu4e-context-vars context) + (cdr (assq 'user-mail-address + (mu4e-context-vars context))))) + mu4e-contexts))) + + ; Signature. + (setq mu4e-compose-signature + (concat + "Miquel Sabaté Solà\n" + "PGP: 4096R / 1BA5 3C7A C93D CA2A CFDF DA97 96BE 8C6F D89D 6565\n")) + + ; Sign outgoing emails + (add-hook 'message-send-hook 'mml-secure-message-sign-pgpmime) + + ; Don't save message to Sent Messages, mbsync will take care of that. + (setq mu4e-sent-messages-behavior 'delete) + + ;; To avoid UID clashes. See: + ;; http://pragmaticemacs.com/emacs/fixing-duplicate-uid-errors-when-using-mbsync-and-mu4e/ + (setq mu4e-change-filenames-when-moving t) + + ; Miscellaneous settings. + (setq mu4e-html2text-command "w3m -T text/html" + mu4e-attachment-dir "~/Downloads" + mu4e-headers-date-format "%Y-%m-%d %H:%M" + message-citation-line-format "%N @ %Y-%m-%d %H:%M %Z:\n" + message-citation-line-function 'message-insert-formatted-citation-line + message-kill-buffer-on-exit t + mu4e-get-mail-command "mbsync -aqV" + mu4e-update-interval 120 + mu4e-compose-dont-reply-to-self t + mu4e-headers-skip-duplicates t + mu4e-headers-include-related t + mu4e-headers-auto-update t) + + ;; Setting up shortcuts that can be composed with mu4e commands. For + ;; example, in this setup, pressin `ji` will jump to the inbox, and `ma` + ;; will move an email to the `All Mail` folder. + (setq mu4e-maildir-shortcuts + '( ("/INBOX" . ?i) + ("/Sent" . ?s) + ("/Trash" . ?t) + ("/Drafts" . ?d))) + + ;; The headers to show in the headers list a pair of a field and its width, + ;; with `nil' meaning 'unlimited' (better only use that for the last field. + ;; These are the defaults: + (setq mu4e-headers-fields + '( (:date . 18) + (:mailing-list . 15) + (:from-or-to . 20) + (:subject . nil))) + + ; Show images + (setq mu4e-view-show-images t + mu4e-view-image-max-width 800) + + ; Use imagemagick, if available + (when (fboundp 'imagemagick-register-types) + (imagemagick-register-types)) + + ; Webkit support for viewing HTML emails. See the following discussion: + ; https://groups.google.com/forum/#!topic/mu-discuss/JqHEGycEyKI + (defun my-mu4e-action-view-with-xwidget (msg) + "View the body of the message inside xwidget-webkit." + (unless (fboundp 'xwidget-webkit-browse-url) + (mu4e-error "No xwidget support available")) + (let* ((html (mu4e-message-field msg :body-html)) + (txt (mu4e-message-field msg :body-txt)) + (tmpfile (format "%s%x.html" temporary-file-directory (random t)))) + (unless (or html txt) + (mu4e-error "No body part for this message")) + (with-temp-buffer + ;; simplistic -- but note that it's only an example... + (insert (or html (concat "<pre>" txt "</pre>"))) + (write-file tmpfile) + (xwidget-webkit-browse-url (concat "file://" tmpfile) t)))) + + (add-to-list 'mu4e-view-actions + '("xViewXWidget" . my-mu4e-action-view-with-xwidget) t) + + ; Spell check + (add-hook 'mu4e-compose-mode-hook + (lambda () + "My settings for message composition." + (set-fill-column 99999) + (flyspell-mode))) + + ; Restore some of my evil config. + ; TODO: doesn't work + (dolist (imap '(mu4e-main-mode-map mu4e-headers-mode-map mu4e-view-mode-map)) + (evil-define-key 'evil-mu4e-state imap + "\C-h" 'evil-window-left + "\C-l" 'evil-window-right + "\C-j" 'evil-window-down + "\C-k" 'evil-window-up + "\M-p" 'helm-projectile-switch-project)) + + ; Desktop notifications + (use-package mu4e-alert + :ensure t + :config + + (mu4e-alert-set-default-style 'libnotify) + (add-hook 'after-init-hook #'mu4e-alert-enable-notifications) + (add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display) + (setq mu4e-alert-interesting-mail-query + "flag:unread AND NOT flag:trashed") + (setq mu4e-alert-email-notification-types '(count))) + + ; Evil mode in mu4e + (eval-after-load 'evil + '(progn + (use-package evil-mu4e + :ensure t + :config)))) + +(setq mu4e-update-interval 120) + +(provide 'init-mu4e) +;;; init-mu4e.el ends here diff --git a/emacs/settings/init-project.el b/emacs/settings/init-project.el new file mode 100644 index 0000000..36b9199 --- /dev/null +++ b/emacs/settings/init-project.el @@ -0,0 +1,119 @@ +;;; init-project.el --- Packages for interacting with projects + +;; Copyright (C) 2016 Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; 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. + +;;; Commentary: +;; +;; Set of packages and their configurations for managing projects inside of +;; GNU Emacs. + +;;; Code: + +(require 'use-package) + +;;; Ayo silver! + +(use-package ag + :ensure t + :config + + (eval-after-load 'evil + '(progn + (add-hook 'ag-mode-hook + (lambda () + (define-key ag-mode-map (kbd "n") 'evil-search-next) + (define-key ag-mode-map (kbd "N") 'evil-search-previous) + (define-key ag-mode-map (kbd "gg") 'evil-goto-first-line))))) + (setq ag-reuse-buffers t) + (setq ag-reuse-window t)) + +;;; Projectile & Helm + +(use-package projectile + :ensure t + :config + (projectile-global-mode +1) + (setq projectile-mode-line + '(:eval (format " %s" (projectile-project-name))))) + +(use-package helm + :ensure t + :config + (setq projectile-completion-system 'helm) + + ; Allow the search pattern to be on the header. Taken from this Reddit thread: + ; https://www.reddit.com/r/emacs/comments/3asbyn/new_and_very_useful_helm_feature_enter_search/ + (setq helm-echo-input-in-header-line t) + + (defun helm-hide-minibuffer-maybe () + "Hide the minibuffer if we are in a Helm session" + + (when (with-helm-buffer helm-echo-input-in-header-line) + (let ((ov (make-overlay (point-min) (point-max) nil nil t))) + (overlay-put ov 'window (selected-window)) + (overlay-put ov 'face (let ((bg-color (face-background 'default nil))) + `(:background ,bg-color :foreground ,bg-color))) + (setq-local cursor-type nil)))) + + (add-hook 'helm-minibuffer-set-up-hook 'helm-hide-minibuffer-maybe) + (setq helm-split-window-in-side-p t) + + ; Preview files with tab + (define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action) + + ; Show available options + (define-key helm-map (kbd "C-a") 'helm-select-action) + + ; Some vim-like bindings + (define-key helm-map (kbd "C-j") 'helm-next-line) + (define-key helm-map (kbd "C-k") 'helm-previous-line) + + (use-package helm-ag + :ensure t)) + +(use-package helm-projectile + :ensure t + :config + (helm-projectile-on)) + +;;; Finally setup the bindings for Evil mode + +(defun mssola-helm-ag () + "Call the right ag command for helm-ag." + + (interactive) + + (condition-case nil + (helm-ag-project-root) + (error (helm-ag)))) + +(defun mssola-find-file () + "Call the proper Helm function for finding files." + + (interactive) + + (condition-case nil + (helm-projectile-find-file) + (error + (helm-find-files nil)))) + +(eval-after-load 'evil + '(progn + (define-key evil-normal-state-map (kbd "C-p") 'mssola-find-file))) + +(eval-after-load 'evil-leader + '(progn + (evil-leader/set-key "a" 'mssola-helm-ag))) + +(provide 'init-project) +;;; init-project.el ends here |
