diff --git a/core/benchmark.el b/core/benchmark.el new file mode 100644 index 000000000..fc68df1a1 --- /dev/null +++ b/core/benchmark.el @@ -0,0 +1,44 @@ +(defvar require-times nil + "A list of (FEATURE . LOAD-DURATION). +LOAD-DURATION is the time taken in milliseconds to load FEATURE.") + +(defadvice require + (around build-require-times (feature &optional filename noerror) activate) + "Note in `require-times' the time taken to require each feature." + (let* ((already-loaded (memq feature features)) + (require-start-time (and (not already-loaded) (current-time)))) + (prog1 + ad-do-it + (when (and (not already-loaded) (memq feature features)) + (add-to-list 'require-times + (cons feature + (float-time (time-subtract (current-time) require-start-time))) + t))))) + +(defun list-times () + (interactive) + (let ((temp-buffer (get-buffer-create "*benchmark*")) + (sum 0.0)) + (popwin:popup-buffer temp-buffer :stick t) + (erase-buffer) + (org-mode) + (dolist (feature require-times) + (if (eq feature 'null) + (progn + (insert "|----+----+----|\n") + (insert (format "| %6f | Subtotal |\n" sum)) + (insert "|----+----+----|\n")) + (let ((time (cdr feature))) + (insert (format "| %6f | %s | %s |\n" time (car feature) (cond ((>= time 0.4) "XXX") + ((>= time 0.1) "X") + ((>= time 0.05) ".") + (t " ")))) + (setq sum (+ sum time))))) + (save-excursion + (insert "|----+----+----|\n") + (insert (format "| %6f | Total |\n" sum)) + (insert (format "| %s | On Init |\n" (emacs-init-time)))) + (org-table-align))) + + +(provide 'benchmark) diff --git a/core/core-auto-insert.el b/core/core-auto-insert.el new file mode 100644 index 000000000..e5fce1ea8 --- /dev/null +++ b/core/core-auto-insert.el @@ -0,0 +1,78 @@ +;;; core-auto-insert.el --- file templates + +(use-package autoinsert + :after yasnippet + :init + (setq auto-insert-query nil) ; Don't prompt before insertion + (setq auto-insert-alist '()) + :config + (auto-insert-mode 1) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;; (add-template! "/\\.gitignore$" "%%" 'gitignore-mode) + + ;; C/C++ + ;; (add-template! "/Makefile$" "%%" 'makefile-gmake-mode) + ;; (add-template! "/main\\.\\(cc\\|cpp\\)$" "%main.cpp%" 'c++-mode) + ;; (add-template! "/win32_\\.\\(cc\\|cpp\\)$" "%winmain.cpp%" 'c++-mode) + ;; (add-template! "\\.\\([Hh]\\|hpp\\)$" "%.h%" 'c++-mode) + ;; (add-template! "\\.\\([Cc]\\|cc\\|cpp\\)$" "%.cpp%" 'c++-mode) + + ;; Shell scripts + ;; (add-template! "\\.z?sh$" "%%" 'sh-mode) + + ;; Ruby + ;; (add-template! "/spec_helper\\.rb$" "%helper%" 'rspec-mode t) + ;; (add-template! "_spec\\.rb$" "%%" 'rspec-mode t) + ;; (add-template! "/\\.rspec$" "%.rspec%" 'rspec-mode) + ;; (add-template! "/Rakefile$" "%Rakefile%" 'ruby-mode t) + ;; (add-template! "/Gemfile$" "%Gemfile%" 'ruby-mode t) + ;; (add-template! "\\.gemspec$" "%.gemspec%" 'ruby-mode t) + ;; (add-template! "/lib/.+\\.rb$" "%module%" 'ruby-mode t) + ;; (add-template! "\\.rb$" "%%" 'ruby-mode) + + ;; ;; Python + ;; (add-template! "tests?/test_.+\\.py$" "%%" 'nose-mode) + ;; (add-template! "/setup\\.py$" "%setup%" 'python-mode) + ;; (add-template! "\\.py$" "%%" 'python-mode) + + ;; ;; PHP + ;; (add-template! "\\.class\\.php$" %class%" 'php-mode) + ;; (add-template! "\\.php$" %%" 'php-mode) + + ;; ;; Markdown + ;; (add-template! "\\.md$" "%%" 'markdown-mode) + ;; (add-template! "/_posts/.+\\.md$" "%jekyll-post" 'markdown-mode) + ;; (add-template! "/_layouts/.+\\.html$" "%jekyll-layout%" 'web-mode) + + ;; ;; Javascript + ;; (add-template! "\\.lbaction/.+/Info.plist$" "%Info.plst%" 'lb6-mode) + ;; (add-template! "\\.lbaction/.+/\\(default\\|suggestions\\)\\.js$" "%default.js%" 'lb6-mode) + ;; (add-template! "/package\\.json$" "%package.json%" 'json-mode) + ;; (add-template! "\\.\\(json\\|jshintrc\\)$" "%%" 'json-mode) + + ;; ;; SCSS + ;; (add-template! "/master\\.scss$" "%master%" 'scss-mode) + ;; (add-template! "/normalize\\.scss$" "%normalize%" 'scss-mode) + ;; (add-template! "\\.scss$" "%%" 'scss-mode) + + ;; ;; HTML + ;; (add-template! "\\.html$" "%%" 'web-mode) + + ;; Lua + ;; (add-template! "\\.love/main\\.lua$" "%love.main%" 'lua-mode) + ;; (add-template! "/conf\\.lua$" "@@love.conf" 'love-mode) + ;; (add-template! "\\.lua$" "%%" 'lua-mode) + + ;; ;; Java + ;; (add-template! "/src/.+/.+\\.java$" "@@" 'java-mode) + ;; (add-template! "/build\\.gradle$" "@@gradle" 'android-mode) + + ;; ;; Elisp + (add-template! "\\.emacs\\.d/.+\\.el$" "@@initfile" 'emacs-lisp-mode) + ;; (add-template! "\\.emacs\\.d/snippets/.+$" "@@" 'snippet-mode) + ) + +(provide 'core-auto-insert) +;;; core-auto-insert.el ends here diff --git a/core/core-company.el b/core/core-company.el new file mode 100644 index 000000000..c8d8406f1 --- /dev/null +++ b/core/core-company.el @@ -0,0 +1,54 @@ +;;; core-company.el --- auto completion backend (Company-mode) +;; see lib/company-macros.el + +(eval-when-compile (require 'core)) + +(use-package company + :diminish (company-mode . "=") + :commands (company-complete-common company-files company-tags + company-ispell company-yasnippet company-semantic + company-dabbrev-code) + :init + (after! abbrev (diminish 'abbrev-mode "A")) + (setq company-idle-delay nil + company-minimum-prefix-length 1 + company-show-numbers nil + company-tooltip-limit 20 + company-dabbrev-downcase nil + company-dabbrev-ignore-case nil + company-tooltip-align-annotations t + company-require-match 'never + company-global-modes '(not eshell-mode comint-mode org-mode erc-mode + message-mode help-mode) + company-frontends '(company-pseudo-tooltip-unless-just-one-frontend + company-echo-metadata-frontend + company-preview-if-just-one-frontend) + company-dict-dir (concat narf-private-dir "dict/")) + :config + (global-company-mode +1) + + ;; (use-package company-dict :defer t) + ;; (setq-default company-backends (append '(company-dict company-keywords) company-backends)) + + (setq-default company-backends (append '(company-keywords) company-backends)) + ;; TODO: Investigate yasnippet + (after! yasnippet + (setq-default company-backends (append '(company-capf company-yasnippet) company-backends))) + (add-to-list 'company-transformers 'company-sort-by-occurrence) + + (add-company-backend! nxml-mode (nxml yasnippet)) + (add-company-backend! emacs-lisp-mode (elisp yasnippet)) + + ;; Rewrite evil-complete to use company-dabbrev + (setq company-dabbrev-code-other-buffers t + company-dabbrev-code-buffers nil + evil-complete-next-func 'narf/company-evil-complete-next + evil-complete-previous-func 'narf/company-evil-complete-previous) + + (shut-up! + (setq company-statistics-file (! (concat narf-temp-dir "company-statistics-cache.el"))) + (require 'company-statistics) + (company-statistics-mode))) + +(provide 'core-company) +;;; core-company.el ends here diff --git a/core/core-completion.el b/core/core-completion.el deleted file mode 100644 index e69de29bb..000000000 diff --git a/core/core-defuns.el b/core/core-defuns.el index 1d089a2f6..ed7cfee32 100644 --- a/core/core-defuns.el +++ b/core/core-defuns.el @@ -1,41 +1,35 @@ -(! (defalias '@--concat-forms 'use-package-concat) - (defalias '@--normalize-symbols 'use-package-normalize-symlist) - (defalias '@--normalize-paths 'use-package-normalize-paths) +(eval-when-compile (require 'cl-lib)) +;; Backwards compatible `with-eval-after-load' +(unless (fboundp 'with-eval-after-load) + (defmacro with-eval-after-load (file &rest body) + `(eval-after-load ,file (lambda () ,@body)))) - ;; Backwards compatible `with-eval-after-load' - (unless (fboundp 'with-eval-after-load) - (defmacro with-eval-after-load (file &rest body) - `(eval-after-load ,file - `(funcall (function ,(lambda () ,@body)))))) +(defmacro λ (&rest body) + "A shortcut for: `(lambda () (interactive) ,@body)" + `(lambda () (interactive) ,@body)) - (defmacro @after (feature &rest forms) - (declare (indent 1)) - `(,(if (or (not (boundp 'byte-compile-current-file)) - (not byte-compile-current-file) - (if (symbolp feature) - (require feature nil :no-error) - (load feature :no-message :no-error))) - 'progn - (message "after: cannot find %s" feature) - 'with-no-warnings) - (with-eval-after-load ',feature ,@forms))) +(defmacro shut-up! (&rest body) + "Silence message output from code." + (declare (indent defun)) + `(let (message-log-max) ,@body (message ""))) - (defmacro @shut-up (&rest body) - "Silence message output from code." - (declare (indent defun)) - `(let (message-log-max) ,@body (message ""))) +(defmacro after! (feature &rest forms) + "A smart wrapper around `with-eval-after-load', that supresses warnings +during compilation." + (declare (indent defun) (debug t)) + `(,(if (or (not (boundp 'byte-compile-current-file)) + (not byte-compile-current-file) + (if (symbolp feature) + (require feature nil :no-error) + (load feature :no-message :no-error))) + 'progn + (message "after: cannot find %s" feature) + 'with-no-warnings) + (with-eval-after-load ',feature ,@forms))) - (defmacro @ (args &rest body) - "A shortcut for: `(lambda ,args ,@body)" - `(lambda ,args ,@body)) - - (defmacro λ (&rest body) - "A shortcut for: `(lambda () (interactive) ,@body)" - `(lambda () (interactive) ,@body)) - - (defmacro @add-hook (hook &rest func-or-forms) - "A convenience macro for `add-hook'. +(defmacro add-hook! (hook &rest func-or-forms) + "A convenience macro for `add-hook'. HOOK can be one hook or a list of hooks. If the hook(s) are not quoted, -hook is appended to them automatically. If they are quoted, they are used verbatim. @@ -45,242 +39,160 @@ forms. Forms will be wrapped in one lambda. A list of symbols will expand into a series of add-hook calls. Examples: - (@add-hook 'some-mode-hook 'enable-something) + (add-hook! 'some-mode-hook 'enable-something) => (add-hook 'some-mode-hook 'enable-something) - (@add-hook some-mode '(enable-something and-another)) + (add-hook! some-mode '(enable-something and-another)) => (add-hook 'some-mode-hook 'enable-something) (add-hook 'some-mode-hook 'and-another) - (@add-hook '(one-mode-hook second-mode-hook) 'enable-something) + (add-hook! '(one-mode-hook second-mode-hook) 'enable-something) => (add-hook 'one-mode-hook 'enable-something) (add-hook 'second-mode-hook 'enable-something) - (@add-hook (one-mode second-mode) 'enable-something) + (add-hook! (one-mode second-mode) 'enable-something) => (add-hook 'one-mode-hook 'enable-something) (add-hook 'second-mode-hook 'enable-something) - (@add-hook (one-mode second-mode) (setq v 5) (setq a 2)) + (add-hook! (one-mode second-mode) (setq v 5) (setq a 2)) => (add-hook 'one-mode-hook (lambda () (setq v 5) (setq a 2))) (add-hook 'second-mode-hook (lambda () (setq v 5) (setq a 2)))" - (declare (indent 1)) - (unless func-or-forms - (error "@add-hook: FUNC-OR-FORMS is empty")) - (let* ((val (car func-or-forms)) - (quoted (eq (car-safe hook) 'quote)) - (hook (if quoted (cadr hook) hook)) - (funcs (if (eq (car-safe val) 'quote) - (if (cdr-safe (cadr val)) - (cadr val) - (list (cadr val))) - (list func-or-forms))) - (forms '())) - (mapc (@ (f) - (let ((func (cond ((symbolp f) `(quote ,f)) - (t `(lambda () ,@func-or-forms))))) - (mapc (@ (h) - (push `(add-hook ',(if quoted h (intern (format "%s-hook" h))) ,func) forms)) - (if (listp hook) hook (list hook))))) funcs) - `(progn ,@forms))) + (declare (indent defun) (debug t)) + (unless func-or-forms + (error "add-hook!: FUNC-OR-FORMS is empty")) + (let* ((val (car func-or-forms)) + (quoted (eq (car-safe hook) 'quote)) + (hook (if quoted (cadr hook) hook)) + (funcs (if (eq (car-safe val) 'quote) + (if (cdr-safe (cadr val)) + (cadr val) + (list (cadr val))) + (list func-or-forms))) + (forms '())) + (mapc + (lambda (f) (let ((func (cond ((symbolp f) `(quote ,f)) + (t `(lambda () ,@func-or-forms))))) + (mapc + (lambda (h) (push `(add-hook ',(if quoted h (intern (format "%s-hook" h))) ,func) forms)) + (if (listp hook) hook (list hook))))) funcs) + `(progn ,@forms))) - (cl-defmacro @associate (mode &key in - &key match - &key files - &allow-other-keys) - "Associate a major or minor mode to certain patterns and project files." - (let* ((minor-p (memq mode minor-mode-alist)) - (modes (@--normalize-symbols ":in" in))) - (@--concat-forms - (when match - `(add-to-list ,(if minor-p 'narf-auto-minor-mode-alist 'auto-mode-alist) - (cons ,match ,mode))) - (when files - `(defun ,(intern (format "narf|init-mode-%s" 'lb6-mode)) () - (when (and (assq major-mode '(,@(@--normalize-paths ":in" in))) - (narf-project-has-files ,@(@--normalize-paths ":files" files))) - (,mode 1))))))) +(cl-defmacro associate! (mode &key in + &key match + &key files + &allow-other-keys) + "Associate a major or minor mode to certain patterns and project files." + (declare (indent 1)) + (let* ((minor-p (assoc mode minor-mode-alist))) + `(progn + (,@(when match + `(add-to-list ',(if minor-p 'narf-auto-minor-mode-alist 'auto-mode-alist) + (cons ,match ',mode)))) + (,@(when files + (unless (or (listp files) (stringp files)) + (user-error "associate! :files expects a string or list of strings")) + (let ((hook-name (intern (format "narf--init-mode-%s" mode)))) + `(progn + (defun ,hook-name () + (when (and (not ,mode) + (narf/project-has-files ,@(-list files))) + (,mode 1))) + ,@(if (and in (listp in)) + (mapcar (lambda (h) `(add-hook ',h ',hook-name)) + (mapcar (lambda (m) (intern (format "%s-hook" m))) in)) + `((add-hook 'find-file-hook ',hook-name)))))))))) - (@after evil - ;; Placeholders to correct binding indentation. Don't use these. - (defmacro :leader (key &rest rest) (declare (indent 1))) - (defmacro :localleader (key &rest rest) (declare (indent 1))) - (defmacro :map (key &rest rest) (declare (indent 1))) - (defmacro :after (key &rest rest) (declare (indent 1))) - (defmacro :when (key &rest rest) (declare (indent 1))) +(after! evil + ;; Register keywords for proper indentation (see `bind!') + (put ':prefix 'lisp-indent-function 'defun) + (put ':map 'lisp-indent-function 'defun) + (put ':after 'lisp-indent-function 'defun) + (put ':when 'lisp-indent-function 'defun) + (put ':unless 'lisp-indent-function 'defun) - (macroexpand `(@map (:map my-map "C-k" 'hello :n "C-p" 'goodbye))) + (defmacro bind! (&rest rest) + (let ((i 0) + key def + first-set + prefix + internal + (default-keymaps '(narf-mode-map)) + (keymaps (if (boundp 'keymaps) keymaps)) + (states (if (boundp 'states) states '())) + (forms '()) + (state-map '(("n" . normal) + ("v" . visual) + ("i" . insert) + ("e" . emacs) + ("o" . operator) + ("m" . motion) + ("r" . replace) + ("I" . iedit)))) + (unless keymaps + (setq keymaps default-keymaps)) + (while rest + (setq key (pop rest)) + (add-to-list + 'forms + (cond ((eq key '-) nil) ; skip this - (defmacro @map (&rest rest) - (declare (indent defun)) - (let ((i 0) - key def - first-set - prefix - (default-keymaps '(narf-mode-map)) - (keymaps (if (boundp 'keymaps) keymaps)) - (states (if (boundp 'states) states '())) - (forms (if (boundp 'forms) forms)) - (state-map '(("n" . normal) - ("v" . visual) - ("i" . insert) - ("e" . emacs) - ("o" . operator) - ("m" . motion) - ("r" . replace) - ("I" . iedit)))) - (unless keymaps - (setq keymaps default-keymaps)) - (while rest - (setq key (pop rest)) - (message ">>> KEY: %s" key) - (add-to-list - 'forms - (cond ((eq key '-)) ; skip this + ((listp key) ; it's a sub exp + `((bind! ,@key))) - ((listp key) ; it's a sub exp - (macroexpand `(@map ,@key))) + ((keywordp key) + (pcase key + ;; TODO: Data checks + (:prefix (setq prefix (kbd (pop rest))) + (if (= i 0) (setq first-set `(:prefix . ,prefix))) + nil) + (:map (setq keymaps (-list (pop rest))) + (if (= i 0) (setq first-set `(:map . ,keymaps))) + nil) + (:unset `((bind! ,(kbd (pop rest)) nil))) + (:after (prog1 `((after! ,(pop rest) (bind! ,@rest))) (setq rest '()))) + (:when (prog1 `((if ,(pop rest) (bind! ,@rest))) (setq rest '()))) + (:unless (prog1 `((if (not ,(pop rest)) (bind! ,@rest))) (setq rest '()))) + (otherwise ; might be a state prefix + (mapc (lambda (letter) + (if (assoc letter state-map) + (add-to-list 'states (cdr (assoc letter state-map))) + (user-error "Invalid mode prefix %s in key %s" letter key))) + (s-split "" (substring (symbol-name key) 1) t)) + (unless states + (user-error "Unrecognized keyword %s" key)) nil))) - ((keywordp key) - (pcase key - ;; TODO: Data checks - (:leader (setq prefix (kbd (pop rest))) nil) - (:localleader (setq prefix (kbd (pop rest))) nil) - (:prefix (setq prefix (kbd (pop rest))) - (if (= i 0) (setq first-set `(:prefix . ,prefix))) - nil) - (:map (setq keymaps (-list (pop rest))) - (if (= i 0) (setq first-set `(:map . ,keymaps))) - nil) - (:unset (prog1 `(@map ,(kbd (pop rest)) nil))) - (:after (prog1 `(@after ,(pop rest) ,(macroexp-progn `(@map ,@rest))) (setq rest '()))) - (:when (prog1 `(when ,(pop rest) ,(macroexp-progn `(@map ,@rest))) (setq rest '()))) - (:unless (prog1 `(unless ,(pop rest) ,(macroexp-progn `(@map ,@rest))) (setq rest '()))) - (otherwise ; might be a state prefix - (mapc (lambda (letter) - (when (assoc letter state-map) - (add-to-list 'states (cdr (assoc letter state-map))))) - (s-split "" (substring (symbol-name key) 1) t)) nil))) + ;; It's a key-def pair + ((or (stringp key) + (characterp key) + (vectorp key)) + (when (stringp key) + (setq key (kbd key))) + (when prefix + (setq key (cond ((vectorp key) (vconcat prefix key)) + (t (concat prefix key))))) + (unless (> (length rest) 0) + (user-error "Map has no definition for %s" key)) + (setq def (pop rest)) + (let ((first-key (car first-set)) + (first-value (cdr first-set)) + out-forms) + (dolist (keymap keymaps) + (if (not states) + ;; (add-to-list 'out-forms `(define-key ,keymap ,key ,def) t) + (add-to-list 'out-forms `(evil-define-key nil ,keymap ,key ,def) t) + (dolist (state states) + (add-to-list 'out-forms `(evil-define-key ',state ,keymap ,key ,def) t)))) + ;; (add-to-list 'out-forms `(define-key (evil-get-auxiliary-keymap ,keymap ',state) ,key ,def) t)))) - ;; It's a key-def pair - ((or (stringp key) - (characterp key) - (vectorp key)) + (setq prefix (if (eq first-key :prefix) first-value)) + (setq keymaps (if (eq first-key :map) first-value default-keymaps)) + (setq states '()) + out-forms)) - (when (stringp key) - (setq key (kbd key))) - (when prefix - (cond ((vectorp key) - (setq key (vconcat prefix key))) - (t - (setq key (concat prefix key))))) - - (unless (car rest) - (user-error "Map has no definition for %s" key)) - - (setq def (pop rest)) - (let ((first-key (car first-set)) - (first-value (cdr first-set)) - out-forms) - (dolist (keymap keymaps) - (if (not states) - (add-to-list 'out-forms `(define-key ,keymap ,key ,def) t) - (dolist (state states) - (add-to-list 'out-forms `(define-key (evil-get-auxiliary-keymap ,keymap ,state t) ,key ,def) t)))) - - (setq prefix (if (eq first-key :prefix) first-value)) - (setq keymaps (if (eq first-key :map) first-value default-keymaps)) - (setq states '()) - out-forms)) - - (t (user-error "" key))) - t) - (cl-incf i)) - `(progn ,@(apply #'nconc (delete nil (delete (list nil) forms)))))) - - ;; (defmacro @map (&rest keys) - ;; "A minimalistic and evil-centric way of binding keys. KEYS is - ;;made up of either: - ;; - ;;1. Any of the following keywords: - ;; - ;;:when CONDITION - ;;:unless CONDITION - ;;:prefix PREFIX Key(s) to prefix keymappings with - ;;:map KEYMAP Keymaps to bind keys to. Can be a list. - ;;:global Tags these keymaps for the global keymap - ;;:local Ditto, but for local keymap - ;; - ;; - ;;2. A key (as a vector e.g. [escape], a string \"\", or - ;;character ?\^?). - ;; - ;;3. A key definition: a symbol or a lambda function. " - ;; (declare (indent defun)) - ;; (let* ((keymaps (-list map)) - ;; (states (-list in)) - ;; (forms '()) - ;; item def) - ;; (while keys - ;; (setq item (pop keys)) - ;; (cond ((keywordp item) - ;; (let ((val (pop keys))) - ;; (pcase item - ;; (:after) - ;; (:when) - ;; (:unless) - ;; (:keymap) - ;; (:in) - ;; (otherwise) - ;; ) - ;; )) - ;; - ;; ((or (and (symbolp item) - ;; (evil-state-p item)) - ;; (and (listp item) - ;; (--all? (evil-state-p it) item))) - ;; (setq states (-list item))) - ;; - ;; ;; item-definition pairs - ;; ((consp item) - ;; (let ((def (cdr item)) - ;; (item (car item))) - ;; (message "k %s : d %s" item def) - ;; - ;; ;;(or (stringp item) - ;; ;; (vectorp item) - ;; ;; (characterp item)) - ;; ;;(unless items (signal 'bind-no-definition item)) - ;; ;;(setq def (pop items)) - ;; (when condition - ;; ;; Process the item - ;; (cond ((stringp item) (setq item (kbd item))) - ;; ((characterp item) (setq item (string item)))) - ;; (when prefix - ;; (setq item (if (vectorp item) - ;; (vconcat prefix item) - ;; (concat (kbd prefix) item)))) - ;; ;; Do the binding - ;; `(,@(if (null states) - ;; (push (mapcar - ;; (lambda (keymap) `(define-key ,keymap ,item ,def)) keymaps) - ;; forms) - ;; (push (mapcar (lambda (state) - ;; (mapcar (lambda (keymap) - ;; `(define-key (evil-get-auxiliary-keymap ,keymap ',state t) ,item ,def)) - ;; keymaps)) - ;; states) forms)))))) - ;; - ;; ;; fallback - ;; (t (signal 'bind-invalid-key key))) - ;; `(progn ,@forms)))) - - (defmacro @exmap (command func) - (evil-ex-define-cmd - ,command - ,(cond ((autoloadp (symbol-function `,func)) - `(lambda () (interactive) (call-interactively ,func))) - ((symbolp `,func) func) - (t (user-error "Command for %s is invalid" command))))))) + (t (user-error "Invalid key %s" key))) + t) + (cl-incf i)) + `(progn ,@(apply #'nconc (delete nil (delete (list nil) forms))))))) ;; Hooks ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -311,7 +223,7 @@ Examples: (defun narf|update-scratch-buffer-cwd () ; see core-editor.el "Make sure scratch buffer is always 'in a project.'" - (let ((dir (narf-project-root))) + (let ((dir (narf/project-root))) (with-current-buffer (get-buffer-create "*scratch*") (cd dir)))) @@ -329,43 +241,26 @@ to abort the minibuffer." (delete-windows-on "*Completions*")) (abort-recursive-edit)))) - -;;;; Project defuns ;;;;;;;;;;;;;;;;;;;; -(defun narf-project-root (&optional strict-p) - "Get the path to the root of your project. Uses `narf-project-root-files' to -determine if a directory is a project." - (let ((home (file-truename "~"))) - (catch 'found - (f-traverse-upwards - (lambda (path) - (let ((path (file-truename path))) - (if (file-equal-p home path) - (throw 'found (if strict-p nil default-directory)) - (dolist (file narf-project-root-files) - (when (file-exists-p (expand-file-name file path)) - (throw 'found path)))))) default-directory) - default-directory))) - -(defun narf-project-has-files (files &optional root) - "Return non-nil if `file' exists in the project root." - (let ((root (or root (narf-project-root))) - (files (if (listp files) files (list files))) - found-p file) - (while (and files (not found-p)) - (setq file (pop files)) - (setq found-p (file-exists-p (narf-project-path-to file root)))) - found-p)) - -(defun narf-project-path-to (file &optional root) - (let ((root (or root (narf-project-root)))) - (expand-file-name file root))) - -(defun narf-project-name (&optional root) - (file-name-nondirectory (directory-file-name (or root (narf-project-root))))) - -(defun narf-project-p () - (not (null (narf-project-root t)))) - +(after! evil + (evil-define-command narf:exit-mode-maybe () + "Exits insert/replace mode using jk without the momentary pause caused by +key-chord-define." + :repeat change + (interactive) + (let ((modified (buffer-modified-p))) + (call-interactively 'self-insert-command) + (let ((evt (read-event nil nil 0.4))) + (cond + ((null evt) (message "")) + ((and (integerp evt) (or (char-equal evt ?k) + (char-equal evt ?K))) + (if (evil-replace-state-p) + (evil-replace-backspace) + (delete-char -1)) + (set-buffer-modified-p modified) + (push 'escape unread-command-events)) + (t + (setq unread-command-events (append unread-command-events (list evt))))))))) (provide 'core-defuns) ;;; core-defuns.el ends here diff --git a/core/core-defuns.elc b/core/core-defuns.elc new file mode 100644 index 000000000..ce4cc5dfa Binary files /dev/null and b/core/core-defuns.elc differ diff --git a/core/core-editor.el b/core/core-editor.el index 06775777c..8f59cf8b5 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -1,3 +1,5 @@ +;;; core-editor.el +;; see lib/editor-defuns.el ;;;; Editor behavior ;;;;;;;;;;;;;;;; (setq-default @@ -12,7 +14,7 @@ fill-column 80 ;; Sane scroll settings - scroll-margin 5 + scroll-margin 0 scroll-conservatively 9999 scroll-preserve-screen-position t @@ -27,61 +29,14 @@ truncate-partial-width-windows nil) -;; Modes 'n hooks ;;;;;;;;;;;;;;;;;;; - -(@associate text-mode :match "/LICENSE[^/]*$") -(@associate sh-mode :match "zsh\\(env\\|rc\\)?$") -(@associate sh-mode :match "z\\(profile\\|login\\|logout\\)?$") -(@associate sh-mode :match "zsh/") -(@associate applescript-mode :match "\\.applescript$") -(@associate emacs-lisp-mode :match "Cask$") -(@associate emacs-lisp-mode :match "\\.el\\.gz$") -(@associate makefile-gmake-mode :match "/Makefile$") -(@associate nxml-mode :match "\\.plist$") - -(@add-hook help-mode 'visual-line-mode) -(@add-hook python-mode 'electric-indent-local-mode) -(@add-hook emacs-lisp-mode 'turn-on-eldoc-mode) -(@add-hook eldoc-mode (diminish 'eldoc-mode " ?")) -(@add-hook makefile-mode 'narf|enable-tabs) ; Use normal tabs in makefiles - -;; Fix code folding -;; (@add-hook prog-mode (unless (bound-and-true-p hs-minor-mode) -;; (hs-minor-mode 1) -;; (diminish 'hs-minor-mode))) - -(@add-hook find-file 'narf|update-scratch-buffer-cwd) -;; (add-hook 'before-save-hook 'delete-trailing-whitespace) - -;; If file is oversized... -(@add-hook find-file (when (> (buffer-size) (* 1024 1024)) - (setq buffer-read-only t) - (buffer-disable-undo) - (fundamental-mode) - (visual-line-mode))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; (global-whitespace-mode 1) ; Show whitespace -(global-font-lock-mode t) ; Enable syntax highlighting for older emacs -(global-auto-revert-mode 1) ; revert buffers for changed files -(electric-indent-mode -1) -(winner-mode 1) ; window config undo/redo - - ;; Automatic minor modes ;;;;;;;;;;; -(defvar narf/auto-minor-mode-alist () - "Alist of filename patterns vs correpsonding minor mode functions, -see `auto-mode-alist' All elements of this alist are checked, meaning -you can enable multiple minor modes for the same regexp.") - (defun narf|enable-minor-mode-maybe () - "Check file name against `narf/auto-minor-mode-alist'." + "Check file name against `narf-auto-minor-mode-alist'." (when buffer-file-name (let ((name buffer-file-name) (remote-id (file-remote-p buffer-file-name)) - (alist narf/auto-minor-mode-alist)) + (alist narf-auto-minor-mode-alist)) ;; Remove backup-suffixes from file name. (setq name (file-name-sans-versions name)) ;; Remove remote file name identification. @@ -93,11 +48,63 @@ you can enable multiple minor modes for the same regexp.") (funcall (cdar alist) 1)) (setq alist (cdr alist)))))) -(@add-hook find-file 'narf|enable-minor-mode-maybe) +(add-hook! find-file 'narf|enable-minor-mode-maybe) + + +;; Modes 'n hooks ;;;;;;;;;;;;;;;;;;; + +(associate! text-mode :match "/LICENSE[^/]*$") +(associate! sh-mode :match "z\\(profile\\|login\\|logout\\)?$") +(associate! sh-mode :match "zsh/") +(associate! applescript-mode :match "\\.applescript$") +(associate! emacs-lisp-mode :match "Cask$") +(associate! emacs-lisp-mode :match "\\.el\\.gz$") +(associate! makefile-gmake-mode :match "/Makefile$") +(associate! nxml-mode :match "\\.plist$") + +(add-hook! help-mode 'visual-line-mode) +(add-hook! python-mode 'electric-indent-local-mode) +(add-hook! makefile-mode 'narf|enable-tabs) ; Use normal tabs in makefiles +(add-hook! before-save 'delete-trailing-whitespace) + +(add-hook! eldoc-mode (diminish 'eldoc-mode " ?")) + +;; Line wrapping +(add-hook! text-mode 'narf|enable-hard-wrap) +(add-hook! prog-mode 'narf|enable-comment-hard-wrap) +(add-hook! auto-fill-mode (diminish 'auto-fill-function)) + +;; If file is oversized... +(add-hook! find-file + (when (> (buffer-size) (* 1024 1024)) + (setq buffer-read-only t) + (buffer-disable-undo) + (fundamental-mode) + (visual-line-mode))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; (global-whitespace-mode 1) ; Show whitespace +(global-font-lock-mode t) ; Enable syntax highlighting for older emacs +(global-auto-revert-mode 1) ; revert buffers for changed files +(electric-indent-mode -1) + +;; window config undo/redo +(winner-mode 1) +(add-hook! after-init (setq winner-boring-buffers narf-ignore-buffers)) ;; Plugins ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(use-package undo-tree + :defer t + :config + ;; Shut up undo-tree's constant complaining: http://youtu.be/Z6woIRLnbmE + (defadvice undo-tree-load-history-hook (around undo-tree-load-history-shut-up activate) + (shut-up! ad-do-it)) + (defadvice undo-tree-save-history-hook (around undo-tree-save-history-shut-up activate) + (shut-up! ad-do-it))) + (use-package ace-jump-mode :functions (ace-jump-char-category ace-jump-do) :commands (ace-jump-line-mode ace-jump-char-mode @@ -122,17 +129,13 @@ you can enable multiple minor modes for the same regexp.") (char-to-string query-char-2)))))) (use-package ace-link - :commands - (ace-link-info ace-link-help ace-link-compilation ace-link-custom ace-link-org) + :commands (ace-link-info ace-link-help ace-link-compilation ace-link-custom ace-link-org) :init - (after "help-mode" - (bind motion :map help-mode-map "go" 'ace-link-help)) - (after "compile" - (bind motion :map compilation-mode-map "go" 'ace-link-compilation)) - (after "info" - (bind motion :map Info-mode-map "go" 'ace-link-info)) - (after "org" - (bind motion :map org-mode-map "go" 'ace-link-org))) + (bind! + (:after help-mode :map help-mode-map :m "go" 'ace-link-help) + (:after compile :map compilation-mode-map :m "go" 'ace-link-compilation) + (:after info :map Info-mode-map :m "go" 'ace-link-info) + (:after org :map org-mode-map :m "go" 'ace-link-org))) (use-package ace-window :commands ace-window @@ -147,77 +150,83 @@ you can enable multiple minor modes for the same regexp.") (use-package expand-region :commands (er/expand-region er/contract-region er/mark-symbol er/mark-word)) -(use-package goto-last-change :defer 3) +(use-package goto-last-change :commands goto-last-change) (use-package hl-todo :commands hl-todo-mode :init - (@add-hook prog-mode 'hl-todo-mode) - (defvar hl-todo-keyword-faces) - '(("\\(\\bTODO\\((.*)\\)?:?\\)" . "#cc9393") - ("\\(\\bNOTE\\((.*)\\)?:?\\)" . "#d0bf8f") - ("\\(\\bFIXME\\((.*)\\)?:?\\)" . "#cc9393"))) + (add-hook! prog-mode 'hl-todo-mode) + (defvar hl-todo-keyword-faces + '(("TODO" . "#cc9393") + ("NOTE" . "#d0bf8f") + ("FIXME" . "#cc9393")))) (use-package hideshow - :diminish hs-minor-mode - :init (@add-hook (prog-mode org-mode) 'hs-minor-mode)) + :commands (hs-minor-mode hs-toggle-hiding hs-already-hidden-p) + :diminish hs-minor-mode) (use-package rainbow-delimiters :commands rainbow-delimiters-mode - :init (@add-hook (emacs-lisp-mode js2-mode scss-mode) 'rainbow-delimiters-mode) + :init (add-hook! (emacs-lisp-mode js2-mode scss-mode) 'rainbow-delimiters-mode) :config (setq rainbow-delimiters-outermost-only-face-count 1)) -(use-package rotate-text :commands (rotate-word-at-point rotate-region)) +(use-package rotate-text :commands (rotate-word-at-point rotate-region)) -(use-package smart-forward - :commands (smart-up smart-down smart-left smart-right)) +(use-package smart-forward :commands (smart-up smart-down smart-left smart-right)) (use-package smartparens :diminish smartparens-mode - :commands smartparens-global-mode - :init (@add-init-hook evil-insert-state-entry 'smartparens-global-mode) + :functions sp-insert-pair + :commands (smartparens-global-mode + sp-pair + sp-local-pair + sp-point-in-string-or-comment) + :init (add-hook! evil-insert-state-entry (unless smartparens-global-mode (smartparens-global-mode 1))) :config - (progn - (setq blink-matching-paren t - sp-autowrap-region nil ; let evil-surround handle this - sp-highlight-pair-overlay nil - sp-show-pair-delay 0) + (setq blink-matching-paren t + sp-autowrap-region nil ; let evil-surround handle this + sp-highlight-pair-overlay nil + sp-show-pair-delay 0) - (use-package smartparens-config) + (require 'smartparens-config) - ;; Handle newlines + spaces - (sp-pair "{" "}" :post-handlers '(("||\n[i]" "RET") ("| " " ")) :unless '(sp-point-before-word-p sp-point-before-same-p)) - (sp-pair "(" ")" :post-handlers '(("||\n[i]" "RET") ("| " " ")) :unless '(sp-point-before-word-p sp-point-before-same-p)) - ;; Auto-close more conservatively - (sp-pair "[" nil :unless '(sp-point-before-word-p sp-point-before-same-p)) - (sp-pair "'" nil :unless '(sp-point-after-word-p sp-point-before-word-p sp-point-before-same-p)) - (sp-pair "\"" nil :unless '(sp-point-after-word-p sp-point-before-word-p sp-point-before-same-p)) - (sp-with-modes '(json-mode js2-mode ruby-mode enh-ruby-mode python-mode) - (sp-local-pair "[" nil :post-handlers '(("||\n[i]" "RET")))) - (sp-with-modes '(c-mode c++-mode objc-mode java-mode scss-mode css-mode php-mode) - (sp-local-pair "/* " " */" :post-handlers '(("||\n[i]" "RET"))) - (sp-local-pair "/**" "*/" :post-handlers '(("||\n[i]" "RET")))) - (sp-with-modes '(c-mode c++-mode objc-mode java-mode) ; Support for generics - (sp-local-pair "<" ">" :when '(sp-point-after-word-p) :unless '(sp-point-before-same-p))) - (sp-with-modes '(objc-mode scss-mode css-mode) - (sp-local-pair "/*\n" "\n */" :post-handlers '(("||[i]" "RET")))) - (sp-with-modes '(c-mode c++-mode php-mode java-mode) - (sp-local-pair "/*" "" :post-handlers '((" ||\n[i]*/" "RET")))) + ;; Handle newlines + spaces + (sp-pair "{" "}" + :post-handlers '(("||\n[i]" "RET") ("| " " ")) + :unless '(sp-point-before-word-p sp-point-before-same-p)) + (sp-pair "(" ")" + :post-handlers '(("||\n[i]" "RET") ("| " " ")) + :unless '(sp-point-before-word-p sp-point-before-same-p)) + ;; Auto-close more conservatively + (sp-pair "[" nil :unless '(sp-point-before-word-p sp-point-before-same-p)) + (sp-pair "'" nil :unless '(sp-point-after-word-p sp-point-before-word-p sp-point-before-same-p)) + (sp-pair "\"" nil :unless '(sp-point-after-word-p sp-point-before-word-p sp-point-before-same-p)) + (sp-with-modes '(json-mode js2-mode ruby-mode enh-ruby-mode python-mode) + (sp-local-pair "[" nil :post-handlers '(("||\n[i]" "RET")))) + (sp-with-modes '(c-mode c++-mode objc-mode java-mode scss-mode css-mode php-mode) + (sp-local-pair "/* " " */" :post-handlers '(("||\n[i]" "RET"))) + (sp-local-pair "/**" "*/" :post-handlers '(("||\n[i]" "RET")))) + (sp-with-modes '(c-mode c++-mode objc-mode java-mode) ; Support for generics + (sp-local-pair "<" ">" :when '(sp-point-after-word-p) :unless '(sp-point-before-same-p))) + (sp-with-modes '(objc-mode scss-mode css-mode) + (sp-local-pair "/*\n" "\n */" :post-handlers '(("||[i]" "RET")))) + (sp-with-modes '(c-mode c++-mode php-mode java-mode) + (sp-local-pair "/*" "" :post-handlers '((" ||\n[i]*/" "RET")))) + (sp-with-modes '(markdown-mode org-mode) + (sp-local-pair "*" "*" :unless '(sp-point-after-bol-p sp-point-before-same-p sp-point-after-same-p))) - (after "yasnippet" - (advice-add 'yas-expand :before 'sp-remove-active-pair-overlay)))) + (after! yasnippet + (advice-add 'yas-expand :before 'sp-remove-active-pair-overlay))) (use-package smex :commands (smex smex-major-mode-commands smex-initialize smex-update) - :init (setq smex-save-file (! (expand-file-name "smex-items" TMP-DIR))) - :config - (smex-initialize) + :init (setq smex-save-file (! (concat narf-temp-dir "smex-items"))) + :config (smex-initialize) ;; Hook up smex to auto-update, rather than update on every run (defun smex-update-after-load (unused) (when (boundp 'smex-cache) (smex-update))) (add-hook 'after-load-functions 'smex-update-after-load)) - (provide 'core-editor) ;;; core-editor.el ends here diff --git a/core/core-evil.el b/core/core-evil.el index cd4470541..fc4a5efb5 100644 --- a/core/core-evil.el +++ b/core/core-evil.el @@ -1,24 +1,25 @@ ;;; core-evil.el --- the root of all evil +;; see lib/evil-defuns.el (use-package evil :init ;; highlight matching delimiters where it's important (defun show-paren-mode-off () (show-paren-mode -1)) - (@add-hook evil-insert-state-entry 'show-paren-mode) - (@add-hook evil-insert-state-exit 'show-paren-mode-off) - (@add-hook evil-visual-state-entry 'show-paren-mode) - (@add-hook evil-visual-state-exit 'show-paren-mode-off) - (@add-hook evil-motion-state-entry 'show-paren-mode) - (@add-hook evil-motion-state-exit 'show-paren-mode-off) - (@add-hook evil-operator-state-entry 'show-paren-mode) - (@add-hook evil-operator-state-exit 'show-paren-mode-off) + (add-hook! evil-insert-state-entry 'show-paren-mode) + (add-hook! evil-insert-state-exit 'show-paren-mode-off) + (add-hook! evil-visual-state-entry 'show-paren-mode) + (add-hook! evil-visual-state-exit 'show-paren-mode-off) + (add-hook! evil-motion-state-entry 'show-paren-mode) + (add-hook! evil-motion-state-exit 'show-paren-mode-off) + (add-hook! evil-operator-state-entry 'show-paren-mode) + (add-hook! evil-operator-state-exit 'show-paren-mode-off) ;; Disable highlights on insert-mode - (@add-hook evil-insert-state-entry 'evil-ex-nohighlight) + (add-hook! evil-insert-state-entry 'evil-ex-nohighlight) - (@add-hook undo-tree-mode (diminish 'undo-tree-mode)) + (add-hook! undo-tree-mode (diminish 'undo-tree-mode)) ;; Always ensure evil-shift-width is consistent with tab-width - (@add-hook evil-local-mode (setq evil-shift-width tab-width)) + (add-hook! evil-local-mode (setq evil-shift-width tab-width)) :config (setq evil-magic t evil-want-C-u-scroll t ; enable C-u for scrolling @@ -37,7 +38,8 @@ (evil-mode 1) (evil-select-search-module 'evil-search-module 'evil-search) - (add-to-list 'evil-overriding-maps 'narf-mode-map) + + (bind! :map evil-command-window-mode-map :n [escape] 'kill-buffer-and-window) (defadvice evil-ex-hl-do-update-highlight (around evil-ex-hl-shut-up activate) "Silence 'Error running timer `evil-ex-hl-do-update-highlight': (error @@ -50,176 +52,13 @@ See `https://bitbucket.org/lyro/evil/issue/527'" (dolist (mode-map '((cider-repl-mode . emacs) (comint-mode . emacs) (term-mode . emacs) - (fundamental-mode . normal) + (fundamental-mode . motion) (help-mode . normal) (message-mode . normal) - (compilation-mode . normal))) + (compilation-mode . normal) + (text-mode . normal))) (evil-set-initial-state `,(car mode-map) `,(cdr mode-map))) - (progn ; evil plugins - (use-package evil-anzu) - - (use-package evil-commentary - :diminish evil-commentary-mode - :commands (evil-commentary - evil-commentary-mode - evil-commentary-yank - evil-commentary-line) - :config (evil-commentary-mode 1)) - - (use-package evil-ex-registers - :commands (evil-get-spec-register - evil-ex-paste-from-register)) - - (use-package evil-exchange - :commands evil-exchange - :config - (defadvice evil-force-normal-state (before evil-esc-quit-exchange activate) - "Remove `evil-exchange' overlays on ESC" - (when evil-exchange--overlays (evil-exchange-cancel)))) -(lambda () - (interactive) - (if (iedit-current-occurrence-string) - (progn - (save-excursion (iedit-restrict-region (region-beginning) (region-end))) - (evil-previous-line) - (evil-iedit-state/iedit-mode)) - (call-interactively 'evil-ret))) - (use-package evil-iedit-state - :functions (iedit-current-occurrence-string iedit-restrict-region) - :commands (evil-iedit-state evil-iedit-state/iedit-mode) - :config - (@map ; Don't interfere with evil-snipe - :I :unset "s" - :I :unset "S" - - :I "V" 'evil-visual-line - :I "C" 'evil-iedit-state/substitute ; instead of s/S - :I "za" 'iedit-toggle-unmatched-lines-visible - - :v "SPC" (λ (if (iedit-current-occurrence-string) - (progn - (save-excursion (iedit-restrict-region (region-beginning) (region-end))) - (evil-previous-line) - (evil-iedit-state/iedit-mode)) - (call-interactively 'evil-ret))))) - - (use-package evil-indent-textobject - :commands (evil-indent-i-indent - evil-indent-a-indent - evil-indent-a-indent-lines) - :init - (@map :map evil-inner-text-objects-map - "i" 'evil-indent-i-indent - "i" 'evil-indent-a-indent - "I" 'evil-indent-a-indent-lines)) - - (use-package evil-jumper - :init - (setq evil-jumper-file (! (expand-file-name "jumplist" narf-temp-dir)) - evil-jumper-auto-center t - evil-jumper-auto-save-interval 3600)) - - (use-package evil-matchit - :commands (evilmi-jump-items global-evil-matchit-mode) - :config (global-evil-matchit-mode 1)) - - (use-package evil-numbers - :commands (evil-numbers/inc-at-pt - evil-numbers/dec-at-pt)) - - (use-package evil-search-highlight-persist - :config (global-evil-search-highlight-persist t)) - - (use-package evil-snipe - :diminish evil-snipe-mode - :commands (evil-snipe-s evil-snipe-S - evil-snipe-x evil-snipe-X - evil-snipe-f evil-snipe-F - evil-snipe-t evil-snipe-T) - :init - (setq-default - evil-snipe-smart-case t - evil-snipe-scope 'line - evil-snipe-repeat-scope 'buffer - evil-snipe-override-evil-repeat-keys nil - evil-snipe-symbol-groups '((?\[ "[[{(]") - (?\] "[]})]"))) - :config - (evil-snipe-mode 1) - (evil-snipe-override-mode 1)) - - (use-package evil-space - :diminish (evil-space-mode . "_") - :config - (progn - (add-to-list 'evil-overriding-maps 'evil-space-mode-map) - - (evil-space-setup "/" "n" "N") - (evil-space-setup "?" "N" "n") - - (@after evil-numbers - (let ((map (evil-get-auxiliary-keymap narf-mode-map 'normal))) - (evil-space-setup "g=" "g=" "g-" map) - (evil-space-setup "g-" "g-" "g=" map))) - - (@after evil-snipe - (let ((map (evil-get-auxiliary-keymap evil-snipe-override-mode-map 'motion))) - (evil-space-setup "t" "C-;" "C-," map) - (evil-space-setup "f" "C-;" "C-," map) - (evil-space-setup "T" "C-," "C-;" map) - (evil-space-setup "F" "C-," "C-;" map)) - (let ((map (evil-get-auxiliary-keymap evil-snipe-mode-map 'motion))) - (evil-space-setup "s" "C-;" "C-," map) - (evil-space-setup "S" "C-," "C-;" map))) - - (@after evil-visualstar - (let ((map (evil-get-auxiliary-keymap evil-visualstar-mode-map 'visual))) - (evil-space-setup "*" "n" "N" map) - (evil-space-setup "#" "n" "N" map))) - - (evil-space-mode))) - - (use-package evil-surround - :commands (global-evil-surround-mode - evil-surround-edit - evil-Surround-edit - evil-surround-region) - :config - (global-evil-surround-mode 1) - - ;; Escaped surround characters - (defun evil-surround-escaped () - (let* ((char (string (read-char "\\"))) - (pair (cond ((string-match "[]})[{(]" char) - (let ((-pair (cdr (assoc (string-to-char char) evil-surround-pairs-alist)))) - `(,(car -pair) . ,(cdr -pair)))) - (t - `(,char . ,char)))) - (format (if (sp-point-in-string) "\\\\%s" "\\%s"))) - (cons (format format (car pair)) - (format format (cdr pair))))) - - (push '(?\\ . evil-surround-escaped) evil-surround-pairs-alist)) - - (use-package evil-visualstar - :commands (global-evil-visualstar-mode - evil-visualstar/begin-search - evil-visualstar/begin-search-forward - evil-visualstar/begin-search-backward) - :config - ;; I cut this down because the original visualstar wouldn't remember - ;; the last search if evil-search-module was 'evil-search. - (defun narf/evil-visualstar/begin-search (beg end direction) - (when (evil-visual-state-p) - (evil-exit-visual-state) - (let ((selection (regexp-quote (buffer-substring-no-properties beg end)))) - (setq isearch-forward direction) - (evil-search selection direction t)))) - (advice-add 'evil-visualstar/begin-search :override 'narf/evil-visualstar/begin-search) - - (global-evil-visualstar-mode 1))) - (progn ; evil hacks (defadvice evil-force-normal-state (before evil-esc-quit activate) (ignore-errors @@ -236,27 +75,154 @@ See `https://bitbucket.org/lyro/evil/issue/527'" (defadvice evil-window-vsplit (after evil-window-vsplit-jump activate) (evil-window-right 1)) - (@after isearch ; Restore vimmish ex-mode keymaps to isearch - ;; Hide keystroke display while isearch is active - (@add-hook isearch-mode (setq echo-keystrokes 0)) - (@add-hook isearch-mode-end (setq echo-keystrokes 0.02)) - (@map :map isearch-mode-map - :unset "C-r" + ;; Restore vimmish ex-mode keymaps to isearch + ;; Hide keystroke display while isearch is active + (add-hook! isearch-mode (setq echo-keystrokes 0)) + (add-hook! isearch-mode-end (setq echo-keystrokes 0.02)) + (bind! :map isearch-mode-map + "C-r" nil - "C-r %" (λ (narf:isearch-paste-from-register ?%)) - "C-r #" (λ (narf:isearch-paste-from-register ?#)) - "C-r /" (λ (narf:isearch-paste-from-register ?/)) - "C-r :" (λ (narf:isearch-paste-from-register ?:)) - "C-r ." (λ (narf:isearch-paste-from-register ?.)) - "C-r -" (λ (narf:isearch-paste-from-register ?-)) - "C-r _" (λ (narf:isearch-paste-from-register ?_)) - "C-r =" (λ (narf:isearch-paste-from-register ?=)) - "C-r +" 'narf:isearch-paste-from-clipboard + "C-r %" (λ (narf/isearch-paste-from-register ?%)) + "C-r #" (λ (narf/isearch-paste-from-register ?#)) + "C-r /" (λ (narf/isearch-paste-from-register ?/)) + "C-r :" (λ (narf/isearch-paste-from-register ?:)) + "C-r ." (λ (narf/isearch-paste-from-register ?.)) + "C-r -" (λ (narf/isearch-paste-from-register ?-)) + "C-r _" (λ (narf/isearch-paste-from-register ?_)) + "C-r =" (λ (narf/isearch-paste-from-register ?=)) + "C-r +" 'narf/isearch-paste-from-clipboard - "C-w" 'narf:isearch-delete-word - "C-u" 'narf:isearch-delete-line - "M-v" 'narf:isearch-paste-from-clipboard)))) + "C-w" 'narf/isearch-delete-word + "C-u" 'narf/isearch-delete-line + "M-v" 'narf/isearch-paste-from-clipboard))) +;; evil plugins +(use-package evil-anzu) + +(use-package evil-commentary + :diminish evil-commentary-mode + :commands (evil-commentary + evil-commentary-yank + evil-commentary-line) + :config (evil-commentary-mode 1)) + +(use-package evil-ex-registers + :commands (evil-get-spec-register + evil-ex-paste-from-register)) + +(use-package evil-exchange + :commands evil-exchange + :config + (advice-add 'evil-force-normal :after 'narf*evil-exchange-off)) + +(use-package evil-iedit-state + :functions (iedit-current-occurrence-string iedit-restrict-region) + :commands (evil-iedit-state evil-iedit-state/iedit-mode) + :config + (bind! ; Don't interfere with evil-snipe + :I :unset "s" + :I :unset "S" + :I "V" 'evil-visual-line + :I "C" 'evil-iedit-state/substitute ; instead of s/S + :I "za" 'iedit-toggle-unmatched-lines-visible + :v "SPC" 'narf:iedit-restrict-to-region)) + +(use-package evil-indent-textobject + :commands (evil-indent-i-indent + evil-indent-a-indent + evil-indent-a-indent-lines) + :init + (bind! :map evil-inner-text-objects-map + "i" 'evil-indent-i-indent + "i" 'evil-indent-a-indent + "I" 'evil-indent-a-indent-lines)) + +(use-package evil-jumper + :init + (setq evil-jumper-file (! (concat narf-temp-dir "jumplist")) + evil-jumper-auto-center t + evil-jumper-auto-save-interval 3600)) + +(use-package evil-matchit + :commands (evilmi-jump-items global-evil-matchit-mode) + :config (global-evil-matchit-mode 1)) + +(use-package evil-numbers + :commands (evil-numbers/inc-at-pt + evil-numbers/dec-at-pt)) + +(use-package evil-search-highlight-persist + :config (global-evil-search-highlight-persist t)) + +(use-package evil-snipe + :diminish evil-snipe-mode + :commands (evil-snipe-s evil-snipe-S + evil-snipe-x evil-snipe-X + evil-snipe-f evil-snipe-F + evil-snipe-t evil-snipe-T + evil-snipe-repeat evil-snipe-repeat-reverse) + :init + (bind! :m "s" 'evil-snipe-s + :m "S" 'evil-snipe-S + :m "f" 'evil-snipe-f + :m "F" 'evil-snipe-F + :m "t" 'evil-snipe-t + :m "T" 'evil-snipe-T + :o "z" 'evil-snipe-s + :o "Z" 'evil-snipe-S + :o "x" 'evil-snipe-x + :o "X" 'evil-snipe-X) + (define-key evil-normal-state-map "s" nil) + (define-key evil-normal-state-map "S" nil) + :config + (setq-default + evil-snipe-smart-case t + evil-snipe-scope 'line + evil-snipe-repeat-scope 'buffer + evil-snipe-override-evil-repeat-keys nil + evil-snipe-symbol-groups '((?\[ "[[{(]") + (?\] "[]})]"))) + (evil-snipe-mode 1) + (evil-snipe-override-mode 1)) + +(use-package evil-space + :diminish (evil-space-mode . "_") + :init (setq evil-space-auto-setup nil) + :config + (evil-space-mode 1) + + (evil-space-setup "/" "n" "N") + (evil-space-setup "?" "N" "n") + + (after! evil-snipe + (evil-space-setup 'evil-snipe-f 'evil-snipe-repeat 'evil-snipe-repeat-reverse) + (evil-space-setup 'evil-snipe-F 'evil-snipe-repeat 'evil-snipe-repeat-reverse) + (evil-space-setup 'evil-snipe-t 'evil-snipe-repeat 'evil-snipe-repeat-reverse) + (evil-space-setup 'evil-snipe-T 'evil-snipe-repeat 'evil-snipe-repeat-reverse) + (evil-space-setup 'evil-snipe-s 'evil-snipe-repeat 'evil-snipe-repeat-reverse) + (evil-space-setup 'evil-snipe-S 'evil-snipe-repeat 'evil-snipe-repeat-reverse)) + + (after! evil-visualstar + (evil-space-setup 'evil-visualstar/begin-search-forward "n" "N") + (evil-space-setup 'evil-visualstar/begin-search-backward "n" "N"))) + +(use-package evil-surround + :commands (global-evil-surround-mode + evil-surround-edit + evil-Surround-edit + evil-surround-region) + :config + (global-evil-surround-mode 1) + ;; Escaped surround characters + (push '(?\\ . narf/evil-surround-escaped) evil-surround-pairs-alist)) + +(use-package evil-visualstar + :commands (global-evil-visualstar-mode + evil-visualstar/begin-search + evil-visualstar/begin-search-forward + evil-visualstar/begin-search-backward) + :config + (global-evil-visualstar-mode 1)) (provide 'core-evil) ;;; core-evil.el ends here diff --git a/core/core-flycheck.el b/core/core-flycheck.el new file mode 100644 index 000000000..7c51d4a26 --- /dev/null +++ b/core/core-flycheck.el @@ -0,0 +1,40 @@ +;;; core-flycheck.el --- check yourself before you shrek yourself +;; Related to: lib/defuns-flycheck.el + +(use-package flycheck + :functions (flycheck-buffer) + :commands (flycheck-mode flycheck-list-errors) + :init + (setq flycheck-indication-mode 'right-fringe + ;; Removed checks on idle/change for snappiness + flycheck-check-syntax-automatically '(save mode-enabled idle-change) + flycheck-disabled-checkers '(emacs-lisp-checkdoc make)) + + (add-hook! (ruby-mode + python-mode + php-mode + lua-mode + shell-mode + scss-mode + c++-mode + c-mode) 'flycheck-mode) + :config + ;; TODO: Implement this + (add-unreal-buffer! "^\\*Flycheck.*\\*$") + + (bind! :map flycheck-error-list-mode-map + :n [escape] 'kill-this-buffer + :n "q" 'kill-this-buffer) + + (evil-initial-state 'flycheck-error-list-mode 'emacs) + + ;; Check buffer when normal mode is entered + (add-hook! evil-normal-state-entry 'narf*flycheck-buffer) + ;; And on ESC in normal mode. + (advice-add 'evil-force-normal-state :after 'narf*flycheck-buffer) + (advice-add 'flycheck-mode-line-status-text :filter-return 'narf*fly-shorter-status)) + +(use-package flyspell :commands flyspell-mode) + +(provide 'core-flycheck) +;;; core-flycheck.el ends here diff --git a/core/core-helm.el b/core/core-helm.el new file mode 100644 index 000000000..02a62a818 --- /dev/null +++ b/core/core-helm.el @@ -0,0 +1,128 @@ +;;; core-helm.el + +(use-package helm + :commands (helm + helm-etags-select + helm-show-kill-ring + helm-bookmarks + helm-wg + helm-ag + helm-alive-p + helm-attrset) + :init + (defvar helm-global-prompt ">>> ") + (setq helm-quick-update t + helm-idle-delay 0.05 + helm-input-idle-delay 0.05 + helm-reuse-last-window-split-state t + helm-buffers-fuzzy-matching t + helm-candidate-number-limit 40 + helm-bookmark-show-location t + ;; let popwin handle this + helm-split-window-default-side 'other + helm-split-window-preferred-function 'narf/helm-split-window) + :config + (require 'helm-ag) + (require 'helm-grep) + + (evil-set-initial-state 'helm-mode 'emacs) + + (add-popwin-rule! "\\`\\*helm.*?\\*\\'" :regexp t :position bottom :height 15) + (add-unreal-buffer! "^\\*[Hh]elm.*\\*$") + (after! winner + ;; Tell winner-mode to ignore helm buffers + (dolist (bufname '("*helm recentf*" + "*helm projectile*" + "*helm imenu*" + "*helm company*" + "*helm buffers*" + ;; "*helm tags*" + "*helm-ag*" + "*Helm Swoop*")) + (push bufname winner-boring-buffers))) + + (bind! :map helm-map + "C-w" 'evil-delete-backward-word + "C-u" 'helm-delete-minibuffer-contents + "C-r" 'evil-ex-paste-from-register ; Evil registers in helm! Glorious! + [escape] 'helm-keyboard-quit) + + (advice-add 'helm-display-mode-line :override 'narf*helm-hide-modeline)) + +(use-package helm-org + :commands (helm-org-in-buffer-headings + helm-org-agenda-files-headings + helm-org-capture-templates)) + +(use-package helm-files + :commands helm-recentf + :config + (defun helm-recentf () + "Reconfigured `helm-recentf' to use `helm', instead of `helm-other-buffer'" + (interactive) + (let ((helm-ff-transformer-show-only-basename nil)) + (helm :sources '(helm-source-recentf) + :buffer "*helm recentf*" + :prompt helm-global-prompt)))) + +(use-package helm-css-scss ; https://github.com/ShingoFukuyama/helm-css-scss + :commands (helm-css-scss + helm-css-scss-multi + helm-css-scss-insert-close-comment)) + +(use-package helm-swoop ; https://github.com/ShingoFukuyama/helm-swoop + :defines (helm-swoop-last-prefix-number) + :commands (helm-swoop helm-multi-swoop helm-multi-swoop-all) + :config + (setq helm-swoop-use-line-number-face t + helm-swoop-split-with-multiple-windows t + helm-swoop-speed-or-color t)) + +(use-package projectile + :diminish projectile-mode + :commands (projectile-ack projectile-ag projectile-compile-project projectile-dired + projectile-grep projectile-find-dir projectile-find-file projectile-find-tag + projectile-find-test-file projectile-invalidate-cache projectile-kill-buffers + projectile-multi-occur projectile-project-root projectile-recentf + projectile-regenerate-tags projectile-replace + projectile-run-async-shell-command-in-root projectile-run-shell-command-in-root + projectile-switch-project projectile-switch-to-buffer projectile-vc + projectile-project-p projectile-global-mode) + :config + (add-hook! kill-emacs 'narf|projectile-invalidate-cache-maybe) + + (setq-default projectile-enable-caching t) + (setq projectile-sort-order 'recentf + projectile-cache-file (! (concat narf-temp-dir "projectile.cache")) + projectile-known-projects-file (! (concat narf-temp-dir "projectile.projects")) + projectile-indexing-method 'alien + projectile-project-root-files narf-project-root-files) + + (add-to-list 'projectile-globally-ignored-files "ido.last") + (add-to-list 'projectile-globally-ignored-directories "assets") + (add-to-list 'projectile-other-file-alist '("scss" "css")) + (add-to-list 'projectile-other-file-alist '("css" "scss")) + + (projectile-global-mode +1) + + (advice-add 'projectile-prepend-project-name :override 'narf*projectile-replace-prompt)) + +(use-package helm-projectile + :commands (helm-projectile-switch-to-buffer + helm-projectile-find-file + helm-projectile-recentf + helm-projectile-find-other-file + helm-projectile-switch-project) + :config + (require 'helm) + (require 'projectile)) + +;; (use-package helm-c-yasnippet :commands helm-yas-visit-snippet-file) +(use-package helm-buffers :commands helm-buffers-list) +(use-package helm-semantic :commands helm-semantic-or-imenu) +(use-package helm-elisp :commands helm-apropos) +(use-package helm-command :commands helm-M-x) +(use-package helm-company :defer t) + +(provide 'core-helm) +;;; core-helm.el ends here diff --git a/core/core-os-linux.el b/core/core-os-linux.el index 146fa01b0..e46f15a9d 100644 --- a/core/core-os-linux.el +++ b/core/core-os-linux.el @@ -4,6 +4,5 @@ (interactive) (error "Not yet implemented")) - (provide 'core-os-linux) ;;; core-os-linux.el ends here diff --git a/core/core-os-osx.el b/core/core-os-osx.el index 7451ca745..71f908c1d 100644 --- a/core/core-os-osx.el +++ b/core/core-os-osx.el @@ -31,7 +31,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(@after evil +(after! evil (when (featurep 'ns) ;; On OSX, stop copying each visual state move to the clipboard: ;; https://bitbucket.org/lyro/evil/issue/336/osx-visual-state-copies-the-region-on @@ -40,14 +40,13 @@ (defadvice evil-visual-update-x-selection (around clobber-x-select-text activate) (unless (featurep 'ns) ad-do-it)))) -;; Send current file to OSX apps (defun narf-open-with (&optional app-name path) + "Send PATH to APP-NAME on OSX." (interactive) (let* ((path (f-full (s-replace "'" "\\'" (or path (if (eq major-mode 'dired-mode) (dired-get-file-for-visit) (buffer-file-name)))))) (command (concat "open " (when app-name (concat "-a " (shell-quote-argument app-name))) " '" path "'"))) (message "Running: %s" command) (shell-command command))) - (provide 'core-os-osx) ;;; core-os-osx.el ends here diff --git a/core/core-os-win32.el b/core/core-os-win32.el index 6a61159a0..6ad680dfc 100644 --- a/core/core-os-win32.el +++ b/core/core-os-win32.el @@ -4,6 +4,5 @@ (interactive) (error "Not yet implemented")) - (provide 'core-os-win32) ;;; core-os-win32.el ends here diff --git a/core/core-project.el b/core/core-project.el new file mode 100644 index 000000000..bdea974b3 --- /dev/null +++ b/core/core-project.el @@ -0,0 +1,96 @@ +;;; core-project.el --- all your (basic) project navigational needs + +(use-package ido + :defines (flx-ido-mode ido-ubiquitous-debug-mode ido-context-switch-command ido-temp-list) + :functions (ido-to-end) + :commands (ido-mode ido-everywhere ido-vertical-mode + flx-ido-mode ido-ubiquitous-mode ido-find-file + ido-find-file-in-dir) + :init + (setq ido-ignore-buffers + '("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*Help\\*" "^\\*Buffer" + "^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-" + "_region_" " output\\*$" "^TAGS$" "^\*Ido") + ido-use-faces nil + ido-confirm-unique-completion t + ido-case-fold t + ido-enable-tramp-completion nil + ido-enable-flex-matching t + ido-create-new-buffer 'always + ido-enable-tramp-completion t + ido-enable-last-directory-history t + ido-save-directory-list-file (! (concat narf-temp-dir "ido.last"))) + :config + (add-to-list 'ido-ignore-files "\\`.DS_Store$") + (add-to-list 'ido-ignore-files "Icon\\?$") + (add-hook! ido-setup + (bind! :map (ido-completion-map ido-file-completion-map) + "C-w" 'ido-delete-backward-word-updir)) + + (ido-mode 1) + (ido-everywhere 1) + + (require 'ido-vertical-mode) + (ido-vertical-mode 1) + + (require 'flx-ido) + (flx-ido-mode 1) + + (require 'ido-ubiquitous) + (ido-ubiquitous-mode 1) + + (advice-add 'ido-sort-mtime :override 'narf*ido-sort-mtime) + (add-hook! (ido-make-file-list ido-make-dir-list) 'narf*ido-sort-mtime) + (add-hook! ido-setup 'narf|ido-setup-home-keybind)) + +(use-package neotree + :commands (neotree-show + neotree-hide + neotree-toggle + neotree-dir + neotree-find + neo-global--window-exists-p) + :functions (neo-buffer--unlock-width neo-buffer--lock-width) + :init + (setq neo-create-file-auto-open t + neo-mode-line-type 'none + neo-persist-show t + neo-window-width 22 + neo-show-updir-line nil + neo-auto-indent-point t + neo-banner-message nil + ;; requires fork of + ;; neotree (at least, until the PR is accepted). Causes neotree to + ;; open in a vertical split that consumes the entire height of the + ;; frame. + neo-modern-sidebar t) + :config + (defun narf|neotree-init-keymap () + (bind! :map evil-motion-state-local-map + "ESC" 'neotree-hide + "\\\\" 'neotree-hide + "RET" 'neotree-enter + "J" 'neotree-select-next-sibling-node + "K" 'neotree-select-previous-sibling-node + "H" 'neotree-select-up-node + "L" 'neotree-select-down-node + "v" 'neotree-enter-vertical-split + "s" 'neotree-enter-horizontal-split + "c" 'neotree-create-node + "d" 'neotree-delete-node + "g" 'neotree-refresh + "q" 'neotree-hide + "r" 'neotree-rename-node + "R" 'neotree-change-root)) + + (add-hook! neotree-mode 'narf|neotree-init-keymap) + (add-hook! window-configuration-change 'narf|neotree-close-on-window-change) + + (evil-set-initial-state 'neotree-mode 'motion) + (after! projectile + (setq projectile-switch-project-action 'neotree-projectile-action)) + + (advice-add 'neo-buffer--insert-fold-symbol :override 'narf*neo-buffer-fold-symbol)) + +(provide 'core-project) +;;; core-project.el ends here diff --git a/core/core-quickrun.el b/core/core-quickrun.el new file mode 100644 index 000000000..4c4a97eea --- /dev/null +++ b/core/core-quickrun.el @@ -0,0 +1,13 @@ +;;; core-quickrun.el + +(use-package quickrun + :commands (quickrun + quickrun-region + quickrun-with-arg + quickrun-shell + quickrun-compile-only + quickrun-replace-region + helm-quickrun)) + +(provide 'core-quickrun) +;;; core-quickrun.el ends here diff --git a/core/core-ui.el b/core/core-ui.el index cf59388cf..b5b707437 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -1,10 +1,23 @@ ;;; core-ui.el --- interface settings +;; see lib/ui-defuns.el -(when (fboundp 'fringe-mode) (fringe-mode '(2 . 8))) -(when window-system ; more informative window title +;; This is kept separate so it can jumpstart emacs; this prevents the unstyled +;; flash of emacs pre-makeover. +(load-theme narf-default-theme t) +(when window-system + (set-frame-font (apply #'font-spec narf-default-font)) + (scroll-bar-mode -1) ; no scrollbar + (tool-bar-mode -1) ; no toolbar + (menu-bar-mode -1) ; no menubar + + (pcase (system-name) + ("io" (set-frame-size (selected-frame) 326 119)) + ("ganymede.home" (set-frame-size (selected-frame) 318 83))) + + (fringe-mode '(2 . 8)) + ;; more informative window title (setq frame-title-format '(buffer-file-name "%f" ("%b")))) - ;; theme and GUI elements are loaded in init.el early (setq show-paren-delay 0) @@ -27,17 +40,14 @@ indicate-empty-lines nil fringes-outside-margins t) ; fringes on the other side of line numbers -(@add-hook after-init +(add-hook! after-init (defadvice save-buffers-kill-emacs (around no-query-kill-emacs activate) "Prevent annoying \"Active processes exist\" query when you quit Emacs." (flet ((process-list ())) ad-do-it))) - -;;;; Line numbers ;;;;;;;;;;;;;;;;;;;;;; - -(use-package nlinum +(use-package nlinum ; line numbers + :defer t :defines nlinum--width - :commands nlinum-mode :preface (defface linum '((t (:inherit default))) "Face for line numbers" :group 'nlinum-mode) @@ -55,7 +65,7 @@ (str (nth 1 disp))) (put-text-property 0 (length str) 'face 'linum str) (setq narf--hl-nlinum-overlay nil - narf--hl-nlinum-line nil)))) + narf--hl-nlinum-line nil)))) (defun narf|nlinum-hl-line (&optional line) (let ((line-no (or line (line-number-at-pos (point))))) @@ -89,17 +99,14 @@ (remove-hook 'post-command-hook 'narf|nlinum-hl-line) (narf|nlinum-unhl-line)) - (@add-hook text-mode 'narf|nlinum-enable) - (@add-hook prog-mode 'narf|nlinum-enable) - (@add-hook org-mode 'narf|nlinum-disable) + (add-hook! text-mode 'narf|nlinum-enable) + (add-hook! prog-mode 'narf|nlinum-enable) + (add-hook! org-mode 'narf|nlinum-disable) ;; Preset width nlinum - (@add-hook nlinum-mode + (add-hook! nlinum-mode (setq nlinum--width (length (number-to-string (count-lines (point-min) (point-max))))))) - -;;;; Modeline ;;;;;;;;;;;;;;;;;;;;;;;;;; - -(use-package smart-mode-line +(use-package smart-mode-line ; customized modeline :init (setq-default sml/no-confirm-load-theme t sml/mode-width 'full @@ -116,13 +123,10 @@ sml/replacer-regexp-list '(("^~/Dropbox/Projects/" "PROJECTS:") ("^~/.emacs.d/" "EMACS.D:") ("^~/Dropbox/notes/" "NOTES:") - ("^/usr/local/Cellar/" "HOMEBREW:")) - mode-line-misc-info - '((which-func-mode ("" which-func-format "")) - (global-mode-string ("" global-mode-string "")))) + ("^/usr/local/Cellar/" "HOMEBREW:"))) :config ;; Hide evil state indicator - (@after evil (setq evil-mode-line-format nil)) + (after! evil (setq evil-mode-line-format nil)) (sml/setup) (sml/apply-theme 'respectful) @@ -175,14 +179,14 @@ (-3 (:propertize (:eval sml/position-percentage-format) face sml/position-percentage help-echo "Buffer Relative Position\nmouse-1: Display Line and Column Mode Menu"))))) - (@after anzu ; Add small gap for anzu + (after! anzu ; Add small gap for anzu (defun narf--anzu-update-mode-line (here total) (concat (anzu--update-mode-line-default here total) " ")) (setq anzu-mode-line-update-function 'narf--anzu-update-mode-line)) ;; Rearrange and cleanup (setq-default mode-line-format - '("%e " + '("%e" mode-line-mule-info mode-line-client ;; mode-line-remote @@ -196,6 +200,5 @@ " " ":" mode-line-position))) - (provide 'core-ui) ;;; core-ui.el ends here diff --git a/core/core-vars.el b/core/core-vars.el index fb6ce0071..71bddc40f 100644 --- a/core/core-vars.el +++ b/core/core-vars.el @@ -16,19 +16,45 @@ (defconst narf--splash-buffer-name "*narf*") -(defvar narf-ignore-buffers '("*Completions*" "*Compile-Log*" "*inferior-lisp*" - "*Fuzzy Completions*" "*Apropos*" "*Help*" "*cvs*" - "*Buffer List*" "*Ibuffer*" "*esh command on file*") - "List of buffer names to ignore when using `switch-to-next-buffer', - `switch-to-previous-buffer', `winner-undo', `winner-redo', or - `narf:cleanup-buffers'") +(defvar narf-auto-minor-mode-alist '() + "Alist of filename patterns vs corresponding minor mode functions, see +`auto-mode-alist'. All elements of this alist are checked, meaning you can +enable multiple minor modes for the same regexp.") + +(defvar narf-unreal-buffers '("^ \\*" + "^\\*scratch\\*" + "^\\*Backtrace\\*$" + "^\\*Warnings\\*$" + "^\\*Compile-Log\\*$" + "^\\*Ediff.*\\*$" + "^\\*helm.*\\*$" + "^\\*eval\\*$" + "^\\*Shell Command Output\\*$" + "^\\*Async Shell Command\\*$" + help-mode + image-mode + dired-mode + reb-mode + messages-buffer-mode) + "A list of regexps or modes whose buffers are considered unreal, and will be +ignored when using `narf:next-real-buffer' and `narf:previous-real-buffer', and +killed by `narf:kill-unreal-buffers'. + +`narf:kill-this-buffer' will also gloss over these buffers when finding a new +buffer to display.") (defvar narf-ignore-buffers '("*Completions*" "*Compile-Log*" "*inferior-lisp*" - "*Fuzzy Completions*" "*Apropos*" "*Help*" "*cvs*" - "*Buffer List*" "*Ibuffer*" "*esh command on file*") - "List of buffer names to ignore when using `switch-to-next-buffer', - `switch-to-previous-buffer', `winner-undo', `winner-redo', or - `narf:cleanup-buffers'") + "*Fuzzy Completions*" "*Apropos*" "*Help*" "*cvs*" + "*Buffer List*" "*Ibuffer*" "*esh command on file*" + "*helm*") + "List of buffer names to ignore when using `winner-undo', or `winner-redo'") + +(defvar narf-cleanup-processes-alist '(("pry" . ruby-mode) + ("irb" . ruby-mode) + ("ipython" . python-mode)) + "An alist of (process-name . major-mode), that `narf:cleanup-processes' checks +before killing processes. If there are no buffers with matching major-modes, it +gets killed.") (defvar narf-project-root-files '(".git" ".hg" ".svn" ".project" "local.properties" "project.properties" diff --git a/core/core-vars.elc b/core/core-vars.elc new file mode 100644 index 000000000..80c05f1d2 Binary files /dev/null and b/core/core-vars.elc differ diff --git a/core/core-vcs.el b/core/core-vcs.el new file mode 100644 index 000000000..2911403ac --- /dev/null +++ b/core/core-vcs.el @@ -0,0 +1,32 @@ +;;; core-vcs.el --- version control awareness + +(use-package git-commit-mode ; + :mode (("/COMMIT_EDITMSG\\'" . git-commit-mode) + ("/NOTES_EDITMSG\\'" . git-commit-mode) + ("/MERGE_MSG\\'" . git-commit-mode) + ("/TAG_EDITMSG\\'" . git-commit-mode) + ("/PULLREQ_EDITMSG\\'" . git-commit-mode)) + :config + (evil-set-initial-state 'git-commit-mode 'insert)) + +(use-package git-rebase-mode + :mode ("/git-rebase-todo\\'" . git-rebase-mode) + :config + (evil-set-initial-state 'git-rebase-mode 'insert)) + +(use-package gitconfig-mode + :mode (("/\\.?git/?config\\'" . gitconfig-mode) + ("/\\.gitmodules\\'" . gitconfig-mode)) + :init (add-hook 'gitconfig-mode-hook 'flyspell-mode)) + +(use-package gitignore-mode + :mode (("/\\.gitignore\\'" . gitignore-mode) + ("/\\.git/info/exclude\\'" . gitignore-mode) + ("/git/ignore\\'" . gitignore-mode))) + +(use-package diff-hl + :init (setq diff-hl-draw-borders nil) + :config (global-diff-hl-mode +1)) + +(provide 'core-vcs) +;;; core-vcs.el ends here diff --git a/core/core-workgroups.el b/core/core-workgroups.el new file mode 100644 index 000000000..f9682d6c9 --- /dev/null +++ b/core/core-workgroups.el @@ -0,0 +1,32 @@ +;;; core-workgroups.el +;; see lib/workgroup-defuns.el + +(use-package workgroups2 + :init + (setq wg-session-file (! (expand-file-name "wg-default" narf-temp-dir)) + wg-workgroup-directory (! (expand-file-name "workgroups" narf-temp-dir)) + wg-first-wg-name "main" + wg-session-load-on-start t + wg-mode-line-display-on nil + wg-mess-with-buffer-list t + ;; What to do on Emacs exit / workgroups-mode exit? + wg-emacs-exit-save-behavior 'save ; Options: 'save 'ask nil + wg-workgroups-mode-exit-save-behavior 'save) + :config + (defvar narf/helm-source-wg + '((name . "Workgroups") + (candidates . wg-workgroup-names) + (action . narf/wg-helm-switch-to-workgroup))) + + (after! projectile + ;; Turns projectile switch-project interface (or helm's interface to it) + ;; create a new workgroup for the new project. + (setq projectile-switch-project-action 'narf/wg-projectile-switch-project)) + + ;; Initialize! + (add-hook! after-init + (workgroups-mode 1) + (diminish 'workgroups-mode))) + +(provide 'core-workgroups) +;;; core-workgroups.el ends here diff --git a/core/core-yasnippet.el b/core/core-yasnippet.el new file mode 100644 index 000000000..cc3057f05 --- /dev/null +++ b/core/core-yasnippet.el @@ -0,0 +1,74 @@ +;;; core-yasnippet.el --- For the lazy typist +;; see lib/yasnippet-defuns.el +;; see lib/yasnippet-macros.el + +(use-package yasnippet + :mode (("emacs\\.d/snippets/.+$" . snippet-mode)) + :diminish (yas-minor-mode . "Y") + :commands (yas-minor-mode + yas-minor-mode-on + yas-expand + yas-insert-snippet + yas-new-snippet + yas-visit-snippet-file) + :init + (add-hook! (prog-mode snippet-mode markdown-mode org-mode) 'yas-minor-mode-on) + (add-hook! snippet-mode 'narf|disable-final-newline) + + (setq yas-verbosity 0 + yas-indent-line 'auto + yas-also-auto-indent-first-line t + yas-wrap-around-region nil + ;; Only load personal snippets + yas-snippet-dirs `(,@narf-snippet-dirs) + yas-prompt-functions '(yas-ido-prompt yas-no-prompt)) + + (bind! :i [(tab)] 'yas-expand + :v "" 'narf/yas-insert-snippet) + + (defvar yas-minor-mode-map + (let ((map (make-sparse-keymap))) + (evil-define-key 'insert map [(tab)] 'yas-expand) + (evil-define-key 'visual map (kbd "") 'narf/yas-insert-snippet) + map)) + :config + (after! helm (add-to-list 'yas-dont-activate 'helm-alive-p)) + + (yas-reload-all) + + ;; Simpler `yas-selected-text' alias for templates + (defvaralias '% 'yas-selected-text) + ;; Undo global maps + (bind! :i [(tab)] nil + :v "" nil) + + ;; keybinds + (bind! :map yas-keymap + "C-e" 'narf/yas-goto-end-of-field + "C-a" 'narf/yas-goto-start-of-field + "" 'narf/yas-goto-end-of-field + "" 'narf/yas-goto-start-of-field + "" 'yas-prev-field + "" 'narf/yas-clear-to-sof + + [backspace] 'narf/yas-backspace + "" 'narf/yas-delete) + + ;; Once you're in normal mode, you're out + (add-hook! evil-normal-state-entry 'yas-abort-snippet) + ;; Strip out the shitespace before a line selection + (add-hook! yas-before-expand-snippet 'narf|yas-before-expand) + ;; Previous hook causes yas-selected-text to persist between expansions. + ;; This little hack gets around it. + (add-hook! yas-after-exit-snippet 'narf|yas-after-expand) + + ;; Exit snippets on ESC in normal mode + (advice-add 'evil-force-normal-state :before 'yas-exit-all-snippets) + ;; Prevents evil's visual-line from mode gobbling up the newline on the + ;; right due to an off-by-one issue. + (defadvice yas-expand-snippet (around yas-expand-snippet-visual-line activate) + (when (narf/evil-visual-line-state-p) + (ad-set-arg 2 (1- (ad-get-arg 2)))) ad-do-it)) + +(provide 'core-yasnippet) +;;; core-yasnippet.el ends here diff --git a/core/core.el b/core/core.el index ad4a43cfc..32c59336c 100644 --- a/core/core.el +++ b/core/core.el @@ -65,7 +65,7 @@ (use-package-sort-keywords (use-package-plist-maybe-put rest :defer t)) state) (apply #'nconc (mapcar (lambda (feature) - `(,(macroexpand `(after! ,feature (require ',name-symbol))))) + `((after! ,feature (require ',name-symbol)))) (delete-dups arg)))))))) ;; Make any folders needed diff --git a/core/defuns/defuns-compile.el b/core/defuns/defuns-compile.el deleted file mode 100644 index ef98bcff8..000000000 --- a/core/defuns/defuns-compile.el +++ /dev/null @@ -1,32 +0,0 @@ -;;; defuns-compile.el - -(! (require 'f) - (setq narf-important-dirs (append (list narf-core-dir narf-modules-dir narf-contrib-dir narf-private-dir) - (f-directories narf-core-dir nil t) - (f-directories narf-modules-dir nil t) - (f-directories narf-contrib-dir nil t)))) - -;;;###autoload (autoload 'narf:byte-compile "defuns-compile") -(evil-define-command narf:byte-compile (&optional bang) - :repeat nil - (interactive "") - (when emacs-lisp-mode - (if (not bang) - (progn - (byte-recompile-file (! (f-expand "core-defuns.el" narf-core-dir)) t 0) - (byte-recompile-file (buffer-file-name) t 0)) - (byte-recompile-file (! (f-expand "init.el" narf-emacs-dir)) nil 0) - (byte-recompile-file (! (f-expand "startup.el" narf-emacs-dir)) nil 0) - (dolist (dir (! narf-impotant-dirs)) - (byte-recompile-directory dir 0 nil))))) - -;;;###autoload (autoload 'narf:autoload-compile "defuns-compile") -(evil-define-command narf:autoload-compile (&optional bang) - :repeat nil - (interactive "") - (defvar generated-autoload-file (! (f-expand "autoloads.el" narf-core-dir))) - (apply #'update-directory-autoloads (! narf-impotant-dirs))) - - -(provide 'defuns-compile) -;;; defuns-compile.el ends here diff --git a/core/defuns/defuns-evil.el b/core/defuns/defuns-evil.el deleted file mode 100644 index fc7de2fdc..000000000 --- a/core/defuns/defuns-evil.el +++ /dev/null @@ -1,33 +0,0 @@ -;;; defuns-evil.el - -;;;###autoload (autoload 'narf:evil-open-folds "defuns-evil") -(evil-define-command narf/evil-open-folds (count) - "Instead of `evil-open-folds'. Accepts COUNT for dictating fold level." - (interactive "P") - (if count (hs-hide-level count) (evil-open-folds))) - -;;;###autoload (autoload 'narf:evil-open-folds "defuns-evil") -(evil-define-command narf/evil-close-folds (count) - "Instead of `evil-close-folds'. Accepts COUNT for dictating fold level." - (interactive "P") - (if count (hs-hide-level count) (evil-close-folds))) - -;;;; Ace Jump ;;;;;;;;;;;;;;;;;;;;;;;;;; -;; https://github.com/winterTTr/ace-jump-mode/issues/23 -;;;###autoload (autoload 'narf:evil-ace-jump-two-chars "defuns-evil") -(evil-define-motion narf/evil-ace-jump-two-chars (count) - :type exclusive - :repeat abort - (evil-without-repeat - (evil-enclose-ace-jump-for-motion - (call-interactively 'ace-jump-two-chars-mode)))) - -;;;###autoload -(defun narf/evil-visual-line-state-p () - "Returns non-nil if in visual-line mode, nil otherwise." - (and (evil-visual-state-p) - (eq (evil-visual-type) 'line))) - - -(provide 'defuns-evil) -;;; defuns-evil.el ends here diff --git a/core/lib/defuns-buffers.el b/core/lib/defuns-buffers.el new file mode 100644 index 000000000..6c8dfd4ee --- /dev/null +++ b/core/lib/defuns-buffers.el @@ -0,0 +1,203 @@ +;;; defuns-buffers.el + +;;;###autoload +(defun narf:narrow (start end) + "Restrict editing in this buffer to the current region, indirectly. + +Inspired from http://demonastery.org/2013/04/emacs-evil-narrow-region/" + (interactive "r") + (deactivate-mark) + (let ((buf (clone-indirect-buffer nil nil))) + (with-current-buffer buf + (narrow-to-region start end)) + (switch-to-buffer buf))) + +;;;###autoload +(defun narf:widen () + (interactive) + (when (buffer-narrowed-p) + (widen))) + +;;;###autoload +(defun narf/set-read-only-region (begin end) + "See http://stackoverflow.com/questions/7410125" + (let ((modified (buffer-modified-p))) + (add-text-properties begin end '(read-only t)) + (set-buffer-modified-p modified))) + +;;;###autoload +(defun narf/set-region-writeable (begin end) + "See http://stackoverflow.com/questions/7410125" + (let ((modified (buffer-modified-p)) + (inhibit-read-only t)) + (remove-text-properties begin end '(read-only t)) + (set-buffer-modified-p modified))) + + +;; Killing Life and Death ;;;;;;;;;;;;;; + +;;;###autoload +(defun narf:kill-real-buffer () + "Kill buffer (but only bury scratch buffer), then switch to a living buffer." + (interactive) + (let ((bname (buffer-name))) + (cond ((string-match-p "^\\*scratch\\*" bname) + (erase-buffer) + (bury-buffer)) + ((string-equal "*" (substring bname 0 1))) + (t (kill-this-buffer)))) + (unless (narf/real-buffer-p (current-buffer)) + (narf/previous-real-buffer))) + +;;;###autoload +(defun narf/get-visible-buffers (&optional buffer-list) + "Get a list of buffers that are not buried (i.e. visible)" + (-remove #'get-buffer-window (or buffer-list (buffer-list)))) + +;;;###autoload +(defun narf/get-buried-buffers (&optional buffer-list) + "Get a list of buffers that are buried (i.e. not visible)" + (-filter #'get-buffer-window (or buffer-list (buffer-list)))) + +;;;###autoload +(defun narf/get-matching-buffers (pattern &optional buffer-list) + "Get a list of buffers that match the pattern" + (--filter (string-match-p pattern (buffer-name it)) (or buffer-list (buffer-list)))) + +;;;###autoload +(defun narf/get-real-buffers(&optional buffer-list) + (-filter (lambda (buffer) + (not (--any? (if (stringp it) + (string-match-p it (buffer-name buffer)) + (eq (buffer-local-value 'major-mode buffer) it)) + narf-unreal-buffers))) + (or buffer-list (buffer-list)))) + +;;;###autoload +(defun narf:kill-unreal-buffers () + "Kill all buried, unreal buffers in current frame. See `narf-unreal-buffers'" + (interactive) + (let* ((real-buffers (narf/get-real-buffers)) + (kill-list (--filter (not (memq it real-buffers)) narf/get-buried-buffers))) + (message "Cleaned up %s buffers" (length kill-list)) + (mapc 'kill-buffer kill-list) + (narf:kill-process-buffers))) + +;;;###autoload +(defun narf:kill-process-buffers () + "Kill all buffers that represent running processes and aren't visible." + (interactive) + (let ((buffer-list (buffer-list))) + (dolist (p (process-list)) + (let* ((process-name (process-name p)) + (assoc (assoc process-name narf-cleanup-processes-alist))) + (when (and assoc + (not (string= process-name "server")) + (process-live-p p) + (not (--any? (let ((mode (buffer-local-value 'major-mode it))) + (eq mode (cdr assoc))) + buffer-list))) + (message "Cleanup: killing %s" process-name) + (delete-process p)))))) + +;;;###autoload +(defun narf:kill-matching-buffers (regexp &optional buffer-list) + (interactive) + (mapc (lambda (b) + (if (string-match-p regexp (buffer-name b)) + (kill-buffer b))) + (if buffer-list buffer-list (buffer-list)))) + +;;;###autoload +(defun narf/cycle-real-buffers (&optional n) + "Switch to the previous buffer and avoid special buffers. If there's nothing +left, create a scratch buffer." + (let ((start-buffer (current-buffer)) + (move-func (if (< n 0) 'switch-to-next-buffer 'switch-to-prev-buffer)) + (real-buffers (narf/get-real-buffers))) + (funcall move-func) + (while (let ((current-buffer (current-buffer))) + (and (if (eq current-buffer start-buffer) + (ignore (switch-to-buffer "*scratch*")) + t) + (not (= n 0)) + (not (eq current-buffer start-buffer)) + (not (memq current-buffer real-buffers)))) + (setq n (1- n)) + (funcall move-func)))) + +;;;###autoload +(defun narf/real-buffer-p (&optional buffer-or-name) + (let ((buffer (if buffer-or-name (get-buffer buffer-or-name) (current-buffer)))) + (when (buffer-live-p buffer) + (not (--any? (if (stringp it) + (string-match-p it (buffer-name buffer)) + (eq (buffer-local-value 'major-mode buffer) it)) + narf-unreal-buffers))))) + +;; From spacemacs +;;;###autoload +(defun narf/next-real-buffer () + "Switch to the next buffer and avoid special buffers." + (interactive) + (narf/cycle-real-buffers +1)) + +;;;###autoload +(defun narf/previous-real-buffer () + "Switch to the previous buffer and avoid special buffers." + (interactive) + (narf/cycle-real-buffers -1)) + +;;;###autoload (autoload 'narf:kill-buried-buffers "defuns-buffers" nil t) +(evil-define-command narf:kill-buried-buffers (&optional bang) + :repeat nil + (interactive "") + (narf:kill-buried-buffers) + (mapc 'kill-buffer + (narf/get-buried-buffers (if bang (projectile-project-buffers) (buffer-list))))) + +;;;###autoload (autoload 'narf:kill-all-buffers "defuns-buffers" nil t) +(evil-define-command narf:kill-all-buffers (&optional bang) + "Kill all project buffers. If BANG, kill *all* buffers." + :repeat nil + (interactive "") + (if (and (not bang) (projectile-project-p)) + (projectile-kill-buffers) + (mapc 'kill-buffer (buffer-list))) + (delete-other-windows) + (unless (narf/real-buffer-p) + (narf/previous-real-buffer))) + +;;;###autoload (autoload 'narf:scratch-buffer "defuns-buffers" nil t) +(evil-define-operator narf:scratch-buffer (&optional beg end bang) + "Send a selection to the scratch buffer. If BANG, then send it to org-capture + instead." + :move-point nil + :type inclusive + (interactive "") + (let ((mode major-mode) + (text (when (and (evil-visual-state-p) beg end) + (buffer-substring beg end)))) + (if bang + ;; use org-capture with bang + (if text + (org-capture-string text) + (org-capture)) + ;; or scratch buffer by default + (let* ((project-dir (narf/project-root t)) + (buffer-name "*scratch*")) + (popwin:popup-buffer (get-buffer-create buffer-name)) + (when (eq (get-buffer buffer-name) (current-buffer)) + (when project-dir + (cd project-dir)) + (if text (insert text)) + (funcall mode)))))) + +;;;###autoload (autoload 'narf:cd "defuns-buffers" nil t) +(evil-define-command narf:cd (dir) + :repeat nil + (interactive "") + (cd (if (zerop (length dir)) "~" dir))) + +(provide 'defuns-buffers) +;;; defuns-buffers.el ends here diff --git a/core/lib/defuns-company.el b/core/lib/defuns-company.el new file mode 100644 index 000000000..ed1fa579b --- /dev/null +++ b/core/lib/defuns-company.el @@ -0,0 +1,18 @@ +;;; defuns-company.el + +;;;###autoload +(defun narf/company-evil-complete-next () + (call-interactively 'company-dabbrev) + (if (eq company-candidates-length 1) + (company-complete))) + +;;;###autoload +(defun narf/company-evil-complete-previous () + (let ((company-selection-wrap-around t)) + (call-interactively 'company-dabbrev) + (if (eq company-candidates-length 1) + (company-complete) + (call-interactively 'company-select-previous)))) + +(provide 'defuns-company) +;;; defuns-company.el ends here diff --git a/core/lib/defuns-compile.el b/core/lib/defuns-compile.el new file mode 100644 index 000000000..5b60e6848 --- /dev/null +++ b/core/lib/defuns-compile.el @@ -0,0 +1,34 @@ +;;; defuns-compile.el + +(! (require 'f)) + +(defun narf--compile-important-dirs () + (append (list narf-core-dir narf-contrib-dir) + (list (concat narf-modules-dir "lib/") + (concat narf-core-dir "lib/")) + (f-directories narf-contrib-dir) + (list narf-modules-dir narf-private-dir))) + +;;;###autoload (autoload 'narf:compile-el "defuns-compile" nil t) +(evil-define-command narf:compile-el (&optional bang) + :repeat nil + (interactive "") + (when (eq major-mode 'emacs-lisp-mode) + (byte-recompile-file (! (f-expand "core-vars.el" narf-core-dir)) t 0) + (byte-recompile-file (! (f-expand "core-defuns.el" narf-core-dir)) t 0) + (if (not bang) + (byte-recompile-file (buffer-file-name) t 0) + (byte-recompile-file (! (f-expand "init-load-path.el" narf-emacs-dir)) nil 0) + (byte-recompile-file (! (f-expand "init.el" narf-emacs-dir)) nil 0) + (dolist (dir (narf--compile-important-dirs)) + (byte-recompile-directory dir 0 nil))))) + +;;;###autoload (autoload 'narf:compile-autoloads "defuns-compile" nil t) +(evil-define-command narf:compile-autoloads (&optional bang) + :repeat nil + (interactive "") + (defvar generated-autoload-file (! (f-expand "autoloads.el" narf-core-dir))) + (apply #'update-directory-autoloads (narf--compile-important-dirs))) + +(provide 'defuns-compile) +;;; defuns-compile.el ends here diff --git a/core/lib/defuns-debug.el b/core/lib/defuns-debug.el new file mode 100644 index 000000000..46a9fbd12 --- /dev/null +++ b/core/lib/defuns-debug.el @@ -0,0 +1,31 @@ +;;; defuns-debug.el + +;;;###autoload +(defun what-face (pos) + "Tells you the name of the face (point) is on." + (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)))) + +;;;###autoload +(defun what-col () + (interactive) + (message "Column %d" (current-column))) + +;;;###autoload +(defun what-bindings (key) + (list + (minor-mode-key-binding key) + (local-key-binding key) + (global-key-binding key))) + +;;;###autoload (autoload 'narf:echo "defuns-debug" nil t) +(evil-define-command narf:echo (bang message) + "Display MSG in echo-area without logging it in *Messages* buffer." + (interactive "") + (let (message-log-max) + (message "%s%s" (if bang ">> " "") message))) + +(provide 'defuns-debug) +;;; defuns-debug.el ends here diff --git a/core/lib/defuns-editor.el b/core/lib/defuns-editor.el new file mode 100644 index 000000000..d6e5d3613 --- /dev/null +++ b/core/lib/defuns-editor.el @@ -0,0 +1,57 @@ +;;; defuns-editor.el +;; for ../core-editor.el + +;; A hacky attempt to replace ace-jump line mode that incrementally shows where +;; you will land as you type the line number. +(defun narf--goto-line (line) + (let ((lines (count-lines (point-min) (point-max)))) + (if (and (<= line (1+ lines)) + (> line 0)) + (narf/nlinum-hl-line line) + (narf/nlinum-hl-line)))) + +;;;###autoload +(defun narf/editor-goto-line () + (interactive) + (let ((keys '()) + (orig-point (point)) + (echo-keystrokes 0)) + (evil-save-echo-area + (catch 'abort + (while t + (let* ((keystr (concat keys)) + (key (read-event (concat ":" keystr)))) + (cond ((eq key 'escape) + (message "%s" key) + (throw 'abort t)) + ((eq key 'return) + (when keys + (goto-line (string-to-number keystr))) + (throw 'abort t)) + ((eq key 'backspace) + (let ((key-len (length keys))) + (if (= key-len 0) + (throw 'abort t) + (if (> key-len 1) + (progn + (nbutlast keys) + (narf--goto-line (string-to-number (concat keys)))) + (setq keys '()) + (narf/nlinum-hl-line))))) + ((and (characterp key) + (s-numeric? (char-to-string key))) + (setq keys (append keys (list key))) + (narf--goto-line (string-to-number (concat keys)))) + (t + (if (or (char-equal key ?\C-n) + (char-equal key ?\C-j)) + (progn + (setq keys (number-to-string (1+ (string-to-number (concat keys))))) + (narf--goto-line (string-to-number (concat keys)))) + (when (or (char-equal key ?\C-p) + (char-equal key ?\C-k)) + (setq keys (number-to-string (1- (string-to-number (concat keys))))) + (narf--goto-line (string-to-number (concat keys))))))))))))) + +(provide 'defuns-editor) +;;; defuns-editor.el ends here diff --git a/core/lib/defuns-evil.el b/core/lib/defuns-evil.el new file mode 100644 index 000000000..08c245026 --- /dev/null +++ b/core/lib/defuns-evil.el @@ -0,0 +1,66 @@ +;;; defuns-evil.el +;; for ../core-evil.el + +;;;###autoload (autoload 'narf:evil-open-folds "defuns-evil" nil t) +(evil-define-command narf/evil-open-folds (count) + "Instead of `evil-open-folds'. Accepts COUNT for dictating fold level." + (interactive "P") + (unless (bound-and-true-p hs-minor-mode) + (hs-minor-mode 1)) + (if count (hs-hide-level count) (evil-open-folds))) + +;;;###autoload (autoload 'narf:evil-open-folds "defuns-evil" nil t) +(evil-define-command narf/evil-close-folds (count) + "Instead of `evil-close-folds'. Accepts COUNT for dictating fold level." + (interactive "P") + (unless (bound-and-true-p hs-minor-mode) + (hs-minor-mode 1)) + (if count (hs-hide-level count) (evil-close-folds))) + +;;;; Ace Jump ;;;;;;;;;;;;;;;;;;;;;;;;;; +;; https://github.com/winterTTr/ace-jump-mode/issues/23 +;;;###autoload (autoload 'narf:evil-ace-jump-two-chars "defuns-evil" nil t) +(evil-define-motion narf/evil-ace-jump-two-chars (count) + :type exclusive + :repeat abort + (evil-without-repeat + (evil-enclose-ace-jump-for-motion + (call-interactively 'ace-jump-two-chars-mode)))) + +;;;###autoload +(defun narf/evil-visual-line-state-p () + "Returns non-nil if in visual-line mode, nil otherwise." + (and (evil-visual-state-p) + (eq (evil-visual-type) 'line))) + +;;;###autoload +(defun narf:iedit-restrict-to-region () + (interactive) + (if (iedit-current-occurrence-string) + (let ((current-prefix-arg '(4))) + (iedit-done) + (call-interactively 'iedit-mode) + (save-excursion (iedit-restrict-region (region-beginning) (region-end))) + (evil-previous-line)) + (call-interactively 'evil-ret))) + +;;;###autoload +(defun narf*evil-exchange-off () + (when evil-exchange--overlays + (evil-exchange-cancel))) + +;;;###autoload +(defun narf/evil-surround-escaped () + "Escaped surround characters." + (let* ((char (string (read-char "\\"))) + (pair (cond ((string-match "[]})[{(]" char) + (let ((-pair (cdr (assoc (string-to-char char) evil-surround-pairs-alist)))) + `(,(car -pair) . ,(cdr -pair)))) + (t + `(,char . ,char)))) + (format (if (sp-point-in-string) "\\\\%s" "\\%s"))) + (cons (format format (car pair)) + (format format (cdr pair))))) + +(provide 'defuns-evil) +;;; defuns-evil.el ends here diff --git a/core/lib/defuns-file.el b/core/lib/defuns-file.el new file mode 100644 index 000000000..d9863d1e3 --- /dev/null +++ b/core/lib/defuns-file.el @@ -0,0 +1,61 @@ +;;; defuns-file.el + +;;;###autoload (autoload 'narf:file-delete "defuns-file" nil t) +(evil-define-command narf:file-delete (&optional bang filename) + "Delete current buffer's file. If bang, then kill the buffer afterwards as well." + :repeat nil + (interactive "") + (let ((filename (file-truename (or filename (buffer-file-name))))) + (if (not (file-exists-p filename)) + (error "File doesn't exist: %s" filename) + (delete-file filename) + (when bang + (kill-this-buffer) + (unless (narf/real-buffer-p) + (narf/previous-real-buffer))) + (save-place-forget-unreadable-files) + (message "File successfully deleted: %s" filename)))) + +(defun narf--save-exit() (save-buffer) (kill-buffer) (remove-hook 'yas-after-exit-snippet-hook '--save-exit)) +;;;###autoload (autoload 'narf:file-create "defuns-file" nil t) +(evil-define-command narf:file-create (path &optional bang) + "Deploy files (and their associated templates) quickly. Will prompt +you to fill in each snippet field before buffer closes unless BANG is +provided." + :repeat nil + (interactive "") + (let ((dir (f-dirname path)) + (fullpath (f-full path)) + (is-auto t)) + (when (and bang (not (file-exists-p dir))) (f-mkdir dir)) + (if (file-exists-p dir) + (if (file-exists-p fullpath) + (error "File already exists: %s" path) + (find-file fullpath) + (add-hook 'yas-after-exit-snippet-hook 'narf--save-exit) + (if bang (narf--save-exit))) + (error "Directory doesn't exist: %s" dir)))) + +;;;###autoload (autoload 'narf:file-move "defuns-file" nil t) +(evil-define-command narf:file-move (path) + "Move current buffer's file to PATH. Replaces %, # and other variables (see + `evil-ex-replace-special-filenames')" + :repeat nil + (interactive "") + (let* ((old-path (buffer-file-name)) + (new-path (cond ((f-dir? path) + (f-expand (f-filename old-path) path)) + ((f-dir? (f-dirname path)) + (f-full path)) + (t (user-error "Not a valid destination: %s" path)))) + (project-root (narf/project-root))) + (rename-file old-path new-path 1) + (rename-buffer (f-filename new-path)) + (set-visited-file-name new-path) + (set-buffer-modified-p nil) + (save-place-forget-unreadable-files) + (message "File '%s' successfully renamed to '%s'" + (f-relative old-path project-root) (f-relative new-path project-root)))) + +(provide 'defuns-file) +;;; defuns-file.el ends here diff --git a/core/lib/defuns-flycheck.el b/core/lib/defuns-flycheck.el new file mode 100644 index 000000000..8d662f5ba --- /dev/null +++ b/core/lib/defuns-flycheck.el @@ -0,0 +1,30 @@ +;;; defuns-flycheck.el +;; for ../core-flycheck.el + +;;;###autoload +(defun narf*fly-shorter-status (result) + (format "[%s]" (replace-regexp-in-string " FlyC:?" "" result))) + +;;;###autoload +(defun narf*flycheck-buffer () + (if (and (featurep 'flycheck) flycheck-mode) + (flycheck-buffer))) + +;;;###autoload +(defun narf/flycheck-next-error () + (interactive) + (call-interactively + (if (bound-and-true-p flycheck-mode) + 'flycheck-next-error + 'next-error))) + +;;;###autoload +(defun narf/flycheck-previous-error () + (interactive) + (call-interactively + (if (bound-and-true-p flycheck-mode) + 'flycheck-previous-error + 'previous-error))) + +(provide 'defuns-flycheck) +;;; defuns-flycheck.el ends here diff --git a/core/lib/defuns-helm.el b/core/lib/defuns-helm.el new file mode 100644 index 000000000..9ed378827 --- /dev/null +++ b/core/lib/defuns-helm.el @@ -0,0 +1,115 @@ +;;; defuns-helm.el +;; see ../core-helm.el + +;;;###autoload +(defun narf|projectile-invalidate-cache-maybe () + (when (narf/project-p) + (projectile-invalidate-cache nil))) + +;;;###autoload +(defun narf*projectile-replace-prompt (&optional string) + "Don't show the project name in the prompts; I already know." + helm-global-prompt) + +;;;###autoload +(defun narf*helm-hide-modeline (source &optional force) + "No persistent header." + (setq mode-line-format nil) + (setq header-line-format nil)) + +;;;###autoload +(defun narf/helm-split-window (window) + "Minimalistic split-fn; leaves popwin to handle helm buffers." + (if (one-window-p t) + (let ((helm-full-frame t)) + (selected-window)) + (other-window-for-scrolling))) + +;;;###autoload +(defun narf/helm-get-org-candidates-in-file (filename min-depth max-depth &optional fontify nofname) + (with-current-buffer (pcase filename + ((pred bufferp) filename) + ((pred stringp) (find-file-noselect filename))) + (and fontify (jit-lock-fontify-now)) + (let ((match-fn (if fontify 'match-string 'match-string-no-properties))) + (save-excursion + (goto-char (point-min)) + (cl-loop with width = (window-width) + while (re-search-forward org-complex-heading-regexp nil t) + if (let ((num-stars (length (match-string-no-properties 1)))) + (and (>= num-stars min-depth) (<= num-stars max-depth))) + collect `(,(let ((heading (funcall match-fn 4)) + (file (unless nofname + (concat (f-no-ext (f-relative filename org-directory)) ":"))) + (level (length (match-string-no-properties 1)))) + (org-format-outline-path + (append (org-get-outline-path t level heading) + (list heading)) width file)) + . ,(point-marker))))))) + +;;;###autoload (autoload 'narf:helm-recentf "defuns-helm" nil t) +(evil-define-command narf:helm-recentf (&optional bang) + "Ex-mode interface for `helm-recentf' and `helm-projectile-recentf'. If + `bang', then `search' is interpreted as regexp." + :repeat nil + (interactive "") + (if bang (helm-recentf) (helm-projectile-recentf))) + +;; Ex-mode interface for `helm-ag'. If `bang', then `search' is interpreted as +;; regexp. +;;;###autoload (autoload 'narf:helm-search "defuns-helm" nil t) +(evil-define-operator narf:helm-search (beg end &optional search hidden-files-p pwd-p regex-p) + :type inclusive + :repeat nil + (interactive "") + (require 'helm-ag) + (let* ((helm-ag--default-directory (if pwd-p default-directory (concat (narf/project-root) "/"))) + (helm-ag-command-option (concat (unless regex-p "-Q ") + (if hidden-files-p "--hidden "))) + (input "") + (header-name (format "Search in %s" helm-ag--default-directory))) + (if search + (progn + (helm-attrset 'search-this-file nil helm-ag-source) + (setq helm-ag--last-query search)) + (if (and beg end (/= beg (1- end))) + (setq input (buffer-substring-no-properties beg end)))) + (helm-attrset 'name header-name helm-ag-source) + (helm :sources (if search (helm-ag--select-source) '(helm-source-do-ag)) + :buffer "*helm-ag*" + :input input + :prompt helm-global-prompt))) + +;;;###autoload (autoload 'narf:helm-regex-search "defuns-helm" nil t) +(evil-define-operator narf:helm-regex-search (beg end &optional search bang) + :type inclusive :repeat nil + (interactive "") + (narf:helm-search beg end search bang nil t)) + +;;;###autoload (autoload 'narf:helm-regex-cwd "defuns-helm" nil t) +(evil-define-operator narf:helm-search-cwd (beg end &optional search bang) + ;; Ex-mode interface for `helm-do-ag'. If `bang', then `search' is interpreted + ;; as regexp + :type inclusive :repeat nil + (interactive "") + (narf:helm-search beg end search bang t nil)) + +;;;###autoload (autoload 'narf:helm-regex-search-cwd "defuns-helm" nil t) +(evil-define-operator narf:helm-regex-search-cwd (beg end &optional search bang) + :type inclusive :repeat nil + (interactive "") + (narf:helm-search beg end search bang t t)) + +;; Ex-mode interface for `helm-swoop', `helm-multi-swoop-all' (if `bang'), or +;; `helm-css-scss' and `helm-css-scss-multi' (if `bang') if major-mode is +;; `scss-mode' +;;;###autoload (autoload 'narf:helm-swoop "defuns-helm" nil t) +(evil-define-command narf:helm-swoop (&optional search bang) + :repeat nil + (interactive "") + (if (eq major-mode 'scss-mode) + (if bang (helm-css-scss-multi search) (helm-css-scss search)) + (if bang (helm-multi-swoop-all search) (helm-swoop :$query search)))) + +(provide 'defuns-helm) +;;; defuns-helm.el ends here diff --git a/core/lib/defuns-ido.el b/core/lib/defuns-ido.el new file mode 100644 index 000000000..99a984689 --- /dev/null +++ b/core/lib/defuns-ido.el @@ -0,0 +1,65 @@ +;;; defuns-ido.el + +;;;###autoload +(defun narf*ido-sort-mtime () + "Sort ido filelist by mtime instead of alphabetically." + (setq ido-temp-list + (sort ido-temp-list + (lambda (a b) + (time-less-p + (sixth (file-attributes (concat ido-current-directory b))) + (sixth (file-attributes (concat ido-current-directory a))))))) + (ido-to-end ;; move . files to end (again) + (delq nil (mapcar + (lambda (x) (and (char-equal (string-to-char x) ?.) x)) + ido-temp-list)))) + +;;;###autoload +(defun narf|ido-setup-home-keybind () + "Go to $HOME with ~" + (define-key ido-file-completion-map (kbd "~") + (λ (if (looking-back "/") + (insert "~/") + (call-interactively 'self-insert-command))))) + +;;;###autoload +(defun narf/ido-find-file (&optional dir) + (interactive) + (let ((default-directory (or dir default-directory))) + (ido-find-file))) + +;;;###autoload +(defun narf/ido-find-file-other-window (&optional dir) + (interactive) + (let ((default-directory (or dir default-directory))) + (ido-find-file-other-window))) + +;;;###autoload +(defun narf/ido-find-project-file () + (interactive) + (let ((default-directory (narf/project-root))) + (ido-find-file))) + +;;;###autoload +(defun narf/ido-find-org-file () + (interactive) + (let ((default-directory org-directory)) + (ido-find-file))) + +;;;###autoload +(defun narf/ido-recentf () + "Use `ido-completing-read' to \\[find-file] a recent file" + (interactive) + (if (find-file (ido-completing-read "Find recent file: " recentf-list)) + (message "Opening file...") + (message "Aborting"))) + +;;;###autoload (autoload 'narf:ido-find-file-in-emacsd "defuns-ido" nil t) +(evil-define-command narf:ido-find-file-in-emacsd (&optional bang) :repeat nil + (interactive "") + (if bang + (ido-find-file-in-dir narf-modules-dir) + (ido-find-file-in-dir narf-emacs-dir))) + +(provide 'defuns-ido) +;;; defuns-ido.el ends here diff --git a/core/defuns/defuns-isearch.el b/core/lib/defuns-isearch.el similarity index 97% rename from core/defuns/defuns-isearch.el rename to core/lib/defuns-isearch.el index bbd235526..4f102cc5b 100644 --- a/core/defuns/defuns-isearch.el +++ b/core/lib/defuns-isearch.el @@ -1,3 +1,4 @@ +;;; defuns-isearch.el ;;;###autoload (defun narf/isearch-delete-word () @@ -29,6 +30,5 @@ (interactive) (narf:isearch-paste-from-register ?+)) - (provide 'defuns-isearch) ;;; defuns-isearch.el ends here diff --git a/core/lib/defuns-neotree.el b/core/lib/defuns-neotree.el new file mode 100644 index 000000000..5972d49e3 --- /dev/null +++ b/core/lib/defuns-neotree.el @@ -0,0 +1,40 @@ +;;; defuns-neotree.el +;; for ../core-project.el + +;;;###autoload +(defun narf/neotree-open (&optional dir) + (interactive) + (neotree-dir (or dir (narf/project-root)))) + +;;;###autoload +(defun narf/neotree-toggle () + (interactive) + (if (neo-global--window-exists-p) + (neotree-hide) + (narf/neotree-open))) + +;;;###autoload +(defun narf/neotree-find () + (interactive) + (save-excursion (narf/neotree-open)) + (neotree-find)) + +;;;###autoload +(defun narf|neotree-close-on-window-change () + "Close neotree to prevent ensuing mindow buggery." + (unless (and (neo-global--window-exists-p) + (eq (current-buffer) (neo-global--get-buffer))) + (neotree-hide))) + +;;;###autoload +(defun narf*neo-buffer-fold-symbol (name) + "Custom hybrid ascii theme with leading whitespace." + (let ((n-insert-symbol (lambda (n) + (neo-buffer--insert-with-face + n 'neo-expand-btn-face)))) + (or (and (equal name 'open) (funcall n-insert-symbol "- ")) + (and (equal name 'close) (funcall n-insert-symbol "> ")) + (and (equal name 'leaf) (funcall n-insert-symbol " "))))) + +(provide 'defuns-neotree) +;;; defuns-neotree.el ends here diff --git a/core/defuns/defuns-nlinum.el b/core/lib/defuns-nlinum.el similarity index 99% rename from core/defuns/defuns-nlinum.el rename to core/lib/defuns-nlinum.el index 516bbb690..d37e19ae6 100644 --- a/core/defuns/defuns-nlinum.el +++ b/core/lib/defuns-nlinum.el @@ -7,6 +7,5 @@ (narf|nlinum-disable) (narf|nlinum-enable))) - (provide 'defuns-nlinum) ;;; defuns-nlinum.el ends here diff --git a/core/defuns/defuns-popwin.el b/core/lib/defuns-popwin.el similarity index 91% rename from core/defuns/defuns-popwin.el rename to core/lib/defuns-popwin.el index fc7d35bbd..800d5a897 100644 --- a/core/defuns/defuns-popwin.el +++ b/core/lib/defuns-popwin.el @@ -1,3 +1,5 @@ +;;; defuns-popwin.el + ;;;###autoload (defun narf/popwin-toggle () (interactive) @@ -5,6 +7,5 @@ (popwin:close-popup-window) (popwin:popup-last-buffer))) - (provide 'defuns-popwin) ;;; defuns-popwin.el ends here diff --git a/core/lib/defuns-project.el b/core/lib/defuns-project.el new file mode 100644 index 000000000..90b64b83c --- /dev/null +++ b/core/lib/defuns-project.el @@ -0,0 +1,44 @@ +;;; defuns-project.el + +;;;###autoload +(defun narf/project-root (&optional strict-p) + "Get the path to the root of your project. Uses `narf-project-root-files' to +determine if a directory is a project." + (let ((home (file-truename "~"))) + (catch 'found + (f-traverse-upwards + (lambda (path) + (let ((path (file-truename path))) + (if (file-equal-p home path) + (throw 'found (if strict-p nil default-directory)) + (dolist (file narf-project-root-files) + (when (file-exists-p (expand-file-name file path)) + (throw 'found path)))))) default-directory) + default-directory))) + +;;;###autoload +(defun narf/project-has-files (files &optional root) + "Return non-nil if `file' exists in the project root." + (let ((root (or root (narf/project-root))) + (files (if (listp files) files (list files))) + found-p file) + (while (and files (not found-p)) + (setq file (pop files)) + (setq found-p (file-exists-p (narf/project-path-to file root)))) + found-p)) + +;;;###autoload +(defun narf/project-path-to (file &optional root) + (let ((root (or root (narf/project-root)))) + (expand-file-name file root))) + +;;;###autoload +(defun narf/project-name (&optional root) + (file-name-nondirectory (directory-file-name (or root (narf/project-root))))) + +;;;###autoload +(defun narf/project-p () + (not (null (narf/project-root t)))) + +(provide 'defuns-project) +;;; defuns-project.el ends here diff --git a/core/lib/defuns-quickrun.el b/core/lib/defuns-quickrun.el new file mode 100644 index 000000000..36c8ea947 --- /dev/null +++ b/core/lib/defuns-quickrun.el @@ -0,0 +1,71 @@ +;;; defuns-quickrun.el + +;;;; Code building ;;;;;;;;;;;;;;;;;;;;;; +(defvar narf--build-command '("make %s" . "Makefile")) +(make-variable-buffer-local 'narf--build-command) + +;;;###autoload +(defun narf/set-build-command (command &optional file) + (when (or (null file) + (narf/project-has-files file)) + (setq narf--build-command `(,command . ,file)))) + +;;;###autoload (autoload 'narf:build "defuns-quickrun" nil t) +(evil-define-command narf:build (arg) + "Call a build command in the current directory. +If ARG is nil this function calls `recompile', otherwise it calls +`compile' passing ARG as build command." + (interactive "") + (when (null narf--build-command) + (user-error "No build command was set")) + (let ((build-file (cdr narf--build-command)) + (build-cmd (car narf--build-command))) + (if (narf/project-has-files build-file) + (compile (format "cd '%s' && %s" build-file (format build-cmd (or arg "")))) + (error "Could not find Makefile")))) + +;;;; Code running ;;;;;;;;;;;;;;;;;;;;;; +;;;###autoload (autoload 'narf:eval-buffer "defuns-quickrun" nil t) +(evil-define-command narf:eval-buffer () + :move-point nil + (interactive) + (cond ((eq major-mode 'emacs-lisp-mode) + (narf:eval-region (point-min) (point-max))) + (t (quickrun)))) + +;;;###autoload (autoload 'narf:eval-region "defuns-quickrun" nil t) +(evil-define-operator narf:eval-region (beg end) + :move-point nil + (interactive "") + (cond ((eq major-mode 'emacs-lisp-mode) + (let* ((pp-escape-newlines nil) + (out (s-trim (pp-to-string (eval (read (buffer-substring-no-properties beg end)))))) + (lines (length (s-lines out)))) + (if (< lines 5) + (princ out t) + (let ((buf (get-buffer-create "*eval*"))) + (with-current-buffer buf + (read-only-mode -1) + (emacs-lisp-mode) + (setq-local scroll-margin 0) + (erase-buffer) + (insert out) + (beginning-of-buffer) + (read-only-mode 1) + (popwin:popup-buffer buf :height (if (> lines 25) 25 (1+ lines)))))))) + (t (quickrun-region beg end)))) + +;;;###autoload (autoload 'narf:eval-region-and-replace "defuns-quickrun" nil t) +(evil-define-operator narf:eval-region-and-replace (beg end) + (interactive "") + (cond ((eq major-mode 'emacs-lisp-mode) + (kill-region beg end) + (condition-case nil + (prin1 (eval (read (current-kill 0))) + (current-buffer)) + (error (message "Invalid expression") + (insert (current-kill 0))))) + (t (quickrun-replace-region beg end)))) + +(provide 'defuns-quickrun) +;;; defuns-quickrun.el ends here diff --git a/core/lib/defuns-tmux.el b/core/lib/defuns-tmux.el new file mode 100644 index 000000000..2d73c9222 --- /dev/null +++ b/core/lib/defuns-tmux.el @@ -0,0 +1,35 @@ +;;; defuns-tmux.el + +(defun narf--tmux-send (command) + (shell-command (format "tmux send-keys %s" command))) + +(evil-define-interactive-code "" + "Ex tmux argument (a mix between and )" + :ex-arg shell + (list (when (evil-ex-p) (evil-ex-file-arg)))) + +;;;###autoload (autoload 'narf:tmux-run "defuns-tmux" nil t) +(evil-define-command narf:tmux-run (&optional command bang) + "Sends input to tmux. Use `bang' to append to tmux" + (interactive "") + (nerf/tmux-send (format (if bang "C-u %s Enter" "%s") + (shell-quote-argument command))) + (when (evil-ex-p) + (message "[Tmux] %s" command))) + +;;;###autoload (autoload 'narf:tmux-chdir "defuns-tmux" nil t) +(evil-define-command narf:tmux-chdir (&optional path bang) + "CDs in tmux using `narf/project-root'" + (interactive "") + (let ((dir (shell-quote-argument + (if (and path (not (s-blank? path))) + (if (file-directory-p path) + (file-truename path) + (error "Directory doesn't exist %s" path)) + (if bang default-directory (narf/project-root)))))) + (nerf/tmux-send (format "C-u cd Space %s Enter" (shell-quote-argument dir))) + (when (evil-ex-p) + (message "[Tmux] cd %s" dir)))) + +(provide 'defuns-tmux) +;;; defuns-tmux.el ends here diff --git a/core/lib/defuns-ui.el b/core/lib/defuns-ui.el new file mode 100644 index 000000000..cd02e01be --- /dev/null +++ b/core/lib/defuns-ui.el @@ -0,0 +1,29 @@ +;;; defuns-ui.el +;; for ../core-ui.el + +;;;###autoload +(defun narf:toggle-transparency () + (interactive) + (let* ((alpha (frame-parameter nil 'alpha)) + (alpha-val (if (listp alpha) (car alpha) alpha))) + (if (/= alpha-val 97) + (set-frame-parameter nil 'alpha 100) + (set-frame-parameter nil 'alpha 0)))) + +;;;###autoload +(defun narf:toggle-fullscreen () + (interactive) + (set-frame-parameter nil 'fullscreen + (when (not (frame-parameter nil 'fullscreen)) 'fullboth))) + +(defvar narf--big-mode nil) +;;;###autoload +(defun narf:toggle-big-mode () + (interactive) + (if narf--big-mode + (set-frame-font (apply #'font-spec narf-default-font)) + (set-frame-font (apply #'font-spec narf-big-font))) + (setq narf--big-mode (not narf--big-mode))) + +(provide 'defuns-ui) +;;; defuns-ui.el ends here diff --git a/core/lib/defuns-whitespace.el b/core/lib/defuns-whitespace.el new file mode 100644 index 000000000..7632dc7e9 --- /dev/null +++ b/core/lib/defuns-whitespace.el @@ -0,0 +1,168 @@ +;;; defuns-whitespace.el + +;;;###autoload +(defun narf--point-at-bol-non-blank() + (save-excursion (evil-first-non-blank) (point))) + +;;;###autoload +(defun narf/surrounded-p () + (and (looking-back "[[{(]\\(\s+\\|\n\\)?\\(\s\\|\t\\)*") + (let* ((whitespace (match-string 1)) + (match-str (concat whitespace (match-string 2) "[])}]"))) + (looking-at-p match-str)))) + +;;;###autoload +(defun narf/backward-kill-to-bol-and-indent () + "Kill line to the first non-blank character. If invoked again +afterwards, kill line to column 1." + (interactive) + (let ((empty-line (sp-point-in-blank-line))) + (evil-delete (point-at-bol) (point)) + (if (not empty-line) + (indent-according-to-mode)))) + +;;;###autoload +(defun narf/move-to-bol () + "Moves cursor to the first non-blank character on the line. If +already there, move it to the true bol." + (interactive) + (evil-save-goal-column + (let ((point-at-bol (narf--point-at-bol-non-blank)) + (point (point))) + (if (= point-at-bol point) + (evil-move-beginning-of-line) + (unless (= (point-at-bol) point) + (evil-first-non-blank)))))) + +;;;###autoload +(defun narf/move-to-eol () + (interactive) + (evil-save-goal-column + (let ((old-point (point))) + (when (comment-search-forward (point-at-eol) t) + (goto-char (match-beginning 0)) + (skip-syntax-backward " ^<*" (narf--point-at-bol-non-blank)) + + (if (eq old-point (point)) ; + (evil-move-end-of-line)))))) + +;; Mimic expandtab in vim +;;;###autoload +(defun narf/backward-delete-whitespace-to-column () + "Delete back to the previous column of whitespace, or as much +whitespace as possible, or just one char if that's not possible." + (interactive) + (cond ;; If in a string + ((sp-point-in-string) + (call-interactively 'backward-delete-char-untabify)) + ;; If using tabs (or at bol), just delete normally + ((or indent-tabs-mode + (= (point-at-bol) (point))) + (call-interactively 'backward-delete-char)) + ;; Delete up to the nearest tab column IF only whitespace between point + ;; and bol. + ((looking-back "^[\\t ]*" (point-at-bol)) + (let ((movement (% (current-column) tab-width)) + (p (point))) + (when (= movement 0) + (setq movement tab-width)) + (save-match-data + (if (string-match "\\w*\\(\\s-+\\)$" + (buffer-substring-no-properties (- p movement) p)) + (backward-delete-char (- (match-end 1) (match-beginning 1))) + (backward-delete-char-untabify 1))))) + ;; Otherwise do a regular delete + (t (backward-delete-char-untabify 1)))) + +;;;###autoload +(defun narf/dumb-indent () + "Inserts a tab character (or spaces x tab-width). Checks if the +auto-complete window is open." + (interactive) + (if indent-tabs-mode + (insert "\t") + (let* ((movement (% (current-column) tab-width)) + (spaces (if (zerop movement) tab-width (- tab-width movement)))) + (insert (s-repeat spaces " "))))) + +;;;###autoload +(defun narf/inflate-space-maybe () + "Checks if point is surrounded by {} [] () delimiters and adds a +space on either side of the point if so." + (interactive) + (if (narf/surrounded-p) + (progn (call-interactively 'self-insert-command) + (save-excursion (call-interactively 'self-insert-command))) + (call-interactively 'self-insert-command))) + +;;;###autoload +(defun narf/deflate-space-maybe () + "Checks if point is surrounded by {} [] () delimiters, and deletes +spaces on either side of the point if so. Resorts to +`narf/backward-delete-whitespace-to-column' otherwise." + (interactive) + (save-match-data + (if (narf/surrounded-p) + (let ((whitespace-match (match-string 1))) + (cond ((not whitespace-match) + (call-interactively 'sp-backward-delete-char)) + ((string-match "\n" whitespace-match) + (evil-delete (point-at-bol) (point)) + (delete-char -1) + (save-excursion (delete-char 1))) + (t + (just-one-space 0)))) + (narf/backward-delete-whitespace-to-column)))) + +;;;###autoload +(defun narf/newline-and-indent () + (interactive) + (cond ((sp-point-in-string) + (newline)) + ((sp-point-in-comment) + (cond ((eq major-mode 'js2-mode) + (js2-line-break)) + ((-contains? '(java-mode php-mode) major-mode) + (c-indent-new-comment-line)) + ((-contains? '(c-mode c++-mode objc-mode css-mode scss-mode) major-mode) + (newline-and-indent) + (insert "* ") + (indent-according-to-mode)) + (t (indent-new-comment-line)))) + (t (newline-and-indent)))) + +;;;###autoload (autoload 'narf:whitespace-retab "defuns-whitespace" nil t) +(evil-define-operator narf:whitespace-retab (beg end) + "Akin to vim's retab, this changes all tabs-to-spaces or spaces-to-tabs, + depending on `indent-tab-mode'. Untested." + :motion nil + :move-point nil + :type line + (interactive "") + (unless (and beg end) + (setq beg (point-min)) + (setq end (point-max))) + (if indent-tabs-mode + (tabify beg end) + (untabify beg end))) + +;;;###autoload (autoload 'narf:whitespace-align "defuns-whitespace" nil t) +(evil-define-command narf:whitespace-align (beg end &optional regexp bang) + :repeat nil + (interactive "") + (when regexp + (align-regexp beg end + (concat "\\(\\s-*\\)" (rxt-pcre-to-elisp regexp)) 1 1))) + +;;;###autoload +(defun narf:toggle-delete-trailing-whitespace () + (interactive) + (if (memq 'delete-trailing-whitespace before-save-hook) + (progn (message "Remove trailing whitespace: OFF") + (remove-hook 'before-save-hook 'delete-trailing-whitespace)) + + (message "Remove trailing whitespace: ON") + (add-hook 'before-save-hook 'delete-trailing-whitespace))) + +(provide 'defuns-whitespace) +;;; defuns-whitespace.el ends here diff --git a/core/lib/defuns-workgroup.el b/core/lib/defuns-workgroup.el new file mode 100644 index 000000000..7bb5b2488 --- /dev/null +++ b/core/lib/defuns-workgroup.el @@ -0,0 +1,56 @@ +;;; defuns-workgroup.el + +;;;###autoload +(defun narf/wg-helm-switch-to-workgroup (name) + (wg-switch-to-workgroup (wg-get-workgroup name))) + +;;;###autoload +(defun narf:helm-wg () + (interactive) + (helm :sources '(narf/helm-source-wg))) + +;;;###autoload +(defun narf/wg-projectile-switch-project () + (let ((workgroup-name (file-name-nondirectory (directory-file-name (narf/project-root))))) + (wg-create-workgroup workgroup-name t) + (helm-projectile-find-file))) + +;;;###autoload (autoload 'narf:save-session "defuns-workgroup" nil t) +(evil-define-command narf:save-session (&optional bang session-name) + (interactive "") + (if session-name + (wg-save-session-as (concat wg-workgroup-directory session-name) (not bang)) + (wg-save-session))) + +;;;###autoload (autoload 'narf:load-session "defuns-workgroup" nil t) +(evil-define-command narf:load-session (&optional bang session-name) + (interactive "") + (wg-open-session (if session-name + (concat wg-workgroup-directory session-name) + wg-session-file))) + +;;;###autoload (autoload 'narf:workgroup-new "defuns-workgroup" nil t) +(evil-define-command narf:workgroup-new (bang name) + (interactive "") + (unless name + (user-error "No name specified for new workgroup")) + (if bang + (wg-clone-workgroup (wg-current-workgroup) name) + (wg-create-workgroup name t))) + +;;;###autoload (autoload 'narf:workgroup-rename "defuns-workgroup" nil t) +(evil-define-command narf:workgroup-rename (new-name) + (interactive "") + (wg-rename-workgroup new-name)) + +;;;###autoload +(defun narf:kill-other-workgroups () + "Kill all other workgroups." + (interactive) + (let (workgroup (wg-current-workgroup)) + (dolist (w (wg-workgroup-list)) + (unless (wg-current-workgroup-p w) + (wg-kill-workgroup w))))) + +(provide 'defuns-workgroup) +;;; defuns-workgroup.el ends here diff --git a/core/lib/defuns-yasnippet.el b/core/lib/defuns-yasnippet.el new file mode 100644 index 000000000..7d7adf056 --- /dev/null +++ b/core/lib/defuns-yasnippet.el @@ -0,0 +1,120 @@ +;;; defuns-yasnippet.el +;; for ../core-yasnippet.el + +;;;###autoload +(defun narf|yas-before-expand () + "Switch to insert mode when expanding a template via backtab, or go back to +normal mode if there are no fields." + ;; Strip out the shitespace before a line selection. + (when (narf/evil-visual-line-state-p) + (setq yas-selected-text + (replace-regexp-in-string + "\\(^ *\\|\n? $\\)" "" + (buffer-substring-no-properties (region-beginning) + (1- (region-end)))))) + (evil-insert-state +1)) + +;;;###autoload +(defun narf|yas-after-expand () + "Switch to insert mode when expanding a template via backtab, or go back to +normal mode if there are no fields." + (setq yas-selected-text nil)) + +;;;###autoload +(defun narf/yas-insert-snippet () + "Switch to insert mode when expanding a template via backtab, or go back to +normal mode if there are no fields." + (interactive) + (yas-insert-snippet) + (evil-insert-state +1)) + +;;;###autoload +(defun narf/yas-goto-start-of-field () + "Go to the beginning of a field." + (interactive) + (let* ((snippet (car (yas--snippets-at-point))) + (position (yas--field-start (yas--snippet-active-field snippet)))) + (if (= (point) position) + (move-beginning-of-line 1) + (goto-char position)))) + +;;;###autoload +(defun narf/yas-goto-end-of-field () + (interactive) + (let* ((snippet (car (yas--snippets-at-point))) + (position (yas--field-end (yas--snippet-active-field snippet)))) + (if (= (point) position) + (move-end-of-line 1) + (goto-char position)))) + +;;;###autoload +(defun narf/yas-backspace (&optional field) + "Prevents Yas from stepping on my toes when I use backspace." + (interactive) + (let ((field (or field (and yas--active-field-overlay + (overlay-buffer yas--active-field-overlay) + (overlay-get yas--active-field-overlay 'yas--field))))) + (cond ((eq (point) (marker-position (yas--field-start field))) nil) + (t (delete-char -1))))) + +;;;###autoload +(defun narf/yas-delete (&optional field) + (interactive) + (let ((field (or field (and yas--active-field-overlay + (overlay-buffer yas--active-field-overlay) + (overlay-get yas--active-field-overlay 'yas--field))))) + (cond ((and field + (not (yas--field-modified-p field)) + (eq (point) (marker-position (yas--field-start field)))) + (yas--skip-and-clear field) + (yas-next-field 1)) + ((eq (point) (marker-position (yas--field-end field))) nil) + (t (delete-char 1))))) + +;;;###autoload +(defun narf/yas-clear-to-sof (&optional field) + (interactive) + (let* ((field (or field (and yas--active-field-overlay + (overlay-buffer yas--active-field-overlay) + (overlay-get yas--active-field-overlay 'yas--field)))) + (sof (marker-position (yas--field-start field)))) + (when (and field (> (point) sof)) + (delete-region sof (point))))) + +;; Snippet helpers ;;;;;;;;;;;;;;;;;;;;; +;;;###autoload +(defun !%! () + "Snippet function. Shorthand defun to surround text with newlines if more +than one line." + (when % + (if (> (length (s-lines %)) 1) + (concat "\n" % "\n") + (s-trim %)))) + +;;;###autoload +(defun !% () + "Snippet function. Shorthand defun for snippets: prepends a newline to + `yas-selected-text' IF it contains more than one line." + (when % + (if (> (length (s-lines %)) 1) + (concat "\n" %) + (s-trim %)))) +;;;###autoload +(defun %1 () + "Trim selection; do no further processing." + (s-trim %)) + +;;;###autoload (autoload 'narf:yas-snippets "defuns-yasnippet" nil t) +(evil-define-command narf:yas-snippets (&optional bang) + (interactive "") + (if bang + (narf/ido-find-file (car narf-snippet-dirs)) + (yas-visit-snippet-file))) + +;;;###autoload +(defun narf:yas-file-templates () + (interactive) + (narf/ido-find-file (cdr narf-snippet-dirs))) + +(provide 'defuns-yasnippet) +;;; nlinum-defuns.el ends here diff --git a/core/lib/macros-auto-insert.el b/core/lib/macros-auto-insert.el new file mode 100644 index 000000000..83217765a --- /dev/null +++ b/core/lib/macros-auto-insert.el @@ -0,0 +1,18 @@ +;;; macros-auto-insert.el +;; for ../core-auto-insert.el + +;;;###autoload +(defmacro add-template! (regexp-or-major-mode uuid yas-mode &optional project-only) + `(define-auto-insert ,regexp-or-major-mode + (lambda () + (unless (or (and ,project-only (not (narf/project-p))) + (not (or (eq major-mode ,yas-mode) + (symbol-value ,yas-mode)))) + (insert ,uuid) + (yas-expand-from-trigger-key) + (if (string-equal ,uuid (s-trim (buffer-string))) + (erase-buffer) + (evil-insert-state 1)))))) + +(provide 'macros-auto-insert) +;;; macros-auto-insert.el ends here diff --git a/core/lib/macros-buffers.el b/core/lib/macros-buffers.el new file mode 100644 index 000000000..ea24eb893 --- /dev/null +++ b/core/lib/macros-buffers.el @@ -0,0 +1,8 @@ +;;; defuns-buffers.el + +;;;###autoload +(defun add-unreal-buffer! (regexp) + (add-to-list 'narf-unreal-buffers regexp)) + +(provide 'defuns-buffers) +;;; defuns-buffers.el ends here diff --git a/core/lib/macros-company.el b/core/lib/macros-company.el new file mode 100644 index 000000000..2f202582f --- /dev/null +++ b/core/lib/macros-company.el @@ -0,0 +1,22 @@ +;;; macros-company.el --- macros for company-mode +;; for ../core-company.el + +;;;###autoload +(defmacro add-company-backend! (hook backends) + "Register a company backend for a mode." + (let ((def-name (intern (format "narf--init-company-%s" hook))) + (quoted (eq (car-safe backends) 'quote))) + `(progn + (defun ,def-name () + (set (make-local-variable 'company-backends) + (append '((,@(mapcar (lambda (backend) + (if quoted + backend + (intern (format "company-%s" backend)))) + (if quoted (cadr backends) backends)) + company-semantic)) + company-backends))) + (add-hook ',(intern (format "%s-hook" hook)) ',def-name)))) + +(provide 'macros-company) +;;; macros-company.el ends here diff --git a/core/macros/macros-popwin.el b/core/lib/macros-popwin.el similarity index 51% rename from core/macros/macros-popwin.el rename to core/lib/macros-popwin.el index d3594e303..193e05fc8 100644 --- a/core/macros/macros-popwin.el +++ b/core/lib/macros-popwin.el @@ -1,14 +1,13 @@ ;;; macros-popwin.el ;;;###autoload -(defmacro @popwin-register (&rest forms) +(defmacro add-popwin-rule! (&rest forms) "Register a rule for popwin. See `popwin:special-display-config'. Example: - (@popwin-register (\"^\\*Flycheck.*\\*$\" :regexp t :position bottom :height 0.25 :noselect t))" + (add-popwin-rule! \"^\\*Flycheck.*\\*$\" :regexp t :position bottom :height 0.25 :noselect t)" (declare (indent defun)) - `(push (,@forms) form)) - + `(push '(,@forms) popwin:special-display-config)) (provide 'macros-popwin) ;;; macros-popwin.el ends here diff --git a/core/lib/macros-quickrun.el b/core/lib/macros-quickrun.el new file mode 100644 index 000000000..7bddce503 --- /dev/null +++ b/core/lib/macros-quickrun.el @@ -0,0 +1,13 @@ +;;; macros-quickrun.el + +;;;###autoload +(defmacro build-for! (mode command build-file) + "Register major/minor MODE with build COMMAND. If FILES are provided, do an +additional check to make sure they exist in the project root." + `(add-hook! ,mode + (when (or (null ,build-file) + (narf/project-has-files ,build-file)) + (setq narf--build-command '(,command . ,build-file))))) + +(provide 'macros-quickrun) +;;; macros-quickrun.el ends here diff --git a/core/lib/macros-yasnippet.el b/core/lib/macros-yasnippet.el new file mode 100644 index 000000000..4e0233843 --- /dev/null +++ b/core/lib/macros-yasnippet.el @@ -0,0 +1,17 @@ +;;; macros-yasnippet.el +;; for ../core-yasnippet.el + +;;;###autoload +(defmacro add-yas-minor-mode! (&rest modes) + "Register minor MODES in yasnippet." + `(after! yasnippet + (when (boundp 'yas-extra-modes) + ,@(mapcar (lambda (mode) + `(after! ,(cadr mode) + (if (symbol-value ,mode) + (yas-activate-extra-mode ,mode) + (setq yas-extra-modes (delq ,mode yas-extra-modes))))) + modes)))) + +(provide 'macros-yasnippet) +;;; macros-yasnippet.el ends here diff --git a/core/macros/macros-company.el b/core/macros/macros-company.el deleted file mode 100644 index 172d8cb32..000000000 --- a/core/macros/macros-company.el +++ /dev/null @@ -1,4 +0,0 @@ - - -(provide 'macros-company) -;;; macros-company.el ends here