(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("gnu" . "http://elpa.gnu.org/packages/")))
(when (not (package-installed-p 'use-package))
(package-refresh-contents)
(package-install 'use-package))
(require 'use-package)
(use-package exec-path-from-shell
:ensure t)
(when (eq system-type 'darwin)
(exec-path-from-shell-initialize))
(use-package reveal-in-osx-finder
:ensure t)
This function and the following font configuration is shamelessly stolen from the excellent Nano configuration, see https://github.com/rougier/nano-emacs.
(defun set-face (face style)
"Reset a face and make it inherit style."
(set-face-attribute face nil
:foreground 'unspecified :background 'unspecified
:family 'unspecified :slant 'unspecified
:weight 'unspecified :height 'unspecified
:underline 'unspecified :overline 'unspecified
:box 'unspecified :inherit style))
Some notes:
- When changing these fonts it seems like you need to restart Emacs for them to take effect.
- Salient used to be #000055.
(defface face-critical '((t :foreground "#ffffff"
:background "#ff6347")) "Critical")
(defface face-popout '((t :foreground "#00bba7")) "Popout")
(defface face-strong '((t :weight regular)) "Strong")
(defface face-actually-bold '((t :weight bold)) "Actually strong")
(defface face-salient '((t :foreground "#221199")) "Salient")
(defface face-faded '((t :foreground "#999999")) "Faded")
(defface face-subtle '((t :background "#f0f0f0")) "Subtle")
(set-face-font 'default "Fira Code 16")
(setq-default line-spacing 5)
(if (eq system-type 'windows-nt)
(set-face-font 'default "Fira Code 11"))
(if (eq system-type 'gnu/linux)
(set-face-font 'default "Fira Code 12"))
(set-face 'font-lock-comment-face 'face-faded)
(set-face 'font-lock-doc-face 'face-faded)
(set-face 'font-lock-string-face 'face-popout)
(set-face 'font-lock-constant-face 'face-salient)
(set-face 'font-lock-warning-face 'face-popout)
(set-face 'font-lock-function-name-face 'face-strong)
(set-face 'font-lock-variable-name-face 'face-strong)
(set-face 'font-lock-builtin-face 'face-salient)
(set-face 'font-lock-type-face 'face-salient)
(set-face 'font-lock-keyword-face 'face-salient)
(set-face 'header-line-highlight 'face-faded)
(set-face 'region 'face-subtle)
(set-face 'highlight 'face-subtle)
(set-face 'org-link 'face-popout)
(set-face 'org-verbatim 'face-salient)
(set-face 'org-headline-done 'face-faded)
(set-face 'bold 'face-actually-bold)
(set-face 'italic 'face-faded)
(set-face 'cursor 'face-strong)
;; This gives a warning, so I'm commenting it out for now.
;; (set-face-attribute 'cursor nil
;; :background (face-foreground 'face-strong))
(set-face 'minibuffer-prompt 'face-strong)
(set-face 'link 'face-salient)
(set-face 'fringe 'face-faded)
(set-face 'isearch 'face-strong)
(set-face 'lazy-highlight 'face-subtle)
(set-face 'show-paren-match 'face-popout)
(set-face 'show-paren-mismatch 'face-normal)
(set-face 'shadow 'face-faded) ;; Used for line numbers
(set-face 'warning 'face-popout)
(set-face 'error 'face-critical)
(set-face 'outline-1 'face-strong)
(set-face 'outline-2 'face-strong)
(set-face 'outline-3 'face-strong)
(set-face 'outline-4 'face-strong)
(set-face 'outline-5 'face-strong)
(set-face 'outline-6 'face-strong)
(set-face 'info-menu-header 'face-strong)
(set-face 'info-header-node 'face-normal)
(set-face 'Info-quoted 'face-faded)
(set-face 'info-title-1 'face-strong)
(set-face 'info-title-2 'face-strong)
(set-face 'info-title-3 'face-strong)
(set-face 'info-title-4 'face-strong)
It should be possible to remove this, since Emacs is supporting ligatures natively now.
(defun enable-ligatures ()
(interactive)
(let ((alist '((33 . ".\\(?:\\(?:==\\|!!\\)\\|[!=]\\)")
(35 . ".\\(?:###\\|##\\|_(\\|[#(?[_{]\\)")
(36 . ".\\(?:>\\)")
(37 . ".\\(?:\\(?:%%\\)\\|%\\)")
(38 . ".\\(?:\\(?:&&\\)\\|&\\)")
;;(42 . ".\\(?:\\(?:\\*\\*/\\)\\|\\(?:\\*[*/]\\)\\|[*/>]\\)") ;; This messes up triple stars in Org mode (***)
(43 . ".\\(?:\\(?:\\+\\+\\)\\|[+>]\\)")
(45 . ".\\(?:\\(?:-[>-]\\|<<\\|>>\\)\\|[<>}~-]\\)")
(46 . ".\\(?:\\(?:\\.[.<]\\)\\|[.=-]\\)")
(47 . ".\\(?:\\(?:\\*\\*\\|//\\|==\\)\\|[*/=>]\\)")
(48 . ".\\(?:x[a-zA-Z]\\)")
(58 . ".\\(?:::\\|[:=]\\)")
(59 . ".\\(?:;;\\|;\\)")
(60 . ".\\(?:\\(?:!--\\)\\|\\(?:~~\\|->\\|\\$>\\|\\*>\\|\\+>\\|--\\|<[<=-]\\|=[<=>]\\||>\\)\\|[*$+~/<=>|-]\\)")
(61 . ".\\(?:\\(?:/=\\|:=\\|<<\\|=[=>]\\|>>\\)\\|[<=>~]\\)")
(62 . ".\\(?:\\(?:=>\\|>[=>-]\\)\\|[=>-]\\)")
(63 . ".\\(?:\\(\\?\\?\\)\\|[:=?]\\)")
(91 . ".\\(?:]\\)")
(92 . ".\\(?:\\(?:\\\\\\\\\\)\\|\\\\\\)")
(94 . ".\\(?:=\\)")
(119 . ".\\(?:ww\\)")
(123 . ".\\(?:-\\)")
(124 . ".\\(?:\\(?:|[=|]\\)\\|[=>|]\\)")
(126 . ".\\(?:~>\\|~~\\|[>=@~-]\\)"))))
(dolist (char-regexp alist)
(set-char-table-range composition-function-table (car char-regexp)
`([,(cdr char-regexp) 0 font-shape-gstring])))))
(defun disable-ligatures ()
(interactive)
(let ((alist '((33 . "")
(35 . "")
(36 . "")
(37 . "")
(38 . "")
(43 . "")
(45 . "")
(46 . "")
(47 . "")
(48 . "")
(58 . "")
(59 . "")
(60 . "")
(61 . "")
(62 . "")
(63 . "")
(91 . "")
(92 . "")
(94 . "")
(119 . "")
(123 . "")
(124 . "")
(126 . ""))))
(dolist (char-regexp alist)
(set-char-table-range composition-function-table (car char-regexp)
`([,(cdr char-regexp) 0 font-shape-gstring])))))
(enable-ligatures)
(setq frame-resize-pixelwise t)
(set-frame-parameter (selected-frame) 'internal-border-width 24)
(fringe-mode '(0 . 0))
;;(add-to-list 'default-frame-alist '(fullscreen . maximized))
(if (eq system-type 'darwin)
(add-to-list 'default-frame-alist '(undecorated . t)))
(setq frame-background-mode 'light)
(set-background-color "#ffffff")
(set-foreground-color "#000000")
See https://github.com/rougier/nano-emacs.
(defun mode-line-render (left right)
"Return a string of `window-width' length containing left, and
right aligned respectively."
(let* ((available-width (- (window-total-width) (length left) )))
(format (format "%%s %%%ds" available-width) left right)))
(setq-default header-line-format
'(:eval (mode-line-render
(format-mode-line
(list
(propertize "" 'face `(:weight regular))
(propertize "%b " 'face `(:weight regular))
'(:eval (if (and buffer-file-name (buffer-modified-p))
(propertize "(modified)"
'face `(:weight light
:foreground "#aaaaaa"))))))
(format-mode-line
(propertize "%3l:%2c "
'face `(:weight light :foreground "#aaaaaa"))))))
(setq-default mode-line-format "") ;; The "normal" mode line (at the bottom)
(set-face-attribute 'mode-line nil
:height 10
:underline "black"
:background "white"
:foreground "white"
:box nil)
(set-face-attribute 'mode-line-inactive nil
:box nil
:inherit 'mode-line)
(set-face-attribute 'mode-line-buffer-id nil
:weight 'light)
(set-face-attribute 'header-line nil
:height (if (or (eq system-type 'windows-nt)
(eq system-type 'gnu/linux))
160
180)
:underline t
:underline "black"
:foreground "black"
:background "white"
:box `(:line-width 12 :color "white" :style nil))
(set-face-attribute 'mode-line nil
:height 10
:underline "black"
:background "white"
:foreground "white"
:box nil)
(set-face 'mode-line-inactive 'mode-line)
(set-face 'mode-line-buffer-id 'default)
(defun mode-line-render (left right)
"Return a string of `window-width' length containing left, and
right aligned respectively."
(let* ((available-width (- (window-total-width) (length left) )))
(format (format "%%s %%%ds" available-width) left right)))
(define-key mode-line-major-mode-keymap [header-line]
(lookup-key mode-line-major-mode-keymap [mode-line]))
(setq-default mode-line-format '(""))
(defun vc-branch ()
(if vc-mode
(let ((backend (vc-backend buffer-file-name)))
(concat "#" (substring-no-properties vc-mode
(+ (if (eq backend 'Hg) 2 3) 2))))
""))
(setq-default header-line-format
'(:eval (mode-line-render
(format-mode-line
(list
(propertize "☰"
'face `(:weight regular)
'mouse-face 'header-line-highlight
'help-echo "Major mode menu"
'local-map mode-line-major-mode-keymap)
" %b "
'(:eval (propertize (vc-branch) 'face `(:foreground ,(face-foreground 'face-popout))))
" "
'(:eval (if (and buffer-file-name (buffer-modified-p))
(propertize "(modified)"
'face `(:foreground ,(face-foreground 'face-faded)))))
))
(format-mode-line
(propertize "%3l:%2c "
'face `(:foreground ,(face-foreground 'face-faded)))))))
(setq cursor-type 'bar)
(set-default 'cursor-type 'bar)
;;(global-display-line-numbers-mode)
(setq linum-format (quote "%4d "))
(show-paren-mode 1)
Always use four spaces.
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)
This is used by org-mode when editing inline code blocks (C-c ‘).
(set-face-foreground 'secondary-selection "#999")
(set-face-background 'secondary-selection "#f0f0f0")
A function for finding out info about font at cursor.
(defun what-face (pos)
(interactive "d")
(let ((face (or (get-char-property (point) 'read-face-name)
(get-char-property (point) 'face))))
(if face (message "Face: %s" face) (message "No face at %d" pos))))
Render hex colors with actual color in the buffer.
(use-package rainbow-mode
:ensure t)
(menu-bar-mode 0)
(tool-bar-mode 0)
(tooltip-mode 0)
(scroll-bar-mode 0)
(setq inhibit-splash-screen t)
(setq inhibit-startup-screen t)
(setq inhibit-startup-echo-area-message t)
(setq inhibit-startup-message t)
(setq initial-scratch-message nil)
(global-set-key (kbd "RET") 'newline-and-indent)
(delete-selection-mode 1)
(if (eq system-type 'windows-nt)
(cua-mode 1))
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(defalias 'yes-or-no-p 'y-or-n-p)
Make new windows get focus to make it easy to interact with them.
(defadvice split-window (after move-point-to-new-window activate)
"Moves the point to the newly created window after splitting."
(other-window 1))
;; Switch to new window when using help
(defadvice describe-key (after move-point-to-new-window activate)
(other-window 1))
(defadvice describe-function (after move-point-to-new-window activate)
(other-window 1))
(defadvice describe-variable (after move-point-to-new-window activate)
(other-window 1))
(defadvice apropos-command (after move-point-to-new-window activate)
(other-window 1))
(defadvice describe-bindings (after move-point-to-new-window activate)
(other-window 1))
(defadvice describe-mode (after move-point-to-new-window activate)
(other-window 1))
(defadvice find-commands-by-name (after move-point-to-new-window activate)
(other-window 1))
(defadvice completion-list-mode (after move-point-to-new-window activate)
(other-window 1))
(global-set-key (kbd "s-+") 'enlarge-window)
(global-set-key (kbd "s--") 'shrink-window)
(global-set-key (kbd "M-+") 'enlarge-window-horizontally)
(global-set-key (kbd "M--") 'shrink-window-horizontally)
(global-set-key (kbd "C-x k") 'kill-current-buffer)
(global-set-key (kbd "M-n") 'next-error)
(global-set-key (kbd "M-p") 'previous-error)
Taken from http://xahlee.info/emacs/emacs/emacs_new_empty_buffer.html
(defun new-empty-buffer ()
"Create a new empty buffer.
New buffer will be named “untitled” or “untitled<2>”, “untitled<3>”, etc.
It returns the buffer (for elisp programing)."
(interactive)
(let (($buf (generate-new-buffer "untitled")))
(switch-to-buffer $buf)
(funcall initial-major-mode)
(setq buffer-offer-save t)
$buf))
(use-package imenu
:ensure t)
(use-package avy
:ensure t)
(require 'avy)
(to the left and right of the cursor)
(defun kill-whitespace ()
"Kill the whitespace between two non-whitespace characters"
(interactive "*")
(save-excursion
(save-restriction
(save-match-data
(progn
(re-search-backward "[^ \t\r\n]" nil t)
(re-search-forward "[ \t\r\n]+" nil t)
(replace-match "" nil nil))))))
(global-set-key [s-backspace] 'kill-whitespace)
(add-hook 'before-save-hook 'whitespace-cleanup)
(defun save-buffer-no-whitespace-cleanup ()
(interactive)
(let ((normally-should-whitespace-cleanup (memq 'whitespace-cleanup before-save-hook)))
(when normally-should-whitespace-cleanup
(remove-hook 'before-save-hook 'whitespace-cleanup))
(save-buffer)
(when normally-should-whitespace-cleanup
(add-hook 'before-save-hook 'whitespace-cleanup))))
(defun smart-open-line-above ()
"Insert an empty line above the current line."
(interactive)
(move-beginning-of-line nil)
(newline-and-indent)
(forward-line -1)
(indent-according-to-mode))
(global-set-key (kbd "<C-return>") 'smart-open-line-above)
Grow a text selection in a smart way based on common programming language delimiters.
(use-package expand-region
:ensure t)
(global-set-key (kbd "s-e") 'er/expand-region)
These characters can’t be written using the normal MacOS shortcuts (on my keyboard) without this fix.
(global-set-key (kbd "M-2") "@")
(global-set-key (kbd "M-4") "$")
(global-set-key (kbd "M-8") "[")
(global-set-key (kbd "M-9") "]")
(global-set-key (kbd "M-(") "{")
(global-set-key (kbd "M-)") "}")
(global-set-key (kbd "M-7") "|")
(global-set-key (kbd "M-/") "\\")
More special characters.
(global-set-key (kbd "C-x M-a") "∧") ; and
(global-set-key (kbd "C-x M-b") "⊥") ; bottom
(global-set-key (kbd "C-x M-c") "∘") ; composition
(global-set-key (kbd "C-x M-d") "⊄") ; not subset
(global-set-key (kbd "C-x M-e") "∈") ; element
(global-set-key (kbd "C-x M-f") "∀") ; for all
(global-set-key (kbd "C-x M-g") "∄") ; there doesn't exist
;; h
(global-set-key (kbd "C-x M-i") "∞") ; infinity
(global-set-key (kbd "C-x M-j") "→") ; implication
(global-set-key (kbd "C-x M-k") "⇒") ; double arrow
(global-set-key (kbd "C-x M-l") "λ") ; lambda
;; m
(global-set-key (kbd "C-x M-n") "¬") ; negation
(global-set-key (kbd "C-x M-o") "∨") ; or
(global-set-key (kbd "C-x M-p") "π") ; pi
(global-set-key (kbd "C-x M-P") "Π") ; capital pi
(global-set-key (kbd "C-x M-q") "∅") ; empty set
(global-set-key (kbd "C-x M-r") "⊢") ; provable
(global-set-key (kbd "C-x M-s") "⊂") ; subset
(global-set-key (kbd "C-x M-S") "Σ") ; sigma
(global-set-key (kbd "C-x M-t") "⊤") ; true
(global-set-key (kbd "C-x M-u") "∪") ; union
(global-set-key (kbd "C-x M-v") "∩") ; intersection
(global-set-key (kbd "C-x M-w") "∉") ; not element
(global-set-key (kbd "C-x M-x") "∃") ; there exists
;; y
(global-set-key (kbd "C-x M-z") "⊃") ; implies
(use-package smartparens
:ensure t
:config
;; Disable automatic pairing for these characters:
(sp-pair "'" nil :actions :rem)
(sp-pair "\"" nil :actions :rem)
(sp-pair "\\\"" nil :actions :rem)
:bind
(("C-)" . sp-forward-slurp-sexp)
("C-(" . sp-backward-slurp-sexp)
("C-M-)" . sp-forward-barf-sexp)
("C-M-(" . sp-backward-barf-sexp)
("C-M-k" . sp-kill-sexp)
("C-M-w" . sp-copy-sexp)
("C-M-<backspace>" . sp-unwrap-sexp)
("C-M-t" . sp-transpose-sexp)
("C-M-j" . sp-join-sexp)
("C-M-s" . sp-split-sexp)
;; Move out and to the right: ( | ) => ( ) |
("C-M-i" . sp-up-sexp)
;; Move out and to the left: ( | ) => | ( )
("C-M-u" . sp-backward-up-sexp)
;; Move down right: | ( ) => ( | )
("C-M-d" . sp-down-sexp)
;; Move down left: ( ) | => ( | )
("C-M-c" . sp-backward-down-sexp)
;; Move right: ( a | b c ) => ( a b | c )
("C-M-f" . sp-forward-sexp)
;; Move left: ( a b | c ) => ( a | b c )
("C-M-b" . sp-backward-sexp)
;; Move left to outmost paren ( ( | ) ) => | ( ( ) )
("C-M-a" . beginning-of-defun)
;; Move right to outmost paren ( ( | ) ) => ( ( ) ) |
("C-M-e" . end-of-defun))
)
Of course we want to be able to start minibuffers from within minibuffers, right?
(setq enable-recursive-minibuffers t)
Oh the pains of choosing a minibuffer completion framework. I’m putting things into functions so that it’s easy to try different ones.
(defun setup-ivy ()
(interactive)
(use-package ivy
:ensure t
:init (ivy-mode)
:config
(setq ivy-display-style 'fancy)
(setq ivy-count-format "(%d/%d) ")
;; Virtual buffers make recent buffers appear on top of the list
(setq ivy-use-virtual-buffers t))
(use-package counsel
:ensure t
:after ivy
:config
(counsel-mode))
;; This one is for buffer and command (M-x) history
(use-package ivy-prescient
:ensure t
:after counsel
:init
(ivy-prescient-mode)
(prescient-persist-mode) ;; save history between sessions
)
(global-set-key (kbd "M-x") 'counsel-M-x))
(setup-ivy)
(global-set-key (kbd "C-x C-b") 'ibuffer)
(setq ibuffer-formats
'((mark modified read-only " "
(name 30 30 :left :elide) ; change: 30s were originally 18s
" "
(size 9 -1 :right)
" "
(mode 16 16 :left :elide)
" " filename-and-process)
(mark " "
(name 16 -1)
" " filename)))
(setq ibuffer-saved-filter-groups
'(("home"
("Magit" (or (name . "magit:")
(name . "magit-diff:")
(name . "magit-process:")
(name . "magit-revision:")
(name . "magit-log:")))
("Dired" (mode . dired-mode))
("Emacs" (or (mode . help-mode)
(name . "\*"))))))
(add-hook 'ibuffer-mode-hook
#'(lambda ()
(ibuffer-switch-to-saved-filter-groups "home")))
(setq ibuffer-show-empty-filter-groups nil)
;; Refresh automatically
(add-hook 'ibuffer-mode-hook (lambda () (ibuffer-auto-mode 1)))
(use-package find-file-in-project
:ensure t
:config
(setq ffip-patterns
'("*.html" "*.org" "*.txt" "*.md" "*.el" "*.idr" "*.fs"
"*.clj" "*.cljs" "*.py" "*.rb" "*.js" "*.pl" "*.go"
"*.sh" "*.erl" "*.hs" "*.ml" "*.css" "*.elm" "*.carp"
"*.h" "*.c" "*.cpp" "*.cs" "*.m" "*.rs" "*.glsl"))
(setq ffip-prune-patterns
'("*/Packages/*"
"*/Temp/*"
"*/Library/*"
"*/PackageCache/*")))
(require 'find-file-in-project)
(global-set-key (kbd "s-p") 'find-file-in-project)
(global-set-key (kbd "C-x p") 'find-file-in-project) ;; overrides set-fill-column
(use-package undo-tree
:config
(setq undo-tree-auto-save-history nil) ;; Don't litter my folders
:ensure t)
(global-undo-tree-mode 1)
(use-package company
:ensure t
:bind
(("M-ESC" . company-complete))
:config
(setq company-tooltip-align-annotations t)
(setq company-minimum-prefix-length 1)
(setq company-idle-delay 0.2)
(setq company-dabbrev-downcase nil) ;; Don't lowercase things!
)
(use-package company-fuzzy
:ensure t)
(add-hook 'after-init-hook 'global-company-mode)
Simultaneously edit all matching symbols in the buffer. Be careful with this one!
(use-package iedit
:ensure t)
;;(global-set-key (kbd "C-;") 'iedit-mode)
(global-set-key (kbd "s-/") 'comment-or-uncomment-region)
(global-set-key (kbd "C-'") 'comment-or-uncomment-region)
(defun my-scroll-down ()
(interactive)
(scroll-up 1))
(defun my-scroll-up ()
(interactive)
(scroll-down 1))
(global-set-key (kbd "M-s-p") 'my-scroll-down)
(global-set-key (kbd "M-s-n") 'my-scroll-up)
(global-set-key [M-s-up] 'my-scroll-down)
(global-set-key [M-s-down] 'my-scroll-up)
(use-package ace-window
:ensure t)
(global-set-key (kbd "M-o") 'ace-window)
(defun smart-beginning-of-line ()
"Move point to first non-whitespace character or beginning-of-line.
Move point to the first non-whitespace character on this line.
If point was already at that position, move point to beginning of line."
(interactive "^") ; Use (interactive "^") in Emacs 23 to make shift-select work
(let ((oldpos (point)))
(back-to-indentation)
(and (= oldpos (point))
(beginning-of-line))))
(global-set-key [s-left] 'smart-beginning-of-line)
(global-set-key [home] 'smart-beginning-of-line)
(global-set-key (kbd "C-a") 'smart-beginning-of-line)
(global-set-key [s-right] 'end-of-line)
(define-key global-map [end] 'end-of-line)
(global-set-key (kbd "C-e") 'end-of-line)
(global-set-key [s-up] 'beginning-of-buffer)
(global-set-key [s-down] 'end-of-buffer)
(defun move-lines (n)
(let ((beg) (end) (keep))
(if mark-active
(save-excursion
(setq keep t)
(setq beg (region-beginning)
end (region-end))
(goto-char beg)
(setq beg (line-beginning-position))
(goto-char end)
(setq end (line-beginning-position 2)))
(setq beg (line-beginning-position)
end (line-beginning-position 2)))
(let ((offset (if (and (mark t)
(and (>= (mark t) beg)
(< (mark t) end)))
(- (point) (mark t))))
(rewind (- end (point))))
(goto-char (if (< n 0) beg end))
(forward-line n)
(insert (delete-and-extract-region beg end))
(backward-char rewind)
(if offset (set-mark (- (point) offset))))
(if keep
(setq mark-active t
deactivate-mark nil))))
(defun move-lines-up (n)
"move the line(s) spanned by the active region up by N lines."
(interactive "*p")
(move-lines (- (or n 1))))
(defun move-lines-down (n)
"move the line(s) spanned by the active region down by N lines."
(interactive "*p")
(move-lines (or n 1)))
(global-set-key (kbd "C-s-<down>") 'move-lines-down)
(global-set-key (kbd "C-s-<up>") 'move-lines-up)
;; Alternative, since the shortcuts above clash with Rectangle.app
(global-set-key (kbd "C-s-n") 'move-lines-down)
(global-set-key (kbd "C-s-p") 'move-lines-up)
;; Gnome-compatible alternative
(global-set-key (kbd "C-M-s-n") 'move-lines-down)
(global-set-key (kbd "C-M-s-p") 'move-lines-up)
(defun duplicate-line ()
(interactive)
(beginning-of-line)
(kill-line)
(yank)
(newline)
(yank))
(global-set-key (kbd "C-c d") 'duplicate-line)
If you want to insert a newline in multiple-cursors-mode, use C-j.
(use-package multiple-cursors
:ensure t)
(global-set-key (kbd "<s-mouse-1>") 'mc/add-cursor-on-click)
(global-set-key (kbd "s-d") 'mc/mark-next-like-this)
;;(global-set-key (kbd "???") 'mc/mark-next-like-this)
(global-set-key (kbd "s-l") 'mc/edit-lines)
;;(global-set-key (kbd "???") 'mc/edit-lines)
(global-set-key (kbd "C-c n") 'smerge-next)
(global-set-key (kbd "C-c p") 'smerge-prev)
(global-set-key (kbd "C-c u") 'smerge-keep-upper)
(global-set-key (kbd "C-c l") 'smerge-keep-lower)
(defun rename-file-and-buffer ()
"Rename the current buffer and file it is visiting."
(interactive)
(let ((filename (buffer-file-name)))
(if (not (and filename (file-exists-p filename)))
(message "Buffer is not visiting a file!")
(let ((new-name (read-file-name "New name: " filename)))
(cond
((vc-backend filename) (vc-rename-file filename new-name))
(t
(rename-file filename new-name t)
(set-visited-file-name new-name t t)))))))
(use-package flycheck
:ensure t)
(use-package flymake-easy
:ensure t)
(use-package flymake-cursor
:ensure t)
(use-package flymake-hlint
:ensure t)
(use-package flycheck-rust
:ensure t)
(use-package free-keys
:ensure t)
(defun delete-frame-or-kill-emacs ()
"Delete frame or kill Emacs if there is only one frame."
(interactive)
(if (> (length (frame-list)) 1)
(delete-frame)
(save-buffers-kill-terminal)))
(global-set-key (kbd "C-x C-c") 'delete-frame-or-kill-emacs)
⌘-Q, “quit”
(global-set-key (kbd "s-q") 'delete-frame-or-kill-emacs)
⌘-W, “close tab”
(global-set-key (kbd "s-w") 'kill-current-buffer)
⌘-F, “find”
(global-set-key (kbd "s-f") 'rgrep)
⌘-G, “go to line”
(global-set-key (kbd "s-g") 'goto-line)
⌘-I, “imenu”
(global-set-key (kbd "s-i") 'imenu)
(global-set-key (kbd "C-x i") 'imenu)
⌘-O, “open”
(global-set-key (kbd "s-o") 'ido-find-file)
⌘-N, “new file”
(global-set-key (kbd "s-n") 'new-empty-buffer)
⌘-M, “minimize”
(global-set-key (kbd "s-m") 'suspend-frame)
⌘-B, “switch buffer”
This one is obviously not standard on macOS, but buffer switching needs to be as smooth as possible.
(global-set-key (kbd "s-b") 'ivy-switch-buffer)
⌘-K, “erase the whole line”
This one is usually not present on MacOS, but maybe it should be! The original Emacs keybinding for this is C-S-backspace, which actually seems fine too?
(global-set-key (kbd "s-k") 'kill-whole-line)
⌘-J, “jump to word”
(define-key global-map (kbd "s-j") 'avy-goto-word-or-subword-1)
(define-key global-map (kbd "M-j") 'avy-goto-word-or-subword-1)
(define-key global-map (kbd "C-x j") 'avy-goto-word-or-subword-1)
(global-auto-revert-mode 1)
(auto-save-mode 0)
(setq ring-bell-function 'ignore)
(setq undo-limit 9999999)
(setq make-backup-files nil)
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(add-hook 'dired-mode-hook
(lambda ()
(local-set-key (kbd "b") 'dired-up-directory)
(auto-revert-mode t)
(set-face 'dired-directory 'face-popout)))
(use-package org
:bind (:map org-mode-map
("C-M-u" . org-up-element)
("C-M-f" . org-next-visible-heading)
("C-M-b" . org-previous-visible-heading))
:config
(setq org-hide-emphasis-markers t) ;; Makes bold/underlined text work properly.
(setq org-src-fontify-natively t)
(setq org-adapt-indentation nil) ;; Don't indent text after heading
(setq org-src-window-setup 'current-window) ;; Make the source editor go fullscreen
(setq org-startup-folded t)
(setq org-structure-template-alist ;; The list shown by (C-c C-,) which inserts a block structure
'(("a" . "export ascii")
("c" . "center")
("C" . "comment")
("e" . "example")
("E" . "export")
("h" . "export html")
("l" . "export latex")
("q" . "quote")
("s" . "src")
("x" . "src emacs-lisp")
("v" . "verse")))
(setq org-capture-templates
'(("l" "Link" entry (file+headline "~/Documents/Organized/links.org" "Unsorted") "* %?\n")
("i" "Idea" entry (file "~/Documents/Organized/ideas.org") "* %?\n")
("t" "Todo" entry (file "~/Documents/Organized/todo.org") "* TODO %?\n"))))
(use-package org-bullets
:ensure t
:config (setq org-bullets-bullet-list '("○" "●" "◇" "•")))
;; The old bullets in case I change my mind ("✸" "◇" "•" "○" "✤" "✩")
(setq-default prettify-symbols-alist '(("#+BEGIN_SRC" . "❝")
("#+END_SRC" . "❞")
("#+begin_src" . "❝")
("#+end_src" . "❞")
("#+RESULTS:" . "➥")
("emacs-lisp" . "ξ")
(":defun:" . "𝑓")))
(setq prettify-symbols-unprettify-at-point 'right-edge)
(add-hook 'org-mode-hook
(lambda ()
(org-bullets-mode 1)
(prettify-symbols-mode 1)
(local-unset-key (kbd "<S-up>"))
(local-unset-key (kbd "<S-down>"))
(local-unset-key (kbd "<S-left>"))
(local-unset-key (kbd "<S-right>"))))
(use-package magit
:ensure t
:config (bind-key "C-x g" 'magit-status))
(add-hook 'after-save-hook 'magit-after-save-refresh-status t)
(defadvice magit-status (around magit-fullscreen activate)
(window-configuration-to-register :magit-fullscreen)
ad-do-it
(delete-other-windows))
(add-hook 'emacs-lisp-mode-hook 'smartparens-mode)
(define-key emacs-lisp-mode-map (kbd "<s-return>") 'eval-defun)
(define-key emacs-lisp-mode-map (kbd "C-c C-l") 'eval-buffer)
(use-package lsp-mode
:ensure t
:config
(setq lsp-headerline-breadcrumb-enable nil)
(setq lsp-file-watch-threshold 5000)
;;(setq lsp-ui-sideline-enable nil)
;;(setq lsp-ui-sideline-show-code-actions nil)
;;(setq lsp-ui-doc-enable nil)
)
(setq lsp-enable-snippet nil)
(use-package haskell-mode
:ensure t
;;:config (setq-default prettify-symbols-alist '(("\\" . "λ") ("." . "ᐤ")))
)
(use-package dante
:ensure t
:after haskell-mode
:commands 'dante-mode)
(setq dante-methods '(stack new-build bare-cabal bare-ghci))
(add-hook 'dante-mode-hook (lambda () (local-set-key (kbd "<C-c C-t>") 'dante-type-at)))
(add-hook 'haskell-mode-hook
(lambda ()
(interactive-haskell-mode 1)
(smartparens-mode 1)
(electric-pair-local-mode 0)
(flycheck-mode 1)
(company-mode 1)
(prettify-symbols-mode 1)
(define-key haskell-mode-map (kbd "s-r") (lambda ()
(interactive)
(shell-command "stack run")))))
(setq haskell-process-type 'stack-ghci)
(use-package ormolu
;;:hook (haskell-mode . ormolu-format-on-save-mode) ;; Handle by dir-locals instead!
:ensure t
:bind
(:map haskell-mode-map
("C-c r" . ormolu-format-buffer)))
(setq racket-program "/Applications/Racket/bin/racket")
(add-hook 'racket-mode-hook 'smartparens-mode)
(add-to-list 'auto-mode-alist '("\\.pie\\'" . racket-mode))
;; (font-lock-add-keywords 'racket-mode '(("Π" . font-lock-keyword-face)))
;; (font-lock-add-keywords 'racket-mode '(("->" . font-lock-keyword-face)))
(font-lock-add-keywords 'racket-mode '(("claim" . font-lock-keyword-face)))
(put 'claim 'racket-indent-function 1)
(use-package clojure-mode
:ensure t)
(use-package cider
:ensure t)
(add-hook 'cider-mode-hook 'eldoc-mode)
(add-hook 'cider-mode-hook 'smartparens-mode)
(add-hook 'clojure-mode-hook 'smartparens-mode)
(add-hook 'clojure-mode-hook
#'(lambda ()
(put-clojure-indent 'match 1)))
(add-hook 'cider-mode-hook
#'(lambda ()
(electric-pair-local-mode 0)
(define-key cider-mode-map (kbd "<s-return>") 'cider-eval-defun-at-point)))
(add-hook 'cider-repl-mode-hook
#'(lambda ()
(electric-pair-local-mode 0)
(local-set-key (kbd "<M-up>") 'cider-repl-previous-input)
(local-set-key (kbd "<M-down>") 'cider-repl-next-input)))
(setq cider-repl-use-clojure-font-lock t)
(setq cider-prompt-save-file-on-load 'always-save)
(setq cider-repl-display-help-banner nil)
(add-to-list 'load-path "/Users/erik/Projects/carp-emacs")
(add-to-list 'load-path "/home/erik/Projects/carp-emacs")
(add-to-list 'load-path "/Users/eriksvedang/Code/carp-emacs")
(add-to-list 'load-path "C:/Users/erik/Documents/Projects/carp-emacs")
(require 'carp-mode)
(require 'carp-flycheck)
(add-hook 'carp-mode-hook
(lambda ()
(electric-pair-local-mode 0)
(smartparens-mode 1)
;;(flycheck-mode 1)
))
(defun compile-c ()
(interactive)
(save-buffer)
(let ((project-dir (locate-dominating-file (buffer-file-name) "makefile")))
(if project-dir
(progn (setq default-directory project-dir)
(compile (format "make")))
(compile (format "clang %s -O0 -g -o %s" (buffer-name) (file-name-sans-extension (buffer-name)))))))
(defun run-c ()
(interactive)
(save-buffer)
(let ((project-dir (locate-dominating-file (buffer-file-name) "makefile")))
(if project-dir
(progn (setq default-directory project-dir)
(compile (format "make run")))
(compile (format "./%s" (file-name-sans-extension (buffer-name)))))))
;; Focus on the compiler output window so it's easier to close with 'q'
;; Not a good idea unfortunately since you can't run the code with C-c C-r when not focused on source.
;; (defadvice compile-c (after move-point-to-new-window activate)
;; (other-window 0))
(add-hook 'c-mode-hook
(lambda ()
(electric-pair-local-mode 1)
(rainbow-mode 0) ;; treats #def as a color
(disable-ligatures)
(setq-default c-basic-offset 4)
(c-set-style "cc-mode")
(define-key c-mode-map (kbd "C-c C-c") 'compile-c)
(define-key c-mode-map (kbd "C-c C-r") 'run-c)
(define-key c-mode-map (kbd "s-r") 'run-c)
(define-key c-mode-map (kbd "C-c C-f") 'ff-find-other-file)))
(use-package csharp-mode
:ensure t
:bind (:map csharp-mode-map)
:config
(add-hook 'csharp-mode-hook #'lsp)
(add-hook 'csharp-mode-hook #'electric-pair-mode)
(add-hook 'csharp-mode-hook #'company-mode)
(add-hook 'csharp-mode-hook #'flycheck-mode)
)
(defun csharp-disable-clear-string-fences (orig-fun &rest args)
"This turns off `c-clear-string-fences' for `csharp-mode'. When
on for `csharp-mode' font lock breaks after an interpolated string
or terminating simple string."
(unless (equal major-mode 'csharp-mode)
(apply orig-fun args)))
(advice-add 'c-clear-string-fences :around 'csharp-disable-clear-string-fences)
(use-package fsharp-mode
:defer t
:ensure t
:config
;;(add-hook 'fsharp-mode-hook (eglot-fsharp t))
)
(use-package eglot-fsharp
:defer t
:ensure t
:config
;;(lambda () (eglot-inlay-hints-mode -1)) ;; annoying "IDE"-like stuff
)
(use-package rust-mode
:ensure t
:config
(add-hook 'rust-mode-hook #'lsp)
(add-hook 'rust-mode-hook #'electric-pair-mode)
(add-hook 'rust-mode-hook #'company-mode)
(add-hook 'rust-mode-hook #'flycheck-mode)
(define-key rust-mode-map (kbd "C-c C-c C-a") 'lsp-execute-code-action)
(define-key rust-mode-map (kbd "C-c r r") 'lsp-rename)
(setq rust-format-on-save t))
(use-package ron-mode
:ensure t)
HTML mode.
(add-hook 'sgml-mode-hook
(lambda ()
(local-set-key (kbd "M-s-.") 'sgml-close-tag)
(local-set-key (kbd "M-s-…") 'sgml-close-tag)))
(add-hook 'sgml-mode-hook 'smartparens-mode)
(add-hook 'html-mode-hook 'smartparens-mode)
(add-to-list 'auto-mode-alist '("\\.p8\\'" . lua-mode))
I probably should start using LSP for Go.
(defun run-go ()
(interactive)
(save-buffer)
(let ((project-dir (locate-dominating-file (buffer-file-name) ".git")))
(if project-dir
(progn (setq default-directory project-dir)
(compile (format "go run")))
(compile (format "go run %s" (buffer-file-name))))))
(defun compile-go ()
(interactive)
(save-buffer)
(let ((project-dir (locate-dominating-file (buffer-file-name) "go.mod")))
(if project-dir
(progn (setq default-directory project-dir)
(compile (format "go build ./...")))
(compile (format "go build %s -o %s" (buffer-name) (file-name-sans-extension (buffer-name)))))))
(use-package go-mode
:ensure t
:bind (:map go-mode-map
("C-c C-t" . godef-describe)
("C-c C-d" . godef-describe)
("C-c C-r" . run-go)
("C-c C-c" . compile-go)
("M-." . godef-jump))
:config
(add-hook 'before-save-hook #'gofmt-before-save)
(add-hook 'go-mode-hook #'go-imenu-setup)
(add-hook 'go-mode-hook (electric-pair-mode 1)))
;; Jump to symbol
(use-package go-imenu
:ensure t)
;; A debugger
(use-package go-dlv
:ensure t)
;; Stub generator
(use-package go-impl
:ensure t)
(require 're-builder)
(setq reb-re-syntax 'string) ;; less escaping
(use-package markdown-mode
:ensure t)
(add-hook 'markdown-mode-hook
(lambda ()
(auto-fill-mode t)))
(use-package toml-mode
:ensure t)
(use-package gdscript-mode
;;:hook (gdscript-mode . eglot-ensure)
:ensure t
:custom (gdscript-eglot-version 3))
Apparently ‘use-package’ is a bit too slow to use for just installing a bunch of packages (this is a bummer) so let’s use a simpler method for making sure that these programming modes are available.
(defun install-misc-programming-modes ()
(interactive)
(package-refresh-contents)
(setq misc-programming-modes
'(sgml-mode
web-mode
php-mode
lua-mode
swift-mode
cc-mode
glsl-mode
cmake-mode
yaml-mode
elm-mode
idris-mode
purescript-mode
tuareg
js2-mode
restclient
dyalog-mode
nix-mode
nix-buffer
gdscript-mode
wgsl-mode
))
(dolist (m misc-programming-modes)
(when (not (package-installed-p m))
(package-install m))))
;; Commented out to not have to run on each Emacs startup
;; (install-misc-programming-modes)