Skip to content

Instantly share code, notes, and snippets.

@nsmaciej
Last active August 29, 2015 14:05
Show Gist options
  • Save nsmaciej/3b59ef19487e32466990 to your computer and use it in GitHub Desktop.
Save nsmaciej/3b59ef19487e32466990 to your computer and use it in GitHub Desktop.
My broken emacs config
;; init.el - Maciej's Emacs config
;; Copyright (C) 2014 Maciej Goszczycki
;; 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 2
;; 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, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
;; Here for you since 2014.. and to think I used to use Vim
;; === TODO ===
;; * Add a _Good_ project management system
;; * MORE Org-mode! Easy note taking
;; * Custom mode-line
;; * Setup TRAMP in detail
;; * Open NeoTree at current location
;; * YuoTube player - not kidding
;; * Popwin.el for Helm and others
;; * Better highlight symbol, highlight jump
;; * A different eshell for each workspace
;; * Emacs IRC, RSS, Email
;; * Set up calendar
;; * Eaasier alias for repeat
;; * Learn to stop using God-mode
;; * Remember mode
;; * More welcome scratch buffer: fortune, my keybindings and stuff to learn
;; First saved change was on 25th of August 2014
;; Proper sectioning and this log was added on 8th of November 2014
;; Copyright notice was added on 18th of November 2014 - I also discovered TRAMP
;; God-mode was replace with Evil-mode on 28th of Devember 2014
;; Stopped using Emacs for AIPO in January 2015
;; Came back to Emac for Haskell on 31st of May 2015
;; All in one file.. It's not versatile if moving your config requires you
;; to scp whole folders. Split into sections instead.
;; on sections, almost copied from: http://metasandwich.com/2013/01/19/emacs-config-youre-doing-it-wrong/
;; Jump menu is globally bound to "C-c i" but with God mode it's just "c <space> i"
;; This file contains volume control which means you/I can safely switch Mac OS X to Function mode without
;; sacrificing volume control.. Control is bound to <f11> and <f12> just like by default. Half-step increase
;; is achieved by adding a raw prefix argument before pressing the function keys
;;;; Early setup
;; Disable stupid GUI things quickly before anything else loads
(tool-bar-mode -1)
(scroll-bar-mode -1)
(blink-cursor-mode -1)
;; Mouse in the terminal
;; Makes it hard to select text :(
;; (xterm-mouse-mode 1)
;; Also disable the stupid Terminal elemenst
(menu-bar-mode -1)
(column-number-mode 1)
;; Load packages for stuff below
(package-initialize)
;; Setup extra package reopsitories
(require 'package)
(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t)
;;;; Config functions
(defun ensure-installed (name)
"Ensure a package is installed"
(unless (package-installed-p name)
(package-install name)))
(defun require-and-ensure-installed (name &rest body)
"Require a package, if not installed, install it"
(ensure-installed name)
(apply 'require name body))
(defmacro defun-hook (hooks arguments &rest body)
"Syntaxtic sugar for add-hook with lambda, for convinience"
(unless (listp hooks) (setq hooks (list hooks)))
(cons 'progn (mapcar (lambda (hook)
`(add-hook (quote ,hook) (lambda ,arguments ,@body))) hooks)))
(defun add-hooks (hooks function &optional append local)
"Add multiple hooks at the same time"
(mapc (lambda (hook) (add-hook hook function append local)) hooks))
;;;; Config variables
;;; Note to self: I should mostly get rid of this section.. with imenu it's easier
;;; to just look variables up than having them all in a giant mess here
(defvar modes-without-electric-indent '(fundamental-mode python-mode org-mode)
"Modes in which to not enable eletric indent")
(defvar custom-fringe-size 5
"Size my init.el will use for fringes")
(defvar extra-path-locations (list "/applications/racket v6.1/bin"
"/usr/local/bin"
(expand-file-name "~/library/haskell/bin"))
"folders to add to various emacs path variables")
(defvar my-default-indention 4 "Default indention my init.el should try to use")
;; WTF! Emacs-lisp-mode isn't derived from Lisp-mode?
(defvar modes-with-eletric-pair '(lisp-mode emacs-lisp-mode)
"Modes for which `electric-pair-mode' should be enabled.
Modes are checked using `derived-mode-p'.")
(defvar my-chosen-shell "bash"
"Shell the launch bash-shell with.. The executable should be in Emacs's PATH
As of the day I'm witting this, all it takes is adding the path do `extra-path-locations'.
Or you can just specify explicit path.. but that's going to break someday")
;;;; Misc handy functions
(defun switch-to-modified (&optional frame)
"Looks for modified buffers in `buffer-list' that has a file associated and switches to it"
(interactive)
(let ((buffers (buffer-list))
(found-buffer nil))
(while (not (or (null buffers)
found-buffer))
(when (and (buffer-modified-p (car buffers))
(buffer-file-name (car buffers)))
(setq found-buffer (car buffers)))
(setq buffers (cdr buffers)))
(if found-buffer
(switch-to-buffer found-buffer)
(message "No modified buffer found"))))
(defun test-function ()
"This is a blank test function. You can change it's definition.
It is bound to \\[test-function] for convenience"
(interactive)
(message "You called the test function with %s") this-command-keys-vector)
(defun jump-to-scratch (p)
"Jump to scratch buffer.. adding a prefix argument creates an extra buffer
named *scratchN* \(where N is the prefix argument\(. No prefix or 1 prefix jumps
to the standard buffer. Handy for quick note taking
Bound to \\[jump-to-scratch] for ease of access.
It also fires `change-major-mode-hook'.. Really it's just a nice generic
way to activate God-mode.. that ensures if I ever change my mind this function
will remain valid"
(interactive "p")
(switch-to-buffer (format "*scratch%s*" (if (> p 1) p "")))
(run-hooks 'change-major-mode-hook))
(defun jump-to-eshell (p)
"Close cousin of `jump-to-scratch'. Specify prefix argument to jump to *eshell*<N>.
1 or no prefix argument jump to just *eshell*. It's bound to \\[jump-to-eshell] for easy access"
(interactive "p")
(if (> p 1) (eshell p) (eshell)))
(defun jump-back-to-buffer ()
"The opposite of `jump-to-scratch' and `jump-to-eshell'. Jumps to the \"other buffer\"
that isn't an \"asterisk\" buffer. The \"other buffer\" is acquired using `window-prev-buffers'
which means that I can quickly jump to scratch and eshell and then get back to the same file."
(interactive)
(let ((prev-buffers (window-prev-buffers))
(found-buffer nil))
(while (not (or (null prev-buffers) found-buffer))
(when (not (or
(string-match-p "^\\*.*\\*\\(<[0-9]+>\\)?$" (buffer-name (caar prev-buffers)))
(eq (current-buffer) (caar prev-buffers))))
(setq found-buffer (caar prev-buffers)))
(setq prev-buffers (cdr prev-buffers)))
(switch-to-buffer found-buffer)))
(defun words-in-paragraph ()
"Count the number of words in the paragraph (selected with `mark-paragraph')"
(interactive)
(save-excursion
(mark-paragraph)
(call-interactively 'count-words-region)))
(defun kill-buffer-save (&optional buffer-name)
"Like to `kill-ring-save' but selects the whole buffer automatically"
(interactive)
(copy-region-as-kill (point-min) (point-max)))
(defun unfill-paragraph ()
"Takes a multi-line paragraph and makes it into a single line of text.
Stefan Monnier <foo at acm.org>"
(interactive)
(let ((fill-column (point-max)))
(fill-paragraph nil)))
;; One day I got pissed of Emacs can't control volume on my mac when function keys
;; are enabled for it.. well now it can
;;TODO: Make it possible to increase/decrease volume in half-steps
(defvar volume-change-by 10
"How much to increase and round volume up to in `volume-increase' and `volume-decrease'")
(defvar volume-alternate 0
"Alternate volume to go into after pressing mute button. It's hard-coded to be 0
in code, so don't bother changing, it wouldn't be mute if it wasn't 0")
;; Rounding routine is really mensed up.. because of how round works we need to multiply
;; by volume-change-by again
(defun volume-add (by &optional divide)
"Increases volume by BY. Used by `volume-increase' and `volume-dcecrease'.
DIVIDE specifies by how to divide the volume increase by. Uses for half step volume increasing "
(interactive)
(unless divide (setq divide 1))
(setq by (round (/ by divide)))
(volume-set (* (round (+ (volume-get) by) by) by)))
(defun volume-decrease (&optional half-step)
"Decrease volume by `volume-change-by'"
(interactive "P")
(volume-add (- volume-change-by) (if half-step 2 1)))
(defun volume-increase (&optional half-step)
"Increase volume by `volume-change-by'"
(interactive "P")
(volume-add volume-change-by (if half-step 2 1)))
(defun volume-mute ()
"Mute the volume to 0, if at 0 already restore volume to `volume-alternate'
This is standard behavior taken from Mac OS X"
(interactive)
(let ((current-volume (volume-get)))
(if (= current-volume 0)
(progn (volume-set volume-alternate)
(setq volume-alternate 0)
(message "Volume restored"))
(setq volume-alternate (volume-get))
(volume-set 0)
(message "Volume muted"))))
(defun volume-set (volume)
"Set sound volume to a number
Uses 'N' interactive code so prefix argument can be used. "
(interactive "NVolume level? ")
(cond ((string= system-type "darwin") (if (= 0 (call-process-shell-command (format "osascript -e 'set volume output volume %d'" volume)))
(message "Volume set to %d" volume)
(message "Error occurred while setting volume")))
(t (message "Not implemented for '%s'" system-type))))
(defun volume-get ()
"Get current volume, very system specific"
(interactive)
(let* ((command "get output volume of (get volume settings)")
(volume (string-to-number (cond ((string= system-type "darwin")
(car (process-lines "osascript" "-e" command)))))))
(when (called-interactively-p 'any) (message "Current volume is %d" volume))
volume))
;;;; Important setup
;; Set up indention
(setq-default indent-tabs-mode nil
tab-width 4)
;; Delete all whitespace on backspace
(setq backward-delete-char-untabify-method 'hungry)
;; FXIME! Breaks stuff
;;(setq indent-line-function 'insert-tab)
;;
;; Show various kinds of whitespace
(global-whitespace-mode 1)
;; What kind of stuff to show
(setq whitespace-style '(face ; Use custom faces
trailing ; Show trailing whitespace
tabs ; Shows tabs
empty ; Empty lines at start or end of buffer
; lines-tail ; Show long lines
tab-mark)) ; Tabs have special symbols
;; Get rid of whitespace on save
(add-hook 'before-save-hook 'whitespace-cleanup)
;; Emacs path
(setq exec-path (append extra-path-locations exec-path))
;; I would enable type-break mode here but it seems to break Emacs's
;; I might try to write my own lightweight alterantive
;; One way fullscreen toggle
(unless (frame-parameter nil 'fullscreen)
(toggle-frame-fullscreen))
;; Disable alarms
(setq ring-bell-function 'ignore)
;; Make all yes/no questions y/n questions
(defalias 'yes-or-no-p 'y-or-n-p)
(setq use-dialog-box nil)
;; Try to set up better scrolling
;; Slightly jaggy but better than default
;; Note: Commented out because seems to break stuff - fix later
;; (setq scroll-step 1
;; scroll-conservatively 200
;; mouse-wheel-progressive-speed nil)
(setq mouse-wheel-progressive-speed nil ; Don't speed up
mouse-wheel-scroll-amount '(4 ((shift) . 1)) ; Scroll by 4 unless pressing shift
scroll-margin 6 ; Margin off scroll
scroll-conservatively 100) ; Don't re-centre so dramatical
;; Right fringe is nearly never needed, disable it
(fringe-mode (cons custom-fringe-size 0))
;; Make command meta in OSX
(setq mac-command-modifier 'control)
;; Change Gui font
(set-frame-font "Monaco-12")
;; Disbale the startup screen
(setq inhibit-startup-message t
initial-scratch-message "")
;;;; Helm config
;; Attempt as using helm
(require-and-ensure-installed 'helm)
(require 'helm-config) ; Supposed to fix upgrades
(helm-mode 1)
;; Make tab more freindly-ish
(define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action)
(define-key helm-map (kbd "C-z") 'helm-select-action)
;; Use mdfind on OSX by default
(setq helm-c-locate-command
(case system-type
('gnu/linux "locate -i -r %s")
('berkeley-unix "locate -i %s")
('windows-nt "es %s")
('darwin "mdfind -name %s %s")
(t "locate %s")))
;; Better describe bindings
(require-and-ensure-installed 'helm-descbinds)
(helm-descbinds-mode 1)
(require-and-ensure-installed 'evil)
(evil-mode 1)
;;;; God mode
;; Alterantive to evil-mode.. Still here for historicall reasons :D
;; I got fed up with Emacs not having a decent editor
;; (require-and-ensure-installed 'god-mode)
;; (god-mode-all)
;; ;; Sets cursor to be bar by default.. it's a horrible hack
;; ;; Emacs has no nice way of calling functions after buffer creation
;; ;; and I got used to seeing box cursor with God mode. This ensures
;; ;; that if God mode is not enabled cursor stays consistent
;; (setq cursor-type 'bar)
;; ;; Few different ways of exiting in terminal
;; (global-set-key (kbd "<escape>") 'god-local-mode)
;; (global-set-key (kbd "<ESC> <ESC>") 'god-local-mode)
;; ;; Update the cursor depending on god mode
;; (defun-hook (god-mode-enabled-hook god-mode-disabled-hook) ()
;; (setq cursor-type (if god-local-mode 'box 'bar)))
;; ;; Allow god mode to run everywhere
;; (setq god-exempt-predicates '())
;; ;; Make sure it's enabled in all new buffers
;; (defun-hook (find-file-hook change-major-mode-hook) ()
;; (god-local-mode 1))
;; When I press enter but I'm in God-mode disable it.. I want to enter data
;; use C-o instead for adding new lines
(defun newline-wihtout-god ()
"Newlines don't believe in God! Disable God! Insert new line to replace God!
Rejoice for you don't have to escape God to enter data after a new line."
(interactive)
(when god-local-mode (god-local-mode -1))
(newline 1 t))
;; (global-set-key (kbd "<RET>") 'newline-wihtout-god)
;;;; Color theme
;; (require-and-ensure-installed 'monokai-theme)
;; (unless (memq 'monokai custom-enabled-themes) ; Allow nice eval-buffer
;; (load-theme 'monokai t))
(require-and-ensure-installed 'monokai-theme)
(unless (memq 'monokai custom-enabled-themes) ; Allow nice eval-buffer
(load-theme 'monokai t))
;; Python mode settings
(setq python-indent-offset my-default-indention)
;; C mode settings
(setq-default c-basic-offset my-default-indention
c-default-style "linux")
;;; Disable auto-newline for now.. it doesn't really help
;; (defun-hook 'c-mode-common-hook () ; Hungry state sounded fun.. but it makes it hard
;; (c-toggle-auto-newline 1)) ; to make empty lines :(
;; Indent stuff by default
;; Enable indent mode everywhere but not in fundamental and python
;; If in Python simply newline-and-indent instead
;; this might be obsolete by now.. I think Emacs 24.4 made it a default option
;; but it will stay here because it's awesome.. I like to see my init.el evolve.. and not
;; everyone uses Emacs 24.4
(electric-indent-mode 1)
(defun-hook electric-indent-functions (char)
(if (member major-mode modes-without-electric-indent) 'no-indent nil))
(defun-hook python-mode-hook () (local-set-key (kbd "RET") 'newline-and-indent))
;; Out of order while paredit kicks ass
;; Auto insert parens
;; (electric-pair-mode 1)
;; Make electric pair a little bit more reasonable: only enable in certain modes
;; (setq electric-pair-inhibit-predicate (lambda (c)
;; (if (some 'derived-mode-p modes-with-eletric-pair)
;; (electric-pair-default-inhibit c)
;; 'no-pair)))
;; Enable rainbow parens
(require-and-ensure-installed 'rainbow-delimiters)
(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
;; Auto complete setup
(require-and-ensure-installed 'smart-tab)
(setq completion-auto-help 'lazy)
(global-smart-tab-mode 1)
;; Disable truncate lines by default
(set-default 'truncate-lines nil)
;; Org-mode setup
(require 'org)
(setq org-log-done t)
(global-set-key (kbd "C-c l") 'org-store-link)
(global-set-key (kbd "C-c a") 'org-agenda)
(define-key org-mode-map (kbd "C-c w") 'words-in-paragraph)
(setq org-startup-indented t)
(add-hook 'org-mode-hook 'refill-mode)
;; Text mode setup
(setq-default fill-column 90)
(defun-hook text-mode-hook ()
(if (y-or-n-p "Enable refill mode?")
(refill-mode 1)
(visual-line-mode 1)))
(add-hook 'text-mode-hook 'flyspell-mode)
(define-key text-mode-map (kbd "C-c w") 'words-in-paragraph)
(define-key text-mode-map (kbd "C-c W") 'count-words)
(define-key text-mode-map (kbd "C-c u") 'unfill-paragraph)
;; Backup setup
(setq backup-directory-alist `((".*" . ,(concat user-emacs-directory ".backups"))))
(setq backup-by-copying t ; Keeps permisions the same and stuff
version-control t ; Doesn't matter because we save in a different dir
delete-old-versions t ; Too much of a good thing...
kept-old-versions 2 ; Keep the first two versions ever made
kept-new-versions 4) ; Keep the last 4 versions
(setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t)))
;;;; Eshell setup
;; Make sure this is above (require 'esehll) - eshell inherits PATH for eshell-path-env
;; By now most of this code is obsolete, I switched to ansi-term because eshell just
;; wasn't cutting it, in the world where all hip commands have color output..
;; not to mention lack of shell redirection and bad performance of built in commands
(setenv "PATH"
(mapconcat 'identity
`(,@(mapcar 'shell-quote-argument extra-path-locations) ,(getenv "PATH")) ":"))
;; Preload all things for eshell
(require 'eshell)
;; Commands that require visual
(setq eshell-visual-subcommands '(("npm" "init")))
;; Gets around a bug in eshell
;; see https://lists.gnu.org/archive/html/bug-gnu-emacs/2013-03/msg00533.html
(setq eshell-prompt-function (lambda ()
(let* ((cwd-part (abbreviate-file-name (eshell/pwd)))
(dollar (if (= (user-uid) 0) "#" "$"))
(prompt (concat
(propertize cwd-part 'face 'shadow)
" "
(propertize dollar 'face 'font-lock-function-name-face 'face 'bold)
" ")))
(propertize prompt
'front-sticky '(face read-only)
'read-only t
'rear-nonsticky '(face read-only)))))
(setq eshell-highlight-prompt nil) ; Don't allow eshell to override our colors
;; Each chnage in prompt function also needs prompt regex
(setq eshell-prompt-regexp "^[^$#]* [$#] ")
;; Disable banner message
(setq eshell-banner-message "")
;; Commands
(defun eshell/clear ()
"Clears the eshell buffer"
(let ((inhibit-read-only t))
(erase-buffer)))
(defun eshell/emacs (&rest args)
"Opens a file in emacs.\n
If a file isn't given, just burries the ehsell buffer using `bury-buffer'\n
See URL `http://www.emacswiki.org/emacs/EshellFunctions' for more info on the extra clutter"
(if (null args)
(bury-buffer)
(mapc #'find-file (mapcar #'expand-file-name (eshell-flatten-list (reverse args))))))
;;;; Keybindings
;;; Swap C-x b with C-x C-b - easier in God mode
(global-set-key (kbd "C-x C-b") 'helm-buffers-list)
(global-set-key (kbd "C-x b") 'list-buffers)
(global-set-key (kbd "C-x C-k") 'kill-buffer)
;;; Helm evrything!
(global-set-key (kbd "M-x") 'helm-M-x)
(global-set-key (kbd "C-h f") 'helm-apropos)
(global-set-key (kbd "C-h v") 'helm-apropos)
(global-set-key (kbd "C-x C-f") 'helm-find-files)
(define-key comint-mode-map (kbd "C-c C-l") 'helm-comint-input-ring)
;; Other custom extensions
(global-set-key (kbd "M-y") 'browse-kill-ring)
;;; Custom ones
(global-set-key (kbd "C-c o") 'overwrite-mode)
(global-set-key (kbd "C-c k") 'kill-this-buffer)
(global-set-key (kbd "C-c g") 'goto-line)
(global-set-key (kbd "C-c m") 'pop-global-mark)
(global-set-key (kbd "C-c i") 'helm-imenu)
(global-set-key (kbd "C-c h") 'hexl-find-file)
(global-set-key (kbd "C-c t") 'toggle-truncate-lines)
;; Searching stuff
(global-set-key (kbd "C-c r r") 'rgrep)
(global-set-key (kbd "C-c r l") 'lgrep)
(global-set-key (kbd "C-c r o") 'helm-occur)
;;; Function keys.. for the really handy stuff
;; The ff-find-other-file is really handy. I discovered it while working
;; on kismac @ Open Wifi. It jumps to the corresponding other file. Like *.h to *.c
(global-set-key (kbd "<f4>") 'ff-find-other-file)
(global-set-key (kbd "<f5>") 'highlight-symbol-mode)
;; Handy for jumping to stuff
;; by my convention prefix argument is used to select buffer number
(global-set-key (kbd "<f1>") 'jump-to-scratch)
(global-set-key (kbd "<f2>") 'jump-to-eshell)
(global-set-key (kbd "<f3>") 'jump-back-to-buffer)
;; Volume control
(global-set-key (kbd "<f12>") 'volume-increase)
(global-set-key (kbd "<f11>") 'volume-decrease)
(global-set-key (kbd "<f10>") 'volume-mute)
;;;; Various extension bits
;; General packages
(ensure-installed 'markdown-mode)
(ensure-installed 'neotree)
(ensure-installed 'restclient)
;; Highlight symbol mode
(setq highlight-symbol-idle-delay 0
highlight-symbol-on-navigation-p t)
;; Tramp mode
(setq tramp-default-method "ssh"
tramp-default-user "root")
;; Disover my Major
(require-and-ensure-installed 'discover-my-major)
(global-set-key (kbd "C-h C-m") 'discover-my-major)
;; Undo tree setup
;; (require-and-ensure-installed 'undo-tree)
;; (global-undo-tree-mode 1)
;; (setq undo-tree-auto-save-history t
;; undo-tree-history-directory-alist nil
;; undo-tree-visualizer-diff t
;; undo-tree-visualizer-timestamps t
;; undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo/")))
;; Tetris score file!
(setq tetris-score-file "~/.emacs.d/tetris.scores")
;; Browse kill-ring
(require-and-ensure-installed 'browse-kill-ring)
(setq browse-kill-ring-highlight-current-entry t)
;; Just require ace-jump-mode
(require-and-ensure-installed 'ace-jump-mode)
(global-set-key (kbd "C-'") 'ace-jump-mode)
(global-set-key (kbd "M-'") 'ace-jump-mode-pop-mark)
;; Haskell mode setup
(require-and-ensure-installed 'haskell-mode)
(add-hook 'haskell-mode-hook 'interactive-haskell-mode)
(add-hook 'haskell-mode-hook 'structured-haskell-mode)
(setq haskell-process-log t)
;; Ispell mode setup
(require 'flyspell)
(setq ispell-program-name (executable-find "ispell"))
(add-hook 'prog-mode-hook 'flyspell-prog-mode)
(add-hook 'org-mode-hook 'flyspell-mode)
(define-key flyspell-mode-map [mouse-3] 'flyspell-correct-word)
;; Subword mode (aka camelCase words mode)
(defun-hook prog-mode-hook ()
(subword-mode 1)
(diminish 'subword-mode))
;; Diminish useless minor mode indicators
(require-and-ensure-installed 'diminish)
(mapc 'diminish '(undo-tree-mode
helm-mode
smart-tab-mode
flyspell-mode
global-whitespace-mode))
;; Perspective mode
(persp-mode)
(global-set-key (kbd "C-z") 'persp-switch)
(global-set-key (kbd "C-S-z") 'persp-kill)
;; Paredit mode setup!
(require-and-ensure-installed 'paredit)
(add-hooks '(emacs-lisp-mode-hook lisp-mode-hook) 'paredit-mode)
;; Emmtet and web mode for web dev
(require-and-ensure-installed 'emmet-mode)
(add-hook 'emmet-mode 'emmet-mode)
;; Make ansi-term start bash by default and in line mode
(defun bash-term ()
"Launch `ansi-term' in line mode and with bash by default \(or whatever
`my-chosen-shell' is set to\)"
(interactive)
(ansi-term (executable-find my-chosen-shell))
(term-line-mode))
;;;; USACO-mode
(defvar usaco-user-id nil
"User id to use in the USACO headers, when using `usaco-insert-header'")
;; Used to be local.. see usaco-run description for details
(defvar usaco-input-buffer nil
"Local to current usaco-mode file buffer to use as input during `usaco-run'")
;; Once again used because compile functions hook changes the current buffer *sigh*
(defvar usaco-code-buffer nil
"Name of the buffer which contains the code being run by `usaco-run'")
(defvar-local usaco-compiler "gcc"
"The compiler `usaco-make' should use by default")
(defvar-local usaco-c-flags "-Wall -Wextra -pedantic -ld"
"Extra flags `usaco-c-flags-function' should use. I consider those sensible defaults")
(defvar-local usaco-cpp-flags "-Wall -Wextra -Wshadow"
"Extra flags `usaco-cpp-flags-function' should use. I consider those sensible defaults")
(defvar usaco-c-flags-function (lambda ()
(format "%s -std=c99 -O%d"
usaco-c-flags
usaco-optimize-level))
"Function that generates flags `usaco-compiler' should use by default when compiling C files.")
(defvar usaco-cpp-flags-function (lambda ()
(format "-Wall -Wextra -std=%s -pedantic -ansi -Wshadow -O%d"
(if usaco-use-cpp11 "C++11" "C++98")
usaco-optimize-level))
"Function that generates flags `usaco-make' should use by default when compiling C++ files.
By default takes `usaco-use-cpp11' into account and switches the -std flag accordingly")
(defvar usaco-use-cpp11 t
"Should `usaco-insert-header' specify language to \"C++11f\" when editing C++ files
and should `usaco-make' use \"C++98\" or \"C++11\" standard")
(defvar-local usaco-optimize-level 0
"Tells `usaco-make' how much to optimize \(-Ox flag).
0 by default because it makes program nicer to debug")
(defmacro if-cpp (cpp &optional justc)
"Macro for quickly executing different code depending on
buffer being in c++ or c mode
Example: \(if-cpp \"C++\" \"C\" \"Not C or C++\")"
`(if (string= (file-name-extension (or (buffer-file-name) (buffer-name))) "cpp")
,cpp ,justc))
;; Nice for quickly starting USACO tasks
(defun usaco-insert-header (task-id)
"Insert the required USACO heading"
(interactive "sTask ID: ")
(unless usaco-user-id
(setq usaco-user-id (read-string "Your USACO user ID: ")))
(let ((language (if-cpp (if usaco-use-cpp11 "C++11f" "C++") "C")))
(goto-char 1)
(insert (format "/*\nID: %s\nLANG: %s\nTASK: %s\n*/\n" usaco-user-id language task-id))
(goto-char (point-max))))
(defun usaco-make ()
"Compile the current C++ or C buffer using `compile'.
Useful for when compiling a single file in a folder of files (like with USACO)
The program is compiled using the `compile' with `usaco-compiler' as compiler and
`usaco-c-flags' or `usaco-cpp-flags' - both being buffer local variables.
Flags passed to `compile' are generated by `usaco-cpp-flags-function' or
`usaco-c-flags-function' they takes into account `usaco-cpp-flags' and `usaco-c-flags'
as well as `usaco-optimize-level' and `usaco-use-cpp11' so it's best to not change them
The binaries' name is the result of `file-name-sans-extension'. And optimization level
is chosen using `usaco-optimize-level'"
(interactive)
(usaco-mode)
(compile (format "%s %s %s -o %s"
usaco-compiler
(if-cpp
(funcall usaco-cpp-flags-function)
(funcall usaco-c-flags-function))
(buffer-file-name)
(file-name-sans-extension (buffer-file-name)))))
;; This gets tricky. Since usaco-actaully-run is called somewhere else I can't pass
;; variables using the dynamic scope.. the call also changes the current buffer so buffer
;; local variables fail (out variable _is_ a buffer - Catch 22). It seems the easiest solution
;; is just to assume I won't be running multiple programs at once and use a global variable
(defun usaco-run (input-buffer)
"Run the C/C++ source in the current buffer using a buffer as input.
Buffer is compiled using `usaco-make' and then run
INPUT-BUFFER is a buffer that contains the text to be send to the running process
When called interactively usaco-run allows user to specify the buffer"
(interactive "bBuffer to use as input: ")
(usaco-mode)
(setq usaco-input-buffer input-buffer)
(setq usaco-code-buffer (buffer-name))
(unless (buffer-file-name) (save-buffer)) ; Make sure it's saved
(add-hook 'compilation-finish-functions 'usaco-actaully-run)
(usaco-make))
(defun usaco-actaully-run (buffer cstatus)
"Run this once we tried compiling. Ran as `compilation-finish-functions' hook"
(when (string= cstatus "finished\n") ; Compiled nicely
(message "Running process...")
(remove-hook 'compilation-finish-functions 'usaco-actaully-run)
(let* ((proc-name (file-name-sans-extension usaco-code-buffer))
(buffer-name "*usaco-output*")
(input-text (with-current-buffer usaco-input-buffer
(buffer-substring-no-properties (point-min) (point-max))))
(proc (start-process-shell-command proc-name buffer-name (concat "./" proc-name))))
;; This was tricky to debug.. it turns out in emacs (current-buffer) doesn't need to be in
;; (selected-window).. aka when this hook was fired it was in *compilation* buffer but the
;; selected window was the source. Using (display-buffer) instead of (switch-buffer) fixes
;; the issue and makes usaco-mode better in general: now my cursor stays in source files at
;; all times, just as with (compile)!
(display-buffer buffer-name)
(with-current-buffer buffer-name
(erase-buffer)
(special-mode))
(message "Sending input text from %s to process.." usaco-input-buffer)
(process-send-string proc-name (if (string= (substring input-text -1) "\n")
input-text
(concat input-text "\n"))) ; Add newline - just in case
(process-send-eof proc-name))))
(define-minor-mode usaco-mode
"Convenient key bindings for working with USACO submissions
Allows specifying arbitrary buffers as input, quickly running
\(and compiling using `compile' and seeing output of programs)"
:lighter " Usaco"
:keymap `((,(kbd "C-c C-m") . usaco-make)
(,(kbd "C-c C-i") . usaco-insert-header)
(,(kbd "C-c C-r") . usaco-run)))
;; Setup imenu on opening emacs-lisp mode.. See note on top of the config
(defun-hook emacs-lisp-mode-hook ()
(setq imenu-prev-index-position-function nil)
(add-to-list 'imenu-generic-expression '("Sections" "^;;;; \\(.+\\)$" 1) t))
;;;; Mode line
;; TODO: Replace with something more custom
(require-and-ensure-installed 'smart-mode-line)
(setq sml/no-confirm-load-theme t)
(sml/setup)
(unless (memq 'smart-mode-line-respectful custom-enabled-themes)
(sml/apply-theme 'respectful))
;; Battery mode line
(display-battery-mode 1)
(column-number-mode -1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment