diff --git a/lisp/init-ansiterm.el b/lisp/init-ansiterm.el new file mode 100644 index 0000000..4e665d1 --- /dev/null +++ b/lisp/init-ansiterm.el @@ -0,0 +1,81 @@ +;; *** ANSI-TERM (inferior shells/interpreters and REPLs) + +;; I use ansi term for inferior shells only. + +;; via [[http://echosa.github.io/blog/2012/06/06/improving-ansi-term/][echosa]] + +;; kill buffer when done +(defadvice term-sentinel (around tvd-advice-term-sentinel (proc msg)) + (if (memq (process-status proc) '(signal exit)) + (let ((buffer (process-buffer proc))) + ad-do-it + (kill-buffer buffer)) + ad-do-it)) +(ad-activate 'term-sentinel) + +;; force utf8 +(defun tvd-term-use-utf8 () + (set-buffer-process-coding-system 'utf-8-unix 'utf-8-unix)) +(add-hook 'term-exec-hook 'tvd-term-use-utf8) + +;; make C-y work +(defun tvd-term-paste (&optional string) + (interactive) + (process-send-string + (get-buffer-process (current-buffer)) + (if string string (current-kill 0)))) + +;; put all term hooks in here +(defun tvd-term-hook () + (goto-address-mode) + (define-key term-raw-map (kbd "C-y") 'tvd-term-paste) + (define-key term-raw-map (kbd "C-c C-d") 'kill-this-buffer) + (define-key term-raw-map (kbd "C-d") 'kill-this-buffer) + (define-key term-raw-map (kbd "C-c C-l") 'term-line-mode) + (define-key term-raw-map (kbd "C-k") + (lambda () + (interactive) + (term-send-raw-string "\C-k") + (kill-line)))) +(add-hook 'term-mode-hook 'tvd-term-hook) + +;; via [[https://www.emacswiki.org/emacs/AnsiTermHints#toc4][emacswiki]]: +;; Use to supply commandline arguments to ansi-term +(defun term-with-args (new-buffer-name cmd &rest switches) + (setq term-ansi-buffer-name (concat "*" new-buffer-name "*")) + (setq term-ansi-buffer-name (generate-new-buffer-name term-ansi-buffer-name)) + (setq term-ansi-buffer-name (apply 'make-term term-ansi-buffer-name cmd nil switches)) + (set-buffer term-ansi-buffer-name) + (term-mode) + (term-char-mode) + (message "Line mode: C-c C-l, Char mode: C-c C-k, Exit: C-c C-d") + (switch-to-buffer term-ansi-buffer-name)) + +;; finally the inferior REPLs: +(defun iperl () + "interactive perl (via perlbrew if exist or global)" + (interactive) + (let ((perlbrew (expand-file-name "~/perl5/perlbrew/bin/perlbrew"))) + (if (file-exists-p perlbrew) + (term-with-args "*perlbrew-de0*" perlbrew "exec" "--" "perl" "-de0") + (term-with-args "*perl-de0*" "perl" "-de0")))) + +(defun iruby () + "interactive ruby" + (interactive) + (term-with-args "*ruby-irb*" "irb")) + +(defun ipython () + "interactive python" + (interactive) + (setenv "PYTHONSTARTUP" (expand-file-name "~/.pythonrc")) + (term-with-args "*python-i*" "python" "-i")) + +(defun icalc () + "interactive calc" + (interactive) + (term-with-args "*calc*" (expand-file-name "~/bin/calc"))) + + +(provide 'init-ansiterm) +;;; init-ansiterm.el ends here diff --git a/lisp/init-autoscratch.el b/lisp/init-autoscratch.el new file mode 100644 index 0000000..8e4bb1a --- /dev/null +++ b/lisp/init-autoscratch.el @@ -0,0 +1,53 @@ +;; ** More scratch space +;; *** Text scratch +;; Sometimes I need a text mode scratch buffer while scratch is +;; already in use. So let's prepare one. I also add a buffer hook so that +;; this never gets deleted, but cleaned instead. + +(with-current-buffer (get-buffer-create "*text*") + (text-mode)) + +;; *** Autoscratch +;; use autoscratch otherwise +;; [[https://github.com/TLINDEN/autoscratch][autoscratch github]] +(use-package autoscratch-mode + :ensure nil + :config + (setq initial-major-mode 'autoscratch-mode) + (add-hook 'autoscratch-mode-hook '(lambda () + (setq autoscratch-triggers-alist + '(("[(;]" . (progn + (call-interactively 'emacs-lisp-mode) + (call-interactively 'enable-paredit-mode) + (call-interactively 'electric-pair-mode))) + ("#" . (progn + (call-interactively 'config-general-mode) + (electric-indent-local-mode t))) + ("[-a-zA-Z0-9]" . (text-mode)) + ("/" . (c-mode)) + ("*" . (progn (insert " ") (org-mode))) + ("." . (fundamental-mode))) + autoscratch-trigger-on-first-char t + autoscratch-reset-default-directory t) + (electric-indent-local-mode nil) + )) + (defalias 'scratch 'autoscratch-buffer)) + +;;; *** Persistent Scratch +;; I also like to be scratch buffers persistent with +;; [[https://github.com/Fanael/persistent-scratch][persistent-scratch]] + +(defun tvd-autoscratch-p () + "Return non-nil if the current buffer is a scratch buffer" + (string-match "scratch*" (buffer-name))) + +(use-package persistent-scratch + :config + (setq persistent-scratch-save-file (expand-file-name "scratches.el" user-emacs-directory)) + (persistent-scratch-setup-default) + + (setq persistent-scratch-scratch-buffer-p-function 'tvd-autoscratch-p)) + + +(provide 'init-autoscratch) +;;; init-autoscratch.el ends here diff --git a/lisp/init-cisco.el b/lisp/init-cisco.el new file mode 100644 index 0000000..8a276d4 --- /dev/null +++ b/lisp/init-cisco.el @@ -0,0 +1,12 @@ +;; *** Cisco Mode + +;; Written by myself many years ago, but I'm still using it daily to +;; view and prepare cisco configs. + +(use-package cisco-mode + :ensure nil ;; static install + :mode "\\.cfg\\'") + + +(provide 'init-cisco) +;;; init-cisco.el ends here diff --git a/lisp/init-completion.el b/lisp/init-completion.el new file mode 100644 index 0000000..5bf6ca3 --- /dev/null +++ b/lisp/init-completion.el @@ -0,0 +1,107 @@ +;; *** IDO mode + +;; There are other completion enhancement packages available like ivy +;; for example, but I love IDO and I am so used to it, it would be +;; impossible to change. So, I'll stick with IDO until end of (my) +;; times. + +;; Hint: Use C-f during file selection to switch to regular find-file + +;; Basic config +(ido-mode t) +(ido-everywhere nil) + +(use-package ido-completing-read+) + +(setq ido-enable-flex-matching t) +(setq ido-use-filename-at-point nil) +(setq ido-use-virtual-buffers t) +(setq ido-auto-merge-work-directories-length -1) + +;; One thing annoys me with IDO though: when writing a new file, IDO +;; wants me to select from a list of existing files. Since it is +;; nearly impossible to disable ido mode for write-file, which I HATE, +;; I came up with this: + +;; I added a new global variable, 'tvd-ido-disabled, which is nil by +;; default. Whenever I hit C-x C-w (in order to execute write-file), +;; it will be set to t, ido mode will be disabled and ido-write-file +;; will be called. Since ido mode is now disabled, it just calls the +;; original write-file, which is what I really want. + +;; When I'm finished selecting a filename and writing, ido mode will +;; be turned on again and the variable will be set to nil. However, +;; sometimes I may abort the process using C-g. In that case ido mode +;; may end up being disabled because the :after advice will not be +;; called on C-g. + +;; So, I also advice the minibuffer C-g function, which is +;; 'abort-recursive-edit, and re-enable ido mode from here if it is +;; still disabled. So far I haven't seen any bad side effects of this. + +(defvar tvd-ido-disabled nil) +(advice-add 'ido-write-file :before '(lambda (&rest args) (ido-mode 0) (setq tvd-ido-disabled t))) +(advice-add 'ido-write-file :after '(lambda (&rest args) (ido-mode 1) (setq tvd-ido-disabled nil))) + +(defun tvd-keyboard-quit-advice (fn &rest args) + (when tvd-ido-disabled + (ido-mode 1) + (setq tvd-ido-disabled nil)) + (apply fn args)) + +(advice-add 'abort-recursive-edit :around #'tvd-keyboard-quit-advice) + +;; from emacs wiki +;; Display ido results vertically, rather than horizontally +(setq ido-decorations (quote ("\n-> " "" "\n " "\n ..." "[" "]" + " [No match]" " [Matched]" " [Not readable]" + " [Too big]" " [Confirm]"))) + +(defun ido-disable-line-truncation () (set (make-local-variable 'truncate-lines) nil)) +(add-hook 'ido-minibuffer-setup-hook 'ido-disable-line-truncation) +(defun ido-define-keys () + (define-key ido-completion-map (kbd "") 'ido-next-match) + (define-key ido-completion-map (kbd "") 'ido-prev-match)) +(add-hook 'ido-setup-hook 'ido-define-keys) + +;; quickly go to home via ~ wherever I am +;; via [[http://whattheemacsd.com/setup-ido.el-02.html][whattheemacs.d]] +(add-hook 'ido-setup-hook + (lambda () + ;; Go straight home + (define-key ido-file-completion-map + (kbd "~") + (lambda () + (interactive) + (if (looking-back "/") + (insert "~/") + (call-interactively 'self-insert-command)))) + ;; same thing, but for ssh/tramp triggered by : + (define-key ido-file-completion-map + (kbd ":") + (lambda () + (interactive) + (if (looking-back "/") + (progn + (ido-set-current-directory "/ssh:") + (ido-reread-directory)) + (call-interactively 'self-insert-command)))))) + +;; by howardism: [re]open non-writable file with sudo +(defadvice ido-find-file (after find-file-sudo activate) + "Find file as root if necessary." + (unless (and buffer-file-name + (file-writable-p buffer-file-name)) + (let* ((file-name (buffer-file-name)) + (file-root (if (string-match "/ssh:\\([^:]+\\):\\(.*\\)" file-name) + (concat "/ssh:" (match-string 1 file-name) + "|sudo:" (match-string 1 file-name) + ":" (match-string 2 file-name)) + (concat "/sudo:localhost:" file-name)))) + (find-alternate-file file-root)))) + +;; FIXME: add ido-ignore-files defun. + + +(provide 'init-completion) +;;; init-completion.el ends here diff --git a/lisp/init-conf.el b/lisp/init-conf.el new file mode 100644 index 0000000..d89a893 --- /dev/null +++ b/lisp/init-conf.el @@ -0,0 +1,16 @@ +;; *** conf-mode + +;; conf-mode annoyingly overwrites the global keybinding C-c C-c with +;; some of its internal crab. So, force it to use my own defun. Also, +;; while we're at it, disable electric indent, it's annoying in +;; configs. Applies for derivates as well. + +(defun tvd-disarm-conf-mode() + (local-set-key (kbd "C-c C-c") 'comment-or-uncomment-region-or-line) + (electric-indent-local-mode 0)) + +(add-something-to-mode-hooks '(conf cisco fundamental conf-space pod) 'tvd-disarm-conf-mode) + + +(provide 'init-conf) +;;; init-conf.el ends here diff --git a/lisp/init-config-general.el b/lisp/init-config-general.el new file mode 100644 index 0000000..10fbfad --- /dev/null +++ b/lisp/init-config-general.el @@ -0,0 +1,129 @@ +;; *** Config::General mode +;; **** Config and doc +;; [[https://github.com/TLINDEN/config-general-mode][config-general-mode]] (also on Melpa). + +;; My own mode for [[http://search.cpan.org/dist/Config-General/][Config::General]] +;; config files. Whenever I write some perl stuff, which needs a config file, I use +;; this module (and I do this a lot). Previously I used conf-mode or html-mode, but +;; both did not satisfy me. Now (as of 20170625) I solved this mess once and for all. + +(use-package config-general-mode + :ensure nil ;; static install + + :config + (require 'sh-script) + + (when (fboundp 'hippie-expand) + (defun config-general-completion-at-point () + "Complete word at point using hippie-expand, if not on a comment." + (interactive) + (when (looking-back "[-%$_a-zA-Z0-9]") + (unless (eq (get-text-property (point) 'face) 'font-lock-comment-face) + (hippie-expand nil)))) + + (local-set-key (kbd "") 'config-general-tab-or-expand) + + ;; Inserting a brace or quote automatically inserts the matching pair + ;; use smartparens now + ;; (electric-pair-mode t) + (setq-local hippie-expand-only-buffers '(config-general-mode)) + + ;; configure order of expansion functions + (if (version< emacs-version "25.1") + (set (make-local-variable 'hippie-expand-try-functions-list) + '(try-expand-dabbrev ;; use patched version + config-general--try-expand-dabbrev-all-buffers + try-complete-file-name-partially + try-complete-file-name)) + (set (make-local-variable 'hippie-expand-try-functions-list) + '(try-expand-dabbrev + try-expand-dabbrev-all-buffers + try-complete-file-name-partially + try-complete-file-name)))) + + (defun config-general-do-electric-tab () + "Enter a or goto current indentation." + (interactive) + (if (eq (point) (line-end-position)) + (indent-for-tab-command) + (back-to-indentation))) + + ;; FIXME: Use this patched version for older emacsen and the default + ;; for version which contain the patch (if any, ever). + ;; + ;; The original function try-expand-dabbrev-all-buffers doesn't work + ;; correctly, it ignores a buffer-local configuration of the variables + ;; hippie-expand-only-buffers and hippie-expand-ignore-buffers. This + ;; is the patched version of the function. + ;; + ;; Bugreport: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=27501 + (defun config-general--try-expand-dabbrev-all-buffers (old) + "Try to expand word \"dynamically\", searching all other buffers. +The argument OLD has to be nil the first call of this function, and t +for subsequent calls (for further possible expansions of the same +string). It returns t if a new expansion is found, nil otherwise." + (let ((expansion ()) + (buf (current-buffer)) + (orig-case-fold-search case-fold-search) + (heib hippie-expand-ignore-buffers) + (heob hippie-expand-only-buffers) + ) + (if (not old) + (progn + (he-init-string (he-dabbrev-beg) (point)) + (setq he-search-bufs (buffer-list)) + (setq he-searched-n-bufs 0) + (set-marker he-search-loc 1 (car he-search-bufs)))) + + (if (not (equal he-search-string "")) + (while (and he-search-bufs + (not expansion) + (or (not hippie-expand-max-buffers) + (< he-searched-n-bufs hippie-expand-max-buffers))) + (set-buffer (car he-search-bufs)) + (if (and (not (eq (current-buffer) buf)) + (if heob + (he-buffer-member heob) + (not (he-buffer-member heib)))) + (save-excursion + (save-restriction + (if hippie-expand-no-restriction + (widen)) + (goto-char he-search-loc) + (setq expansion + (let ((case-fold-search orig-case-fold-search)) + (he-dabbrev-search he-search-string nil))) + (set-marker he-search-loc (point)) + (if (not expansion) + (progn + (setq he-search-bufs (cdr he-search-bufs)) + (setq he-searched-n-bufs (1+ he-searched-n-bufs)) + (set-marker he-search-loc 1 (car he-search-bufs)))))) + (setq he-search-bufs (cdr he-search-bufs)) + (set-marker he-search-loc 1 (car he-search-bufs))))) + + (set-buffer buf) + (if (not expansion) + (progn + (if old (he-reset-string)) + ()) + (progn + (he-substitute-string expansion t) + t)))) + + (electric-indent-mode) + + ;; de-activate some senseless bindings + (local-unset-key (kbd "C-c C-c")) + (local-unset-key (kbd "C-c C-p")) + (local-unset-key (kbd "C-c C-u")) + (local-unset-key (kbd "C-c C-w")) + (local-unset-key (kbd "C-c C-x")) + (local-unset-key (kbd "C-c :")) + + ;; from shell-script-mode, turn << into here-doc + (sh-electric-here-document-mode 1)) + + +(provide 'init-config-general) +;;; init-config-general.el ends here diff --git a/lisp/init-dictcc.el b/lisp/init-dictcc.el new file mode 100644 index 0000000..3c7dad2 --- /dev/null +++ b/lisp/init-dictcc.el @@ -0,0 +1,7 @@ +(use-package dictcc + :config + (defalias 'dict 'dictcc)) + + +(provide 'init-dictcc) +;;; init-dictcc.el ends here diff --git a/lisp/init-dired.el b/lisp/init-dired.el new file mode 100644 index 0000000..de55cf6 --- /dev/null +++ b/lisp/init-dired.el @@ -0,0 +1,271 @@ +;; *** Dired + +;; I use dired for two things: from inside magit as a convenient way +;; to add or remove files from a repository. Or if I want to rename a +;; bunch of files using search/replace and other editing commands. + +;; But as with everything else I use, it must fit and so I managed to +;; tune this as well. + +;; [[http://ergoemacs.org/emacs/emacs_dired_tips.html][More Hints]] + +;; **** dired-k + +;; dired-k is k for dired/emacs: it colorizes files and directory by +;; age, that is, the older the greyer they get. And it displays flags +;; about the git status of each file, which is really handy. + +;; However, it only works with git installed and if enabled stops +;; dired to work completely. So I define an exception here and don't +;; load k if there's no git (e.g. on my notebook at work) +(when (string-match "version" (shell-command-to-string "git version")) + (use-package dired-k + :init + (add-hook 'dired-initial-position-hook 'dired-k) + (add-hook 'dired-after-readin-hook #'dired-k-no-revert) + + :config + (setq dired-k-padding 2))) + +;; **** dired-hacks + +;; [[https://github.com/Fuco1/dired-hacks][Fuco1s dired-hacks]] is a +;; place to find the really cool stuff, I mostly use the filters. +(use-package dired-filter + :config + + (defun tvd-dired-quit-or-filter-pop (&optional arg) + "Remove a filter from the filter stack. If none left, quit the dired buffer." + (interactive "p") + (if dired-filter-stack + (dired-filter-pop arg) + (quit-window)))) + +(use-package dired-ranger) + +;; **** dired sort helpers + +;; This sort function by [[http://ergoemacs.org/emacs/dired_sort.html][Xah Lee]] +;; is easy to use and does what it should, great!, However, I added some -desc +;; sister sorts for reverse sorting. +(defun xah-dired-sort () + "Sort dired dir listing in different ways. +Prompt for a choice. +URL `http://ergoemacs.org/emacs/dired_sort.html' +Version 2015-07-30" + (interactive) + (let (sort-by arg) + (setq sort-by (ido-completing-read "Sort by:" '( "date" "size" "name" "dir" "date-desc" "size-desc" "name-desc" "dir-desc" ))) + (cond + ((equal sort-by "name") (setq arg "-Al --si --time-style long-iso ")) + ((equal sort-by "date") (setq arg "-Al --si --time-style long-iso -t")) + ((equal sort-by "size") (setq arg "-Al --si --time-style long-iso -S")) + ((equal sort-by "dir") (setq arg "-Al --si --time-style long-iso --group-directories-first")) + ((equal sort-by "name-desc") (setq arg "-Al --si --time-style long-iso -r")) + ((equal sort-by "date-desc") (setq arg "-Al --si --time-style long-iso -t -r")) + ((equal sort-by "size-desc") (setq arg "-Al --si --time-style long-iso -S -r")) + ((equal sort-by "dir-desc") (setq arg "-Al --si --time-style long-iso --group-directories-first -r")) + (t (error "logic error 09535" ))) + (dired-sort-other arg ))) + +;; **** dired git helpers + +;; [[http://blog.binchen.org/posts/the-most-efficient-way-to-git-add-file-in-dired-mode-emacsendiredgit.html][via bin chen]]: +;; make git commands available from dired buffer, which can be used in +;; those rare cases, where my wrappers below don't fit. +(defun diredext-exec-git-command-in-shell (command &optional arg file-list) + "Run a shell command git COMMAND ' on the marked files. if no +files marked, always operate on current line in dired-mode" + (interactive + (let ((files (dired-get-marked-files t current-prefix-arg))) + (list + ;; Want to give feedback whether this file or marked files are used: + (dired-read-shell-command "git command on %s: " current-prefix-arg files) + current-prefix-arg + files))) + (unless (string-match "[?][ \t]\'" command) + (setq command (concat command " *"))) + (setq command (concat "git " command)) + (dired-do-shell-command command arg file-list) + (message command)) + +;; some git commandline wrappers which directly work on git files, +;; called with "hydras". +(defun tvd-dired-git-add(&optional arg file-list) + "Add marked or current file to current repository (stash)." + (interactive + (let ((files (dired-get-marked-files t current-prefix-arg))) + (list current-prefix-arg files))) + (dired-do-shell-command "git add -v * " arg file-list) + (revert-buffer)) + +(defun tvd-dired-git-rm(&optional arg file-list) + "Remove marked or current file from current repository and filesystem." + (interactive + (let ((files (dired-get-marked-files t current-prefix-arg))) + (list current-prefix-arg files))) + (dired-do-shell-command "git rm -rf * " arg file-list) + (revert-buffer)) + +(defun tvd-dired-git-ungit(&optional arg file-list) + "Like `tvd-dired-git-rm' but keep the files in the filesystem (unstage)." + (interactive + (let ((files (dired-get-marked-files t current-prefix-arg))) + (list current-prefix-arg files))) + (dired-do-shell-command "git rm -rf --cached * " arg file-list) + (revert-buffer)) + +;; **** dired navigation + +;; I'm used to jump around with pos1+end +(defun tvd-dired-begin () + "Move point to the first directory in the listing .." + (interactive) + (goto-char (point-min)) + (dired-next-dirline 2)) + +(defun tvd-dired-end () + "Move point to the last file or directory in the listing." + (interactive) + (goto-char (point-max)) + (dired-previous-line 1)) + +;; **** dired buffer names + +;; This took me a long time to figure out, but I finally got it: I +;; really hate it how dired names its buffers, it just uses the +;; basename part of the current working directory as buffer name. So +;; when there are a couple of dozen buffers open and one of them is +;; named "tmp" I just can't see it. So what I do here is to rename +;; each dired buffer right after its creation by advising +;; `dired-internal-noselect'. My dired buffers have such names now: +;; *dired: ~/tmp*. I can find them easily, and I can reach all dired +;; buffers very fast thanks to the *dired prefix. And they are now +;; clearly marked as non-file buffers. In fact I consider this +;; behavior as a bug, but I doubt many people would agree :) + +(advice-add 'dired-internal-noselect + :filter-return + '(lambda (buffer) + "Modify dired buffer names to this pattern: *dired: full-path*" + (interactive) + (with-current-buffer buffer + (rename-buffer (format "*dired: %s*" default-directory))) + buffer)) + +;; **** dired config and key bindings + +;; and finally put everything together. + +(eval-after-load 'dired + '(progn + ;; dired vars + (setq dired-listing-switches "-lt") + + ;; stay with 1 dired buffer per instance + ;; when changing directories + (define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file) + (define-key dired-mode-map (kbd "") 'dired-find-alternate-file) + (define-key dired-mode-map (kbd "^") (lambda () (interactive) (find-alternate-file ".."))) + (define-key dired-mode-map (kbd "") (lambda () (interactive) (find-alternate-file ".."))) + + ;; Xah Lee'S custom sort's + (define-key dired-mode-map (kbd "s") 'xah-dired-sort) + + ;; my git "hydras" + (define-prefix-command 'tvd-dired-git-map) + (define-key dired-mode-map (kbd "g") 'tvd-dired-git-map) + (define-key tvd-dired-git-map (kbd "a") 'tvd-dired-git-add) + (define-key tvd-dired-git-map (kbd "d") 'tvd-dired-git-rm) + (define-key tvd-dired-git-map (kbd "u") 'tvd-dired-git-ungit) + + ;; edit filenames + (defalias 'edit-dired 'wdired-change-to-wdired-mode) + (define-key dired-mode-map (kbd "C-c C-c") 'wdired-change-to-wdired-mode) + + ;; dired-hacks filters + (define-key dired-mode-map (kbd "f") dired-filter-map) + (define-key dired-mode-map (kbd "q") 'tvd-dired-quit-or-filter-pop) + (define-key dired-mode-map (kbd "Q") 'dired-filter-pop-all) + + ;; ranger, multi file copy/move + (define-prefix-command 'tvd-dired-ranger-map) + (define-key dired-mode-map (kbd "r") 'tvd-dired-ranger-map) + (define-key tvd-dired-ranger-map (kbd "c") 'dired-ranger-copy) + (define-key tvd-dired-ranger-map (kbd "p") 'dired-ranger-paste) + (define-key tvd-dired-ranger-map (kbd "m") 'dired-ranger-move) + + ;; navigation, use TAB and C-TAB to move + ;; point to next or prev dir like in info + ;; mode, and HOME+END to reach the end or + ;; beginning of the listing. + (define-key dired-mode-map (kbd "") 'dired-next-dirline) + (define-key dired-mode-map (kbd "") 'dired-prev-dirline) + (define-key dired-mode-map (kbd "") 'tvd-dired-begin) + (define-key dired-mode-map (kbd "") 'tvd-dired-end) + + ;; overwrite some defaults I do not use anyway + (define-key dired-mode-map (kbd "n") 'dired-create-directory) + )) +;; **** Dired Hydra + +;; FIXME: not yet customized to fit my own config +(when (fboundp 'defhydra) + (defhydra hydra-dired (:hint nil :color pink) + " +_+_ mkdir _v_iew _m_ark _(_ details _i_nsert-subdir _W_dired (EDIT FILENAMES) +_C_opy _O_ view other _U_nmark all _)_ omit-mode _$_ hide-subdir C-c C-c : edit +_D_elete _o_pen other _u_nmark _l_ redisplay _w_ kill-subdir +_R_ename _M_ chmod _t_oggle _g_ revert buf _e_ ediff _q_uit +_Y_ rel symlink _G_ chgrp _E_xtension mark _s_ort _=_ pdiff +_S_ymlink ^ ^ _F_ind marked _._ toggle hydra \\ flyspell +_r_sync ^ ^ ^ ^ ^ ^ _?_ summary +_z_ compress-file _A_ find regexp / Filter +_Z_ compress _Q_ repl regexp + +T - tag prefix + +" + ("\\" dired-do-ispell nil) + ("(" dired-hide-details-mode nil) + (")" dired-omit-mode nil) + ("+" dired-create-directory nil) + ("=" diredp-ediff nil) ;; smart diff + ("?" dired-summary nil) + ("$" diredp-hide-subdir-nomove nil) + ("A" dired-do-find-regexp nil) + ("C" dired-do-copy nil) ;; Copy all marked files + ("D" dired-do-delete nil) + ("E" dired-mark-extension nil) + ("e" dired-ediff-files nil) + ("F" dired-do-find-marked-files nil) + ("G" dired-do-chgrp nil) + ("g" revert-buffer nil) ;; read all directories again (refresh nil) + ("i" dired-maybe-insert-subdir nil) + ("l" dired-do-redisplay nil) ;; relist the marked or singel directory + ("M" dired-do-chmod nil) + ("m" dired-mark nil) + ("O" dired-display-file nil) + ("o" dired-find-file-other-window nil) + ("Q" dired-do-find-regexp-and-replace nil) + ("R" dired-do-rename nil) + ("r" dired-do-rsynch nil) + ("S" dired-do-symlink nil) + ("s" dired-sort-toggle-or-edit nil) + ("t" dired-toggle-marks nil) + ("U" dired-unmark-all-marks nil) + ("u" dired-unmark nil) + ("v" dired-view-file nil) ;; q to exit, s to search, = gets line # + ("w" dired-kill-subdir nil) + ("W" wdired-change-to-wdired-mode nil) + ("Y" dired-do-relsymlink nil) + ("z" diredp-compress-this-file nil) + ("Z" dired-do-compress nil) + ("q" nil nil) + ("." nil nil :color blue)) + + (define-key dired-mode-map "?" 'hydra-dired/body)) + + +(provide 'init-dired) +;;; init-dired.el ends here diff --git a/lisp/init-display.el b/lisp/init-display.el new file mode 100644 index 0000000..3a74e3a --- /dev/null +++ b/lisp/init-display.el @@ -0,0 +1,29 @@ +;;; Display settings + +;; better visibility of cursor in console sessions +(unless (display-graphic-p) + ;; instead of closing the terminal, just kill the buffer + (global-set-key (kbd "C-x c") 'kill-this-buffer) + (set-face-attribute 'fringe nil :inverse-video t) + (invert-face 'default) + (invert-face 'mode-line)) + +(use-package solarized-theme + :ensure t + :config + (load-theme 'solarized-dark-high-contrast t)) + +;;; ** increase default font size on startup +(set-face-attribute 'default nil :height 133) + +;; customize mode-line colors to have a little more contrast to the content +;; for reference, I used thes color codes as a base: +;; https://gist.github.com/ninrod/b0f86d77ebadaccf7d9d4431dd8e2983 +;; and tweaked them in gimp a little +(set-face-background 'mode-line "#02161B") ;; base02 darkened +(set-face-foreground 'mode-line-inactive "#4d4d4d") ;; just grey +(set-face-background 'mode-line-inactive "#02161B") + + +(provide 'init-display) +;;; init-display.el ends here diff --git a/lisp/init-dumpjump.el b/lisp/init-dumpjump.el new file mode 100644 index 0000000..a776d9f --- /dev/null +++ b/lisp/init-dumpjump.el @@ -0,0 +1,28 @@ +;; *** Dumpjump +;; https://github.com/jacktasia/dumb-jump/raw/master/dumb-jump.el +(use-package dumb-jump + :config + + (add-hook 'xref-backend-functions #'dumb-jump-xref-activate) + (setq xref-show-definitions-function #'xref-show-definitions-completing-read) + + (setq dumb-jump-quiet t) + + (when (fboundp 'defhydra) + (defhydra hydra-dumb-jump (:color blue :columns 3) + "Dumb Jump" + ("j" dumb-jump-go "Go") + ("o" dumb-jump-go-other-window "Other window") + ("e" dumb-jump-go-prefer-external "Go external") + ("x" dumb-jump-go-prefer-external-other-window "Go external other window") + ("i" dumb-jump-go-prompt "Prompt") + ("l" dumb-jump-quick-look "Quick look") + ("b" dumb-jump-back "Back")) + + (global-set-key (kbd "C-x j") 'hydra-dumb-jump/body)) + (global-set-key (kbd "C-c j") 'dumb-jump-go) + (global-set-key (kbd "C-c b") 'dumb-jump-back)) + + +(provide 'init-dumpjump) +;;; init-dumpjump.el ends here diff --git a/lisp/init-ediff.el b/lisp/init-ediff.el new file mode 100644 index 0000000..4de08d2 --- /dev/null +++ b/lisp/init-ediff.el @@ -0,0 +1,65 @@ +;; *** Ediff Config + +;; Force ediff to use 1 frame (the current) and not open a new frame +;; for control and help. Also changing the split orientation doesnt +;; open a new frame. + +(eval-after-load "ediff" + '(progn + (setq ediff-diff-options "-w" + ediff-split-window-function 'split-window-horizontally + ediff-window-setup-function 'ediff-setup-windows-plain) + + (add-hook 'ediff-startup-hook 'ediff-toggle-wide-display) + (add-hook 'ediff-cleanup-hook 'ediff-toggle-wide-display) + (add-hook 'ediff-suspend-hook 'ediff-toggle-wide-display) + + (add-hook 'ediff-mode-hook + (lambda () + (ediff-setup-keymap) + ;; merge left to right + (define-key ediff-mode-map ">" 'ediff-copy-A-to-B) + ;; merge right to left + (define-key ediff-mode-map "<" 'ediff-copy-B-to-A))) + + ;; restore window config on quit + (add-hook 'ediff-after-quit-hook-internal 'winner-undo) + )) + +;; from emacswiki: +;; Usage: emacs -diff file1 file2 +(defun command-line-diff (switch) + (let ((file1 (pop command-line-args-left)) + (file2 (pop command-line-args-left))) + (ediff file1 file2))) +(add-to-list 'command-switch-alist '("diff" . command-line-diff)) + + + +;; *** Smerge Config + +;; smerge-mode is being issued during editing of conflicts from magit, +;; however, I hate its default prefix, but don't have any practical +;; prefixes left AND am using it far too rare to deserve its own +;; prefix. So just a hydra will do. + +(when (fboundp 'defhydra) + (defhydra hydra-smerge (:color blue :timeout 30.0) + " +^Smerge Mode^ +^^------------------------------------------------------- +_n_ext conflict keep _u_pper m_e_rge conflicts in Ediff +_p_revious conflict keep _l_ower _q_uit" + + ("n" smerge-next nil :exit nil) + ("p" smerge-prev nil :exit nil) + ("u" smerge-keep-upper nil :exit nil) + ("l" smerge-keep-lower nil :exit nil) + ("e" smerge-ediff nil :color red) + ("q" nil nil :color red)) + + (defalias 'merge 'hydra-smerge/body)) + + +(provide 'init-ediff) +;;; init-ediff.el ends here diff --git a/lisp/init-elget.el b/lisp/init-elget.el new file mode 100644 index 0000000..1bcfc91 --- /dev/null +++ b/lisp/init-elget.el @@ -0,0 +1,102 @@ +;;; setup el-get for non-package modes + +;; This MUST be the first init file to be loaded. + +;; I use el-get to install non-(m)elpa packages from github or direct +;; download. That way I don't have to manually keep'em up to date. + +(add-to-list 'load-path (expand-file-name "el-get/el-get" tvd-config-dir)) + +(unless (require 'el-get nil 'noerror) + (package-refresh-contents) + (package-install 'el-get) + (require 'el-get)) + +(el-get-bundle viking-mode + :type github + :pkgname "tlinden/viking-mode" + :features viking-mode) + +(el-get-bundle autoscratch + :type github + :pkgname "tlinden/autoscratch" + :features autoscratch-mode) + +(el-get-bundle novel-mode + :type github + :pkgname "tlinden/novel-mode" + :features novel-mode) + +(el-get-bundle config-general-mode + :type github + :pkgname "tlinden/config-general-mode" + :features config-general-mode) + +(el-get-bundle mark-copy-yank-things-mode + :type github + :pkgname "tlinden/mark-copy-yank-things-mode" + :features mark-copy-yank-things-mode) + +(el-get-bundle followcursor-mode + :type github + :pkgname "tlinden/followcursor-mode" + :features followcursor-mode) + +(el-get-bundle novel-mode + :type github + :pkgname "tlinden/novel-mode" + :features novel-mode) + +(el-get-bundle cisco-mode + :type http-tar + :options ("xzf") + :url "https://www.daemon.de/idisk/Scripts/cisco-mode-0.2.tar.gz") + +(el-get-bundle pod-mode + :type http-tar + :options ("xzf") + :url "https://cpan.metacpan.org/authors/id/F/FL/FLORA/pod-mode-1.04.tar.gz") + +(el-get-bundle info+ + :type http + :features info+ + :url "https://www.emacswiki.org/emacs/download/info+.el") + +(el-get-bundle narrow-indirect + :type http + :features narrow-indirect + :url "https://www.emacswiki.org/emacs/download/narrow-indirect.el") + +(el-get-bundle rotate-text + :type http + :url "http://nschum.de/src/emacs/rotate-text/rotate-text.el") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; These packages doesn't install via melpa for unknown reasons +;; see https://github.com/melpa/melpa/issues/8480 +;; (el-get-bundle expand-region +;; :type github +;; :pkgname "magnars/expand-region.el" +;; :website "https://github.com/magnars/expand-region.el#readme") + +;; ;; same thing, dependes on the latter +;; (el-get-bundle change-inner +;; :type github +;; :pkgname "magnars/change-inner.el" +;; :website "https://github.com/magnars/change-inner.el#readme") + +;; (el-get-bundle iedit +;; :type github +;; :pkgname "victorhge/iedit" +;; :website "https://github.com/victorhge/iedit/") + +;; (el-get-bundle tablist +;; :type github +;; :pkgname "emacsorphanage/tablist" +;; :website "https://github.com/emacsorphanage/tablist") + +(el-get 'sync) + + +(provide 'init-elget) +;;; init-elget.el ends here diff --git a/lisp/init-elisp.el b/lisp/init-elisp.el new file mode 100644 index 0000000..8a09492 --- /dev/null +++ b/lisp/init-elisp.el @@ -0,0 +1,186 @@ +;; *** Emacs LISP interactive + +;; General configuration for all things elisp. + +;; By using C-x-e I can push region or buffer +;; of lisp code (i.e. inside *scratch*) into +;; REPL where it will be evaluated + +(defun tvd-get-code() + "helper: returns marked region or the whole buffer contents" + ;; FIXME: mv to string helpers? + (if mark-active + (let ( ;; save region and buffer + (partb (buffer-substring-no-properties (region-beginning) (region-end))) + (whole (buffer-substring-no-properties (point-min) (point-max))) + ) + (if (> (length partb) 0) + partb + whole + ) + ) + ;; no mark, also return everything + (buffer-substring-no-properties (point-min) (point-max)))) + +(defun tvd-send-region-to-repl () + "put region or buffer into elisp repl and eval" + (interactive) + (let ( ;; fetch region or buffer contents + (code (tvd-get-code))) + (progn + (if (not (get-buffer "*ielm*")) + ;; ielm not yet running, start it in split window but stay here + (progn + (split-window-horizontally) + (other-window 1) + (ielm) + (other-window 1))) + ;; finially, paste content into ielm and evaluate it + ;; still we stay where we are + (with-current-buffer "*ielm*" + (goto-char (point-max)) + (insert code) + (ielm-return))))) + +(defun tvd-elisp-eval() + "just eval region or buffer whatever feasible" + (interactive) + (progn + (if mark-active + (eval-region) + (eval-buffer)))) + +(defun ff () + "Jump to function definition at point." + (interactive) + (find-function-other-window (symbol-at-point))) + +(defun tvd-make-defun-links () + "experimental: make function calls clickable, on click, jump to definition of it" + (interactive) + (let ((beg 0) + (end 0) + (fun nil)) + (goto-char (point-min)) + (while (re-search-forward "(tvd[-a-z0-9]*" nil t) + (setq end (point)) + (re-search-backward "(" nil t) + (forward-char 1) + (setq beg (point)) + (setq fun (buffer-substring-no-properties beg end)) + (make-button beg end 'action + (lambda (x) + (find-function-other-window (symbol-at-point)))) + (goto-char end)))) + +(defun emacs-change-version (v) + "Change version of .emacs (must be the current buffer). +Returns t if version changed, nil otherwise." + (interactive + (list + (completing-read "New config version (press TAB for old): " + (list tvd-emacs-version)))) + (if (equal v tvd-emacs-version) + nil + (save-excursion + (show-all) + (beginning-of-buffer) + (tvd-replace-all (format "\"%s\"" tvd-emacs-version) + (format "\"%s\"" v)) + (setq tvd-emacs-version v) + (message (format "New config version set: %s" v)) + t))) + +(defun emacs-change-log (entry) + "Add a changelog entry to .emacs Changelog" + (interactive "Menter change log entry: ") + (let ((newversion (call-interactively 'emacs-change-version))) + (save-excursion + (show-all) + (beginning-of-buffer) + (re-search-forward ";; .. Changelog") + (next-line) + ;; (tvd-outshine-end-of-section) + (when newversion + (insert (format "\n;; %s\n" tvd-emacs-version))) + (insert (format ";; - %s\n" entry))))) + +;; elisp config +(add-hook 'emacs-lisp-mode-hook + (lambda() + ;; non-separated x-e == eval hidden, aka current buffer + (local-set-key (kbd "C-x C-e") 'tvd-elisp-eval) + ;; separate 'e' == separate buffer + (local-set-key (kbd "C-x e") 'tvd-send-region-to-repl) + (setq mode-name "EL" + show-trailing-whitespace t) + (eldoc-mode t) + + ;; enable outline + (outline-minor-mode) + + (electric-indent-local-mode t))) + +;; use UP arrow for history in *ielm* as well, just as C-up +(add-hook 'comint-mode-hook + (lambda() + (define-key comint-mode-map [up] 'comint-previous-input))) + +;; sometimes I use lisp in minibuffer +(defun ee() + (interactive) + (electric-pair-mode) + (call-interactively 'eval-expression) + (electric-pair-mode)) + +;; sometimes I eval regions +(defalias 'er 'eval-region) + +;; ... or defuns +(defalias 'ef 'eval-defun) + +;; I like to have some functions fontified differently +(font-lock-add-keywords + 'emacs-lisp-mode + '(("(\\s-*\\(eq\\|if\\|cond\\|and\\|set\\|or\\|not\\|when\\|setq\\|let**\\|lambda\\|kbd\\|defun\\|car\\|cdr\\)\\s-+" + 1 'font-lock-keyword-face))) + +;; same applies for quoted symbols +(font-lock-add-keywords + 'emacs-lisp-mode + '(("'[-a-zA-Z_][-a-zA-Z0-9_]*\\>" 0 'font-lock-constant-face))) + +;; I hate it when help, debug, ielm and other peripheral buffers +;; litter my emacs window setup. So, this function fixes this: it +;; opens a new frame with all those buffers already opened and pinned. + +(defun dev () + "Open a new emacs frame with some development peripheral buffers." + (interactive) + (let ((F (make-frame))) + (with-selected-frame F + (with-current-buffer (get-buffer-create "*Help*") + (help-mode)) + (with-current-buffer (get-buffer-create "*ielm*") + (ielm)) + (with-current-buffer (get-buffer-create "*suggest*") + (suggest)) + (switch-to-buffer "*ielm*") + (split-window-horizontally) + (split-window-vertically) + (windmove-down) + (switch-to-buffer "*suggest*") + (tvd-suggest-reload) + (tvd-suggest-reload) + (windmove-right) + (switch-to-buffer "*Help*") + (split-window-vertically) + (windmove-down) + (switch-to-buffer "*scratch*") + (set-window-dedicated-p (selected-window) t) + (set-background-color "azure")))) + + + +(provide 'init-elisp) +;;; init-elisp.el ends here diff --git a/lisp/init-eshell.el b/lisp/init-eshell.el new file mode 100644 index 0000000..97f67bf --- /dev/null +++ b/lisp/init-eshell.el @@ -0,0 +1,214 @@ +;; *** eShell stuff, or if interactive stuff is needed, use ansi-term + +;; I am a hardcore bash user, but from time to time eshell is good +;; enough. It's great when used remote when only sftp is supported. + +(require 'eshell) + +;; fac'ifier +(defmacro with-face (str &rest properties) + `(propertize ,str 'face (list ,@properties))) + +;; custom prompt, which resembles my bash prompt +(defun shk-eshell-prompt () + (let ((header-bg "Azure")) + (concat + (with-face "\n") + (with-face (format-time-string + "[%Y-%m-%d %H:%M] --- [" + (current-time)) :background header-bg :foreground "Black") + (with-face (concat (eshell/pwd) "") :background header-bg :foreground "Blue") + (with-face "] --- " :background header-bg :foreground "Black") + (with-face (or + (ignore-errors (format "(%s)" (vc-responsible-backend default-directory))) + "") :background header-bg) + (with-face "\n" :background header-bg) + (with-face user-login-name :foreground "blue") + "@" + (with-face "localhost" :foreground "blue") + (if (= (user-uid) 0) + (with-face " #" :foreground "red") + " $") + " "))) + +(setq eshell-prompt-function 'shk-eshell-prompt) +(setq eshell-highlight-prompt nil) + +;; I use my own virtual loggin target /dev/log, just redirect +;; command output to /dev/log and it will be saved to +;; the *LOG* buffer. > inserts, >> appends +;; N.B: /dev/kill puts the stuff into the kill-ring. +(defun log-comment () + (with-current-buffer (get-buffer-create "*LOG*") + (insert (format "# %s\n" (time-stamp-string))))) + +(defun log-insert (string) + (with-current-buffer (get-buffer-create "*LOG*") + (delete-region (point-min) (point-max)) + (log-comment) + (insert string) + (message "wrote output to *LOG* buffer"))) + +(defun log-append (string) + (with-current-buffer (get-buffer-create "*LOG*") + (end-of-buffer) + (newline) + (log-comment) + (insert string) + (message "wrote output to *LOG* buffer"))) + +;; must return a defun which gets the stuff as ARG1 +;; 'mode is 'overwrite or 'append +(add-to-list 'eshell-virtual-targets '("/dev/log" (lambda (mode) + (if (eq mode 'overwrite) + 'log-insert + 'log-append)) + t + )) + +;; eshell config +(eval-after-load "eshell" + '(progn + (add-hook 'eshell-mode-hook + (lambda () + (local-unset-key (kbd "C-c C-r")) ; we're already using this for windresize + (add-to-list 'eshell-visual-commands "tail") + (add-to-list 'eshell-visual-commands "top") + (add-to-list 'eshell-visual-commands "vi") + (add-to-list 'eshell-visual-commands "ssh") + (add-to-list 'eshell-visual-commands "tail") + (add-to-list 'eshell-visual-commands "mutt") + (add-to-list 'eshell-visual-commands "note") + (setenv "TERM" "xterm") + (local-set-key (kbd "C-l") 'eshell/clear) + (define-key viking-mode-map (kbd "C-d") nil) ;; need to undef C-d first + (local-set-key (kbd "C-d") 'eshell/x) + (setq mode-name "ESH" + eshell-hist-ignoredups t + eshell-history-size 5000 + eshell-where-to-jump 'begin + eshell-review-quick-commands nil + eshell-smart-space-goes-to-end t + eshell-scroll-to-bottom-on-input 'all + eshell-error-if-no-glob t + eshell-save-history-on-exit t + eshell-prefer-lisp-functions t))))) + +;; exit and restore viking key binding afterwards +(defun eshell/x (&rest args) + (interactive) + (eshell-life-is-too-much) + (define-key viking-mode-map (kbd "C-d") 'viking-kill-thing-at-point)) + +;; open files in emacs, split the shell if not already splitted +;; open empty window if no file argument given. +(defun eshell/emacs (&rest args) + "Editor commands fired from eshell will be handled by emacs, which already runs anyway." + (interactive) + (let* ((framesize (frame-width)) + (winsize (window-body-width))) + (progn + (if (eq winsize framesize) + (split-window-horizontally)) + (other-window 1) + (if (null args) + (bury-buffer) + (mapc #'find-file (mapcar #'expand-file-name (eshell-flatten-list (reverse args)))))))) + +(defun eshell/clear () + "Better clear command than (recenter 0) which doesn't work as I +want. This version really removes the output of previous commands +and puts the shell to the beginning of a really (then) empty eshell +buffer. However, just to be sure that I do no accidentally clear +some shell output that might be useful in the future, it also +copies the cleared stuff into a backup buffer named +*eshell-log-buffer*, just in case." + (interactive) + (let ((beg (point-min)) + (end (point-max)) + (savebuffer "*eshell-log-buffer*") + (log (buffer-substring-no-properties (point-min) (point-max)))) + (progn + (if (not (get-buffer savebuffer)) + (get-buffer-create savebuffer)) + (with-current-buffer savebuffer + (goto-char (point-max)) + (insert log)) + (delete-region beg end) + (eshell-emit-prompt)))) + +(defun eshell/perldoc (&rest args) + "Like `eshell/man', but invoke `perldoc'." + (funcall 'eshell/perldoc (apply 'eshell-flatten-and-stringify args))) + +(defun eshell/perldoc (man-args) + (interactive "sPerldoc: ") + (require 'man) + (let ((manual-program "perldoc")) + (man man-args))) + +(defun eshell-here () + "Opens up a new shell in the directory associated with the +current buffer's file. The eshell is renamed to match that +directory to make multiple eshell windows easier." + (interactive) + (let* ((parent (if (buffer-file-name) + (file-name-directory (buffer-file-name)) + default-directory)) + (height (/ (window-total-height) 3)) + (name (car (last (split-string parent "/" t))))) + (split-window-vertically (- height)) + (other-window 1) + (eshell "new") + (rename-buffer (concat "*eshell: " name "*")) + (insert (concat "ls")) + (eshell-send-input))) + +;; via howardism +(defun eshell-there (host) + "Opens a shell on a remote host using tramp." + (interactive "sHost: ") + (let ((default-directory (format "/%s:" host))) + (eshell host))) + +(defalias 'es 'eshell-here) +(defalias 'et 'eshell-there) +(defalias 'eshell/vi 'eshell/emacs) + +;; plan9 smart command, edit while exec if not silent or successful +(require 'em-smart) + +;; eshell shell aliases. I set the global +;; defvar here so there's no need to transport +;; ~/.emacs.d/eshell/aliases across networks +(setq eshell-command-aliases-list ()) + +(defun +alias (al cmd) + "handy wrapper function to convert alias symbols +to alias strings to avoid writing 4 quotes per alias. +AL is a single-word symbol naming the alias, CMD is +a list symbol describing the command." + (add-to-list 'eshell-command-aliases-list + (list (symbol-name al) + (mapconcat 'symbol-name cmd " ")))) + +;; actual aliases +(+alias 'l '(ls -laF $*)) +(+alias 'll '(ls -l $*)) +(+alias 'la '(ls -a $*)) +(+alias 'lt '(ls -ltr $*)) +(+alias '.. '(cd ..)) +(+alias '... '(cd ../..)) +(+alias '.... '(cd ../../..)) +(+alias '..... '(cd ../../../..)) +(+alias 'md '(mkdir -p $*)) +(+alias 'emacs '(find-file $1)) +(+alias 'less '(find-file-read-only $1)) +(+alias 'x '(eshell/exit)) + +;; no need for less or more, this is emacs, isn't it? +(setenv "PAGER" "cat") + + +(provide 'init-eshell) +;;; init-eshell.el ends here diff --git a/lisp/init-ewww.el b/lisp/init-ewww.el new file mode 100644 index 0000000..b6305fa --- /dev/null +++ b/lisp/init-ewww.el @@ -0,0 +1,40 @@ +;; *** EWW browser stuff + +;; Emacs has a builtin browser, which is not too bad. + +(require 'eww) + +;; see also: shr-render-[buffer|region] ! +(defun eww-render-current-buffer () + "Render HTML in the current buffer with EWW" + (interactive) + (beginning-of-buffer) + (eww-display-html 'utf8 (buffer-name))) + +(defalias 'render-html 'eww-render-current-buffer) + +;; eww config +(add-hook 'eww-mode-hook #'toggle-word-wrap) +(add-hook 'eww-mode-hook #'visual-line-mode) + +;; custom short commands, see ? for the defaults +(define-key eww-mode-map "o" 'eww) ; aka other page +(define-key eww-mode-map "f" 'eww-browse-with-external-browser) ; aka firefox +(define-key eww-mode-map "j" 'next-line) +(define-key eww-mode-map "r" 'eww-reload) +(define-key eww-mode-map "s" 'shr-save-contents) +(define-key eww-mode-map "v" 'eww-view-source) +(define-key eww-mode-map "b" 'eww-add-bookmark) +(define-key eww-mode-map "p" 'eww-back-url) +(define-key eww-mode-map "n" 'eww-forward-url) + +;; link short commands +(define-key eww-link-keymap "c" 'shr-copy-url) +(define-key eww-link-keymap "s" 'shr-save-contents) + +;; FIXME: latest GIT version of eww contains 'eww-readable, which +;; hides menus and distractions! Update emacs. + + +(provide 'init-ewww) +;;; init-ewww.el ends here diff --git a/lisp/init-followcursor.el b/lisp/init-followcursor.el new file mode 100644 index 0000000..ce2809b --- /dev/null +++ b/lisp/init-followcursor.el @@ -0,0 +1,21 @@ +;; *** Followcursor Mode + +;; [[https://github.com/TLINDEN/followcursor-mode][source on github]] + +;; From time to time I need to refactor configs and the like based on +;; lists. For example in the left window I have a list of bgp peers +;; and in the right window a config file for all peers which I have to +;; modify based on current settings. With followcursor-mode I can put +;; point on an ip address and the line in the config containing this +;; ip address will be highlighted. If I move on to the next address +;; the next line on the right will be highlighted. + +;; The mode is a work-in-progress... + +(use-package followcursor-mode + :ensure nil) + + + +(provide 'init-followcursor) +;;; init-followcursor.el ends here diff --git a/lisp/init-functions.el b/lisp/init-functions.el new file mode 100644 index 0000000..dbef181 --- /dev/null +++ b/lisp/init-functions.el @@ -0,0 +1,624 @@ +;; * Productivity Functions +;; -------------------------------------------------------------------------------- +;; ** goto line with tmp line numbers + +;; I stole this somewhere, as far as I remember, emacswiki, however, I +;; always had F7 for goto-line + +(defun goto-line-with-feedback () + "Show line numbers temporarily, while prompting for the line number input" + (interactive) + (unwind-protect + (progn + (linum-mode 1) + (call-interactively 'goto-line)) + (linum-mode -1))) + +(global-set-key (kbd "") 'goto-line-with-feedback) + +;; -------------------------------------------------------------------------------- +;; ** invert fore- and background + +;; Sometimes when sitting in a very dark enviroment, my usual light +;; emacs frame is a too stark contrast to the environment. With this +;; function I can just invert my current color settings to dark +;; background and light foreground. + +;; remember last inverse +(defvar tvd-invert-state t) + +;; invert everything, reverse it when called again +(defun tvd-invert() + "invert fg-bg" + (interactive) + (invert-face 'default) + (invert-face 'mode-line) + (set-face-attribute 'fringe nil :inverse-video tvd-invert-state) + (setq tvd-invert-state (not tvd-invert-state)) ;; cycle variable tvd-invert-state + ) + +;; fast +(global-set-key (kbd "C-c C-i") 'tvd-invert) + +;; -------------------------------------------------------------------------------- +;; ** Some useful bindings for Home and End keys Hit the key once to + +;; Go to the beginning/end of a line, hit it twice in a row to go to +;; the beginning/end of the window, three times in a row goes to the +;; beginning/end of the buffer. NB that there is no timeout involved. + +;; Uses a function of viking-mode to establish key repeats, see below. + +(defun pc-keys-home () + "Go to beginning of line/window/buffer. First hitting key goes +to beginning of line, second in a row goes to beginning of +window, third in a row goes to beginning of buffer." + (interactive) + (let* ((key-times (viking-last-key-repeats))) + (cond + ((eq key-times 3) + (if mark-active + (goto-char (point-min)) + (beginning-of-buffer))) + ((eq key-times 2) + (if mark-active () (push-mark)) + (move-to-window-line 0)) + ((eq key-times 1) + (beginning-of-line))))) + +(defun pc-keys-end () + "Go to end of line/window/buffer. First hitting key goes +to end of line, second in a row goes to end of +window, third in a row goes to end of buffer." + (interactive) + (let* ((key-times (viking-last-key-repeats))) + (cond + ((eq key-times 3) + (if mark-active + (goto-char (point-max)) + (end-of-buffer))) + ((eq key-times 2) + (if mark-active () (push-mark)) + (move-to-window-line -1) + (end-of-line)) + ((eq key-times 1) + (end-of-line))))) + +;; This is the most natural use for those keys +(global-set-key (kbd "") 'pc-keys-home) +(global-set-key (kbd "") 'pc-keys-end) + + + +;; -------------------------------------------------------------------------------- +;; ** percent function +;; by Jens Heunemann: jump to percent position into current buffer + +(defun goto-percent (p) ;goto Prozentwert (0-100): F8 + (interactive "nProzent: ") + (if (> (point-max) 80000) + (goto-char (* (/ (point-max) 100) p)) ;Ueberlauf vermeiden: (max/100)*p + (goto-char (/ (* p (point-max)) 100))) ;Rundungsfehler verm.: (max*p)/100 + (beginning-of-line)) + +(global-set-key (kbd "") 'goto-percent) ;F8 goto percent + +;; -------------------------------------------------------------------------------- +;; ** Simulate vi's % (percent) function + +;; There's not a lot about vi[m] I like, but jumping with % to a +;; matching paren is one of THOSE features, I also need in emacs. + +;; with ideas from [[https://www.emacswiki.org/emacs/NavigatingParentheses#toc2][emacswiki]] + +;; If (point) is on a paren, jump to the matching paren, otherwise, +;; just insert a literal ?%. Only make sense if bound to %. +;; Does not jump in inside () though +(defun jump-paren-match-or-insert-percent (arg) +"Go to the matching parenthesis if on parenthesis. Otherwise +insert %. Mimics vi stle of % jumping to matching brace." +(interactive "p") +(cond ((looking-at "\\s\(\\|\{\\|\\[") (forward-list 1) (backward-char 1)) + ((and (looking-at "\\s\)\\|\}\\|\\]") + (not (looking-back "\\s\(\\|\{\\|\\["))) + (forward-char 1) (backward-list 1)) + (t (insert "%")))) + +;; only useful in programming modes +(define-key prog-mode-map (kbd "%") 'jump-paren-match-or-insert-percent) + +;; -------------------------------------------------------------------------------- +;; ** Move region + +;; Mark a region, then use M-up|down to move it around +;; via [[https://www.emacswiki.org/emacs/MoveRegion][emacswiki]] +;; code from [[https://github.com/targzeta/move-lines/blob/master/move-lines.el][move-lines]] + +(defun move-lines--internal (n) + (let* ((start (point)) ;; The position of beginning of line of the first line + (end start) ;; The position of eol+\n of the end line + col-init ;; The current column for the first line + (col-end (current-column)) ;; The current column for the end line + exchange_pm ;; If I had exchanged point and mark + delete-latest-newline) ;; If I had inserted a newline at the end + + ;; STEP 1: Identifying the line(s) to cut. + ;; --- + ;; If region is actives, I ensure that point always is at the end of the + ;; region and mark at the beginning. + (when (region-active-p) + (when (< (point) (mark)) + (setq exchange_pm t) + (exchange-point-and-mark)) + (setq start (mark) + end (point) + col-end (current-column))) + + (goto-char start) (setq col-init (current-column)) + (beginning-of-line) (setq start (point)) + + (goto-char end) (end-of-line) + ;; If point == point-max, this buffers doesn't have the trailing newline. + ;; In this case I have to insert a newline otherwise the following + ;; `forward-char' (to keep the "\n") will fail. + (when (= (point) (point-max)) + (setq delete-latest-newline t) + (insert-char ?\n) (forward-char -1)) + (forward-char 1) (setq end (point)) + + ;; STEP 2: Moving the lines. + ;; --- + ;; The region I'm cutting span from the beginning of line of the current + ;; line (or current region) to the end of line + 1 (newline) of the current + ;; line (or current region). + (let ((line-text (delete-and-extract-region start end))) + (forward-line n) + ;; If the current-column != 0, I have moved the region at the bottom of a + ;; buffer doesn't have the trailing newline. + (when (not (= (current-column) 0)) + (insert-char ?\n) + (setq delete-latest-newline t)) + (setq start (+ (point) col-init)) ;; Now, start is the start of new region + (insert line-text)) + + ;; STEP 3: Restoring + ;; --- + ;; I'm at the end of new region (or line) and start has setted at the + ;; beginning of new region (if a region is active). + ;; Restoring the end column. + (forward-line -1) + (forward-char col-end) + + (when delete-latest-newline + (save-excursion + (goto-char (point-max)) + (delete-char -1))) + + (when (region-active-p) + (setq deactivate-mark nil) + (set-mark start) + (if exchange_pm + (exchange-point-and-mark))))) + +(defun move-lines-up (n) + "Moves the current line or, if region is actives, the lines surrounding +region, up by N lines, or 1 line if N is nil." + (interactive "p") + (if (eq n nil) + (setq n 1)) + (move-lines--internal (- n))) + +(defun move-lines-down (n) + "Moves the current line or, if region is actives, the lines surrounding +region, down by N lines, or 1 line if N is nil." + (interactive "p") + (if (eq n nil) + (setq n 1)) + (move-lines--internal n)) + +(global-set-key (kbd "M-") 'move-lines-up) +(global-set-key (kbd "M-") 'move-lines-down) + +;; -------------------------------------------------------------------------------- +;; ** comment-uncomment region with one key binding +;; via [[http://stackoverflow.com/a/9697222/3350881][stackoverflow]] +(defun comment-or-uncomment-region-or-line () + "Comments or uncomments the region or the current line if there's no active region." + (interactive) + (let (beg end) + (if (region-active-p) + (setq beg (region-beginning) end (region-end)) + (setq beg (line-beginning-position) end (line-end-position))) + (comment-or-uncomment-region beg end))) + +(global-set-key (kbd "C-c C-c") 'comment-or-uncomment-region-or-line) + +;; -------------------------------------------------------------------------------- +;; ** search for symbol at point + +;; Simulate the # function of vi, marks the symbol at point, C-s then +;; searches for it. I use this a lot. + +;; via [[http://ergoemacs.org/emacs/modernization_isearch.html][ergomacs]] + +(defun xah-search-current-word () + "Call `isearch' on current word or text selection. + 'word' here is A to Z, a to z, and hyphen and underline, independent of syntax table. +URL `[[http://ergoemacs.org/emacs/modernization_isearch.html'][ergomacs]] +Version 2015-04-09" + (interactive) + (let ( xahp1 xahp2 ) + (if (use-region-p) + (progn + (setq xahp1 (region-beginning)) + (setq xahp2 (region-end))) + (save-excursion + (skip-chars-backward "-_A-Za-z0-9") + (setq xahp1 (point)) + (right-char) + (skip-chars-forward "-_A-Za-z0-9") + (setq xahp2 (point)))) + (setq mark-active nil) + (when (< xahp1 (point)) + (goto-char xahp1)) + (isearch-mode t) + (isearch-yank-string (buffer-substring-no-properties xahp1 xahp2)) + (message "Now use C-s to search for it ...") + )) + +(global-set-key (kbd "C-#") 'xah-search-current-word) + +;; -------------------------------------------------------------------------------- +;; ** Window Margin + +;; Kinda screen reader for the poor. I use this sometimes with info +;; or woman mode. I also use a full featured screen reader: nove-mode, +;; see below. + +;; left+right margin on demand (but nothing else) +(defun margin(m) + "set left and right margins for better readability" + (interactive "nEnter Margin (0 to disable) [0-9]+: ") + (set-window-margins (car (get-buffer-window-list (current-buffer) nil t)) m m) ;; set immediately + (setq left-margin-width m) ;; persist until reset + (setq right-margin-width m) + (message "To reset, change Buffer or call again with arg 0.") + ) + +;; -------------------------------------------------------------------------------- +;; ** Fill and justify a paragraph + +;; this is just a shortcut for: +;; C-u 70 M-x fill-paragraph +;; but C-q is just easier to remember + +;; however, if pressed again it un-fills the paragraph, +;; idea via: [[http://endlessparentheses.com/fill-and-unfill-paragraphs-with-a-single-key.html][endlessparentheses]] +(defun tvd-fill-and-justify-or-unfill() + (interactive) + (let ((fill-column + (if (eq last-command 'tvd-fill-and-justify-or-unfill) + (progn (setq this-command nil) + (point-max)) + fill-column))) + (fill-paragraph 70))) + +(global-set-key (kbd "C-q") 'tvd-fill-and-justify-or-unfill) ; like M-q, which is bound to x-window-quit in xmonad: fill+justify + +;; -------------------------------------------------------------------------------- +;; ** Make a read-only copy of the current buffer + +;; I just create a new read-only buffer and copy the contents of the +;; current one into it, which can be used as backup. I use this in +;; cases where I need to re-factor a file and do lots of changes. With +;; the buffer copy I have a reference to compare without the need to +;; leave emacs and look at revision control diffs or the like, and if +;; a file is not maintained via VC anyway. + +(defvar copy-counter 0) + +(defun get-copy-buffer-name() + "return unique copy buffer name" + (let ((name (concat "*COPY " (buffer-name (current-buffer)) " (RO)"))) + (if (not (get-buffer name)) + (progn + (setq copy-counter (1+ copy-counter)) + (concat name "<" (number-to-string copy-counter) ">")) + (concat name)))) + +(defun copy-buffer-read-only() + "Create a read-only copy of the current buffer" + (interactive) + (let ((old-buffer (current-buffer)) + (new-buffer-name (get-copy-buffer-name))) + (progn + (delete-other-windows) + (split-window-horizontally) + (other-window 1) + (if (not (eq (get-buffer new-buffer-name) nil)) + (kill-buffer (get-buffer new-buffer-name))) + (set-buffer (get-buffer-create new-buffer-name)) + (insert-buffer-substring old-buffer) + (read-only-mode) + (switch-to-buffer new-buffer-name) + (other-window 1)))) + +(defalias 'cp 'copy-buffer-read-only) + +(global-set-key (kbd "C-c C-p") 'copy-buffer-read-only) ; make read-only buffer copy + +;; -------------------------------------------------------------------------------- +;; ** Cleanup, close all windows and kill all buffers + +;; From time to time I get annoyed by the many dozen buffers +;; opened. In such cases I like to close them all at once. + +;; No key binding though, just in case I stumble upon it and kill my +;; setup accidentally. + +(defun kill-all-buffers () + "Kill all buffers, clean up, close all windows" + (interactive) + (when (y-or-n-p "Close all windows and kill all buffers?") + (delete-other-windows) + (clean-buffer-list) + (dolist (buffer (buffer-list)) + (kill-buffer buffer)) + (delete-minibuffer-contents) + (if (fboundp 'tramp-cleanup-all-connections) + (tramp-cleanup-all-connections)) + (with-current-buffer (get-buffer-create "*text*") + (text-mode)) + (autoscratch-buffer))) + +;; -------------------------------------------------------------------------------- +;; ** Cleanup current buffer + +;; Remove TABs, leading and trailing spaces, re-indent a buffer. + +;; via [[http://whattheemacsd.com/buffer-defuns.el-01.html][whattheemacs.d]] + +(defun cleanup-buffer () + "Perform a bunch of safe operations on the whitespace content of a buffer. +Does not indent buffer, because it is used for a before-save-hook, and that +might be bad." + (interactive) + (untabify (point-min) (point-max)) + (delete-trailing-whitespace) + (save-excursion + (replace-regexp "^\n\\{3,\\}" "\n\n" nil (point-min) (point-max))) + (set-buffer-file-coding-system 'utf-8) + (indent-region (point-min) (point-max))) + +(defalias 'cb 'cleanup-buffer) + +;; related, I use this to cleanup directories and rename files and +;; directories to my liking. Sometimes I get a disk or stick from +;; Windows users and they use every character available on their +;; keyboards to name files and dirs. I can't have this shit. +;; +(defun cleanup-dired-buffer () + "If inside a wdired (edit) buffer, rename everything" + (interactive) + (delete-trailing-whitespace) + (save-excursion + (replace-regexp "[\(\)'`#,_&\!]" "" nil (point-min) (point-max)) + (replace-string " " "-" nil (point-min) (point-max)) + (replace-regexp "--*" "-" nil (point-min) (point-max)) + (replace-string "ä" "ae" nil (point-min) (point-max)) + (replace-string "ö" "oe" nil (point-min) (point-max)) + (replace-string "ü" "ue" nil (point-min) (point-max)) + (replace-string "Ä" "Ae" nil (point-min) (point-max)) + (replace-string "Ö" "Oe" nil (point-min) (point-max)) + (replace-string "Ü" "Ue" nil (point-min) (point-max)) + (replace-string "ß" "ss" nil (point-min) (point-max)) + (replace-string ".." "." nil (point-min) (point-max)))) + +(defun cleanup-dir() + "Cleanup wdired buffer in one whole step, used for emacsclient buffers" + (interactive) + (wdired-change-to-wdired-mode) + (cleanup-dired-buffer) + (wdired-finish-edit) + (kill-this-buffer)) + + +;; -------------------------------------------------------------------------------- +;; ** Remove Umlauts and other crab in current buffer + +;; converts: +;; Stan Lem - ein schönes Leben & sonst nix(ungekuerzte Ausgabe) +;; to: +;; Stan_Lem-ein_schoenes_Leben_sonst_nix_ungekuerzte_Ausgabe +;; +;; used in dired buffers to cleanup filenames by german windows users. +(defun umlaute-weg() + (interactive) + (let ((umlaute '((Ü . Ue) + (Ä . Ae) + (Ö . Oe) + (ü . ue) + (ä . ae) + (ö . oe) + (ß . ss))) + (regs (list + '(" " . "_") + '("_-_" . "-") + '("[\(\)&]" . "_") + '("__*" . "_") + '("_$" . "") + ))) + (save-excursion + (dolist (pair umlaute) + (replace-regexp (symbol-name (car pair)) + (symbol-name (cdr pair)) + nil + (point-min) (point-max))) + (dolist (reg regs) + (replace-regexp (car reg) (cdr reg) nil + (point-min) (point-max)))))) + +;; -------------------------------------------------------------------------------- +;; ** Better newline(s) + +;; Add newline and jump to indent from wherever I am in the current +;; line, that is it is not required to be on the end of line. + +;; via [[http://whattheemacsd.com/editing-defuns.el-01.html][whattheemacs.d]] + +(defun open-line-below () + (interactive) + (end-of-line) + (newline) + (indent-for-tab-command)) + +(defun open-line-above () + (interactive) + (end-of-line) + (beginning-of-line) + (newline) + (forward-line -1) + (indent-for-tab-command)) + +;; disabled, interferes with modes. + +;; (global-set-key (kbd "") 'open-line-below) + +;; (global-set-key (kbd "") 'open-line-above) + +;; -------------------------------------------------------------------------------- +;; ** Mouse Rectangle + +;; There's not much use for the mouse in emacs, but this gimick is +;; funny and works like a charm. + +;; via [[http://emacs.stackexchange.com/a/7261][stackoverflow]] +(defun mouse-start-rectangle (start-event) + (interactive "e") + (deactivate-mark) + (mouse-set-point start-event) + (rectangle-mark-mode +1) + (let ((drag-event)) + (track-mouse + (while (progn + (setq drag-event (read-event)) + (mouse-movement-p drag-event)) + (mouse-set-point drag-event))))) + +(global-set-key (kbd "S-") 'mouse-start-rectangle) + +;; -------------------------------------------------------------------------------- +;; ** DOS <=> UNIX conversion helpers + +(defun dos2unix () + (interactive) + (set-buffer-file-coding-system 'utf-8-unix) + (message (format "converted current buffer to %s" buffer-file-coding-system))) + +(defun unix2dos () + (interactive) + (set-buffer-file-coding-system 'utf-8-dos) + (message (format "converted current buffer to %s" buffer-file-coding-system))) +;; -------------------------------------------------------------------------------- +;; ** helper do add the same thing to multiple mode hooks +;; via [[http://stackoverflow.com/posts/3900056/revisions][stackoverflow]] +;; usage samples below. +(defun add-something-to-mode-hooks (mode-list something) + "helper function to add a callback to multiple hooks" + (dolist (mode mode-list) + (add-hook (intern (concat (symbol-name mode) "-mode-hook")) something))) + +;; -------------------------------------------------------------------------------- + +;; ** helper to catch load errors + +;; Try to eval 'fn, catch errors, if any but make it possible for +;; emacs to continue undisturbed, used with SMEX, see below. +(defmacro safe-wrap (fn &rest clean-up) + `(unwind-protect + (let (retval) + (condition-case ex + (setq retval (progn ,fn)) + ('error + (message (format "Caught exception: [%s]" ex)) + (setq retval (cons 'exception (list ex))))) + retval) + ,@clean-up)) +;; -------------------------------------------------------------------------------- +;; ** Alignment Wrappers + +;; align-regexp is already a very usefull tool, however, sometimes I +;; want to repeat the alignment and I hate C-u, so here are some +;; wrappers to make this easier. + +(defun align-repeat (regex &optional alignment) + "Aply REGEX to all columns not just the first. Align by +ALIGNMENT which must be 'left or 'right. The default is 'left. + +Right alignment: + +col1 ,col2 +col1 ,col2 + +Left alignment: + +col1, col2 +col1, col2" + (interactive "MRepeat Align Regex [ ]: ") + (let ((spc " ") + (beg (point-min)) + (end (point-max)) + (areg "%s\\(\\s-*\\)" ; default left aligned + )) + (when (string= regex "") + (setq regex spc)) + (when (region-active-p) + (setq beg (region-beginning)) + (setq end (region-end))) + (when (eq alignment 'right) + (setq areg "\\(\\s-*\\)%s")) + (align-regexp beg end (format areg regex) 1 1 t))) + +(defun align-repeat-left (regex) + (interactive "MRepeat Left Align Regex [ ]: ") + (align-regexp-repeat regex 'left)) + +(defun align-repeat-right (regex) + (interactive "MRepeat Left Align Regex [ ]: ") + (align-regexp-repeat regex 'right)) + + +;; ** String Helpers + +;; Some helper functions I use here and there. + +(defun tvd-alist-keys (A) + "return a list of keys of alist A" + (let ((K ())) + (dolist (e A) + (push (car e) K) + ) + K)) + +(defun tvd-get-line () + "return current line in current buffer" + (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + +(defun tvd-starts-with (s begins) + "Return non-nil if string S starts with BEGINS." + (cond ((>= (length s) (length begins)) + (string-equal (substring s 0 (length begins)) begins)) + (t nil))) + +(defun tvd-replace-all (regex replace) + "Replace all matches of REGEX with REPLACE in current buffer." + (interactive) + (goto-char (point-min)) + (while (re-search-forward regex nil t) + (replace-match replace))) + + +(provide 'init-functions) +;;; init-functions.el ends here diff --git a/lisp/init-globalbindings.el b/lisp/init-globalbindings.el new file mode 100644 index 0000000..031af81 --- /dev/null +++ b/lisp/init-globalbindings.el @@ -0,0 +1,97 @@ +;;; * Global Key Bindings +;; -------------------------------------------------------------------------------- +;; ** c-h != delete +(keyboard-translate ?\C-h ?\C-?) +(keyboard-translate ?\C-? ?\C-h) + +;; -------------------------------------------------------------------------------- +;; ** general keys (re-)mappings +;(global-set-key (kbd "C-s") 'isearch-forward-regexp) +;(global-set-key (kbd "C-r") 'isearch-backward-regexp) +(global-set-key (kbd "M-C-s") 'isearch-forward) +(global-set-key (kbd "M-C-r") 'isearch-backward) +(global-set-key (kbd "M-%") 'query-replace-regexp) +(global-set-key (kbd "") 'dabbrev-completion) ; shift-tab, inline completion + +(global-set-key (kbd "") 'html-mode) +(global-set-key (kbd "") 'delete-char) ; Entf Char loeschen +(global-set-key (kbd "") 'backward-delete-char) ; Shift+Backspace dito +(global-set-key (kbd "S-") 'kill-word) ; Shift+Entf Wort loeschen +(global-set-key (kbd "S-") 'backward-kill-word) ; Shift+Backspace dito +(global-set-key (kbd "C-") 'kill-word) ; Shift+Entf dito +(global-set-key (kbd "C-") 'backward-kill-word) ; Shift+Backspace dito +(global-unset-key (kbd "C-z")) +(global-set-key (kbd "C-x k") 'kill-this-buffer) ; C-x k really kill current buffer w/o asking +(global-set-key (kbd "C-x C-b") 'buffer-menu) + + + +;; -------------------------------------------------------------------------------- +;; ** display a list of my own global key bindings and aliases +;; via [[https://www.emacswiki.org/emacs/OccurMode#toc9][emacswiki]] + +;; Inside *Occur*: q - quit, e - edit, g - reload +;; more help with: describe-function occur-mode + +(defun occur-mode-clean-buffer () + "Removes all commentary from the *Occur* buffer, leaving the + unadorned lines." + (interactive) + (if (get-buffer "*Occur*") + (save-excursion + (set-buffer (get-buffer "*Occur*")) + (goto-char (point-min)) + (toggle-read-only 0) + (if (looking-at "^[0-9]+ lines matching \"") + (kill-line 1)) + (while (re-search-forward "^[ \t]*[0-9]+:" + (point-max) + t) + (replace-match "") + (forward-line 1))) + (message "There is no buffer named \"*Occur*\"."))) + +(defun show-definition(REGEX) + (interactive) + (let ((dotemacs-loaded nil) + (occur-b "*Occur*") + (occur-c "")) + (if (get-buffer ".emacs") + (progn + (switch-to-buffer ".emacs") + (setq dotemacs-loaded t)) + (find-file "~/.emacs")) + (occur REGEX) + (with-current-buffer occur-b + (occur-mode-clean-buffer) + (setq occur-c (current-buffer)) + (let ((inhibit-read-only t)) (set-text-properties (point-min) (point-max) ())) + (while (re-search-forward "[0-9]*:" nil t) + (replace-match "")) + (beginning-of-buffer) + (kill-line) + (sort-lines nil (point-min) (point-max)) + (emacs-lisp-mode) + (beginning-of-buffer) + (insert (format ";; *SHOW*: %s\n" REGEX)) + (highlight-regexp REGEX) + (beginning-of-buffer)) + (switch-to-buffer occur-b) + (delete-other-windows) + (if (eq dotemacs-loaded nil) + (kill-buffer ".emacs")))) + +(defun show-keys() + (interactive) + (show-definition "^(global-set-key")) + +(defun show-aliases() + (interactive) + (show-definition "^(defalias")) + +(defalias 'sk 'show-keys) +(defalias 'sa 'show-aliases) + + +(provide 'init-globalbindings) +;;; init-globalbindings.el ends here diff --git a/lisp/init-go.el b/lisp/init-go.el new file mode 100644 index 0000000..10eff26 --- /dev/null +++ b/lisp/init-go.el @@ -0,0 +1,33 @@ +;; *** Go Lang + +(use-package go-mode + :mode "\\.go\\'" + :mode "\\.mod\\'" + + :config + (setq gofmt-args '("-s")) + (setq tab-width 4) + ;; (setq indent-tabs-mode 1) + + :init + ;; disabled, I'm now trying lsp-mode, see below: + ;; (add-hook 'before-save-hook 'gofmt-before-save) + ;; :hook (go-mode lsp-deferred) + + (when (fboundp 'lsp-deferred) + (defun lsp-go-install-save-hooks () + (add-hook 'before-save-hook #'lsp-format-buffer t t) + (add-hook 'before-save-hook #'lsp-organize-imports t t)) + + (add-hook 'go-mode-hook #'lsp-deferred) + (add-hook 'go-mode-hook #'lsp-go-install-save-hooks) + (add-hook 'go-mode-hook #'ivy-mode) + + ;; overwrite dump-jump settions here + (bind-key* (kbd "C-c j") #'lsp-find-definition) + (bind-key* (kbd "C-c b") #'xref-pop-marker-stack) + )) + + +(provide 'init-go) +;;; init-go.el ends here diff --git a/lisp/init-grep.el b/lisp/init-grep.el new file mode 100644 index 0000000..aa76c26 --- /dev/null +++ b/lisp/init-grep.el @@ -0,0 +1,30 @@ +;; https://github.com/leoliu/ack-el + +(defun tvd-kill-ack() + "Close the *ack* window and kill the associated buffer along +with the ack process" + (interactive) + (let ((kill-buffer-query-functions nil)) + (delete-window) + (kill-buffer "*ack*"))) + +(defun tvd-hook-kill-ack() + "set local keys in temporary ack buffer" + (local-set-key (kbd "q") 'tvd-kill-ack)) + +(use-package ack + :config + + ;; don't annoy me with git search & co + (setq ack-defaults-function 'ack-legacy-defaults) + + ;; focus the *ack* buffer directly + (advice-add 'ack-mode :after + '(lambda () + (pop-to-buffer "*ack*"))) + :init + (add-hook 'ack-mode-hook 'tvd-hook-kill-ack)) + + +(provide 'init-grep) +;;; init-grep.el ends here diff --git a/lisp/init-help.el b/lisp/init-help.el new file mode 100644 index 0000000..a676a18 --- /dev/null +++ b/lisp/init-help.el @@ -0,0 +1,26 @@ +;; *** Help Mode + +;; I even customize help windows! ... at least a little :) + +(defun tvd-close-help () + (interactive) + (kill-this-buffer) + (winner-undo)) + +(eval-after-load "Help" + '(progn + (add-hook 'help-mode-hook + (lambda () + ; doesn' work the way I like + ;(local-set-key (kbd "q") 'tvd-close-help) + (local-set-key (kbd "q") 'quit-window) + (local-set-key (kbd "p") 'help-go-back) + (local-set-key (kbd "b") 'help-go-back) + (local-set-key (kbd "n") 'help-go-forward) + (local-set-key (kbd "f") 'help-go-forward) + (setq help-window-select t) + )))) + + +(provide 'init-help) +;;; init-help.el ends here diff --git a/lisp/init-hydra.el b/lisp/init-hydra.el new file mode 100644 index 0000000..f93864f --- /dev/null +++ b/lisp/init-hydra.el @@ -0,0 +1,6 @@ +;; Used here and there below, loaded as early as possible +(use-package hydra) + + +(provide 'init-hydra) +;;; init-hydra.el ends here diff --git a/lisp/init-ibuffer.el b/lisp/init-ibuffer.el new file mode 100644 index 0000000..ff1285b --- /dev/null +++ b/lisp/init-ibuffer.el @@ -0,0 +1,138 @@ +;; *** iBuffer mode + +;; iBuffer is a great interactive buffer management tool included with +;; emacs. I use it with a couple of custom groups, my own +;; collapse-code () and formats. + +(require 'ibuffer) + +;; from github: +(use-package ibuffer-vc) +(use-package ibuffer-tramp) + +;; replace default list-buffers with ibuffer +(global-set-key (kbd "C-x C-b") 'ibuffer) + +;; group name +(setq tvd-ibuffer-filter-group-name "tvd-filters") + +;; filter group config +;; with hints from [[https://ogbe.net/emacsconfig.html][Ogbe]] et.al. +(setq ibuffer-saved-filter-groups + (list (nreverse + `( + ("Org" (mode . org-mode)) + ("Shell" (or (mode . term-mode) + (mode . eshell-mode) + (mode . shell-mode))) + ("Emacs-Config" (filename . "emacs")) + ("Cisco-Config" (mode . cisco-mode)) + ("Code" (or (mode . cperl-mode) + (mode . c-mode) + (mode . python-mode) + (mode . shell-script-mode) + (mode . makefile-mode) + (mode . cc-mode))) + ("Text" (or (mode . text-mode) + (filename . "\\.pod$"))) + ("LaTeX" (mode . latex-mode)) + ("Interactive" (or + (mode . inferior-python-mode) + (mode . slime-repl-mode) + (mode . inferior-lisp-mode) + (mode . inferior-scheme-mode) + (name . "*ielm*"))) + ("Crab" (or + (name . "^\\*\\(Help\\|scratch\\|Messages\\)\\*") + )) + ,tvd-ibuffer-filter-group-name)))) + +;; Reverse the order of the filter groups. Kind of confusing: Since +;; I'm reversing the order of the groups above, this snippet ensures +;; that the groups are ordered in the way they are written above, with +;; the "Default" group on top. This advice might need to be ported to +;; the new advice system soon. + +(defadvice ibuffer-generate-filter-groups + (after reverse-ibuffer-groups () activate) + (setq ad-return-value (nreverse ad-return-value))) + +(defun ibuffer-add-dynamic-filter-groups () + (interactive) + (dolist (group (ibuffer-vc-generate-filter-groups-by-vc-root)) + (add-to-list 'ibuffer-filter-groups group)) + (dolist (group (ibuffer-tramp-generate-filter-groups-by-tramp-connection)) + (add-to-list 'ibuffer-filter-groups group))) + +(defun tvd-ibuffer-hooks () + (ibuffer-auto-mode 1) + (ibuffer-switch-to-saved-filter-groups tvd-ibuffer-filter-group-name) + (ibuffer-add-dynamic-filter-groups) + (ibuffer-vc-set-filter-groups-by-vc-root) + ) +(add-hook 'ibuffer-mode-hook 'tvd-ibuffer-hooks) + +;; Only show groups that have active buffers +(setq ibuffer-show-empty-filter-groups nil) + +;; Don't show the summary or headline +(setq ibuffer-display-summary nil) + +;; do not prompt for every action +(setq ibuffer-expert t) + +;; buffers to always ignore +(add-to-list 'ibuffer-never-show-predicates "^\\*\\(Completions\\|tramp/\\)") + +;; Use human readable Size column instead of original one +(define-ibuffer-column size-h + (:name "Size" :inline t) + (cond + ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0))) + ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0))) + ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0))) + (t (format "%8d" (buffer-size))))) + +;; Modify the default ibuffer-formats +(setq ibuffer-formats + '((mark modified read-only " " + (name 20 40 :left :elide) + " " + (size-h 9 -1 :right) + " " + (mode 16 16 :left :elide) + " " + (vc-status 16 16 :left) + " " + filename-and-process))) + +;; hide annoying groups, but keep its buffers available +(defvar ibuffer-collapsed-groups (list "Crab")) + +(advice-add 'ibuffer :after '(lambda (&rest args) + (ignore args) + (save-excursion + (dolist (group ibuffer-collapsed-groups) + (ignore-errors + (ibuffer-jump-to-filter-group group) + (ibuffer-toggle-filter-group)))))) + +;; move point to most recent buffer when entering ibuffer +(defadvice ibuffer (around ibuffer-point-to-most-recent) () + "Open ibuffer with cursor pointed to most recent (non-minibuffer) buffer name" + (let ((recent-buffer-name + (if (minibufferp (buffer-name)) + (buffer-name + (window-buffer (minibuffer-selected-window))) + (buffer-name (other-buffer))))) + ad-do-it + (ibuffer-jump-to-buffer recent-buffer-name))) +(ad-activate 'ibuffer) + +;; override ibuffer M-o binding +(define-key ibuffer-mode-map (kbd "M-o") 'other-window-or-switch-buffer) + + + +(provide 'init-ibuffer) +;;; init-ibuffer.el ends here diff --git a/lisp/init-imenu.el b/lisp/init-imenu.el new file mode 100644 index 0000000..8f5bd17 --- /dev/null +++ b/lisp/init-imenu.el @@ -0,0 +1,62 @@ +;;; ** load imenu +(require 'imenu) +(define-key global-map [C-down-mouse-2] 'imenu) + +;; via https://superuser.com/questions/601982/how-to-quickly-navigate-jump-between-functions-on-emacs +(defun ido-imenu () + "Update the imenu index and then use ido to select a symbol to navigate to. +Symbols matching the text at point are put first in the completion list." + (interactive) + (imenu--make-index-alist) + (let ((name-and-pos '()) + (symbol-names '())) + (flet ((addsymbols + (symbol-list) + (when (listp symbol-list) + (dolist (symbol symbol-list) + (let ((name nil) (position nil)) + (cond + ((and (listp symbol) (imenu--subalist-p symbol)) + (addsymbols symbol)) + + ((listp symbol) + (setq name (car symbol)) + (setq position (cdr symbol))) + + ((stringp symbol) + (setq name symbol) + (setq position + (get-text-property 1 'org-imenu-marker symbol)))) + + (unless (or (null position) (null name)) + (add-to-list 'symbol-names name) + (add-to-list 'name-and-pos (cons name position)))))))) + (addsymbols imenu--index-alist)) + ;; If there are matching symbols at point, put them at the beginning + ;; of `symbol-names'. + (let ((symbol-at-point (thing-at-point 'symbol))) + (when symbol-at-point + (let* ((regexp (concat (regexp-quote symbol-at-point) "$")) + (matching-symbols + (delq nil (mapcar + (lambda (symbol) + (if (string-match regexp symbol) symbol)) + symbol-names)))) + (when matching-symbols + (sort matching-symbols (lambda (a b) (> (length a) (length b)))) + (mapc + (lambda (symbol) + (setq symbol-names (cons symbol (delete symbol symbol-names)))) + matching-symbols))))) + (let* ((selected-symbol (ido-completing-read "Symbol? " symbol-names)) + (position (cdr (assoc selected-symbol name-and-pos)))) + (push-mark) + (if (overlayp position) + (goto-char (overlay-start position)) + (goto-char position))))) + +(global-set-key (kbd "C-c C-j") 'ido-imenu) + + +(provide 'init-imenu) +;;; init-imenu.el ends here diff --git a/lisp/init-indentation.el b/lisp/init-indentation.el new file mode 100644 index 0000000..249bbd5 --- /dev/null +++ b/lisp/init-indentation.el @@ -0,0 +1,38 @@ +;; *** Highlighting Indentation +;; provides: highlight-indentation-mode and highlight-indentation-current-column-mode +(use-package highlight-indentation + :config + + (add-something-to-mode-hooks + '(yaml python ruby) 'highlight-indentation-current-column-mode) + + (set-face-background 'highlight-indentation-face "#e3e3d3") + (set-face-background 'highlight-indentation-current-column-face "#c3b3b3")) + + +;;; ** global TAB/Indent config + +;; I use spaces everywhere but Makefiles. If I encounter TABs I +;; replace them with spaces, if I encounter users entering TABs into +;; files, I block them. + +;; FIXME: also check [[https://github.com/glasserc/ethan-wspace][ethan-wspace]] ! + +(setq indent-line-function 'insert-tab) +(setq tab-stop-list (quote (4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 112 116 120))) +(setq tab-always-indent 'complete) ; FIXME: doesnt work in cperl-mode +(setq show-trailing-whitespace t) + +(defun indent-buffer () + ;; Author: Mathias Creutz + "Re-Indent every line in the current buffer." + (interactive) + (indent-region (point-min) (point-max) nil)) + +;; Use normal tabs in makefiles +(add-hook 'makefile-mode-hook '(lambda() (setq indent-tabs-mode t))) + + + +(provide 'init-indentation) +;;; init-indentation.el ends here diff --git a/lisp/init-info.el b/lisp/init-info.el new file mode 100644 index 0000000..7452d0e --- /dev/null +++ b/lisp/init-info.el @@ -0,0 +1,109 @@ +;; *** INFO Mode + +(require 'info) + +;; open an info file somewhere outside %infodir% with info-mode +(defun info-find-file (file) + (interactive "f") + (info-setup file + (pop-to-buffer-same-window + (format "*info*<%s>" + (file-name-sans-extension + (file-name-nondirectory file)))))) + +;; easier navigation in Info mode, intuitive history back and forth. +(eval-after-load "Info" + '(progn + (define-key Info-mode-map (kbd "") 'Info-history-back) + (define-key Info-mode-map (kbd "") 'Info-history-forward) + (use-package info+ + :ensure nil))) + +;; make Info great again! +;; [[http://mbork.pl/2014-12-27_Info_dispatch][based on Marcins]] info dispatch, +;; contains (interactive) code from 'info-display-manual for manual selection. +(defun i (manual) + "Read documentation for MANUAL in the info system. Name the +buffer '*Info MANUAL*'. If that buffer is already present, just +switch to it. + +If MANUAL not given as argument, ask interactively with completion +to select from a list of installed manuals." + (interactive + (list + (progn + (info-initialize) + (ido-completing-read "Manual name: " + (info--manual-names current-prefix-arg) + nil t)))) + (let ((buffer (format "*Info %s*" manual))) + (if (get-buffer buffer) + (switch-to-buffer bufer) + (info manual buffer)))) + +;; from examples, I love this one!, replaces the ? buffer +(define-key Info-mode-map (kbd "?") #'hydra-info/body) +(defhydra hydra-info (:color blue + :hint nil) + " +Info-mode: + ^^_]_ forward (next logical node) ^^_l_ast (←) _u_p (↑) _f_ollow reference _T_OC + ^^_[_ backward (prev logical node) ^^_r_eturn (→) _m_enu (↓) (C-u for new window) _i_ndex _d_irectory + ^^_n_ext (same level only) ^^_H_istory _g_oto (C-u for new window) _,_ next index item _c_opy node name + ^^_p_rev (same level only) _<_/_t_op _b_eginning of buffer virtual _I_ndex _C_lone buffer + regex _s_earch (_S_ case sensitive) ^^_>_ final _e_nd of buffer ^^ _a_propos + + _1_ .. _9_ Pick first .. ninth item in the node's menu. + +" + ("]" Info-forward-node nil) + ("[" Info-backward-node nil) + ("n" Info-next nil) + ("p" Info-prev nil) + ("s" Info-search nil) + ("S" Info-search-case-sensitively nil) + + ("l" Info-history-back nil) + ("r" Info-history-forward nil) + ("H" Info-history nil) + ("t" Info-top-node nil) + ("<" Info-top-node nil) + (">" Info-final-node nil) + + ("u" Info-up nil) + ("^" Info-up nil) + ("m" Info-menu nil) + ("g" Info-goto-node nil) + ("b" beginning-of-buffer nil) + ("e" end-of-buffer nil) + + ("f" Info-follow-reference nil) + ("i" Info-index nil) + ("," Info-index-next nil) + ("I" Info-virtual-index nil) + + ("T" Info-toc nil) + ("d" Info-directory nil) + ("c" Info-copy-current-node-name nil) + ("C" clone-buffer nil) + ("a" info-apropos nil) + + ("1" Info-nth-menu-item nil) + ("2" Info-nth-menu-item nil) + ("3" Info-nth-menu-item nil) + ("4" Info-nth-menu-item nil) + ("5" Info-nth-menu-item nil) + ("6" Info-nth-menu-item nil) + ("7" Info-nth-menu-item nil) + ("8" Info-nth-menu-item nil) + ("9" Info-nth-menu-item nil) + + ("?" Info-summary "Info summary") + ("h" Info-help "Info help") + ("q" Info-exit "Info exit") + ("C-g" nil "cancel" :color blue)) + + + +(provide 'init-info) +;;; init-info.el ends here diff --git a/lisp/init-kubernetes.el b/lisp/init-kubernetes.el new file mode 100644 index 0000000..d68eef9 --- /dev/null +++ b/lisp/init-kubernetes.el @@ -0,0 +1,15 @@ +(defun tvd-kill-ro-buffer() + (interactive) + (when buffer-read-only + (kill-buffer-and-window))) + +(when nil + (use-package kubel + :after (vterm) + :config (kubel-vterm-setup) + :bind + ("q" . tvd-kill-ro-buffer))) + + +(provide 'init-kubernetes) +;;; init-kubernetes.el ends here diff --git a/lisp/init-lsp.el b/lisp/init-lsp.el new file mode 100644 index 0000000..b1875a3 --- /dev/null +++ b/lisp/init-lsp.el @@ -0,0 +1,25 @@ +;; LSP mode +(use-package lsp-mode + :config + (lsp-register-custom-settings + '(("gopls.completeUnimported" t t) + ("gopls.staticcheck" t t))) + + ;; disable infantile nonsense + (setq lsp-headerline-breadcrumb-enable nil) + (setq lsp-modeline-code-actions-enable nil) + + :init + ;; I'm not using any of th lsp commands, but better define a prefix + ;; than being unable to reach it + (setq lsp-keymap-prefix "C-c C-l") + + :commands lsp) + +;; I use ivy +(use-package lsp-ivy + :commands lsp-ivy-global-workspace-symbol) + + +(provide 'init-lsp) +;;; init-lsp.el ends here diff --git a/lisp/init-macros.el b/lisp/init-macros.el new file mode 100644 index 0000000..2e63a5f --- /dev/null +++ b/lisp/init-macros.el @@ -0,0 +1,174 @@ +;; *** MACROs + +;; help: [[https://www.emacswiki.org/emacs/KeyboardMacrosTricks][emacswiki macro tricks]]. + +;; Default keybindings: +;; start-kbd-macro default binding: ‘C-x (’ — Starts recording a keyboard macro. +;; end-kbd-macro default binding: ‘C-x )’ — Ends recording of a keyboard macro. +;; call-last-kbd-macro default binding: ‘C-x e’ — Executes the last keyboard macro defined. + +;; however, I use [[https://github.com/Silex/elmacro][elmacro]]. + +(use-package elmacro + :config + (elmacro-mode) + + (setq tvd-macro-name "last-macro") + + ;; ignore stuff + (add-to-list 'elmacro-unwanted-commands-regexps "^(mouse.*)$") + (add-to-list 'elmacro-unwanted-commands-regexps "^(tvd-start-or-stop-macro)$") + + (defun tvd-get-macro-name() + "Ask for a macro name, check for duplicates. +If the given name is already defined, ask again (and again until unique). +If a buffer with the given name exists, kill it (that is, the buffer is +there but has not been saved or evaluated yet). Return the name as string." + (interactive) + (let ((done nil) + (name nil) + (mbuf nil) + (err "")) + (while (not done) + (setq name (read-string + (format "%s - enter macro name (last-macro): " err) nil nil "last-macro")) + (if (fboundp (intern name)) + (setq err (format "macro '%s is already defined" name)) + (setq mbuf (format "* elmacro - %s *" name)) + (if (get-buffer mbuf) + (with-current-buffer mbuf + (kill-buffer mbuf))) + (setq done t))) + name)) + + (defun tvd-get-exec-macro-name() + "Ask for a macro name to be executed" + (interactive) + (let ((macros ()) + (N 1) + (S nil) + (T "")) + (dolist (entry (cdr (assoc tvd-macro-file load-history ))) + (setq S (cdr entry)) + (setq T (symbol-name S)) + (push (list T N) macros) + (setq N (1+ N))) + (completing-read "enter macro name: " macros nil t nil))) + + ;; the heart of my elmacro stuff + (defun tvd-start-or-stop-macro() + "start macro or stop if started" + (interactive) + (if (eq defining-kbd-macro nil) + (progn + (elmacro-clear-command-history) + (start-kbd-macro nil) + (message "Recording macro. Finish with ...")) + (progn + (call-interactively 'end-kbd-macro) + (setq tvd-macro-name (tvd-get-macro-name)) + (elmacro-show-last-macro tvd-macro-name) + (message "Recording done. Execute with , save or buffer...")))) + + ;; better than the default function + (defun tvd-exec-last-macro(&optional ARG) + "execute last macro (or ARG, if given) repeatedly after every +, abort with C-g or q, and repeat until EOF after pressing a. + +If macro defun is known (i.e. because you evaluated the elmacro buffer +containing the generated defun), it will be executed. Otherwise the +last kbd-macro will be executed." + (interactive) + (let ((melm-count 0) + (melm-all nil) + (melm-abort nil) + (melm-beg (eobp)) + (melm-code (or ARG tvd-macro-name))) + (if (eobp) + (if (yes-or-no-p "(point) is at end of buffer. Jump to top?") + (goto-char (point-min)))) + (while (and (not melm-abort) + (not (eobp))) + (when (not melm-all) + (message (concat + (format + "Executing last macro '%s (%d). Keys:\n" melm-code melm-count) + " repeat once\n" + "a repeat until EOF\n" + "e enter macro name to execute\n" + " or q abort ..\n ")) + (setq K (read-event)) + (cond ((or (eq K 'return) (eq K 'C-f6)) t) + ((equal (char-to-string K) "q") (setq melm-abort t)) + ((equal (char-to-string K) "a") (message "Repeating until EOF")(setq melm-all t)) + ((equal (char-to-string K) "e") (setq tvd-macro-name (tvd-get-exec-macro-name))) + (t (setq melm-abort t)))) + (if (not melm-abort) + (progn + (if (fboundp (intern melm-code)) + (call-interactively (intern melm-code)) + (call-interactively 'call-last-kbd-macro)) + (setq melm-count (1+ melm-count))))) + (if (and (eq melm-count 0) (eq (point) (point-max))) + (message "(point) is at end of buffer, aborted") + (message (format "executed '%s %d times" melm-code melm-count))))) + + ;; My macro recording workflow: + ;; - shift-F6 + ;; - ... do things ... + ;; - shift-F6 again + ;; - enter a name + ;; - a new buffer with macro defun appears + ;; - C-x C-e evals it + ;; - C-F6 (repeatedly) executes it. + (global-set-key (kbd "") 'tvd-start-or-stop-macro) + (global-set-key (kbd "") 'tvd-exec-last-macro) + + ;; I use my own macro file + (setq tvd-macro-file (concat tvd-config-dir "/macros.el")) + + ;; but only load if in use + (if (file-exists-p tvd-macro-file) + (load-file tvd-macro-file)) + + (defun tvd-macro-store() + "store current macro to emacs config" + (interactive) + (copy-region-as-kill (point-min) (point-max)) + (if (not (get-buffer "macros.el")) + (find-file tvd-macro-file)) + (with-current-buffer "macros.el" + (goto-char (point-max)) + (newline) + (insert ";;") + (newline) + (insert (format ";; elmacro added on %s" (current-time-string))) + (newline) + (yank) + (newline) + (save-buffer)) + (switch-to-buffer nil) + (delete-window)) + + (defalias 'ms 'tvd-macro-store) + + (defun tvd-macro-gen-repeater-and-save() + "generate repeater and save the defun's +Runs when (point) is at 0,0 of generated +defun." + (next-line) + (goto-char (point-max)) + (newline) + (insert (format "(defun %s-repeat()\n" tvd-macro-name)) + (insert " (interactive)\n") + (insert (format " (tvd-exec-last-macro \"%s\"))\n" tvd-macro-name)) + (newline) + (eval-buffer) + (tvd-macro-store)) + + (advice-add 'elmacro-show-defun :after '(lambda (&rest args) + (tvd-macro-gen-repeater-and-save)))) + + +(provide 'init-macros) +;;; init-macros.el ends here diff --git a/lisp/init-magit.el b/lisp/init-magit.el new file mode 100644 index 0000000..ddd9624 --- /dev/null +++ b/lisp/init-magit.el @@ -0,0 +1,106 @@ +;; *** Magit + +;; TODO: add blamer.el (https://github.com/Artawower/blamer.el), currently fails to install (2023/05/08) + +;; Not much to say about Magit +(use-package magit + :ensure t + + :init + (use-package magit-todos) + + :config + (magit-todos-mode) + + (defun tvd-magit-status () + "Always call `magit-status' with prefix arg." + (interactive) + (let ((current-prefix-arg t)) + (call-interactively 'magit-status))) + + (setq magit-view-git-manual-method 'woman) + + (defalias 'git 'magit-status) + (defalias 'gitlog 'magit-log-buffer-file) + + (defun tvd-magit-cycle-down() + "hide current section, jump down to the next and show it" + (interactive) + (magit-section-hide (magit-current-section)) + (magit-section-forward-sibling) + (magit-section-show (magit-current-section))) + + (defun tvd-magit-cycle-up() + "hide current section, jump up to the next and show it" + (interactive) + (magit-section-hide (magit-current-section)) + (magit-section-backward-sibling) + (magit-section-show (magit-current-section))) + + ;; configure magit + (with-eval-after-load 'magit + (dolist (dir (list (expand-file-name "~/dev/D/github") + (expand-file-name "~/fits/git") + (expand-file-name "~/dev"))) + (when (file-exists-p dir) + (add-to-list 'magit-repository-directories (cons dir 1)))) + (setq magit-completing-read-function 'magit-ido-completing-read) + + ;; use timestamps in log buffers + (setq magit-log-margin '(t "%Y-%m-%d " magit-log-margin-width t 18)) + + ;; navigate magit buffers as I do in org-mode, that is going down + ;; hides the current sibling, and vice versa + (define-key magit-mode-map (kbd "") 'tvd-magit-cycle-down) + (define-key magit-mode-map (kbd "") 'tvd-magit-cycle-up) + (define-key magit-mode-map (kbd "") 'magit-delete-thing)) + + ;; one thing though: on startup it bitches about git version, but it + ;; works nevertheless. So I disable this specific warning. + + (defun tvd-ignore-magit-warnings-if-any () + (interactive) + (when (get-buffer "*Warnings*") + (with-current-buffer "*Warnings*" + (goto-char (point-min)) + (when (re-search-forward "Magit requires Git >=") + (kill-buffer-and-window))))) + + (add-hook 'after-init-hook 'tvd-ignore-magit-warnings-if-any t) + + ;; now, THIS is the pure genius me: hit "ls in magit-status buffer + ;; and end up in a dired buffer of current repository. The default + ;; binding for this is C-M-i, which is not memorizable, while "ls" + ;; is. That is, 'l' is a prefix command leading to magit-log-popup + ;; and 's' is undefined, which I define here, which then jumps to + ;; dired. + ;; see: https://github.com/magit/magit/wiki/Converting-popup-modifications-to-transient-modifications#adding-an-action + (transient-append-suffix 'magit-log "l" + '("s" "dired" magit-dired-jump)) + + ;; after an exhausting discussion on magit#3139 I use this function + ;; to (kind of) switch to another repository from inside magit-status. + (defun tvd-switch-magit-repo () + (interactive) + (let ((dir (magit-read-repository))) + (magit-mode-bury-buffer) + (magit-status dir))) + (define-key magit-mode-map (kbd "C") 'tvd-switch-magit-repo) + + ;; via + ;; http://manuel-uberti.github.io/emacs/2018/02/17/magit-bury-buffer/: + ;; a great enhancement, when closing the magit status buffer, ALL + ;; other possibly still remaining magit buffers will be killed as + ;; well AND the window setup will be restored. + (defun tvd-kill-magit-buffers() + "Restore window setup from before magit and kill all magit buffers." + (interactive) + (let ((buffers (magit-mode-get-buffers))) + (magit-restore-window-configuration) + (mapc #'kill-buffer buffers))) + + (define-key magit-status-mode-map (kbd "q") #'tvd-kill-magit-buffers)) + + +(provide 'init-magit) +;;; init-magit.el ends here diff --git a/lisp/init-markdown.el b/lisp/init-markdown.el new file mode 100644 index 0000000..4153677 --- /dev/null +++ b/lisp/init-markdown.el @@ -0,0 +1,59 @@ +;; *** Markdown + +;; I rarely use markdown, but sometimes I stumble upon such a file and +;; like to view it with emacs without rendering. +;; Source: [[http://jblevins.org/projects/markdown-mode/][jblevins.org]] + +;; via https://stackoverflow.com/a/26297700 +(defun tvd-cleanup-org-tables () + (save-excursion + (goto-char (point-min)) + (while (search-forward "-+-" nil t) (replace-match "-|-")))) + +(defun tvd-markdown-todo () + "Create dynamically highlighted TODO list of MD list" + (interactive) + (highlight-regexp "^- .*" "hi-yellow") + (highlight-regexp "^- .*ok" "hi-green") + (highlight-regexp "^- .*fail" "hi-pink")) + +(defun tvd-markdown-cleanup() + "Convert org table into markdown table if in markdown-mode and +save the buffer [again], also check if parens are balanced" + (when (equal major-mode 'markdown-mode) + (when (check-parens) + (tvd-cleanup-org-tables) + (save-buffer)))) + +(use-package markdown-mode + :mode "\\.text\\'" + :mode "\\.markdown\\'" + :mode "\\.md\\'" + + :config + (modify-syntax-entry ?\" "\"" markdown-mode-syntax-table) + + ;; (defun tvd-markdown-hooks () + ;; (when buffer-file-name + ;; (add-hook 'after-save-hook + ;; 'check-parens + ;; nil t) + ;; (add-hook 'after-save-hook 'tvd-cleanup-org-tables nil 'make-it-local)) + + ;; (modify-syntax-entry ?\" "\"" markdown-mode-syntax-table) + + ;; (when (fboundb 'orgtbl-mode) + ;; (add-hook 'markdown-mode-hook 'orgtbl-mode)) + + ;; ;; (when (fboundb 'orgalist)) + ;; (add-hook 'markdown-mode-hook 'orgalist-mode)) + + (add-hook 'after-save-hook 'tvd-cleanup-org-tables nil 'make-it-local) + + :hook ;; tvd-markdown-hooks + (markdown-mode . orgalist-mode) + (markdown-mode . orgtbl-mode)) + + +(provide 'init-markdown) +;;; init-markdown.el ends here diff --git a/lisp/init-mmm.el b/lisp/init-mmm.el new file mode 100644 index 0000000..85279f5 --- /dev/null +++ b/lisp/init-mmm.el @@ -0,0 +1,26 @@ +;; *** MMM Mode +(use-package mmm-mode + :config + (require 'cl-lib) + (require 'mmm-auto) + (require 'mmm-vars) + + (setq mmm-submode-decoration-level 2) + + ;; [[https://github.com/purcell/mmm-mode][mmm-mode github]] + ;; see doc for class definition in var 'mmm-classes-alist + ;; **** MMM config for POD mode + (mmm-add-classes + '((html-pod + :submode html-mode ;; web-mode doesnt work this way! + :delimiter-mode nil + :front "=begin html" + :back "=end html"))) + + (mmm-add-mode-ext-class 'pod-mode nil 'html-pod) + + :hook (pod-mode mmm-mode-on)) + + +(provide 'init-mmm) +;;; init-mmm.el ends here diff --git a/lisp/init-modeline.el b/lisp/init-modeline.el new file mode 100644 index 0000000..3c7c1d9 --- /dev/null +++ b/lisp/init-modeline.el @@ -0,0 +1,50 @@ +;; ** mode-line setup (must be the last mode) + +;; I just append the current version of my emacs config and leave out +;; some stuff to keep the modeline short, so that everything can be +;; seen even if I have multiple windows open. + +;; smaller pos +(setq-default mode-line-position + '((-3 "%p") (size-indication-mode ("/" (-4 "%I"))) + " " + (line-number-mode + ("%l" (column-number-mode ":%c"))))) + +;; when macro recording is active, signal it with coloring instead of +;; just a character +(defface rec-face + '((t (:background "red" :foreground "white" :weight bold))) + "Flag macro recording in mode-line" + :group 'tvd-mode-line-faces) + +;; custom modeline +(setq-default mode-line-format + (list + "%e" + mode-line-front-space + mode-line-mule-info + mode-line-modified + mode-line-remote + " " + mode-line-buffer-identification + " " + mode-line-position + " (%m) " + + " [" tvd-emacs-version "] " + + ; added because of eyebrowse + mode-line-misc-info + + '(:eval (propertize + (if (eq defining-kbd-macro t) + "[REC]" + "") + 'face 'rec-face)) + + mode-line-end-spaces)) + + +(provide 'init-moine.el) +;;; init-modeline.el ends here diff --git a/lisp/init-narrow.el b/lisp/init-narrow.el new file mode 100644 index 0000000..3f5431d --- /dev/null +++ b/lisp/init-narrow.el @@ -0,0 +1,26 @@ +;; *** narrowing (no mode but fits here) + +;; I use narrowing quite frequently, so here are some enhancements. + +;; easier narrowing with Indirect Buffers +;; Source: [[https://www.emacswiki.org/emacs/NarrowIndirect3][emacswiki]] +(require 'narrow-indirect) +(defalias 'nf 'ni-narrow-to-defun-indirect-other-window) +(defalias 'nr 'ni-narrow-to-region-indirect-other-window) + +;; I like to have an orange fringe background when narrowing is +;; active, since I forget that it is in effect otherwise sometimes. + +;; via [[https://emacs.stackexchange.com/questions/33288/how-to-find-out-if-narrow-to-region-has-been-called-within-save-restriction][stackoverflow]] +(defun tvd-narrowed-fringe-status () + "Make the fringe background reflect the buffer's narrowing status." + (set-face-attribute + 'fringe nil :background (if (buffer-narrowed-p) + tvd-fringe-narrow-bg + nil))) + +(add-hook 'post-command-hook 'tvd-narrowed-fringe-status) + + +(provide 'init-narrow) +;;; init-narrow.el ends here diff --git a/lisp/init-novel.el b/lisp/init-novel.el new file mode 100644 index 0000000..81b013c --- /dev/null +++ b/lisp/init-novel.el @@ -0,0 +1,12 @@ +;; *** Novel Mode - Screen Reader + +;; my own emacs screen reader, very handy to read docs on the road. + +(use-package novel-mode + :ensure nil + :config + (global-set-key (kbd "C-c C-n") 'novel-mode)) + + +(provide 'init-novel) +;;; init-novel.el ends here diff --git a/lisp/init-occur.el b/lisp/init-occur.el new file mode 100644 index 0000000..f883f42 --- /dev/null +++ b/lisp/init-occur.el @@ -0,0 +1,19 @@ +;; *** Occur +;; https://oremacs.com/2015/01/26/occur-dwim/ +;; https://github.com/abo-abo/hydra/wiki/Emacs +(defun occur-dwim () + "Call `occur' with a sane default, chosen as the thing under point or selected region" + (interactive) + (push (if (region-active-p) + (buffer-substring-no-properties + (region-beginning) + (region-end)) + (let ((sym (thing-at-point 'symbol))) + (when (stringp sym) + (regexp-quote sym)))) + regexp-history) + (call-interactively 'occur)) + + +(provide 'init-occur) +;;; init-occur.el ends here diff --git a/lisp/init-org.el b/lisp/init-org.el new file mode 100644 index 0000000..dca74bc --- /dev/null +++ b/lisp/init-org.el @@ -0,0 +1,251 @@ +;; *** org mode + +;; I use org mode to take notes mostly at work. I also track projects +;; and TODO lists etc. I do not, however, use agenda or any +;; scheduling whatsoever. + +;; I like custom bullets +(use-package org-bullets + :config + (setq org-bullets-bullet-list '("►" "✜" "✸" "✿" "♦"))) + +(use-package org + :config + + + ;; enable syntax highlighting for embedded source blocks + (require 'ob-python) + (require 'ob-perl) + (require 'ob-shell) + + + ;; capture target, os-dependend + ;; FIXME: put this file outside emacs? + (setq tvd-org-file (concat tvd-config-dir "/notizen.org") + org-attach-directory (concat tvd-config-dir "/attachments")) + + ;; easier to open that way + (defun notizen() + (interactive) + (switch-to-buffer (find-file tvd-org-file)) + (agenda)) + + ;; text formatting made easy, bound to C-c keys locally + (defun tvd-org-emphasize(CHAR) + "expand once if no region and apply emphasize CHAR" + (interactive) + (unless (region-active-p) + (er/expand-region 1)) + (org-emphasize CHAR)) + + (defun bold() + "bold text in org mode" + (interactive) + (tvd-org-emphasize '42)) + + (defun italic() + "italic text in org mode" + (interactive) + (tvd-org-emphasize '47)) + + (defun code() + "verbatim text in org mode" + (interactive) + (tvd-org-emphasize '126)) + + (defun underline() + "underline text in org mode" + (interactive) + (tvd-org-emphasize '95)) + + ;; my org-mode specific and + (defun tvd-org-left-or-level-up() + "jump one word to the left if not on a org heading, +otherwise fold current level and jump one level up." + (interactive) + (if (and (org-at-heading-p) (looking-at "*")) + (progn + (hide-subtree) + (outline-up-heading 1)) + (left-word))) + + (defun tvd-org-heading-up() + "If on a heading, fold current heading, jump one level +up and unfold it, otherwise jump paragraph as usual." + (interactive) + (if (and (org-at-heading-p) (looking-at "*")) + (progn + (hide-subtree) + (org-backward-heading-same-level 1) + (org-cycle)) + (backward-paragraph))) + + (defun tvd-org-heading-down() + "If on a heading, fold current heading, jump one level +down and unfold it, otherwise jump paragraph as usual." + (interactive) + (if (and (org-at-heading-p) (looking-at "*")) + (progn + (hide-subtree) + (org-forward-heading-same-level 1) + (org-cycle)) + (forward-paragraph))) + + ;; org-mode specific config, after load + (eval-after-load "org" + '(progn + (add-hook 'org-mode-hook + (lambda () + (setq + org-M-RET-may-split-line nil + org-agenda-files (list tvd-org-file) + org-agenda-restore-windows-after-quit t + org-blank-before-new-entry (quote ((heading . auto) (plain-list-item . auto))) + org-catch-invisible-edits (quote error) + org-columns-default-format "%80ITEM %22Timestamp %TODO %TAGS %0PRIORITY" + org-insert-heading-always-after-current (quote t) + org-mouse-1-follows-link nil + org-remember-store-without-prompt t + org-reverse-note-order t + org-startup-indented t + org-startup-truncated nil + org-return-follows-link t + org-use-speed-commands t + org-yank-adjusted-subtrees t + org-refile-targets '((nil . (:maxlevel . 5))) + org-refile-use-outline-path t + org-outline-path-complete-in-steps nil + org-completion-use-ido t + org-support-shift-select t + org-hide-emphasis-markers t + org-fontify-done-headline t + org-pretty-entities t + org-use-sub-superscripts nil + org-confirm-babel-evaluate nil) + ; shortcuts + (setq org-speed-commands-user + (quote ( + ("0" . ignore) + ("1" . delete-other-windows) + ("2" . ignore) + ("3" . ignore) + ("d" . org-archive-subtree-default-with-confirmation) ; delete, keep track + ("v" . org-narrow-to-subtree) ; only show current heading ("view") + ("q" . widen) ; close current heading and show all ("quit") + (":" . org-set-tags-command) ; add/edit tags + ("t" . org-todo) ; toggle todo type, same as C-t + ("z" . org-refile) ; archive the (sub-)tree + ("a" . org-attach) ; manage attachments + ))) + ; same as toggle + (local-set-key (kbd "C-t") 'org-todo) + + ; alt-enter = insert new subheading below current + (local-set-key (kbd "") 'org-insert-subheading) + + ; search for tags (ends up in agenda view) + (local-set-key (kbd "C-f") 'org-tags-view) + + ; run presenter, org-present must be installed and loadedwhite + (local-set-key (kbd "C-p") 'org-present) + + ; todo colors + (setq org-todo-keyword-faces '( + ("TODO" . (:foreground "deepskyblue" :weight bold)) + ("START" . (:foreground "olivedrab" :weight bold)) + ("WAIT" . (:foreground "darkorange" :weight bold)) + ("DONE" . (:foreground "forestgreen" :weight bold)) + ("CANCEL" . (:foreground "darkorchid" :weight bold)) + ("FAIL" . (:foreground "red" :weight bold)) + )) + + (local-set-key (kbd "C-c b") 'bold) + (local-set-key (kbd "C-c /") 'italic) + (local-set-key (kbd "C-c 0") 'code) ; aka = without shift + (local-set-key (kbd "C-c _") 'underline) + + ; edit babel src block in extra buffer: + ; default is C-c ' which is hard to type + ; brings me to src code editor buffer + ; Also note: enter ") 'tvd-org-heading-up) + (local-set-key (kbd "") 'tvd-org-heading-down) + + ;; move word left or heading up, depending where point is + (local-set-key (kbd "") 'tvd-org-left-or-level-up) + + ;; use nicer bullets + (when (fboundp 'org-bullets-mode) + (org-bullets-mode 1)) + + (org-babel-do-load-languages 'org-babel-load-languages + '((python . t) + (emacs-lisp . t) + (shell . t) + (perl . t))))))) + + ;; no more ... at the end of a heading + (setq org-ellipsis " ⤵") + + ;; my own keywords, must be set globally, not catched correctly inside hook + (setq org-todo-keywords + '((sequence "TODO" "START" "WAIT" "|" "DONE" "CANCEL" "FAIL"))) + + ;; I always want to be able to capture, even if no ORG is running + (global-set-key (kbd "C-n") (lambda () (interactive) (org-capture))) + + ;; must be global since code edit sub buffers run their own major mode, not org + (global-set-key (kbd "C-c C-#") 'org-edit-src-exit) + + ;; some org mode vars must be set globally + (setq org-default-notes-file tvd-org-file + org-startup-indented t + org-indent-indentation-per-level 4) + + ;; my own capture templates + (setq org-capture-templates + '(("n" "Project" entry (file+headline tvd-org-file "Unsorted Tasks") + "* TODO %^{title}\n%u\n** Kostenstelle\n** Contact Peer\n** Contact Customer\n** Aufträge\n** Daten\n** Notizen\n %i%?\n" + :prepend t :jump-to-captured t) + + ("t" "Todo Item" entry (file+headline tvd-org-file "Manual-Agenda-Tasks") + "* TODO %^{title}\n:LOGBOOK:\n%u:END:\n" :prepend t :immediate-finish t) + + ("s" "Scheduled Item" entry (file+headline tvd-org-file "Scheduled-Agenda-Tasks") + "* TODO %^t %^{title}\n:LOGBOOK:\n%u:END:\n" :prepend t :immediate-finish t) + + ("j" "Journal" entry (file+headline tvd-org-file "Kurznotizen") + "* TODO %^{title}\n%u\n %i%?\n" :prepend t :jump-to-captured t) + + ("c" "Copy/Paste" entry (file+headline tvd-org-file "Kurznotizen") + "* TODO %^{title}\n%u\n %x\n" :immediate-finish t :prepend t :jump-to-captured t))) + + ;; follow links using eww, if present + ;; (if (fboundp 'eww-browse-url) + ;; (setq browse-url-browser-function 'eww-browse-url)) + + ;; mark narrowing with an orange fringe, the advice for 'widen + ;; is in the outline section. + (advice-add 'org-narrow-to-subtree :after + '(lambda (&rest args) + (set-face-attribute 'fringe nil :background tvd-fringe-narrow-bg))) + + ;; always use the latest docs + (with-eval-after-load 'info + (info-initialize) + (add-to-list 'Info-directory-list + (expand-file-name "~/.emacs.d/lisp/org/doc"))) + + ;; orange fringe when narrowed + (advice-add 'org-narrow-to-subtree :after + '(lambda (&rest args) + (set-face-attribute 'fringe nil :background tvd-fringe-narrow-bg)))) + + +(provide 'init-org) +;;; init-org.el ends here diff --git a/lisp/init-orgagenda.el b/lisp/init-orgagenda.el new file mode 100644 index 0000000..53c41d2 --- /dev/null +++ b/lisp/init-orgagenda.el @@ -0,0 +1,212 @@ +;; *** org agenda mode + +;; I use org mode for along time now, primarily at work, but did not +;; use agenda. Instead I developed the habit of maintaining one org +;; entry which contains just a list with all things to do today. I +;; just edited this list manually and it worked. However, recently I +;; found out that agenda provides lots of features and commands +;; precisely for what I already did manually. So, now, finally (as of +;; november 2018) I switch to using the agenda. + +;; My agenda use is very simple though: I don't use any scheduling, no +;; priorities, no recurring events, no daily or other time based +;; views. I just keep a list of TODO entries and another of entries in +;; WAIT state, that's it. All those entries are located under a +;; special org entry with the title "Heute" and the category (as +;; property) WORK, which I use for filtering out agenda items. + +;; The general workflow is as follows: I execute (agenda) which starts +;; directly my custom agenda view. It lists open TODO items and +;; waiting WAIT items below. If I press `n', I will be asked for a +;; title and a new TODO item appears in my agenda. I can press `d' to +;; mark it as DONE, it will also be archived into a subsibling below +;; "HEUTE". I can press `w' to move an item into WAIT state and I can +;; press `a' to add text to the org entry under point (like "waiting +;; for customer email"). + +;; So, I don't use my regular org entries, which are in most cases +;; very large containing lots of information, as agenda items, but +;; only very short ones which act as reminders about what work I have +;; to do. However, since I have the org buffer always opened and +;; visible in a split buffer next to the agenda, it is no problem to +;; go to such a deep entry for editing or viewing. + +(when (package-installed-p 'org) + (require 'org-agenda) + + ;; This is my one and only agenda custom view, it displays TODO items + ;; below entries categorized as WORK and WAIT items under the same + ;; category. The cool thing here is, that the `tags' agenda view can + ;; be used to filter for properties as well. In order to have this + ;; working the following property drawer must exist in an entry with + ;; TODO siblings: + ;; + ;; * START Arbeit + ;; :PROPERTIES: + ;; :CATEGORY: WORK + ;; :END: + ;; ** TODO a thing to do + ;; ** WAIT a thing waiting for something + ;; + (setq org-agenda-custom-commands + '(("o" "Daily TODO Tasks" + ( + ;; a block containing only items scheduled for today, if any + (agenda "" + ((org-agenda-span 1) + (org-agenda-overriding-header "Tasks scheduled:"))) + ;; manually created todo items due (state TODO) + (tags "CATEGORY=\"WORK\"" + ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo '("CANCEL" "START" "DONE" "WAIT"))) + (org-agenda-overriding-header "\nTasks to do today:") + (org-agenda-follow-mode t) + (org-agenda-entry-text-mode t))) + ;; manually created todo items in wait state + (tags "CATEGORY=\"WORK\"" + ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo '("CANCEL" "START" "DONE" "TODO"))) + (org-agenda-overriding-header "\nTasks Waiting:")))) + ((org-agenda-compact-blocks t))))) + + ;; A shortcut to reach my custom view directly + (defun agenda () + "Visit my org agenda directly, splits left" + (interactive) + (org-agenda nil "o") + (tvd-flip-windows) + (other-window-or-switch-buffer)) + + ;; Add a line of text to the top of an existing TODO entry and refresh + ;; the agenda + (defun tvd-org-agenda-edit-entry (note) + "Insert a note as plain text into an entry" + (interactive "sEnter note: ") + (save-excursion + (org-agenda-switch-to) + (end-of-line) + (newline) + (insert note)) + (switch-to-buffer "*Org Agenda*") + (org-agenda-redo t)) + + ;; Mark an entry as DONE, archive it to an archive sibling and refresh + ;; the agenda + (defun tvd-org-agenda-done() + (interactive) + (org-agenda-todo 'done) + (org-agenda-archive-to-archive-sibling) + (org-agenda-redo t)) + + ;; Mark an entry as WAIT, archive it to an archive sibling and refresh + ;; the agenda + (defun tvd-org-agenda-wait() + (interactive) + (org-agenda-todo "WAIT") + (org-agenda-redo t)) + + ;; A wrapper which executes an org capture directly. `t' is the + ;; shortcut for the capture, defined above in org mode. + (defun tvd-org-agenda-capture (task) + "Capture a task in agenda mode, using the date at point" + (interactive) + (let ((org-overriding-default-time (org-get-cursor-date))) + (org-capture nil task) + (org-agenda-redo t))) + + (defun tvd-org-agenda-capture-todo () + "Capture a todo task in agenda mode" + (interactive) + (tvd-org-agenda-capture "t")) + + (defun tvd-org-agenda-capture-scheduled () + "Capture a scheduled task in agenda mode" + (interactive) + (tvd-org-agenda-capture "s")) + + + ;; Sometimes it is nice to see the agenda alone, so I press + ;; `o'. However, since follow mode is enabled, once I move point, the + ;; org buffer re-appears. + (defun tvd-org-agenda-solitair () + (interactive) + (delete-other-windows) + (setq org-agenda-follow-mode nil) + (message "Org Agenda Follow Mode Disabled")) + + ;; The original function scrolls the buffer every time when it runs a + ;; little bit up, which is annoying, to say the least + (defun tvd-org-agenda-redo() + (interactive) + (org-agenda-redo t) + (beginning-of-buffer)) + + ;; Since I learned to love hydra, I have one for my agenda as well, of course: + (defhydra hydra-org-agenda (:color blue + :pre (setq which-key-inhibit t) + :post (setq which-key-inhibit nil) + :hint none) + " +Org Agenda (_q_uit) + +^Tasks^ ^Options^ ^Movement^ +-^^^^^^------------------------------------------------------------------------------------- +_n_: create new todo task _f_: follow =?f? ENTER: switch to entry +_N_: create new scheduled task _e_: entry =?e? C-: go one entry up +_d_: mark task done and archive _o_: one window C-: go one entry down +_w_: mark task waiting ^^ M-: move entry up +_t_: toggle todo state ^^ M-: move entry down +_z_: archive task ^ ^ +_+_: increase prio +_-_: decrease prio ^Marking^ +_g_: refresh _m_: mark entry +_s_: save org buffer(s) _u_: un-mark entry +_a_: add a note to the entry _U_: un-mark all +_k_: delete a task w/o archiving _B_: bulk action + +" + ("a" tvd-org-agenda-edit-entry nil) + ("n" tvd-org-agenda-capture-todo nil) + ("N" tvd-org-agenda-capture-scheduled nil) + ("o" tvd-org-agenda-solitair nil) + ("g" tvd-org-agenda-redo) + ("t" org-agenda-todo) + ("d" tvd-org-agenda-done nil) + ("w" tvd-org-agenda-wait nil) + ("z" org-agenda-archive-to-archive-sibling nil) + ("+" org-agenda-priority-up nil) + ("-" org-agenda-priority-down nil) + ("s" org-save-all-org-buffers nil) + ("f" org-agenda-follow-mode + (format "% -3S" org-agenda-follow-mode)) + ("e" org-agenda-entry-text-mode + (format "% -3S" org-agenda-entry-text-mode)) + ("m" org-agenda-bulk-mark nil) + ("u" org-agenda-bulk-unmark nil) + ("U" org-agenda-bulk-remove-all-marks nil) + ("B" org-agenda-bulk-action nil) + ("k" org-agenda-kill nil) + ("q" nil nil :color red)) + + ;; Configuration and key bindings for org agenda (same as in the hydra) + (add-hook 'org-agenda-mode-hook '(lambda () (progn + (setq org-agenda-follow-mode t + org-log-into-drawer t + org-agenda-entry-text-mode t + org-agenda-sorting-strategy '(priority-down timestamp-down)) + (local-set-key (kbd "n") 'tvd-org-agenda-capture-todo) + (local-set-key (kbd "N") 'tvd-org-agenda-capture-scheduled) + (local-set-key (kbd "o") 'tvd-org-agenda-solitair) + (local-set-key (kbd "a") 'tvd-org-agenda-edit-entry) + (local-set-key (kbd "d") 'tvd-org-agenda-done) + (local-set-key (kbd "w") 'tvd-org-agenda-wait) + (local-set-key (kbd "g") 'tvd-org-agenda-redo) + (local-set-key (kbd "f") 'org-agenda-follow-mode) + (local-set-key (kbd "e") 'org-agenda-entry-text-mode) + (local-set-key (kbd "k") 'org-agenda-kill) + (local-set-key (kbd "z") 'org-agenda-archive-to-archive-sibling) + (local-set-key (kbd "C-") 'org-agenda-previous-item) + (local-set-key (kbd "C-") 'org-agenda-next-item) + (local-set-key (kbd "?") 'hydra-org-agenda/body))))) + + +(provide 'init-orgagenda) +;;; init-orgagenda.el ends here diff --git a/lisp/init-orgalist.el b/lisp/init-orgalist.el new file mode 100644 index 0000000..2aba5f8 --- /dev/null +++ b/lisp/init-orgalist.el @@ -0,0 +1,6 @@ +;; https://elpa.gnu.org/packages/orgalist.html +(use-package orgalist) + + +(provide 'init-orgalist) +;;; init-orgalist.el ends here diff --git a/lisp/init-orgtable.el b/lisp/init-orgtable.el new file mode 100644 index 0000000..81e89a3 --- /dev/null +++ b/lisp/init-orgtable.el @@ -0,0 +1,419 @@ +;; *** org table mode + +;; I'm so used to lovely org mode tables, I need them everywhere! +(when (package-installed-p 'org) + (require 'org-table) + + + ;; convert CSV region to table + (defun tablify (regex) + "Convert a whitespace separated column list into +an org mode table and enable orgtbl-mode. You can +specify another regex for cell splitting." + (interactive "MConvert [region] to table with regex ([\t\s]+): ") + (let ((spc "[\t\s]+")) + (when (string= regex "") + (setq regex spc)) + (delete-trailing-whitespace) + (if (org-region-active-p) + (org-table-convert-region (region-beginning) (region-end) regex) + (org-table-convert-region (point-min) (point-max) regex)) + (when (not (eq major-mode 'org-mode)) + (orgtbl-mode)))) + + ;; table sorting shortcuts + (defun sort-table-numeric () + "sort org table numerically by current column" + (interactive) + (org-table-sort-lines nil ?n)) + + (defun sort-table-numeric-desc () + "reverse sort org table numerically by current column" + (interactive) + (org-table-sort-lines nil ?N)) + + (defun sort-table-alphanumeric () + "sort org table charwise by current column" + (interactive) + (org-table-sort-lines nil ?a)) + + (defun sort-table-alphanumeric-desc () + "reverse sort org table charwise by current column" + (interactive) + (org-table-sort-lines nil ?A)) + + (defun sort-table-time () + "sort org table by times by current column" + (interactive) + (org-table-sort-lines nil ?t)) + + (defun sort-table-time-desc () + "reverse sort org table by times by current column" + (interactive) + (org-table-sort-lines nil ?T)) + + ;; [[http://irreal.org/blog/?p=3542][via jcs/irreal.org]] + ;; however, I renamed the actual sort wrappers to match my + ;; naming scheme + (defun jcs-ip-lessp (ip1 ip2 &optional op) + "Compare two IP addresses. +Unless the optional argument OP is provided, this function will return T +if IP1 is less than IP2 or NIL otherwise. The optional argument OP is +intended to be #'> to support reverse sorting." + (setq cmp (or op #'<)) + (cl-labels ((compare (l1 l2) + (if (or (null l1) (null l2)) + nil + (let ((n1 (string-to-number (car l1))) + (n2 (string-to-number (car l2)))) + (cond + ((funcall cmp n1 n2) t) + ((= n1 n2) (compare (cdr l1) (cdr l2))) + (t nil)))))) + (compare (split-string ip1 "\\.") (split-string ip2 "\\.")))) + + (defun sort-table-ip () + (interactive) + (org-table-sort-lines nil ?f #'org-sort-remove-invisible #'jcs-ip-lessp)) + + (defun sort-table-ip-desc () + (interactive) + (org-table-sort-lines nil ?F #'org-sort-remove-invisible + (lambda (ip1 ip2) (jcs-ip-lessp ip1 ip2 #'>)))) + + + ;; easy access for the shortcuts + (defalias 'stn 'sort-table-numeric) + (defalias 'stnd 'sort-table-numeric-desc) + (defalias 'sta 'sort-table-alphanumeric) + (defalias 'stad 'sort-table-alphanumeric-desc) + (defalias 'stt 'sort-table-time) + (defalias 'sttd 'sort-table-time-desc) + (defalias 'sti 'sort-table-ip) + (defalias 'stid 'sort-table-ip-desc) + + ;; generic table exporter + (defun tvd-export-org-table (fmt) + "export an org table using format FMT" + (interactive) + (let ((efile "/tmp/org-table-export.tmp") + (ebuf (format "*table-%s*" fmt))) + (when (file-exists-p efile) + (delete-file efile)) + (org-table-export efile (format "orgtbl-to-%s" fmt)) + (other-window 1) + (if (not (eq (get-buffer ebuf) nil)) + (kill-buffer (get-buffer ebuf))) + (set-buffer (get-buffer-create ebuf)) + (insert-file-contents efile) + (switch-to-buffer ebuf) + (delete-file efile))) + + ;; FIXME: once there's an org solution to this, remove this code + ;; format specific exporters + (defun tvd-org-quote-csv-field (s) + "Quote every field." + (if (string-match "." s) + (concat "=\"" (mapconcat 'identity + (split-string s "\"") "\"\"") "\"") + s)) + + (defun table-to-excel () + "export current org table to CSV format suitable for MS Excel." + (interactive) + ;; quote everything, map temporarily 'org-quote-csv-field + ;; to my version + (cl-letf (((symbol-function 'org-quote-csv-field) + #'tvd-org-quote-csv-field)) + (tvd-export-org-table "csv"))) + + (defun table-to-csv () + "export current org table to CSV format" + (interactive) + (tvd-export-org-table "csv")) + + (defun table-to-latex () + "export current org table to LaTeX format" + (interactive) + (tvd-export-org-table "latex")) + + (defun table-to-html () + "export current org table to HTML format" + (interactive) + (tvd-export-org-table "html")) + + (defun table-to-csv-tab () + "export current org table to CSV format, separated by " + (interactive) + (tvd-export-org-table "tsv")) + + (defun table-to-aligned () + "export current org table to space-aligned columns format" + (interactive) + (tvd-export-org-table "csv") + (with-current-buffer "*table-csv*" + (align-regexp (point-min) (point-max) "\\(\\s-*\\)," 1 1 t) + (while (re-search-forward " ," nil t) + (replace-match " ")))) + + ;; exporter shortcuts + (defalias 'ttc 'table-to-csv) + (defalias 'tte 'table-to-excel) + (defalias 'ttl 'table-to-latex) + (defalias 'tth 'table-to-html) + (defalias 'ttt 'table-to-csv-tab) + (defalias 'tta 'table-to-aligned) + + ;; In org mode I sometimes want to copy the content of a table cell + (defun tvd-beginning-of-cell (&optional arg) + "move (point) to the beginning of a org mode table cell" + (interactive) + (if (re-search-backward "|" (line-beginning-position) 3 1) + (forward-char))) + + (defun tvd-end-of-cell (&optional arg) + "move (point) to the end of a org mode table cell" + (interactive) + (if (re-search-forward "|" (line-end-position) 3 1) + (backward-char))) + + (defun tvd-copy-org-table-cell(&optional arg) + "Copy an org mode table cell to the kill ring using MCYT" + (interactive "P") + (mcyt--blink-and-copy-thing 'tvd-beginning-of-cell 'tvd-end-of-cell arg)) + + (defun tvd-del-org-table-cell (&optional arg) + "Delete a cell" + (interactive "P") + (let + ((beg (progn (tvd-beginning-of-cell) (point))) + (end (progn (tvd-end-of-cell) (point)))) + (delete-region beg end) + (org-table-align))) + + (defun tvd-del-org-table-row () + "Delete a table row's contents" + (interactive) + (org-beginning-of-line 1) + (kill-line) + (org-table-insert-row nil)) + + (defun tvd-del-org-table-col () + "Delete a table column's contents, keep heading as is" + (interactive) + (let ((head (org-table-get 1 nil))) + (org-table-delete-column) + (re-search-forward "|") + (org-table-insert-column) + (tvd-org-table-goto-col-beginning) + (insert head) + (org-table-align)) + ) + + ;; Sometimes I need to copy whole columns too: + ;; via [[https://emacs.stackexchange.com/questions/28270/how-to-select-and-copy-a-column-of-an-org-table-without-rectangle-selection][stackoverflow]] + + (defun tvd-org-table-goto-col-beginning () + "Go to beginning of current column and return `point'." + (interactive) + (assert (org-table-p) "Not in org-table.") + (org-table-align) + (let ((col (org-table-current-column))) + (goto-char (org-table-begin)) + (org-table-goto-column col)) + (point)) + + (defun tvd-org-table-col-beginning () + "Return beginning position of current column." + (save-excursion + (tvd-org-table-goto-col-beginning))) + + (defun tvd-org-table-goto-col-end () + "Goto end of current column and return `point'." + (interactive) + (assert (org-table-p) "Not in org-table.") + (org-table-align) + (let ((col (org-table-current-column))) + (goto-char (1- (org-table-end))) + (org-table-goto-column col) + (skip-chars-forward "^|")) + (point)) + + (defun tvd-org-table-col-end () + "Return end position of current column." + (save-excursion + (tvd-org-table-goto-col-end))) + + (defun tvd-org-table-select-col () + "Select current column." + (interactive) + (set-mark (tvd-org-table-col-beginning)) + (tvd-org-table-goto-col-end) + (rectangle-mark-mode)) + + (defun tvd-copy-org-table-col () + "Copy current column." + (interactive) + (tvd-org-table-select-col) + (sit-for 0.2 t) + (copy-region-as-kill nil nil t) + (with-temp-buffer + (yank) + (delete-trailing-whitespace) + (delete-whitespace-rectangle (point-min) (point-max)) + (font-lock-unfontify-buffer) + (copy-region-as-kill (point-min) (point-max)))) + + (defun tvd-copy-org-table-row () + "Copy current row, space aligned" + (interactive) + (mcyt-copy-line) + (with-temp-buffer + (yank) + (goto-char (point-min)) + (let ((spc "")) + (while (re-search-forward "|[ ]*" nil t) + (replace-match spc) + (setq spc " "))) + (delete-trailing-whitespace) + (copy-region-as-kill (point-min) (point-max)))) + + ;; Move single cells using C-M-up C-M-down C-M-left C-M-right + ;; [[https://cs.gmu.edu/~kauffman/software/org-table-move-single-cell.el][via Kauffmann]] + (defun org-table-swap-cells (i1 j1 i2 j2) + "Swap two cells" + (let ((c1 (org-table-get i1 j1)) + (c2 (org-table-get i2 j2))) + (org-table-put i1 j1 c2) + (org-table-put i2 j2 c1) + (org-table-align))) + + (defun org-table-move-single-cell (direction) + "Move the current cell in a cardinal direction according to the + parameter symbol: 'up 'down 'left 'right. Swaps contents of + adjacent cell with current one." + (unless (org-at-table-p) + (error "No table at point")) + (let ((di 0) (dj 0)) + (cond ((equal direction 'up) (setq di -1)) + ((equal direction 'down) (setq di +1)) + ((equal direction 'left) (setq dj -1)) + ((equal direction 'right) (setq dj +1)) + (t (error "Not a valid direction, must be up down left right"))) + (let* ((i1 (org-table-current-line)) + (j1 (org-table-current-column)) + (i2 (+ i1 di)) + (j2 (+ j1 dj))) + (org-table-swap-cells i1 j1 i2 j2) + (org-table-goto-line i2) + (org-table-goto-column j2)))) + + (defun org-table-move-single-cell-up () + "Move a single cell up in a table; swap with anything in target cell" + (interactive) + (org-table-move-single-cell 'up)) + + (defun org-table-move-single-cell-down () + "Move a single cell down in a table; swap with anything in target cell" + (interactive) + (org-table-move-single-cell 'down)) + + (defun org-table-move-single-cell-left () + "Move a single cell left in a table; swap with anything in target cell" + (interactive) + (org-table-move-single-cell 'left)) + + (defun org-table-move-single-cell-right () + "Move a single cell right in a table; swap with anything in target cell" + (interactive) + (org-table-move-single-cell 'right)) + + ;; actual org table config + (with-eval-after-load "org" + (add-hook 'org-mode-hook + (lambda () + (local-set-key (kbd "C-c t l") 'tvd-copy-org-table-col) + (local-set-key (kbd "C-c t r") 'tvd-copy-org-table-row) + (local-set-key (kbd "C-c t c") 'tvd-copy-org-table-cell) + (local-set-key (kbd "C-M-") 'org-table-move-single-cell-left) + (local-set-key (kbd "C-M-") 'org-table-move-single-cell-right) + (local-set-key (kbd "C-M-") 'org-table-move-single-cell-up) + (local-set-key (kbd "C-M-") 'org-table-move-single-cell-down)))) + + ;; eval-after-load 'orgtbl doesn't work + (add-hook 'orgtbl-mode-hook '(lambda () + (define-key orgtbl-mode-map (kbd "C-c t l") 'tvd-copy-org-table-col) + (define-key orgtbl-mode-map (kbd "C-c t r") 'tvd-copy-org-table-row) + (define-key orgtbl-mode-map (kbd "C-c t c") 'tvd-copy-org-table-cell))) + + ;; integers, reals, positives, set via custom + (setq org-table-number-regexp "^[-+]?\\([0-9]*\\.[0-9]+\\|[0-9]+\\.?[0-9]*\\)$") + + ;; table hydras, maybe better than aliases?! + (defhydra hydra-org-tables (:color blue) + " +^Sort by^ ^Transform to^ ^Copy/Del what^ ^Modify^ ^Outside Org^ +^^^^^^^^----------------------------------------------------------------------------------------------------------------------- +_sa_: alphanumeric _tc_: CSV _cl_: Copy Column (C-c t l) _ic_: Insert Column _ot_: Table to Org Mode +_sA_: -alphanumeric _te_: Excel _cr_: Copy Row (C-c t r) _ir_: Insert Row _oe_: Enable Org-Tbl Mode +_si_: ip _tl_: Latex _cc_: Copy Cell (C-c t c) _il_: Insert Line _oc_: Turn region to columns +_sI_: -ip _th_: HTML _dd_: Delete Cell _tr_: Transpose Table +_sn_: numeric _tt_: Tab _dc_: Delete Column _mr_: Move Cell right +_sN_: -numeric _ta_: Aligned _dr_: Delete Row _ml_: Move Cell left +_st_: time ^^ _kr_: Kill Row _mu_: Move Cell up +_sT_: -time ^^ _kc_: Kill Column _md_: Move Cell down _q_: Cancel + + +^^^^^^^^----------------------------------------------------------------------------------------------------------------------- +Reach this hydra with +^^^^^^^^----------------------------------------------------------------------------------------------------------------------- + + +" + ("mr" org-table-move-single-cell-right) + ("ml" org-table-move-single-cell-left) + ("mu" org-table-move-single-cell-up) + ("md" org-table-move-single-cell-down) + ("sa" sort-table-alphanumeric nil) + ("sA" sort-table-alphanumeric-desc nil) + ("si" sort-table-ip nil) + ("sI" sort-table-ip-desc nil) + ("sn" sort-table-numeric nil) + ("sN" sort-table-numeric-desc nil) + ("st" sort-table-time nil) + ("sT" sort-table-time-desc nil) + + ("tc" table-to-csv nil) + ("te" table-to-excel nil) + ("tl" table-to-latex nil) + ("th" table-to-html nil) + ("tt" table-to-csv-tab nil) + ("ta" table-to-aligned nil) + + ("cl" tvd-copy-org-table-col nil) + ("cr" tvd-copy-org-table-row nil) + ("cc" tvd-copy-org-table-cell nil) + ("dd" org-table-blank-field nil) + ("dr" tvd-del-org-table-row nil) + ("dc" tvd-del-org-table-col nil) + ("kc" org-table-delete-column nil) + ("kr" org-table-kill-row nil) + + ("ic" org-table-insert-column nil) + ("ir" org-table-insert-row nil) + ("il" org-table-hline-and-move nil) + ("tr" org-table-transpose-table-at-point nil) + + ("ot" tablify nil) + ("oe" orgtbl-mode nil) + ("oc" align-repeat nil) + + ("q" nil nil :color red)) + + ;; allow me to insert org tables everywhere on request + (defalias 'table 'hydra-org-tables/body) + (global-set-key (kbd "C-x t") 'hydra-org-tables/body)) + + +(provide 'init-orgtable) +;;; init-orgtable.el ends here diff --git a/lisp/init-outline.el b/lisp/init-outline.el new file mode 100644 index 0000000..a1d3611 --- /dev/null +++ b/lisp/init-outline.el @@ -0,0 +1,72 @@ +;; *** outline mode + +;; I use the very same cycle style as in org mode: when on a heading, +;; hide it, jump to next heading on the same level and expand that (or +;; vice versa). however, when NOT on a heading behave as loved: jump +;; paragraphs. + +;; Note, that this also affects outshine mode, since that inherits +;; from outline. + +(defun tvd-outline-left-or-level-up() + "jump one word to the left if not on a heading, +otherwise fold current level and jump one level up." + (interactive) + (if (outline-on-heading-p) + (progn + (hide-subtree) + (outline-up-heading 1)) + (left-word))) + +(defun tvd-outline-heading-up() + "fold current heading, jump one level up and unfold it" + (interactive) + (if (not (outline-on-heading-p)) + (backward-paragraph) + (hide-subtree) + (outline-backward-same-level 1) + (outline-cycle))) + +(defun tvd-outline-heading-down() + "fold current heading, jump one level down and unfold it" + (interactive) + (if (not (outline-on-heading-p)) + (forward-paragraph) + (hide-subtree) + (outline-forward-same-level 1) + (outline-cycle))) + +;; unused, see tvd-outshine-jump +(defun tvd-outline-jump (part) + "Jump interactively to next header containing PART using search." + (interactive "Mjump to: ") + (let ((done nil) + (pwd (point))) + (beginning-of-buffer) + (outline-show-all) + (when (re-search-forward (format "^;; \\*+.*%s" part) (point-max) t) + (when (outline-on-heading-p) + (beginning-of-line) + (setq done t))) + (when (not done) + (message (format "no heading with '%s' found" part)) + (goto-char pwd)))) + +;; outline mode config +(eval-after-load "outline" + '(progn + (add-hook 'outline-minor-mode-hook + (lambda () + ;; narrowing, we use org functions, it's loaded anyway + (defalias 'n 'org-narrow-to-subtree) + (defalias 'w 'widen) + (define-key outline-minor-mode-map (kbd "") 'tvd-outline-heading-up) + (define-key outline-minor-mode-map (kbd "") 'tvd-outline-heading-down) + ;;(define-key outline-minor-mode-map (kbd "") 'tvd-outline-left-or-level-up) + )))) + + + + +(provide 'init-outline) +;;; init-outline.el ends here diff --git a/lisp/init-perl.el b/lisp/init-perl.el new file mode 100644 index 0000000..6b26877 --- /dev/null +++ b/lisp/init-perl.el @@ -0,0 +1,117 @@ +;; *** cperl mode + +;; I am a perl addict. I love it, therefore, emacs must be prepared +;; for my addiction. Most importantly, I prefer cperl instead of the +;; default perl mode. I do not use the cperl version delivered with +;; emacs though, but the latest git version. + +(use-package cperl + :ensure nil ;; builtin + + :mode ("\.pl$" . cperl-mode) + :mode ("\.pm$" . cperl-mode) + + :config + (defalias 'perl-mode 'cperl-mode) + + ;; enable the most important cperl features + (setq cperl-indent-left-aligned-comments nil) + (setq cperl-comment-column 32) + (setq cperl-hairy t) + (setq cperl-electric-linefeed t) + (setq cperl-electric-keywords t) + (setq cperl-electric-parens t) + (setq cperl-electric-parens-string nil) + + (defun perl-kill () + "get rid of hanging perl compile or run buffers" + (interactive) + (delete-windows-on perl-run-out-buffer) + (kill-buffer perl-run-out-buffer)) + + (defun perl-run (switches parameters prefix) + "execute current perl buffer" + (interactive "sPerl-switches:\nsParameter:\nP") + (let ((file buffer-file-name)) + (if (eq prefix nil) + (shell-command (concat "perl " switches " " file " " parameters "&")) + (shell-command (concat "perl -wc " switches " " file " " parameters "&"))) + (save-excursion + (set-buffer perl-run-out-buffer) + (setq perl-error-fontified nil + perl-error-start nil + perl-error-end nil) + (goto-char (point-min))) + )) + + (defun perl-next-error () + "jump to next perl run error, if any" + (interactive) + (let (line + errorfile + (window (get-buffer-window (buffer-name))) + (file buffer-file-name) + (buffer (buffer-name)) + ) + (select-window (display-buffer perl-run-out-buffer)) + (set-buffer perl-run-out-buffer) + (if (eq perl-error-fontified t) + (progn + (set-text-properties perl-error-start perl-error-end ()) + (setq perl-error-fontified nil) + ) + ) + (if (re-search-forward (concat "at \\([-a-zA-Z:0-9._~#/\\]*\\) line \\([0-9]*\\)[.,]") (point-max) t) + () + (goto-char (point-min)) + (message "LAST ERROR, jumping to first") + (re-search-forward (concat "at \\([-a-zA-Z:0-9._~#/\\]*\\) line \\([0-9]*\\)[.,]") (point-max) t) + ) + (recenter) + (set-text-properties (match-beginning 1) + (match-end 2) + (list 'face font-lock-keyword-face)) + (setq perl-error-fontified t + perl-error-start (match-beginning 1) + perl-error-end (match-end 2) + errorfile (buffer-substring + (match-beginning 1) + (match-end 1)) + line (string-to-int (buffer-substring + (match-beginning 2) + (match-end 2)))) + (select-window window) + (find-file errorfile) + (goto-line line))) + + ;; cperl indent region + (defun own-cperl-indent-region-or-paragraph (start end) + (interactive "r") + (if mark-active + (cperl-indent-region start end) + (save-excursion + (mark-paragraph) + (cperl-indent-region (point) (mark t)) + ))) + + (defun tvd-cperl-hook() + (make-variable-buffer-local 'perl-error-fontified) + (make-variable-buffer-local 'perl-error-start) + (make-variable-buffer-local 'perl-error-end) + (make-variable-buffer-local 'perl-old-switches) + (make-variable-buffer-local 'perl-old-parameters) + (local-set-key "\C-hF" 'cperl-info-on-current-command) + (local-set-key "\C-hf" 'describe-function) + (local-set-key "\C-hV" 'cperl-get-help) + (local-set-key "\C-hv" 'describe-variable) + (local-set-key "\C-cr" 'perl-run) + (local-set-key "\C-ck" 'perl-kill) + (local-set-key "\C-c#" 'perl-next-error) + (local-set-key "\M-\C-q" 'own-cperl-indent-region-or-paragraph) + (setq mode-name "PL")) + + :hook tvd-cperl-hook) + + +(provide 'init-perl) +;;; init-perl.el ends here diff --git a/lisp/init-pod.el b/lisp/init-pod.el new file mode 100644 index 0000000..23f7693 --- /dev/null +++ b/lisp/init-pod.el @@ -0,0 +1,110 @@ +;; *** POD mode + +;; I LOVE POD! POD is the documentation format of perl and there's a +;; solid toolset available for it. I use it to write documentation and +;; manual pages. It's much more powerfull than lame markdown and you +;; can even program great tools yourself around POD (like I did with +;; PodWiki years ago!) + +;; Although cperl mode already has some POD support, pod mode is way +;; better. + +;; Source: [[https://github.com/renormalist/emacs-pod-mode][emacs-pod-mode]] +(when (package-installed-p 'expand-region) + ;; using expand-region: apply pod entity formatting to word at point. + (defun tvd-outline-emphasize(CHAR) + "expand once if no region and apply emphasize CHAR" + (interactive) + (unless (use-region-p) + (er/expand-region 1)) + (save-excursion + (goto-char (region-beginning)) + (insert (format "%c<" CHAR)) + (goto-char (region-end)) + (insert ">"))) + + (defun pod-bold() + "bold text in outline mode" + (interactive) + (tvd-outline-emphasize ?B)) + + (defun pod-italic() + "italic text in outline mode" + (interactive) + (tvd-outline-emphasize ?I)) + + (defun pod-code() + "verbatim text in outline mode" + (interactive) + (tvd-outline-emphasize ?C)) + + ;; enhance abbrevs and jump into expanded area to the + ;; $ character (if it exists), which it also removes + (setq tvd-abbrev-pos 0) + (advice-add 'expand-abbrev :before + '(lambda () (setq tvd-abbrev-pos (point)))) + (advice-add 'expand-abbrev :after + '(lambda () + (ignore-errors + (when (re-search-backward "\\$" (- tvd-abbrev-pos 0)) + (delete-char 1)))))) + +(use-package pod-mode + :ensure nil ;; static install + :mode "\\.pod\\'" + + :config + (add-hook 'pod-mode-hook 'font-lock-mode) + + ;; tune syntax table + (modify-syntax-entry ?= "w" pod-mode-syntax-table) + + ;; POD contains headers and I'm used to outlining if there are headers + ;; so, enable outlining + (setq outline-heading-alist '(("=head1" . 1) + ("=head2" . 2) + ("=head3" . 3) + ("=head4" . 4) + ("=over" . 5) + ("=item" . 6) + ("=begin" . 5) + ("=pod" . 5) + )) + + ;; outline alone, however, works well + (outline-minor-mode) + + ;; my own abbrevs for POD using mode-specific abbrev table + (define-abbrev-table 'pod-mode-abbrev-table '( + ("=o" "=over\n\n=item *$\n\n=back\n\n") + ("=i" "=item ") + ("=h1" "=head1 ") + ("=h2" "=head2 ") + ("=h3" "=head3 ") + ("=h4" "=head4 ") + ("=c" "=cut") + ("=b" "=begin") + ("=e" "=end") + ("b" "B<$>") + ("c" "C<$>") + ("l" "L<$>") + ("i" "I<$>") + ("e" "E<$>") + ("f" "F<$>")) + "POD mode abbreviations, see .emacs") + + (abbrev-table-put pod-mode-abbrev-table :case-fixed t) + (abbrev-table-put pod-mode-abbrev-table :system t) + + ;; enable abbreviations + (setq local-abbrev-table pod-mode-abbrev-table) + (abbrev-mode 1) + + ;; POD easy formatting + (local-set-key (kbd "C-c b") 'pod-bold) + (local-set-key (kbd "C-c /") 'pod-italic) + (local-set-key (kbd "C-c c") 'pod-code)) + + +(provide 'init-pod) +;;; init-pod.el ends here diff --git a/lisp/init-printing.el b/lisp/init-printing.el new file mode 100644 index 0000000..bb77657 --- /dev/null +++ b/lisp/init-printing.el @@ -0,0 +1,32 @@ +;; *** Printing +;; overwrites printing default menu, access via menu File => Print +;; or: +;; - M-x ps-spool-buffer-with-faces +;; - go to *spool* buffer +;; - save to .ps file +;; - print +(require 'printing) +(pr-menu-bind) + +;; via [[https://emacs.stackexchange.com/questions/9364/convert-a-text-buffer-to-a-pdf-file][stackoverflow]] +(when (executable-find "ps2pdf") + (defun print-to-pdf (&optional filename) + "Print file in the current buffer as pdf, including font, color, and +underline information. This command works only if you are using a window system, +so it has a way to determine color values. + +C-u COMMAND prompts user where to save the Postscript file (which is then +converted to PDF at the same location." + (interactive (list (if current-prefix-arg + (ps-print-preprint 4) + (concat (file-name-sans-extension (buffer-file-name)) + ".ps")))) + (ps-print-with-faces (point-min) (point-max) filename) + (shell-command (concat "ps2pdf " filename)) + (delete-file filename) + (message "Deleted %s" filename) + (message "Wrote %s" (concat (file-name-sans-extension filename) ".pdf")))) + + +(provide 'init-printing) +;;; init-printing.el ends here diff --git a/lisp/init-projectile.el b/lisp/init-projectile.el new file mode 100644 index 0000000..53c9aed --- /dev/null +++ b/lisp/init-projectile.el @@ -0,0 +1,42 @@ +;; *** Projectile +(use-package projectile + :config + (projectile-mode +1) + + (defun tvd-dir-to-projectile () + "drop a .projectile wherever we are" + (interactive) + (with-temp-file ".projectile" + (insert "-.snapshot\n-.git\n-.RCS\n")) + (message (format "Turned %s into projectile project" default-directory))) + + ;; FIXME: add custom docstring + (when (fboundp 'defhydra) + (defhydra hydra-projectile + ( :color teal + :columns 4) + "Projectile (use C-p for this menu)" + ("s" projectile-switch-project "Switch Project") + ("f" projectile-find-file "Find File") + ("r" projectile-recentf "Recent Files") + ("b" projectile-ibuffer "Show Project Buffers") + + ("g" projectile-grep "Grep") + ("o" projectile-multi-occur "Multi Occur") + ("d" projectile-dired "Project Dired") + ("R" projectile-replace "Replace in Project") + + ("C" projectile-invalidate-cache "Clear Cache") + ("t" projectile-regenerate-tags "Regenerate Tags") + ("X" projectile-cleanup-known-projects "Cleanup Known Projects") + ("n" tvd-dir-to-projectile "Turn current directory into Projectile") + + ("c" projectile-commander "Commander") + ("k" projectile-kill-buffers "Kill Buffers") + ("q" nil "Cancel" :color blue)) + + (global-set-key (kbd "C-x p") 'hydra-projectile/body))) + + +(provide 'init-projectile) +;;; init-projectile.el ends here diff --git a/lisp/init-python.el b/lisp/init-python.el new file mode 100644 index 0000000..97ef226 --- /dev/null +++ b/lisp/init-python.el @@ -0,0 +1,16 @@ +(use-package python + :mode ("\\.py\\'" . python-mode) + :interpreter ("python" . python-mode) + :mode "\\.py\\'" + :hook + (function + (lambda() + (local-set-key [delete] 'py-electric-delete) + (setq-default indent-tabs-mode nil) + (setq mode-name "PY") + (outline-minor-mode 0) ;; turn off outline here. FIXME: find out where it's turned on! + ))) + + +(provide 'init-python) +;;; init-python.el ends here diff --git a/lisp/init-recentfiles.el b/lisp/init-recentfiles.el new file mode 100644 index 0000000..1cb7761 --- /dev/null +++ b/lisp/init-recentfiles.el @@ -0,0 +1,65 @@ +;;; *** Recent Files + +;; You know the file you edited yesterday had "kri" in its name, but +;; where was it? You don't remember. But don't worry, recent files is +;; your friend. It shows the last N files you edited recently. +;; I use it permanently. + +;; see also: ido-mode and smex + +(defun tvd-buffer-exists-p (bufname) + (not (eq nil (get-file-buffer bufname)))) + +;; setup +(use-package recentf + :config + (require 'cl-lib) + (setq recentf-auto-cleanup 'never) ;; avoid stat() on tramp buffers + (recentf-mode 1) + + ;; I like to have a longer list reaching deeper into the past + (setq recentf-max-menu-items 200 + recentf-max-saved-items nil) + + ;; enable IDO completion + ;; via [[http://emacsredux.com/blog/2013/04/05/recently-visited-files/][emacsredux]] + ;; modified to exclude already visited files + (defun recentf-ido-find-file () + "Find a recent file using ido." + (interactive) + (let ((file (ido-completing-read + "Choose recent file: " + (cl-remove-if 'tvd-buffer-exists-p recentf-list) nil t))) + (when file + (find-file file)))) + + (global-set-key (kbd "C-x C-r") 'recentf-ido-find-file) ; open recent files, same as M-x rf + + ;; now if I incidentally closed a buffer, I can re-open it, thanks to + ;; recent-files + (defun undo-kill-buffer (arg) + "Re-open the last buffer killed. With ARG, re-open the nth buffer." + (interactive "p") + (let ((recently-killed-list (copy-sequence recentf-list)) + (buffer-files-list + (delq nil (mapcar (lambda (buf) + (when (buffer-file-name buf) + (expand-file-name (buffer-file-name buf)))) (buffer-list))))) + (mapc + (lambda (buf-file) + (setq recently-killed-list + (delq buf-file recently-killed-list))) + buffer-files-list) + (find-file + (if arg (nth arg recently-killed-list) + (car recently-killed-list))))) + + ;; exclude some auto generated files + (setq recentf-exclude (list "ido.last" + "/elpa/" + ".el.gz$" + '(not (file-readable-p))))) + + +(provide 'init-recentfiles) +;;; init-recentfiles.el ends here diff --git a/lisp/init-rust.el b/lisp/init-rust.el new file mode 100644 index 0000000..96a8349 --- /dev/null +++ b/lisp/init-rust.el @@ -0,0 +1,17 @@ +;; *** rust mode +(use-package rust-mode + :config + + (defun rustlings-done () + "I use this with rustlings" + (interactive) + (search-backward "DONE") + (move-beginning-of-line 1) + (kill-line) + (save-buffer)) + + :mode "\\.rs\\'") + + +(provide 'init-rust) +;;; init-rust.el ends here diff --git a/lisp/init-sgml.el b/lisp/init-sgml.el new file mode 100644 index 0000000..25108d9 --- /dev/null +++ b/lisp/init-sgml.el @@ -0,0 +1,22 @@ +;; *** sgml + +;; Used for XML and the likes. + +(setq sgml-set-face t) +(setq sgml-balanced-tag-edit t) +(setq sgml-omittag-transparent nil) +(setq sgml-auto-insert-required-elements t) + +(setq sgml-markup-faces + '((start-tag . font-lock-function-name-face) + (end-tag . font-lock-function-name-face) + (comment . font-lock-comment-face) + (pi . font-lock-other-type-face) + (sgml . font-lock-variable-name-face) + (doctype . font-lock-type-face) + (entity . font-lock-string-face) + (shortref . font-lock-keyword-face))) + + +(provide 'init-sgml) +;;; init-sgml.el ends here diff --git a/lisp/init-shellscript.el b/lisp/init-shellscript.el new file mode 100644 index 0000000..ac6df43 --- /dev/null +++ b/lisp/init-shellscript.el @@ -0,0 +1,12 @@ +;; *** Shell-Script Mode + +;; C-c C-c [un-]comments everywhere, force in shell-script-mode as well +(add-hook + 'sh-mode-hook + (function + (lambda() + (local-set-key (kbd "C-c C-c") 'comment-or-uncomment-region-or-line)))) + + +(provide 'init-shellscript) +;;; init-shellscript.el ends here diff --git a/lisp/init-smartparens.el b/lisp/init-smartparens.el new file mode 100644 index 0000000..9b206d5 --- /dev/null +++ b/lisp/init-smartparens.el @@ -0,0 +1,213 @@ +;; I'm trying to migrate to smart-parens, since it supports all of +;; paredit but can do more + +;; Also look at: +;; https://github.com/Fuco1/.emacs.d/blob/master/files/smartparens.el +;; https://github.com/Fuco1/smartparens/wiki +;; https://ebzzry.io/en/emacs-pairs/ + +(defun tvd-disable-par-and-pair() + "Disables Paredit and Electric-Pair-Mode if currently active. +Used when enabling smartparens-mode." + (interactive) + (when electric-pair-mode + (electric-pair-mode)) + (when (fboundp 'pparedit-mode) + (when paredit-mode + (disable-paredit-mode)))) + +;; I use my own lisp comment tool until sp#942 is fixed +(defun tvd-lisp-comment () + (interactive) + (if (not (looking-at "\s*\(")) + (self-insert-command 1) + (let ((beg (point))) + (forward-list 1) + (when (looking-at "\s*\)") + (insert "\n")) + (comment-region beg (point)) + (indent-for-tab-command) + (goto-char beg)))) + +(use-package smartparens + :init + (require 'smartparens-config) + (require 'cl-lib) + + ;; :custom-face + ;; (sp-show-pair-match-face ((t (:foreground "White")))) + + :config + ;; enable sp in minibuffer as well + ;; maybe, see: https://github.com/Fuco1/smartparens/issues/33: + ;; (sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil) + (setq sp-ignore-modes-list + (delete 'minibuffer-inactive-mode sp-ignore-modes-list)) + + ;; automatically enable where needed + (add-something-to-mode-hooks + '(rust emacs-lisp ielm lisp elisp lisp-interaction scheme slime-repl ) 'smartparens-mode) + + (add-something-to-mode-hooks + '(emacs-lisp ielm lisp elisp lisp-interaction scheme slime-repl ) 'electric-pair-mode) + + ;; also in some select prog modes + ;; (add-something-to-mode-hooks + ;; '(perl ruby c c++ sh makefile config-general yaml go) (smartparens-mode t)) + ;; the above doesn't work anymore, for whatever reasons, so I enable it for all + (smartparens-global-mode t) + + ;; via https://ebzzry.io/en/emacs-pairs/: + (defmacro def-pairs (pairs) + "Define functions for pairing. PAIRS is an alist of (NAME . STRING) +conses, where NAME is the function name that will be created and +STRING is a single-character string that marks the opening character. + + (def-pairs ((paren . \"(\") + (bracket . \"[\")) + +defines the functions WRAP-WITH-PAREN and WRAP-WITH-BRACKET, +respectively." + `(progn + ,@(cl-loop for (key . val) in pairs + collect + `(defun ,(read (concat + "wrap-with-" + (prin1-to-string key) + "s")) + (&optional arg) + (interactive "p") + (sp-wrap-with-pair ,val))))) + + (def-pairs ((paren . "(") + (bracket . "[") + (brace . "{") + (single-quote . "'") + (double-quote . "\"") + (back-quote . "`"))) + + ;;(add-hook 'smartparens-enabled-hook #'tvd-disable-par-and-pair) + ;;(add-hook 'smartparens-enabled-hook #'turn-on-smartparens-strict-mode) + + (when (fboundp 'defhydra) + (defhydra hydra-smartparens (:hint nil) + " +Sexps [quit with _q_, reach this hydra with 'C-x ('] + +^Nav^ ^Barf/Slurp^ ^Depth^ +^---^-----------------------^----------^---------------------------^-----^----------------- +_f_: forward (C-M-right) _→_: slurp forward (C-right) _R_: splice +_b_: backward (C-M-left) _←_: barf forward (C-left) _r_: raise +_u_: backward ↑ _C-_: slurp backward _↑_: raise backward +_d_: forward ↓ (C-M-down) _C-_: barf backward _↓_: raise forward +_p_: backward ↓ +_n_: forward ↑ (C-M-up) + +^Kill^ ^Misc^ ^Wrap^ +^----^-----------^----^-----------------------^----^------------------ +_w_: copy _j_: join _(_: wrap with ( ) +_k_: kill (C-k) _s_: split _{_: wrap with { } +^^ _t_: transpose _'_: wrap with ' ' +^^ _c_: convolute _\"_: wrap with \" \" +^^ _i_: indent defun + +" + ("q" nil) + ;; Wrapping + ("(" (lambda (_) (interactive "P") (sp-wrap-with-pair "("))) + ("{" (lambda (_) (interactive "P") (sp-wrap-with-pair "{"))) + ("'" (lambda (_) (interactive "P") (sp-wrap-with-pair "'"))) + ("\"" (lambda (_) (interactive "P") (sp-wrap-with-pair "\""))) + ;; Navigation + ("f" sp-forward-sexp ) + ("b" sp-backward-sexp) + ("u" sp-backward-up-sexp) + ("d" sp-down-sexp) + ("p" sp-backward-down-sexp) + ("n" sp-up-sexp) + ;; Kill/copy + ("w" sp-copy-sexp) + ("k" sp-kill-sexp) + ;; Misc + ("t" sp-transpose-sexp) + ("j" sp-join-sexp) + ("s" sp-split-sexp) + ("c" sp-convolute-sexp) + ("i" sp-indent-defun) + ;; Depth changing + ("R" sp-splice-sexp) + ("r" sp-splice-sexp-killing-around) + ("" sp-splice-sexp-killing-backward) + ("" sp-splice-sexp-killing-forward) + ;; Barfing/slurping + ("" sp-forward-slurp-sexp) + ("" sp-forward-barf-sexp) + ("C-" sp-backward-barf-sexp) + ("C-" sp-backward-slurp-sexp)) + + (define-key smartparens-mode-map (kbd "C-x (") 'hydra-smartparens/body)) + + :bind (:map smartparens-mode-map + ;; auto wrapping w/o region + ( "C-c (" . 'wrap-with-parens) + ( "C-c [" . 'wrap-with-brackets) + ( "C-c {" . 'wrap-with-braces) + ( "C-c '" . 'wrap-with-single-quotes) + ( "C-c \"" . 'wrap-with-double-quotes) + ( "C-c `" . 'wrap-with-back-quotes) + + ;; modification + ("C-k" . 'sp-kill-hybrid-sexp) + ("C-" . 'sp-forward-slurp-sexp) + ("C-" . 'sp-forward-barf-sexp) + + ;; movement + ;; Also Check: https://github.com/Fuco1/smartparens/wiki/Working-with-expressions + ;; (look for "quick summary for each navigation function") + ;; + ;; Jump after the next balanced expression. If inside one and + ;; there is no forward exp., jump after its closing pair. + ("C-M-" . 'sp-forward-sexp) + ;; Jump before the previous balanced expression. If inside one + ;; and there is no previous exp., jump before its opening pair. + ("C-M-" . 'sp-backward-sexp) + ;; Jump up one level from the current balanced expression. This + ;; means skipping all the enclosed expressions within this and + ;; then jumping after the closing pair. + ("C-M-" . 'sp-up-sexp) + ;; Jump after the opening pair of next balanced expression. This + ;; effectively descends one level down in the "expression + ;; hierarchy". If there is no expression to descend to, jump + ;; after current expression's opening pair. This can be used to + ;; quickly navigate to the beginning of current balanced + ;; expression. + ("C-M-" . 'sp-down-sexp) + ;; Jump to the beginning of following balanced expression. If + ;; there is no following expression on the current level, jump + ;; one level up backward, effectively doing sp-backward-up-sexp. + ("C-S-" . 'sp-previous-sexp) + ;; Jump to the end of the previous balanced expression. If there + ;; is no previous expression on the current level, jupm one level + ;; up forward, effectively doing sp-up-sexp. + ("C-S-" . 'sp-next-sexp) + + ;; comment the whole sexp + (";" . 'tvd-lisp-comment) + + ;; replace my global setting + ;; FIXME: fhceck/fix M! + ("M-" . 'sp-forward-symbol) + ("M-" . 'sp-backward-symbol))) + +;;; Parens config goes here as well + +;; display matching braces +(show-paren-mode 1) + +;; 'mixed: highlight all if the other paren is invisible +;; 'expression: highlight the whole sexp +(setq show-paren-style 'mixed) + + +(provide 'init-smartparens) +;;; init-smartparens.el ends here diff --git a/lisp/init-suggest.el b/lisp/init-suggest.el new file mode 100644 index 0000000..4d53c45 --- /dev/null +++ b/lisp/init-suggest.el @@ -0,0 +1,47 @@ +;; *** Suggest Mode + +;; [[https://github.com/Wilfred/suggest.el][suggest mode]] is a great +;; elisp development tool. Execute `M-x suggest' and try it. + +;; FIXME: doesn't install, seems to use a different version from elpa, which requires spinner ?!?!: +;; Error (use-package): Failed to install suggest: Opening output file: +;; No such file or directory, /home/scip/.emacs-init.d/elpa-27.1/spinner-1.7.4/spinner-autoloads.el +;; spinner is here: https://elpa.gnu.org/packages/spinner.html +(when nil + (use-package suggest + :config + + ;; I use my own clearing function, since suggest doesn't provide this + (defun tvd-suggest-reload () + "Clear suggest buffer and re-paint it." + (interactive) + (let ((inhibit-read-only t)) + (erase-buffer) + (suggest--insert-heading suggest--inputs-heading) + (insert "\n\n\n") + (suggest--insert-heading suggest--outputs-heading) + (insert "\n\n\n") + (suggest--insert-heading suggest--results-heading) + (insert "\n") + (suggest--nth-heading 1) + (forward-line 1))) + + (defun tvd-suggest-jump () + "Jump between input and output suggest buffer." + (interactive) + (forward-line -1) + (if (eq (line-number-at-pos) 1) + (suggest--nth-heading 2) + (suggest--nth-heading 1)) + (forward-line 1)) + + (eval-after-load "suggest" + '(progn + (add-hook 'suggest-mode-hook + (lambda () + (local-set-key (kbd "C-l") 'tvd-suggest-reload) + (local-set-key (kbd "") 'tvd-suggest-jump))))))) + + +(provide 'init-suggest) +;;; init-suggest.el ends here diff --git a/lisp/init-system.el b/lisp/init-system.el new file mode 100644 index 0000000..5698e8b --- /dev/null +++ b/lisp/init-system.el @@ -0,0 +1,241 @@ +;;; Backup Config + +;; I save backup files in a central location below the init dir, that +;; way they don't clutter productive file systems or repos. + +(setq tvd-backup-directory (expand-file-name "backups" tvd-config-dir)) +(if (not (file-exists-p tvd-backup-directory)) + (make-directory tvd-backup-directory t)) + +;; there's even a trash +(setq tvd-trash-directory (expand-file-name "trash" tvd-backup-directory)) + + +;; actual configuration of all things backup related: +(setq make-backup-files t ; backup of a file the first time it is saved. + backup-by-copying t ; don't clobber symlinks + version-control t ; version numbers for backup files + delete-old-versions t ; delete excess backup files silently + delete-by-moving-to-trash t + kept-old-versions 6 ; oldest versions to keep when a new numbered backup is made (default: 2) + kept-new-versions 9 ; newest versions to keep when a new numbered backup is made (default: 2) + auto-save-default t ; auto-save every buffer that visits a file + auto-save-timeout 20 ; number of seconds idle time before auto-save (default: 30) + auto-save-interval 200 ; number of keystrokes between auto-saves (default: 300) + delete-by-moving-to-trash t + trash-directory tvd-trash-directory + backup-directory-alist `(("emacs.d/\\(recentf\\|ido.last\\|places\\)" . nil) ; do not backup state files + ("." . ,tvd-backup-directory))) ; backup everything else + +;; However, if the file to be backed up is remote, backup +;; per remote directory. that way, no root owned files end +;; up in my home directory, ready to be read by everyone. +;; This is system specific and only matches special host names. +;; FIXME: find out programatically hostname und remote user to make this generic +(advice-add 'make-backup-file-name-1 :before + '(lambda (&rest file) + (let ((filename (car file))) + (if (string-match "\\(/ssh:.devel[0-9]+\\):/" filename) + (setq backup-directory-alist `(("." . ,(concat (match-string 1 filename) ":/root/.emacs.d/backups")))) + (setq backup-directory-alist `(("." . ,tvd-backup-directory))))))) + +;; FIXME: and/or check [[https://www.gnu.org/software/tramp/#Auto_002dsave-and-Backup][gnu.org]] +;; + tramp-default-proxies-alist + + + +;;; console backspace fix + +;; make backspace work in console sessions +(define-key key-translation-map [?\C-h] [?\C-?]) + + +;;; ** y means yes +;; y is shorter than yes and less error prone. +(defalias 'yes-or-no-p 'y-or-n-p) + + +;;; ** show col in modeline +;; very useful to know current column +(column-number-mode t) + +;;; ** file or buffer in title +;; this can be seen in xmobar +(setq frame-title-format '(buffer-file-name "emacs %f" ("emacs %b"))) + + +;;; ** avoid invalid files +(setq require-final-newline t) + + +;;; ** byte-compile all of them, if needed +;; ;; handy function to recompile all lisp files +;; (defun recompile-el() +;; (interactive) +;; (byte-recompile-directory tvd-config-dir 0)) + + +;;; ** re-read a modified buffer + +;; F5 == reload file if it has been modified by another process, shift +;; because Xmonad +(global-set-key (kbd "S-") ; re-read a buffer from disk (revert) + (lambda (&optional force-reverting) + "Interactive call to revert-buffer. Ignoring the auto-save + file and not requesting for confirmation. When the current buffer + is modified, the command refuses to revert it, unless you specify + the optional argument: force-reverting to true." + (interactive "P") + ;;(message "force-reverting value is %s" force-reverting) + (if (or force-reverting (not (buffer-modified-p))) + (revert-buffer :ignore-auto :noconfirm) + (error "The buffer has been modified")))) + + + +;;; ** handy aliases + +;; M-x q r is short enough for me, no need for key bindings for +;; those + +(defalias 'qrr 'query-replace-regexp) +(defalias 'qr 'query-replace) +(defalias 'cr 'comment-region) +(defalias 'ur 'uncomment-region) +(defalias 'ir 'indent-region) +(defalias 'dv 'describe-variable) +(defalias 'dk 'describe-key) +(defalias 'df 'describe-function) +(defalias 'dp 'describe-char) +(defalias 'dm 'describe-mode) +(defalias 'db 'describe-bindings) +(defalias 'dl 'finder-commentary) ; aka "describe library" +(defalias 'repl 'ielm) +(defalias 'ws 'window-configuration-to-register) ; save window config +(defalias 'wr 'jump-to-register) ; restore window config +(defalias 'rec 'rectangle-mark-mode) +(defalias '| 'shell-command-on-region) ; apply shell command on region + + +;;; ** various settings + +;; point stays while scrolling +(setq scroll-preserve-screen-position t) + +;; do not save until I hit C-x-s +(setq auto-save-default nil) + +;; show all buffers in buffer menu +(setq buffers-menu-max-size nil) + +;; start to wrap at 30 entries +(setq mouse-buffer-menu-mode-mult 30) + +;; I'm grown up! +(setq disabled-command-function nil) + + +;;; ** copy/paste Config + +;; Related: +;; - see also mark-copy-yank-things-mode below! +;; - see also: move-region below (for M-) +;; - see also: expand-region below (for C-0) + +;; middle mouse button paste at click not where cursor is +(setq mouse-yank-at-point t) + +;; highlight selected stuff (also allows DEL of active region) +(setq transient-mark-mode t) + +;; pasting onto selection deletes it +(delete-selection-mode t) + +;; delete whole lines +(setq kill-whole-line t) + +;; middle-mouse, shift-INSERT use both X-selection and Emacs-clipboard +(setq x-select-enable-primary t) +(setq x-select-enable-clipboard nil) +(setq select-enable-primary t) ;; c-y use primary + +;; marked region automatically copied, also on win +(setq mouse-drag-copy-region t) + + +;;; ** use more mem +(setq gc-cons-threshold 20000000) + + +;;; ** better file name completion + +;; Complete filenames case insensitive and ignore certain files during completion. +(setq read-file-name-completion-ignore-case t) +(setq read-buffer-completion-ignore-case t) + +;; via [[http://endlessparentheses.com/improving-emacs-file-name-completion.html][endlessparantheses]] +(mapc (lambda (x) + (add-to-list 'completion-ignored-extensions x)) + '(".aux" ".bbl" ".blg" ".exe" + ".log" ".meta" ".out" ".pdf" + ".synctex.gz" ".tdo" ".toc" + "-pkg.el" "-autoloads.el" ".elc" + ".dump" ".ps" ".png" ".jpg" + ".gz" ".tgz" ".zip" + "Notes.bib" "auto/")) + + +;;; ** abbreviations + +;; Do I really need those anymore? Added ca 1999... + +(define-abbrev-table 'global-abbrev-table '( + ("oe" "ö" nil 0) + ("ue" "ü" nil 0) + ("ae" "ä" nil 0) + ("Oe" "Ö" nil 0) + ("Ue" "Ü" nil 0) + ("Ae" "Ä" nil 0) + ("
  • " "
  • " nil 0) + ("
      " "
      " nil 0) + )) + +;; do NOT ask to save abbrevs on exit +(setq save-abbrevs nil) + + +;;; ** meaningful names for buffers with the same name + +;; from ([[https://github.com/bbatsov/prelude][prelude]]) + +(require 'uniquify) +(setq uniquify-buffer-name-style 'forward) +(setq uniquify-separator "/") +(setq uniquify-after-kill-buffer-p t) ; rename after killing uniquified +(setq uniquify-ignore-buffers-re "^\\*") ; don't muck with special buffers + + +;;; ** My own global variables + +;; narrowed fringe background +(defvar tvd-fringe-narrow-bg "OrangeRed") + + +;;; ** Recenter config + +;; [[http://oremacs.com/2015/03/28/recenter/][via abo abo]] + +;; However, I set the first position to 1, which causes the window to +;; be recentered on the second line, that is, I can see one line above +;; the current one. It works the same with bottom, which I intend, but +;; I think this is a recenter calculation bug. + +(setq recenter-positions '(1 middle bottom)) + +;; On my new linux system running kubuntu I am unable to insert ^ ` or ~. +;; see: https://unix.stackexchange.com/questions/28170/some-keys-are-invalid-on-emacs-when-using-german-keyboard +(require 'iso-transl) + + +(provide 'init-system) +;;; init-system.el ends here diff --git a/lisp/init-tablist.el b/lisp/init-tablist.el new file mode 100644 index 0000000..45d1ad5 --- /dev/null +++ b/lisp/init-tablist.el @@ -0,0 +1,37 @@ +;; *** Tabulated List Mode +;; built-in, used by many interactive major modes + + (defun tvd-close-window () + (interactive) + (kill-this-buffer) + (delete-window)) + +;; +tablist, which provides many cool features +;; [[https://github.com/politza/tablist][github source]] +;; important commands: +;; - < shrink column +;; - > enlarge column +;; - s sort column +;; - / prefix for filter commands +;; / e edit filter, e.g. do not list auto-complete sub-packages in melpa: +;; / a ! Package =~ ac- +(use-package tablist + :ensure t + + :config + + ;; we need to kill tablist's binding in order to have ours run (see below) + (define-key tablist-minor-mode-map (kbd "q") nil) + (define-key tablist-minor-mode-map (kbd "q") 'tvd-close-window) + + (eval-after-load "tabulated-list" + '(progn + (add-hook 'tabulated-list-mode-hook + (lambda () + (tablist-minor-mode) + (local-set-key (kbd "Q") 'delete-other-windows) + (hl-line-mode)))))) + + +(provide 'init-tablist) +;;; init-tablist.el ends here diff --git a/lisp/init-test.el b/lisp/init-test.el new file mode 100644 index 0000000..2b47509 --- /dev/null +++ b/lisp/init-test.el @@ -0,0 +1,6 @@ +(message (format "DEBUG: default-text-scale installed: %s" (package-installed-p 'default-text-scale))) +(package-install 'default-text-scale) + + +(provide 'init-test) +;;; init-test.el ends here diff --git a/lisp/init-textmanipulation.el b/lisp/init-textmanipulation.el new file mode 100644 index 0000000..d23d3f6 --- /dev/null +++ b/lisp/init-textmanipulation.el @@ -0,0 +1,289 @@ +;; ** Text Manupilation +;; *** expand-region + +;; One of the best modes I ever discovered. Press C-= multiple times +;; to create a larger and larger region. I use C-0 (zero) because on a +;; german keyboard this is the same as C-= without pressing shift. + +(use-package expand-region + :config + (global-set-key (kbd "C-0") 'er/expand-region)) ; C-= without pressing shift on DE keyboard + +;; related to ER: +;; *** Mark, Copy, Yank Things + +;; For a long time this stuff was located here in my emacs config. As +;; it grew larger and larger I decided to put it into its own mode: +;; mark-copy-yank-things-mode, which can be found on github these +;; days. + +;; With this, you can quickly mark or copy or copy+yank things like +;; words, ip's, url's, lines or defun's with one key binding. I use +;; this permanently and couldn't live without it anymore. + +;; A special feature is the copy+yanking, this is something vi offers: +;; go to a line, press yy, then p and the current line will be yanked +;; below. Prefix with a number and copy+yank more lines. This is +;; really cool and (in vi) often used. So, with this mode, I can use +;; it with emacs as well. For example, say you edit a configuration +;; file and added a complicated statement. Next you need to add +;; another very similar statement. Instead of entering it again, you +;; just hit and the current line appears as a copy +;; below. Change the differences and you're done! + +(use-package mark-copy-yank-things-mode + :ensure nil ;; instaled by el-get + :config + (mark-copy-yank-things-global-mode) + + ;; The mode has a rather impractical prefix since it's published on + ;; github and therefore must be written in a way not to disturb other + ;; modes. However, I myself need those simple prefixes: + (define-key mark-copy-yank-things-mode-map (kbd "C-c") 'mcyt-copy-map) + (define-key mark-copy-yank-things-mode-map (kbd "M-a") 'mcyt-mark-map) + ;; I use the default yank map + + ;; With this I put the last thing copied into a register 'c. I can + ;; then later yank this using C-v anytime without browsing the + ;; kill-ring if I kill things between yanking. So, C-v always inserts + ;; the last copied thing, while C-y yanks the last thing killed, which + ;; might be something else. + (advice-add 'mcyt--copy-thing + :after + '(lambda (&rest args) + (with-temp-buffer + (yank) + (copy-to-register 'c (point-min) (point-max))))) + + (defun tvd-insert-c-register () + "Paste from the global yank register, fed by mcyt-copy-*" + (interactive) + (insert-register 'c)) + + (global-set-key (kbd "C-v") 'tvd-insert-c-register) + + ;; copy a real number and convert it to german punctuation upon + ;; yanking, so I can do some calculations in 'calculator, copy the + ;; result NNN.NN and paste it into my online banking formular, where + ;; it appears as NNN,NN. + (defun tvd-mcyt-copy-euro (&optional arg) + "Copy euro at point into kill-ring and convert to german punctuation" + (interactive "P") + (mcyt--blink-and-copy-thing 'mcyt-beginning-of-ip 'mcyt-end-of-ip arg) + (with-temp-buffer + (yank) + (beginning-of-buffer) + (while (re-search-forward "\\." nil t) + (replace-match ",")) + (kill-region (point-min) (point-max)))) + + (define-key mcyt-copy-map (kbd "g") 'tvd-mcyt-copy-euro)) + + +;; -------------------------------------------------------------------------------- + +;; *** change-inner + +;; I use change-inner with a prefix key and some wrappers around +;; mark-copy-yank-things-mode, which is related to change-inner +;; and expand-region. + +;; first some functions: +(defun tvd-ci (beg end &optional ins) + "change-inner simulator which works with symbols instead of strings. + +BEG and END must be executable elisp symbols moving (point). Everything +in between will be killed. If INS is non-nil, it will be inserted then." + (interactive) + (let ((B nil)) + (funcall beg) + (setq B (point)) + (funcall end) + (kill-region B (point)) + (when ins + (insert ins)))) + +;; define some custom change-inner's based on mcyt mode +(when (fboundp 'mcyt-beginning-of-comment-block) + (defun tvd-ci-comment () + "\"change inner\" a whole comment [block]." + (interactive) + (tvd-ci 'mcyt-beginning-of-comment-block + 'mcyt-end-of-comment-block + (format "%s;# " comment-start))) + + (defun tvd-ci-quote () + "\"change inner\" quoted text." + (interactive) + (tvd-ci 'mcyt-beginning-of-quote + 'mcyt-end-of-quote)) + + (defun tvd-ci-word () + "\"change inner\" a word (like cw in vi)." + (interactive) + (tvd-ci 'mcyt-beginning-of-symbol + 'mcyt-end-of-symbol))) + +;; some more +(defun tvd-ci-line () + "\"change inner\" a whole line." + (interactive) + (tvd-ci 'beginning-of-line + 'end-of-line)) + +(defun tvd-ci-paragraph () + "\"change inner\" a whole paragraph." + (interactive) + (tvd-ci 'backward-paragraph + 'forward-paragraph)) + +(defun tvd-ci-buffer () + "\"change inner\" a whole buffer." + (interactive) + (tvd-ci 'point-min + 'point-max)) + +(defun tvd-ci-sexp () + "\"change inner\" a whole sexp." + (interactive) + (er/mark-inside-pairs) + (call-interactively 'kill-region)) + +;; [[https://github.com/magnars/change-inner.el][github source]]: +(use-package change-inner + :config + + ;; Define ALT_R (AltGR) + i as my prefix command for change-inner stuff. + ;; Since I use a german keyboard, this translates to →. + ;; I'll refrence it here now as + (define-prefix-command 'ci-map) + (global-set-key (kbd "→") 'ci-map) + + ;; typing the prefix key twice calls the real change-inner + (define-key ci-map (kbd "→") 'change-inner) ;; + + (when (fboundp 'mcyt-beginning-of-comment-block) + (define-key ci-map (kbd "c") 'tvd-ci-comment) ;; + (define-key ci-map (kbd "¢") 'tvd-ci-comment) ;; + + (define-key ci-map (kbd "q") 'tvd-ci-quote) ;; + (define-key ci-map (kbd "@") 'tvd-ci-quote) ;; + + (define-key ci-map (kbd "w") 'tvd-ci-word) ;; + (define-key ci-map (kbd "ł") 'tvd-ci-word)) ;; + + (define-key ci-map (kbd "l") 'tvd-ci-line) ;; + + (define-key ci-map (kbd "s") 'tvd-ci-sexp) ;; + (define-key ci-map (kbd "ſ") 'tvd-ci-sexp) ;; + + (define-key ci-map (kbd "p") 'tvd-ci-paragraph) ;; + (define-key ci-map (kbd "þ") 'tvd-ci-paragraph) ;; + + (define-key ci-map (kbd "a") 'tvd-ci-buffer) ;; + (define-key ci-map (kbd "æ") 'tvd-ci-buffer)) ;; + +;; -------------------------------------------------------------------------------- +;; *** Rotate text + +;; This one is great as well, I use it to toggle flags and such stuff +;; in configs or code with just one key binding. + +;; Source: [[http://nschum.de/src/emacs/rotate-text/][nschum.de]] + +;; (autoload 'rotate-text "rotate-text" nil t) +;; (autoload 'rotate-text-backward "rotate-text" nil t) + +(use-package rotate-text + :ensure nil ;; installed by el-get + :config + + ;; my toggle list + (setq rotate-text-words '(("width" "height") + ("left" "right" "top" "bottom") + ("ja" "nein") + ("off" "on") + ("true" "false") + ("nil" "t") + ("yes" "no"))) + + ;; C-t normally used by transpose-chars, so, unbind it first + (global-unset-key (kbd "C-t")) + + ;; however, we cannot re-bind it globally since then it could not be + ;; used in org-mode for org-todo (see below) FIXME: I only use the "t" + ;; short command anymore, so C-t would be free now, wouldn't it? + (add-something-to-mode-hooks + '(c c++ cperl vala web emacs-lisp python ruby) + '(lambda () + (local-set-key (kbd "C-t") 'rotate-text)))) + +;; -------------------------------------------------------------------------------- + +;; *** Word wrapping +;; same as word-wrap but without the fringe which I hate the most! +(add-something-to-mode-hooks '(tex text eww) 'visual-line-mode) + +;; however, it's required when coding, so enable it globally +;; overwritten by visual-line-mode above for specifics +(setq word-wrap t) +;; -------------------------------------------------------------------------------- + +;; *** Viking Mode + +;; Delete stuff fast. Press the key multiple times - delete more +;; things. Inspired by expand-region. Written by myself. + +(use-package viking-mode + :ensure nil ;; installed by el-get + :config + (viking-global-mode) + (setq viking-greedy-kill nil) + (define-key viking-mode-map (kbd "M-d") 'viking-repeat-last-kill)) +;; -------------------------------------------------------------------------------- + +;; *** HTMLize + +;; extracted from debian package emacs-goodies-el-35.2+nmu1, since +;; there's no other source left. Generates a fontified html version of +;; the current buffer, however it looks. + +(use-package htmlize + :config + (setq htmlize-output-type "inline-css")) + + +;; *** iEdit (inline edit multiple searches) + +;; Edit all occurences of something at once. Great for re-factoring. + +;; (global-set-key (kbd "C-c C-e") 'iedit-mode) +(setq tvd-buffer-undo-list nil) + +(use-package iedit + :config + + ;; Keep buffer-undo-list as is while iedit is active, that is, as long + ;; as I am inside iedit I can undo/redo current occurences. However, + ;; if I leave iedit and issue the undo command, ALL changes made with + ;; iedit are undone, whereas the default behaviour would be to go + ;; through every change made iside iedit, which I hate. + + ;; iedit doesn't provide a customizable flag to configure it's undo + ;; behavior, so, I modify it myself using defadvice. + + (advice-add 'iedit-mode :before '(lambda (&rest args) ;; save current + (setq tvd-buffer-undo-list buffer-undo-list))) + + (advice-add 'iedit-mode :after '(lambda (&rest args) ;; restore previously saved + (setq buffer-undo-list tvd-buffer-undo-list)))) + +(add-hook 'text-mode-hook + (lambda () (electric-indent-local-mode -1))) + +(add-hook 'fundamental-mode-hook + (lambda () (electric-indent-local-mode -1))) + + +(provide 'init-textmanipulation) +;;; init-textmanipulation.el ends here diff --git a/lisp/init-textscale.el b/lisp/init-textscale.el new file mode 100644 index 0000000..a111103 --- /dev/null +++ b/lisp/init-textscale.el @@ -0,0 +1,25 @@ +;;; ** increase fontsize with ctrl-+ and ctrl-- + +;; I use those bindings everywhere (firefox, terminal, etc), and in +;; emacs as well. +;; using https://github.com/purcell/default-text-scale +(use-package default-text-scale + :config + (defun tvd-global-font-size-bigger () + "Make font size larger." + (interactive) + ;; (text-scale-increase 0.5) + (default-text-scale-increase)) + + (defun tvd-global-font-size-smaller () + "Change font size back to original." + (interactive) + ;; (text-scale-increase -0.5) + (default-text-scale-decrease)) + + (global-set-key (kbd "C-+") 'tvd-global-font-size-bigger) + (global-set-key (kbd "C--") 'tvd-global-font-size-smaller)) + + +(provide 'init-textscale) +;;; init-textscale.el ends here diff --git a/lisp/init-tramp.el b/lisp/init-tramp.el new file mode 100644 index 0000000..47bcd74 --- /dev/null +++ b/lisp/init-tramp.el @@ -0,0 +1,56 @@ +;; *** tramp mode + +;; Edit remote files, one of the best things in emacs. I use it every day heavily. + +;; Sample: C-x-f /$host:/$file ($host as of .ssh/config or direct, $file including completion) + +;; doku: [[http://www.gnu.org/software/tramp/][gnu.org]] +;; use tramp version, see: +;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=39399 + +;; (setq tramppkg (expand-file-name "el-get/tramp" tvd-config-dir)) + +;; doesnt work: +;; FIXME: see current error + +;; (el-get-bundle tramp +;; :type git +;; :url "https://git.savannah.gnu.org/git/tramp.git" +;; ;; tramp-loaddefs.el uses `tramp-verion' before it's defined, +;; ;; work around this by loading trampver.el first. +;; :autoloads ("trampver.el" "tramp-loaddefs.el") +;; :checkout "ELPA-2.6.0.3" +;; :build `(("make" "autoloads"))) + +;; Current error: +;; Error (el-get): while initializing tramp: Symbol’s function definition +;; is void: tramp-compat-rx [2 times] + +;;; Tramp version fix +;; for now I have to install tramp from elpa manually, because +;; use-package doesn't do it, since it's already loaded on startup +;; (because enabled by default). +;; +;; The el-get version above doesn't work as well, it leads to mixed +;; loading of system tramp and git tramp. +;; +;; FIXME: find out how to force use-package to install and use elpa tramp! +;; (setq tramppkg (expand-file-name "tramp-2.6.0.3" package-user-dir)) +;;; (setq tramppkg (expand-file-name "tramp" tvd-sitelisp-dir)) + +;; (message (format "tramp: installed: %s" (package-installed-p 'tramp))) + +(use-package tramp + ;;:load-path tramppkg + :ensure nil + :config + (setq tramp-default-method "ssh" + tramp-default-user nil + ;;tramp-verbose 9 + ido-enable-tramp-completion t)) + +;; see also backup section in system.el + + +(provide 'init-tramp) +;;; init-tramp.el ends here diff --git a/lisp/init-ui.el b/lisp/init-ui.el new file mode 100644 index 0000000..41e1ffc --- /dev/null +++ b/lisp/init-ui.el @@ -0,0 +1,177 @@ +;;; *** highlight todo keywords (such as FIXME) + +;; Absolutely needed! + +(use-package fic-mode + :config + (add-something-to-mode-hooks '(c c++ cperl vala web emacs-lisp ruby python yaml) 'fic-mode)) + + +;;; *** UNDO Tree Mode + +;; Better undo, with redo support. + +;; C-_ (`undo-tree-undo') +;; Undo changes. +;; +;; C-: (`undo-tree-redo') +;; Redo changes. +;; +;; more: see undo-tree.el +(use-package undo-tree + :config + + ;; use always + (global-undo-tree-mode) + + ;; M-_ catched by Xmonad + (global-set-key (kbd "C-:") 'undo-tree-redo)) ; C-: == REDO C-_ == UNDO + + + +;;; *** Smarter M-x Mode (smex) + +;; This is really cool and I don't know how I could ever live without it. +(use-package smex + :config + (smex-initialize) + (global-set-key (kbd "M-x") 'smex) + (global-set-key (kbd "M-X") 'smex-major-mode-commands)) + + + +;;; *** Smarter Search + +;; test, replace isearch-forward-regexp first only. +;; dir: ivy/ +(use-package swiper + :config + (setq ivy-wrap t) + (global-set-key "\C-s" 'swiper)) + + + +;;; *** Which Func +;; display current function - if any - in mode line +(add-something-to-mode-hooks + '(c c++ cperl vala makefile ruby shell-script python go) + 'which-func-mode) + + + +;;; *** Show current-line in the Fringe +(use-package fringe-current-line + :config + (global-fringe-current-line-mode 1) + + ;; also change the color (matching the mode line + ;; (set-face-attribute 'fringe nil :foreground "NavyBlue") + ) + + + +;;; *** Save cursor position + +;; So the next time I start emacs and open a file I were editing +;; previously, (point) will be in the exact places where it was +;; before. +(save-place-mode 1) + + + +;;; *** Hightligt TABs + +;; not a mode, but however: higlight TABs in certain modes + +(defface extra-whitespace-face + '((t (:background "pale green"))) + "Used for tabs and such.") + +(defvar tvd-extra-keywords + '(("\t" . 'extra-whitespace-face))) + +(add-something-to-mode-hooks '(c c++ vala cperl emacs-lisp python shell-script ruby) + (lambda () (font-lock-add-keywords nil tvd-extra-keywords))) + + +;;; *** Browse kill-ring + +;; when active use n and p to browse, to select, it's the same +;; as and I never really use it... + +(use-package browse-kill-ring + :config + (setq browse-kill-ring-highlight-current-entry t + browse-kill-ring-highlight-inserted-item t)) + + +;;; *** goto-last-change + +;; Very handy, jump to last change[s]. + +(use-package goto-last-change + :config + (global-set-key (kbd "C-b") 'goto-last-change)) + + + +;;; *** Bookmarks + +;; I use the builtin bookmark feature quite a lot and am happy with +;; it. Especially at work, where many files are located in large +;; path's on remote storage systems, it great to jump quickly to such +;; places. + +;; everytime bookmark is changed, automatically save it +(setq bookmark-save-flag 1 + bookmark-version-control t) + +;; I use the same aliases as in apparix for bash (since I'm used to them) +(defalias 'to 'bookmark-jump) +(defalias 'bm 'bookmark-set) +(defalias 'bl 'bookmark-bmenu-list) + + + +;;; *** which-key + +;; One of the best unobstrusive modes for key help ever. Just start +;; entering a key chord, prefix or whatever, and it pops a small +;; buffer (on the right side in my case) showing the avialable keys to +;; press from there along with the associated functions. + +(use-package which-key + :config + (which-key-mode) + (which-key-setup-side-window-right)) + + + +;;; *** Beacon mode (pointer blink) +;; Source: [[https://github.com/Malabarba/beacon][beacon mode]] + +;; Blink the cursor shortly when moving across large text sections or +;; when changing windows. That way it is easier to find the current +;; editing position. + +(use-package beacon + :config + + (setq beacon-blink-duration 0.1 + beacon-blink-when-point-moves-vertically 10 + beacon-color 0.3) + + (add-hook 'beacon-dont-blink-predicates + (lambda () (bound-and-true-p novel-mode))) + + (beacon-mode)) + + + +;;; other aliases +;; show available colors: +(defalias 'colors 'list-colors-display) + + +(provide 'init-ui) +;;; init-ui.el ends here diff --git a/lisp/init-webmode.el b/lisp/init-webmode.el new file mode 100644 index 0000000..ad16f54 --- /dev/null +++ b/lisp/init-webmode.el @@ -0,0 +1,61 @@ +;; *** web-mode (JS, HTML, CSS combined) + +;; Web development is shit. Tech involved is a mess, and in most cases +;; intermixed. web-mode provides a great fix for this: it handles +;; HTML, CSS and Javascript in the same buffer very well. + +;; See: [[http://web-mode.org/][web-mode.org]] + +(use-package web-mode + :mode "\\.html\\'" + :mode "\\.tpl\\'" + :mode "\\.php\\'" + :mode "\\.sp\\'" + :mode "\\.erb\\'" + :mode "\\.mustache\\'" + :mode "\\.js\\'" + + :config + (setq web-mode-markup-indent-offset 2) + (setq web-mode-css-indent-offset 2) + (setq web-mode-code-indent-offset 2) + (setq web-mode-style-padding 1) + (setq web-mode-script-padding 1) + (setq web-mode-block-padding 0) + (setq web-mode-enable-auto-pairing t) + (setq web-mode-enable-auto-expanding t) + + ;; some handy html code inserters + (defun html-insert-p() + (interactive) + (web-mode-element-wrap "p")) + + (defun html-insert-li() + (interactive) + (web-mode-element-wrap "li")) + + (defun html-insert-ul() + (interactive) + (web-mode-element-wrap "ul")) + + (defun html-insert-b() + (interactive) + (web-mode-element-wrap "b")) + + ;; convert a text list into a html list. + (defun html-listify (beg end) + (interactive "r") + (save-excursion + (let* ((lines (split-string (buffer-substring beg end) "\n" t))) + (delete-region beg end) + (insert "
        \n") + (while lines + (insert "
      • ") + (insert (pop lines)) + (insert "
      • \n")) + (insert "
      \n")))) + ) + + +(provide 'init-webmode) +;;; init-webmode.el ends here diff --git a/lisp/init-windowmgmt.el b/lisp/init-windowmgmt.el new file mode 100644 index 0000000..8f805bb --- /dev/null +++ b/lisp/init-windowmgmt.el @@ -0,0 +1,219 @@ +;; NOTDONE +;;; ** WINDOW management stuff +;; *** resize windows by keyboard + +;; Very practical: resize windows easily. + +;; hit C-c C-r then use cursor keys to resize, to finish +(use-package windresize + :config + (global-set-key (kbd "C-c C-r") 'windresize)) + +;; *** switch windows with MS-WINDOWS key +(use-package windmove + :ensure nil + :config + (windmove-default-keybindings 'super) + (setq windmove-wrap-around t)) + +;; *** M-o switch window or buffer + +;; The key M-o has different functions depending on context: +;; +;; If there's only 1 window open, switch to the last active. +;; +;; If there are 2 windows open, switch back and forth between the +;; two. In order to flip them, Use M-O (that is: ALT+shift+o). +;; +;; And if there are more than 2 windows open, call the hydra, which +;; allows to switch to another window using the arrow keys. The hydra +;; stays for 1 second unles an arrow key has been pressed. So, I can +;; press multiple arrow keys in a row as long as I'm fast enough +;; between key presses. If I stop,the hydra disappears and I end up +;; whereever I was last. +;; +;; Also, within the hydra 'o' jumps to the last active window and +;; 'f' flips all windows. + +;; from https://github.com/lukhas/buffer-move +(use-package buffer-move) + +(defun tvd-previous-window (&optional ignore) + "Toggle between the last two selected windows." + (interactive) + (let ((win (get-mru-window t t t))) + (unless win (error "Last window not found.")) + (select-window win))) + +;; via +;; [[http://whattheemacsd.com/buffer-defuns.el-02.html][whattheemacs.d]]: +;; exchange left with right buffer (or up and down), love it. +(defun tvd-flip-windows () + "Rotate your windows" + (interactive) + (cond ((not (> (count-windows)1)) + (message "You can't rotate a single window!")) + (t + (setq i 1) + (setq numWindows (count-windows)) + (while (< i numWindows) + (let* ( + (w1 (elt (window-list) i)) + (w2 (elt (window-list) (+ (% i numWindows) 1))) + (b1 (window-buffer w1)) + (b2 (window-buffer w2)) + (s1 (window-start w1)) + (s2 (window-start w2)) + ) + (set-window-buffer w1 b2) + (set-window-buffer w2 b1) + (set-window-start w1 s2) + (set-window-start w2 s1) + (setq i (1+ i))))))) + +(when (and (fboundp 'windmove-up) (fboundp 'buf-move-up) (fboundp 'defhydra)) + (defhydra hydra-switch-windows (:color pink: :timeout 2.5) + " +Switch to buffer: ← ↑ → ↓ | _o_: previous | _f_: flip | MOVE: _u_: up _d_: down _l_: left _r_: right" + ("" windmove-up nil) + ("" windmove-down nil) + ("" windmove-left nil) + ("" windmove-right nil) + ("o" tvd-previous-window nil :color blue) + ("f" tvd-flip-windows nil :color blue) + ("u" buf-move-up nil :color blue) + ("d" buf-move-down nil :color blue) + ("l" buf-move-left nil :color blue) + ("r" buf-move-right nil :color blue) + ("q" nil nil :color red)) + + ;; via [[http://mbork.pl/2017-02-26_other-window-or-switch-buffer][mbork]] + ;; modified to call the above hydra if there are more than 2 windows + (defun other-window-or-switch-buffer () + "Call `other-window' if more than one window is visible, switch +to next buffer otherwise." + (interactive) + (if (one-window-p) + (switch-to-buffer nil) + (progn + (if (= (length (window-list)) 2) + (other-window 1) + (hydra-switch-windows/body))))) + + (global-set-key (kbd "M-o") 'other-window-or-switch-buffer) + + ;; M-o doesn't work when using emacs via Win->RDP->VNC->X11->Xmonad, + ;; so fall back to C-o. + (global-set-key (kbd "C-o") 'other-window-or-switch-buffer) + + ;; Use only in X11 emacs - setting M-O inside console causes and + ;; to stop working properly, for whatever reasons. + (if (display-graphic-p) + (global-set-key (kbd "M-O") 'tvd-flip-windows))) + +;; -------------------------------------------------------------------------------- +;; *** Split window to 4 parts + +(defun tvd-quarter-windows () + (interactive) + (split-window-vertically) + (split-window-horizontally) + (windmove-down) + (split-window-horizontally)) + +(global-set-key (kbd "C-x 4") 'tvd-quarter-windows) + +;; -------------------------------------------------------------------------------- + +;; *** Remember and Restore Window Configurations - winner mode + +(winner-mode 1) + +;; keybindings: C-c left - winner-undo +;; keybindings: C-c right - winner-redo + + +;; *** Window Hydra + +;; brightness wrappers +(defun tvd-bg-brighter () + (interactive) + (doremi-increment-background-color-1 ?v -1)) + +(defun tvd-bg-darker () + (interactive) + (doremi-increment-background-color-1 ?v 1)) + +(defun tvd-pre-resize () + "Called as pre execute hook py hydra-windows-resize/body and +executes the called key once, so that no key press gets lost from +hydra-windows (a,s,d,w)" + (interactive) + (let + ((key (car (reverse (append (recent-keys) nil))))) + (cond + ((eq key ?a) + (shrink-window-horizontally 1)) + ((eq key ?d) + (enlarge-window-horizontally 1)) + ((eq key ?w) + (shrink-window 1)) + ((eq key ?s) + (enlarge-window 1))))) + +(when (fboundp 'defhydra) + (defhydra hydra-windows-resize (:color pink :pre (tvd-pre-resize)) + ;; small sub hydra for window resizing, it leaves as much room for + ;; windows as possible + " +_a_ || _d_ |---| _w_ --- _s_ =" + ("a" shrink-window-horizontally nil) + ("d" enlarge-window-horizontally nil) + ("w" shrink-window nil) + ("s" enlarge-window nil) + ("q" nil nil :color red)) + + (defhydra hydra-windows (:color blue) + " + +^Window Management^ +^^------------------------------------------------------------------------ +_+_ Increase Font | _-_ Decrease Font Resize ^ ^ _w_ ^ ^ +_f_: Flip Windows ^^ Current _a_ ^ ^ _d_ +_4_: Quarter Windows ^^ Window: ^ ^ _s_ ^ ^ +_u_: Windows Undo +_r_: Windows Redo ^^ _l_: Adjust Background brighter +_i_: Invert Colors ^^ _b_: Adjust Background darker + +_h_: Toggle Highlight Line Mode ^^ _e_: Eyebrowse Workspaces (C-x C-x) +_n_: Toogle Line Number Mode + +^^------------------------------------------------------------------------ +Reach this hydra with +^^------------------------------------------------------------------------ + +" + ("+" tvd-global-font-size-bigger nil :color pink) + ("-" tvd-global-font-size-smaller nil :color pink) + ("f" tvd-flip-windows nil) + ("4" tvd-quarter-windows nil) + ("u" winner-undo nil) + ("r" winner-redo nil) + ("i" tvd-invert nil) + ("b" tvd-bg-darker nil :color pink) + ("l" tvd-bg-brighter nil :color pink) + ("a" hydra-windows-resize/body nil) + ("d" hydra-windows-resize/body nil) + ("w" hydra-windows-resize/body nil) + ("s" hydra-windows-resize/body nil) + ("e" hydra-eyebrowse/body nil) + ("h" hl-line-mode nil) + ("n" linum-mode nil) + ("q" nil nil :color red)) + + (global-set-key (kbd "C-x w") 'hydra-windows/body)) + + + +(provide 'init-windowmgmt) +;;; init-windowmgmt.el ends here diff --git a/lisp/init-workspaces.el b/lisp/init-workspaces.el new file mode 100644 index 0000000..f65eed2 --- /dev/null +++ b/lisp/init-workspaces.el @@ -0,0 +1,72 @@ +;; *** Eyebrowse Workspaces +;; workspace configuration, like desktops. Seems to be a good implementation, w/o save though + + +(use-package eyebrowse + :init + ;; "prefix key" has to be set before loading, keep in mind, that + ;; if you change it here, you need to change it below as well! + (setq eyebrowse-keymap-prefix (kbd "C-x C-x")) + (global-unset-key (kbd "C-x C-x")) + + :config + + (defun tvd-new-eyebrowse-workspace () + "Create new scratch buffer and ask for a name" + (interactive) + (eyebrowse-rename-window-config (eyebrowse--get 'current-slot) (read-string "Workspace: ")) + (autoscratch-buffer)) + + ;; always enable + (eyebrowse-mode t) + + (setq eyebrowse-new-workspace 'tvd-new-eyebrowse-workspace + eyebrowse-switch-back-and-forth t + eyebrowse-wrap-around t + eyebrowse-mode-line-style t + eyebrowse-slot-format "%s:init") + + ;; add a hydra, just in case, it contains hydra commands and hints + ;; about the actual key bindings, it's also being refered from + ;; hydra-window/body with "e" + (when (fboundp 'defhydra) + (defhydra hydra-eyebrowse (:color blue) + " +^Eyebrowse Workspace Management^ +^^-------------------------------------- +_l_: last window (C-x C-x C-x) +_n_: next window (C-x C-x ) +_p_: prev window (C-x C-x ) +_x_: close workspace (C-x C-x q) +_t_: rename workspace (C-x C-x t) +_c_: create workspace (C-x C-x n) + +Use C-x C-x to access eyebrowse directly. +" + ("l" eyebrowse-last-window-config nil) + ("n" eyebrowse-next-window-config nil) + ("p" eyebrowse-prev-window-config nil) + ("x" eyebrowse-close-window-config nil) + ("t" eyebrowse-rename-window-config nil) + ("c" eyebrowse-create-window-config nil) + ("q" nil nil :color red))) + + ;;Modifying the eyebrowse keymap directly doesn't work because it's + ;; not setup correctly. I sent a pull request to fix this: + ;; https://github.com/wasamasa/eyebrowse/pull/94, however, vaslilly choosed + ;; not to accept it. So, I need to configure the whole chords for every + ;; function I use AND set the "prefix key" using the weird way above + ;; (before loading). + (global-set-key (kbd "C-x C-x C-x") 'eyebrowse-last-window-config) + (global-set-key (kbd "C-x C-x ") 'eyebrowse-next-window-config) + (global-set-key (kbd "C-x C-x ") 'eyebrowse-prev-window-config) + (global-set-key (kbd "C-x C-x q") 'eyebrowse-close-window-config) + (global-set-key (kbd "C-x C-x t") 'eyebrowse-rename-window-config) + (global-set-key (kbd "C-x C-x n") 'eyebrowse-create-window-config) + (global-set-key (kbd "C-x C-x ?") 'hydra-eyebrowse/body)) + +;; There's also some face config, see defcustom at end of file! + + +(provide 'init-workspaces) +;;; init-workspaces.el ends here diff --git a/lisp/init-xmodmap.el b/lisp/init-xmodmap.el new file mode 100644 index 0000000..8170d80 --- /dev/null +++ b/lisp/init-xmodmap.el @@ -0,0 +1,15 @@ +;; *** Xmodmap Mode + +;; the shortest mode ever, [[https://www.emacswiki.org/emacs/XModMapMode][via emacswiki]]. + +(define-generic-mode 'xmodmap-mode + '(?!) + '("add" "clear" "keycode" "keysym" "pointer" "remove") + '(("[0-9]+" . 'font-lock-variable-name-face)) + '("[xX]modmap\\(rc\\)?\\'") + nil + "Simple mode for xmodmap files.") + + +(provide 'init-xmodmap) +;;; init-xmodmap.el ends here diff --git a/lisp/init-yaml.el b/lisp/init-yaml.el new file mode 100644 index 0000000..2976303 --- /dev/null +++ b/lisp/init-yaml.el @@ -0,0 +1,75 @@ +;; *** Yaml Mode + +(use-package yaml-mode + :mode "\\.yml\\'" + :mode "\\.yaml\\'" + :mode "\\.j2\\'" + + :config + (defun yaml-point-in-comment-p () + "Test if character at cursor is a comment." + (interactive) + (save-excursion + (right-char) ; visible cursor position needed + (nth 4 (syntax-ppss)))) + + (defun yaml-goto-beginning() + "Move cursor to beginning of yaml key on current line" + (interactive) + (beginning-of-line) + (skip-chars-forward " ")) + + (defun yaml-next-block() + "Jump to the next yaml block with the same indent as the current one" + (interactive) + (yaml-goto-beginning) + (next-line) + (while (or (looking-at " ") (yaml-point-in-comment-p)) + (next-line)) + (yaml-goto-beginning)) + + (defun yaml-prev-block() + "Jump to the previous yaml block with the same indent as the current one" + (interactive) + (yaml-goto-beginning) + (previous-line) + (while (or (looking-at " ") (yaml-point-in-comment-p)) + (previous-line)) + (yaml-goto-beginning)) + + (defun yaml-level-down() + "Jump down to the next yaml child block" + (interactive) + (yaml-goto-beginning) + (next-line) + (while (or (not (looking-at " ")) (yaml-point-in-comment-p)) + (next-line)) + (skip-chars-forward " ")) + + (defun yaml-level-up() + "Jump up to the next yaml parent block" + (interactive) + (yaml-goto-beginning) + (left-char) + (previous-line) + (while (or (looking-at " ") (yaml-point-in-comment-p)) + (previous-line)) + (yaml-goto-beginning)) + + (define-key yaml-mode-map "\C-m" 'newline-and-indent) + + ;; works, but then cannot be used for re-indenting :( + ;; (outline-minor-mode) + ;; (define-key yaml-mode-map (kbd "TAB") 'outline-toggle-children) + ;; (setq outline-regexp "^ *\\([A-Za-z0-9_-]*: *[>|]?$\\|-\\b\\)") + (define-key smartparens-mode-map (kbd "") nil) + (define-key smartparens-mode-map (kbd "") nil) + + (define-key yaml-mode-map (kbd "") 'yaml-next-block) + (define-key yaml-mode-map (kbd "") 'yaml-prev-block) + (define-key yaml-mode-map (kbd "") 'yaml-level-down) + (define-key yaml-mode-map (kbd "") 'yaml-level-up)) + + +(provide 'init-yaml) +;;; init-yaml.el ends here diff --git a/lisp/kubernetes.el b/lisp/kubernetes.el new file mode 100644 index 0000000..39e847f --- /dev/null +++ b/lisp/kubernetes.el @@ -0,0 +1,15 @@ +(defun tvd-kill-ro-buffer() + (interactive) + (when buffer-read-only + (kill-buffer-and-window))) + +(when nil + (use-package kubel + :after (vterm) + :config (kubel-vterm-setup) + :bind + ("q" . tvd-kill-ro-buffer))) + + +(provide 'init-70-kubernetes) +;;; init-70-kubernetes.el ends here