From 37cf6598d15916f27e5eba08ce95d6cf7aebdc95 Mon Sep 17 00:00:00 2001 From: Edward Steere Date: Sun, 12 May 2019 10:24:15 +0000 Subject: [PATCH] Initial commit of Quiescent Emacs --- .gitignore | 40 + .mc-lists.el | 145 + conf/system-vars.el | 37 + custom.el | 73 + cython/cython-mode.el | 65 + early-init | 83 + eshell/alias | 1 + idutils/Makefile.am | 3 + idutils/Makefile.in | 1431 +++++ idutils/id-utils.map | 109 + idutils/idutils.el | 69 + init.el | 57 + lisp/quiescent/Makefile | 78 + lisp/quiescent/avy.el | 63 + lisp/quiescent/bare-init.el | 30 + lisp/quiescent/buffer-disjunction.el | 37 + lisp/quiescent/buffer-set-difference.el | 32 + lisp/quiescent/code-explorer.el | 48 + lisp/quiescent/compile-on-guix.sh | 3 + lisp/quiescent/compile-on-nixos.sh | 3 + lisp/quiescent/cross-link-mode.el | 34 + lisp/quiescent/drawing-interface.el | 48 + lisp/quiescent/editing.el | 22 + lisp/quiescent/emacs-lisp.el | 512 ++ lisp/quiescent/eshell.el | 129 + lisp/quiescent/git.el | 166 + lisp/quiescent/haskell.el | 63 + lisp/quiescent/hippie-expand-function.el | 28 + lisp/quiescent/install-on-mac.sh | 3 + lisp/quiescent/javascript.el | 227 + lisp/quiescent/maven.el | 46 + lisp/quiescent/other-window.el | 38 + lisp/quiescent/sql.el | 78 + lisp/quiescent/start-server.el | 4 + lisp/too-long-lines-mode/.gitattributes | 1 + lisp/too-long-lines-mode/.gitignore | 1 + lisp/too-long-lines-mode/README.md | 5 + lisp/too-long-lines-mode/example.html | 16 + .../screenshots/example.png | Bin 0 -> 92274 bytes .../too-long-lines-mode.el | 186 + lisp/wiki/Makefile | 17 + lisp/wiki/linkd.el | 1280 +++++ lisp/wiki/rotate-text.el | 224 + mis-recipes/org/to-docx/Makefile | 20 + schemas.xml | 4 + schemas/maven-v4_0_0.rnc | 461 ++ snippets/emacs-lisp-mode/act | 6 + snippets/emacs-lisp-mode/ar | 5 + snippets/emacs-lisp-mode/as | 5 + snippets/emacs-lisp-mode/d | 6 + snippets/emacs-lisp-mode/diff | 5 + snippets/emacs-lisp-mode/for | 5 + snippets/emacs-lisp-mode/get | 5 + snippets/emacs-lisp-mode/l | 5 + snippets/emacs-lisp-mode/ln | 5 + snippets/emacs-lisp-mode/m | 5 + snippets/emacs-lisp-mode/mh | 5 + snippets/emacs-lisp-mode/mq | 5 + snippets/emacs-lisp-mode/mr | 5 + snippets/emacs-lisp-mode/ms | 5 + snippets/emacs-lisp-mode/pc | 6 + snippets/emacs-lisp-mode/pl | 6 + snippets/emacs-lisp-mode/put | 5 + snippets/emacs-lisp-mode/r | 5 + snippets/emacs-lisp-mode/rn | 5 + snippets/emacs-lisp-mode/sp | 5 + snippets/java-mode/cc | 10 + snippets/java-mode/f | 5 + snippets/java-mode/get | 11 + snippets/java-mode/getset | 21 + snippets/java-mode/mc | 7 + snippets/java-mode/p | 5 + snippets/java-mode/s | 5 + snippets/java-mode/set | 11 + snippets/js2-mode/asdeq | 5 + snippets/js2-mode/aseq | 5 + snippets/js2-mode/asprox | 5 + snippets/js2-mode/cfn | 5 + snippets/js2-mode/chai | 8 + snippets/js2-mode/describe | 7 + snippets/js2-mode/i | 5 + snippets/js2-mode/it | 7 + snippets/js2-mode/lambda | 5 + snippets/js2-mode/lambda1 | 5 + snippets/js2-mode/log | 5 + snippets/js2-mode/probe | 11 + snippets/js2-mode/str | 5 + snippets/js2-mode/th | 5 + snippets/nxml-mode/dep | 9 + snippets/nxml-mode/id | 7 + snippets/nxml-mode/otm | 7 + snippets/nxml-mode/prop | 7 + snippets/nxml-mode/scp | 5 + snippets/scala-mode/describe | 7 + snippets/scala-mode/it | 7 + snippets/scala-mode/log | 5 + snippets/scala-mode/probe | 8 + snippets/scala-mode/test | 7 + snippets/sql-mode/br | 7 + snippets/sql-mode/sel | 6 + snippets/sql-mode/selw | 7 + snippets/sql-mode/up | 6 + snippets/typescript-mode/asdeq | 5 + snippets/typescript-mode/aseq | 5 + snippets/typescript-mode/describe | 7 + snippets/typescript-mode/i | 5 + snippets/typescript-mode/it | 7 + snippets/typescript-mode/log | 5 + snippets/typescript-mode/probe | 11 + snippets/typescript-mode/str | 5 + snippets/typescript-mode/tell | 5 + startup.org | 5031 +++++++++++++++++ 112 files changed, 11463 insertions(+) create mode 100644 .gitignore create mode 100644 .mc-lists.el create mode 100644 conf/system-vars.el create mode 100644 custom.el create mode 100644 cython/cython-mode.el create mode 100644 early-init create mode 100644 eshell/alias create mode 100644 idutils/Makefile.am create mode 100644 idutils/Makefile.in create mode 100644 idutils/id-utils.map create mode 100644 idutils/idutils.el create mode 100644 init.el create mode 100644 lisp/quiescent/Makefile create mode 100644 lisp/quiescent/avy.el create mode 100644 lisp/quiescent/bare-init.el create mode 100644 lisp/quiescent/buffer-disjunction.el create mode 100644 lisp/quiescent/buffer-set-difference.el create mode 100644 lisp/quiescent/code-explorer.el create mode 100755 lisp/quiescent/compile-on-guix.sh create mode 100755 lisp/quiescent/compile-on-nixos.sh create mode 100644 lisp/quiescent/cross-link-mode.el create mode 100644 lisp/quiescent/drawing-interface.el create mode 100644 lisp/quiescent/editing.el create mode 100644 lisp/quiescent/emacs-lisp.el create mode 100644 lisp/quiescent/eshell.el create mode 100644 lisp/quiescent/git.el create mode 100644 lisp/quiescent/haskell.el create mode 100644 lisp/quiescent/hippie-expand-function.el create mode 100755 lisp/quiescent/install-on-mac.sh create mode 100644 lisp/quiescent/javascript.el create mode 100644 lisp/quiescent/maven.el create mode 100644 lisp/quiescent/other-window.el create mode 100644 lisp/quiescent/sql.el create mode 100644 lisp/quiescent/start-server.el create mode 100644 lisp/too-long-lines-mode/.gitattributes create mode 100644 lisp/too-long-lines-mode/.gitignore create mode 100644 lisp/too-long-lines-mode/README.md create mode 100644 lisp/too-long-lines-mode/example.html create mode 100644 lisp/too-long-lines-mode/screenshots/example.png create mode 100644 lisp/too-long-lines-mode/too-long-lines-mode.el create mode 100644 lisp/wiki/Makefile create mode 100644 lisp/wiki/linkd.el create mode 100644 lisp/wiki/rotate-text.el create mode 100644 mis-recipes/org/to-docx/Makefile create mode 100644 schemas.xml create mode 100644 schemas/maven-v4_0_0.rnc create mode 100644 snippets/emacs-lisp-mode/act create mode 100644 snippets/emacs-lisp-mode/ar create mode 100644 snippets/emacs-lisp-mode/as create mode 100644 snippets/emacs-lisp-mode/d create mode 100644 snippets/emacs-lisp-mode/diff create mode 100644 snippets/emacs-lisp-mode/for create mode 100644 snippets/emacs-lisp-mode/get create mode 100644 snippets/emacs-lisp-mode/l create mode 100644 snippets/emacs-lisp-mode/ln create mode 100644 snippets/emacs-lisp-mode/m create mode 100644 snippets/emacs-lisp-mode/mh create mode 100644 snippets/emacs-lisp-mode/mq create mode 100644 snippets/emacs-lisp-mode/mr create mode 100644 snippets/emacs-lisp-mode/ms create mode 100644 snippets/emacs-lisp-mode/pc create mode 100644 snippets/emacs-lisp-mode/pl create mode 100644 snippets/emacs-lisp-mode/put create mode 100644 snippets/emacs-lisp-mode/r create mode 100644 snippets/emacs-lisp-mode/rn create mode 100644 snippets/emacs-lisp-mode/sp create mode 100644 snippets/java-mode/cc create mode 100644 snippets/java-mode/f create mode 100644 snippets/java-mode/get create mode 100644 snippets/java-mode/getset create mode 100644 snippets/java-mode/mc create mode 100644 snippets/java-mode/p create mode 100644 snippets/java-mode/s create mode 100644 snippets/java-mode/set create mode 100644 snippets/js2-mode/asdeq create mode 100644 snippets/js2-mode/aseq create mode 100644 snippets/js2-mode/asprox create mode 100644 snippets/js2-mode/cfn create mode 100644 snippets/js2-mode/chai create mode 100644 snippets/js2-mode/describe create mode 100644 snippets/js2-mode/i create mode 100644 snippets/js2-mode/it create mode 100644 snippets/js2-mode/lambda create mode 100644 snippets/js2-mode/lambda1 create mode 100644 snippets/js2-mode/log create mode 100644 snippets/js2-mode/probe create mode 100644 snippets/js2-mode/str create mode 100644 snippets/js2-mode/th create mode 100644 snippets/nxml-mode/dep create mode 100644 snippets/nxml-mode/id create mode 100644 snippets/nxml-mode/otm create mode 100644 snippets/nxml-mode/prop create mode 100644 snippets/nxml-mode/scp create mode 100644 snippets/scala-mode/describe create mode 100644 snippets/scala-mode/it create mode 100644 snippets/scala-mode/log create mode 100644 snippets/scala-mode/probe create mode 100644 snippets/scala-mode/test create mode 100644 snippets/sql-mode/br create mode 100644 snippets/sql-mode/sel create mode 100644 snippets/sql-mode/selw create mode 100644 snippets/sql-mode/up create mode 100644 snippets/typescript-mode/asdeq create mode 100644 snippets/typescript-mode/aseq create mode 100644 snippets/typescript-mode/describe create mode 100644 snippets/typescript-mode/i create mode 100644 snippets/typescript-mode/it create mode 100644 snippets/typescript-mode/log create mode 100644 snippets/typescript-mode/probe create mode 100644 snippets/typescript-mode/str create mode 100644 snippets/typescript-mode/tell create mode 100644 startup.org diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eabff02 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Ignored File Types +*.cache +*.data +*.elc +*.eld +*.jpg +*.out +*.zip +*~ + +# Folders +.cask/ +url/ +4clojure-answers/ +request/ +semanticdb/ +server/ +cedet/ +predictive/ +elpa/ + +# Files +org-settings/inbox.org +conf/quiescent-system-choice.el +conf/mail-settings.el +eshell/history +eshell/lastdir +abbrev_defs +bookmarks +history +org-clock-save.el +places +recentf +snippets/**/.yas*.el +srecode-map.el +startup.el +tramp +company-statistics-cache.el +recentf +.org-id-locations \ No newline at end of file diff --git a/.mc-lists.el b/.mc-lists.el new file mode 100644 index 0000000..4401ebd --- /dev/null +++ b/.mc-lists.el @@ -0,0 +1,145 @@ +;; This file is automatically generated by the multiple-cursors extension. +;; It keeps track of your preferences for running commands with multiple cursors. + +(setq mc/cmds-to-run-for-all + '( + Custom-newline + artist-backward-char + artist-forward-char + artist-next-line + artist-previous-line + artist-toggle-first-arrow + auto-completion-self-insert + backward-sexp + backward-up-list + beginning-of-buffer + c-electric-brace + c-electric-colon + c-electric-delete-forward + c-electric-paren + c-electric-semi&comma + c-electric-slash + complete-or-cycle-word-at-point + complete-symbol + completion-at-point + completion-backward-delete-char + completion-delete-char + completion-fill-paragraph + completion-kill-line + completion-kill-sexp + completion-resolve-then-run-command + completion-self-insert + completion-yank + composable-kill-region + composable-save-region + dabbrev-completion + delete-horizontal-space + dired-mark + dired-next-line + down-list + ediff-dir-diff-copy-file + electric-pair-delete-pair + eval-expression + forward-sexp + gnus-summary-mark-as-read-forward + haskell-indentation-common-electric-command + haskell-indentation-newline-and-indent + helm-show-kill-ring + ibuffer-mark-for-delete + ibuffer-mark-forward + indent-for-tab-command + iy-go-to-char--command + jdee-kill-camel-tok + js2-line-break + kill-sentence + kill-sexp + magit-discard + magit-stage + mark-defun + mark-sexp + markdown-outdent-or-delete + org-beginning-of-line + org-cycle + org-delete-char + org-end-of-line + org-force-self-insert + org-forward-sentence + org-kill-line + org-open-line + org-return + org-schedule + org-self-insert-command + org-shiftdown + org-yank + other-frame + paredit-backslash + paredit-backward + paredit-backward-up + paredit-doublequote + paredit-forward + paredit-forward-delete + paredit-forward-down + paredit-forward-kill-word + paredit-forward-slurp-sexp + paredit-kill + paredit-open-round + paredit-raise-sexp + paredit-semicolon + picture-clear-column + picture-clear-line + picture-end-of-line + picture-self-insert + proced-mark + python-nav-backward-up-list + quiescent-correct-linting-errors-at-point + quiescent-hydra-iy-go-to-char/nil + rjsx-delete-creates-full-tag + save-some-buffers + sh-assignment + smartscan-symbol-go-backward + sp-backward-delete-char + sp-backward-kill-word + sp-delete-char + sp-forward-slurp-sexp + sp-kill-hybrid-sexp + sp-kill-word + sp-raise-sexp + sp-remove-active-pair-overlay + transpose-sexps + transpose-words + typescript-insert-and-indent + undefined + wdired-capitalize-word + wdired-downcase-word + wdired-finish-edit + wdired-next-line + wdired-previous-line + wgrep-finish-edit + yaml-electric-dash-and-dot + yaml-electric-backspace + yas-expand + )) + +(setq mc/cmds-to-run-once + '( + dired-do-delete + dired-toggle-read-only + end-of-buffer + ibuffer-do-delete + ibuffer-do-kill-on-deletion-marks + ibuffer-toggle-sorting-mode + iedit-mode + iedit-switch-to-mc-mode + keyboard-escape-quit + kmacro-end-macro + kmacro-start-macro + mark-whole-buffer + overwrite-mode + proced-send-signal + switch-to-buffer + wgrep-change-to-wgrep-mode + window-jump-down + window-jump-left + window-jump-up + yank-rectangle + )) diff --git a/conf/system-vars.el b/conf/system-vars.el new file mode 100644 index 0000000..72b26af --- /dev/null +++ b/conf/system-vars.el @@ -0,0 +1,37 @@ +;;; system-vars -- A collection of variables which control which parts +;;; of my Emacs are active per machine. + +;;; Commentary: + +;; On my Mac: +;; To get aspell working as the back end for flyspell-mode I had to make a symbolic link: +;; `sudo ln -s /usr/local/bin/aspell /usr/bin/aspell' + +;; A simple list of variables listed here which are specific to this +;; particular computer. I use these variables to control switches +;; which are only applicable to Windows and specific default +;; configuration. + +;;; Code: + +(defvar quiescent-work-machine nil + "Whether I'm on my work machine..") + +(defvar quiescent-exwm-machine nil + "Whether this machine uses EXWM as it's window manager.") + +(defvar quiescent-exwm-multiple-monitors nil + "Whether this machine uses EXWM and has multiple monitors.") + +(defvar quiescent-home-pc-linux nil + "Whether this computer is my Home Linux PC.") + +(defvar quiescent-macbook nil + "Whether this computer is my macbook.") + +(defun quiescent-computer-with-langtool-installed-p () + "Produce t if this computer is using langtool." + (or quiescent-macbook)) + +(provide 'system-vars) +;;; system-vars.el ends here diff --git a/custom.el b/custom.el new file mode 100644 index 0000000..f2330ed --- /dev/null +++ b/custom.el @@ -0,0 +1,73 @@ +;;; custom --- custom variables, faces etc. from the emacs custom system -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(custom-set-variables + ;; custom-set-variables was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(LaTeX-command "latex") + '(ansi-color-faces-vector + [default bold shadow italic underline bold bold-italic bold]) + '(avy-keys '(49 50 51 52 53 54 55 56 57 48)) + '(bmkp-last-as-first-bookmark-file "~/.emacs.d/bookmarks") + '(completion-styles '(flex basic partial-completion emacs22)) + '(demo-it--insert-text-speed :fast) + '(ecb-options-version "2.50") + '(ede-project-directories t) + '(ediff-window-setup-function 'ediff-setup-windows-plain) + '(enable-recursive-minibuffers t) + '(eshell-history-size 1024) + '(global-subword-mode t) + '(haskell-tags-on-save t) + '(icomplete-compute-delay 0) + '(icomplete-delay-completions-threshold 400) + '(icomplete-max-delay-chars 0) + '(icomplete-minibuffer-setup-hook nil) + '(icomplete-show-matches-on-no-input t) + '(large-file-warning-threshold 50000000) + '(magit-tag-arguments nil) + '(mail-source-delete-incoming 10) + '(max-specpdl-size 32000) + '(minibuffer-auto-raise nil) + '(minibuffer-depth-indicate-mode t) + '(org-agenda-files '("~/.emacs.d/org-settings/work.org")) + '(org-src-window-setup 'current-window) + '(package-selected-packages + '(docker-tramp ggtags use-package dockerfile forge org-re-reveal expand-region sql-indent ensime realgud ivy cobol-mode key-chords doom-modeline writeroom-mode centered-cursor-mode json-mode avy doom-themes visible-mark intero flymake-haskell-multi flymake-hlint ereader zerodark-theme bbdb zenburn-theme esh-autosuggest company-ghc js2-highlight-vars paredit sass-mode ob-mongo restclient editorconfig wgrep-ag wgrep crontab-mode tide xref-js2 rjsx-mode esup ace-jump-mode company-statistics company-tern company-mode hindent diff-hl ag emr org-tree-slide demo-it slack 4clojure org-beautify-theme org-bullets org-plus-contrib nlinum exec-path-from-shell sbt-mode projectile zerodark make-it-so smex recentf-ext indium dot-mode julia-mode tern jade js2-refactor jade-mode git-timemachine eros helm cider slime epresent pt kanban plantuml-mode ox-reveal htmlize flx-ido flx typescript-mode yasnippet yaml-mode window-jump web-mode vimgolf use-package-chords typing trie tiny thingatpt+ synonyms swiper swap-regions sr-speedbar smartscan smartparens smart-mode-line rust-mode rainbow-delimiters pp+ powershell page-break-lines ob-http neotree multiple-cursors markdown-mode magit macrostep langtool lacarte keyfreq js2-mode javap-mode javadoc-lookup jammer iy-go-to-char info+ iedit icomplete+ ibuffer-projectile hydra haskell-mode groovy-mode graphviz-dot-mode goto-chg god-mode gnuplot fuzzy-match fsharp-mode frame-cmds fireplace firefox-controller feature-mode fancy-narrow ecb doremi-frm dockerfile-mode docker dired-subtree dired-filter csv-mode crosshairs composable chess bookmark+ auto-highlight-symbol auctex apropos-fn+var aggressive-indent ace-window)) + '(read-buffer-completion-ignore-case t) + '(read-file-name-completion-ignore-case t) + '(send-mail-function 'smtpmail-send-it) + '(show-paren-mode t) + '(smtpmail-smtp-server "smtp.gmail.com") + '(smtpmail-smtp-service 25) + '(tags-revert-without-query t) + '(tide-imenu-flatten t)) + +;; Rainbow delimeters theme from: https://ericscrivner.me/2015/06/better-emacs-rainbow-delimiters-color-scheme/ +(custom-set-faces + ;; custom-set-faces was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(default ((t (:inherit nil :stipple nil :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 105 :width normal :foundry "unknown" :family "Hack")))) + '(col-highlight ((t (:background "dim gray")))) + '(eros-result-overlay-face ((t (:slant italic)))) + '(hl-line ((t (:inherit nil :background "#2c323b")))) + '(js2-highlight-vars-face ((t (:underline t)))) + '(js2-highlight-vars-second-face ((t (:underline "white")))) + '(rainbow-delimiters-depth-1-face ((t (:foreground "light steel blue")))) + '(rainbow-delimiters-depth-2-face ((t (:foreground "cornflower blue")))) + '(rainbow-delimiters-depth-3-face ((t (:foreground "pale turquoise")))) + '(rainbow-delimiters-depth-4-face ((t (:foreground "turquoise")))) + '(rainbow-delimiters-depth-5-face ((t (:foreground "dodger blue")))) + '(rainbow-delimiters-depth-6-face ((t (:foreground "royal blue")))) + '(rainbow-delimiters-depth-7-face ((t (:foreground "dark slate blue")))) + '(rainbow-delimiters-depth-8-face ((t (:foreground "navy")))) + '(tide-hl-identifier-face ((t (:inherit nil :underline t))))) + +(provide 'custom) +;;; custom ends here diff --git a/cython/cython-mode.el b/cython/cython-mode.el new file mode 100644 index 0000000..66a2d5e --- /dev/null +++ b/cython/cython-mode.el @@ -0,0 +1,65 @@ +;; Cython mode + +;; Load python-mode if available, otherwise use builtin emacs python package +(when (not(require 'python-mode nil t)) + (require 'python)) + +(add-to-list 'auto-mode-alist '("\\.pyx\\'" . cython-mode)) +(add-to-list 'auto-mode-alist '("\\.pxd\\'" . cython-mode)) +(add-to-list 'auto-mode-alist '("\\.pxi\\'" . cython-mode)) + + +(defun cython-compile () + "Compile the file via Cython." + (interactive) + (let ((cy-buffer (current-buffer))) + (with-current-buffer + (compile compile-command) + (set (make-local-variable 'cython-buffer) cy-buffer) + (add-to-list (make-local-variable 'compilation-finish-functions) + 'cython-compilation-finish))) + ) + +(defun cython-compilation-finish (buffer how) + "Called when Cython compilation finishes." + ;; XXX could annotate source here + ) + +(defvar cython-mode-map + (let ((map (make-sparse-keymap))) + ;; Will inherit from `python-mode-map' thanks to define-derived-mode. + (define-key map "\C-c\C-c" 'cython-compile) + map) + "Keymap used in `cython-mode'.") + +(defvar cython-font-lock-keywords + `(;; new keywords in Cython language + (,(regexp-opt '("by" "cdef" "cimport" "cpdef" "ctypedef" "enum" "except?" + "extern" "gil" "include" "nogil" "property" "public" + "readonly" "struct" "union" "DEF" "IF" "ELIF" "ELSE") 'words) + 1 font-lock-keyword-face) + ;; C and Python types (highlight as builtins) + (,(regexp-opt '("NULL" "bint" "char" "dict" "double" "float" "int" "list" + "long" "object" "Py_ssize_t" "short" "size_t" "void") 'words) + 1 font-lock-builtin-face) + ;; cdef is used for more than functions, so simply highlighting the next + ;; word is problematic. struct, enum and property work though. + ("\\<\\(?:struct\\|enum\\)[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" + 1 py-class-name-face) + ("\\/dev/null; then \ + $$dry rm -f elc-stamp; \ + $(MAKE) $(AM_MAKEFLAGS) elc-stamp; \ + $$dry rmdir elc-lock; \ + else \ + while test -d elc-lock && test -z "$$dry"; do sleep 1; done; \ + $$dry test -f elc-stamp; exit $$?; \ + fi; \ + else : ; fi +install-dist_lispLISP: $(dist_lisp_LISP) $(ELCFILES) + @$(NORMAL_INSTALL) + @if test "$(EMACS)" != no && test -n "$(lispdir)"; then \ + $(MKDIR_P) "$(DESTDIR)$(lispdir)"; \ + list='$(dist_lisp_LISP)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + $(am__strip_dir) \ + echo " $(dist_lispLISP_INSTALL) '$$d$$p' '$(DESTDIR)$(lispdir)/$$f'"; \ + $(dist_lispLISP_INSTALL) "$$d$$p" "$(DESTDIR)$(lispdir)/$$f" || exit $$?; \ + if test -f $${p}c; then \ + echo " $(dist_lispLISP_INSTALL) '$${p}c' '$(DESTDIR)$(lispdir)/$${f}c'"; \ + $(dist_lispLISP_INSTALL) "$${p}c" "$(DESTDIR)$(lispdir)/$${f}c" || exit $$?; \ + else : ; fi; \ + done; \ + else : ; fi + +uninstall-dist_lispLISP: + @$(NORMAL_UNINSTALL) + @test "$(EMACS)" != no && test -n "$(lispdir)" || exit 0; \ + list='$(dist_lisp_LISP)'; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + files="$$files "`echo "$$files" | sed 's|$$|c|'`; \ + dir='$(DESTDIR)$(lispdir)'; $(am__uninstall_files_from_dir) + +clean-lisp: + -rm -f elc-stamp $(ELCFILES) +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LISP) $(ELCFILES) +installdirs: + for dir in "$(DESTDIR)$(lispdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-lisp mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dist_lispLISP + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dist_lispLISP + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-lisp \ + distclean distclean-generic distdir dvi dvi-am html html-am \ + info info-am install install-am install-data install-data-am \ + install-dist_lispLISP install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am uninstall uninstall-am uninstall-dist_lispLISP + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/idutils/id-utils.map b/idutils/id-utils.map new file mode 100644 index 0000000..a3bb5c8 --- /dev/null +++ b/idutils/id-utils.map @@ -0,0 +1,109 @@ +# Welcome to the mkid language mapper. +# +# The format of each line is: +# +# [options] +# +# Filenames are matched top-to-bottom against the patterns, and the +# first match is chosen. The special language `IGNORE' means that +# this file should be ignored by mkid. The options are +# language-specific command-line options to mkid. +# +# If a file name doesn't match any pattern, it is assigned the default +# language. The default language may be specified here with the +# special pattern `**', or overridden from the mkid command-line with +# the `--default-lang=LANG' option. +# +# The special pattern `***' means to include the named file that +# immediately follows. If no file is named, then the default system +# language mapper file (i.e., this file) is included. + +# Default language +** IGNORE # Although this is listed first, + # the default language pattern is + # logically matched last. + +# Backup files +*~ IGNORE +*.bak IGNORE +*.bk[0-9] IGNORE + +# SCCS files +[sp].* IGNORE + +# C dependencies created by automake +*/.deps/* IGNORE + +*.h C +*.h.in C +*.in.h C +*.H C++ +*.hh C++ +*.hpp C++ +*.hxx C++ + +*.l C +*.lex C +*.y C +*.yacc C + +*.c C +*.C C++ +*.cc C++ +*.cpp C++ +*.cxx C++ + +*.java Java + +ChangeLog* Cdoc + +*.[sS] asm --comment=; +*.asm asm --comment=; + +# [nt]roff +*.[0-9] roff +*.ms roff +*.me roff +*.mm roff + +*.tex TeX +*.ltx TeX +*.texi texinfo +*.texinfo texinfo + +# portable object (i18n) +*.po po + +*.el lisp +*.elc lisp +*.lisp lisp +*.scm lisp + +*.am make +*.mk make +Makefile make +Makefile.* make +GNUmakefile make + +*.doc text +*.txt text +*.org text + +*.m4 m4 +*.ac m4 +configure.in m4 + +*.PL perl +*.pl perl +*.pm perl + +*.rb perl + +*.Z FILTER gzip -d <%s +*.gz FILTER gzip -d <%s +*.bz2 FILTER bzip2 -d <%s +*.xz FILTER xz -d <%s +*.lzma FILTER xz -d <%s + +# My own settings +*.js Java \ No newline at end of file diff --git a/idutils/idutils.el b/idutils/idutils.el new file mode 100644 index 0000000..3eeaabb --- /dev/null +++ b/idutils/idutils.el @@ -0,0 +1,69 @@ +;;; idutils.el --- emacs interface to `lid -R grep', a.k.a. `gid' +;;; Copyright (C) 1995-1996, 2006-2012 Free Software Foundation, Inc. +;;; Greg McGary . + +;; This file is part of GNU idutils. + +;; GNU idutils is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; GNU idutils is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; This package provides the tools meant to help editing PO files, +;;; as documented in the GNU idutils user's manual. See this manual +;;; for user documentation, which is not repeated here. + +;;; To install, merely put this file somewhere GNU Emacs will find it, +;;; then add the following lines to your .emacs file: +;;; +;;; (autoload 'gid "idutils" "run idutils' gid command" t) +;;; +;;; You may also adjust some customizations variables, below, by defining +;;; them in your .emacs file. + +(require 'compile) +(require 'thingatpt) + +(defvar gid-command "gid" "The command run by the gid function.") + +(defvar gid-mode-font-lock-keywords + '(("^\\(Compilation\\|Gid\\) \\(started\\|finished\\).*" + (0 '(face nil message nil help-echo nil mouse-face nil) t)))) + +(defvar gid-use-search-in-buffer-name t + "If non-nil, use the search string in the gid buffer's name.") + +(define-compilation-mode gid-mode "Gid" + "Specialization of compilation-mode for use with gid." + nil) + +;;;###autoload +(defun gid (args) + "Run gid, with user-specified ARGS, and collect output in a buffer. +While gid runs asynchronously, you can use the \\[next-error] command to +find the text that gid hits refer to. The command actually run is +defined by the gid-command variable." + (interactive (list (read-shell-command + (concat "Run " gid-command " (with args): ") (thing-at-point 'symbol)))) + (let (compile-command + (compilation-error-regexp-alist grep-regexp-alist) + (compilation-directory default-directory) + (gid-full-buffer-name (concat "*gid-" args "*"))) + (save-some-buffers (not compilation-ask-about-save) nil) + (compilation-start (concat gid-command " " args) 'gid-mode + (when gid-use-search-in-buffer-name + (function (lambda (ignore) + gid-full-buffer-name))) + (regexp-quote args)))) + +(provide 'idutils) + +;;; idutils.el ends here diff --git a/init.el b/init.el new file mode 100644 index 0000000..b622442 --- /dev/null +++ b/init.el @@ -0,0 +1,57 @@ +;;; .emacs --- My emacs root config file. + +;;; Commentary: +;; This file is now pointing at a literate program version of +;; my Emacs config in ~/.emacs.d/startup.org +;; +;; Important extra files which are required to be in ~/.emacs.d/ +;; - conf/system-vars.el +;; Defines a set of variables specific to the system which this +;; config is being run on (such as which OS is running.) +;; - org-settings/org-agenda-file-list.el +;; Sets the list of files which org should use for agenda on +;; this computer. +;; +;; There's an important and wierd fix which I have to make for OS X. +;; I need to define `x-max-tooltip-size'. I'm still not really sure +;; why, but I think that 50 should be fine. + +;;; Code: + +;; Send this to the dude on the mailing list: +;; (defcustom semantic-lex-spp-macro-max-length-to-save 200 +;; "Maximum length of an SPP macro before we opt to not save it." +;; :type 'integer +;; :group 'semantic) + +;; For debugging what gets compiled at startup +;;(debug-on-entry #'byte-compile) + +;; Custom variables etc. +(setq custom-file "~/.emacs.d/custom.el") +(load custom-file) + +(let ((file-name-handler-alist nil)) + + (add-to-list 'load-path (concat user-emacs-directory "lisp")) + (require 'org) + (org-reload) ;; This forces the overriden org to be loaded + + (let* ((org-babel-use-quick-and-dirty-noweb-expansion t) + (org-babel-noweb-error-all-langs t) + (default-directory "~/.emacs.d") + (config-source (expand-file-name "startup.org" + user-emacs-directory)) + (config-tangled (expand-file-name "startup.el" + user-emacs-directory)) + (config-compiled (expand-file-name "startup.elc" + user-emacs-directory))) + (when (or (not (file-exists-p config-tangled)) + (file-newer-than-file-p config-source config-tangled)) + (org-babel-tangle-file config-source config-tangled 'emacs-lisp)) + (when (null (byte-recompile-file config-tangled nil 0)) + (error "Compilation errors")) + (load-file config-compiled))) + +(put 'narrow-to-region 'disabled nil) +(put 'scroll-left 'disabled nil) diff --git a/lisp/quiescent/Makefile b/lisp/quiescent/Makefile new file mode 100644 index 0000000..a1bce75 --- /dev/null +++ b/lisp/quiescent/Makefile @@ -0,0 +1,78 @@ +EMACS_D_DIR ?=~/.emacs.d + +EMACS ?=emacs +EMACS_SERVER=--no-init --daemon +EMACS_CLIENT ?=emacsclient +EMACS_FLAGS= +EMACS_BARE_INIT=--eval '(load-file "bare-init.el")' +EMACS_STOP=--eval '(kill-emacs)' + +TARGETS=buffer-disjunction.elc \ + buffer-set-difference.elc \ + code-explorer.elc \ + cross-link-mode.elc \ + drawing-interface.elc \ + editing.elc \ + emacs-lisp.elc \ + eshell.elc \ + git.elc \ + haskell.elc \ + hippie-expand-function.elc \ + javascript.elc \ + maven.elc \ + other-window.elc \ + sql.elc + +compile: init_server $(TARGETS) compile_log + +start_server: + @echo "==========Starting Compilation Server==========" + $(EMACS) $(EMACS_FLAGS) $(EMACS_SERVER) + +init_server: + @echo "==========Init Server==========" + $(EMACS_CLIENT) $(EMACS_FLAGS) $(EMACS_BARE_INIT) + +stop_server: + @echo "==========Stopping Compilation Server==========" + $(EMACS_CLIENT) $(EMACS_STOP) + +all: clean start_server bootstrap build_wiki compile stop_server start_emacs + +all_windows: clean bootstrap compile + +# Temporarily use my staging repo to check out how it's working +get_external_dependencies: + @echo "==========No Externals ATM==========" + +build_external_dependencies: + @echo "==========No Externals ATM==========" + +build_wiki: + @echo "==========Building Wiki Libraries==========" + cd $(EMACS_D_DIR)/lisp/wiki && make all + +bootstrap: get_external_dependencies build_external_dependencies + +%.elc: %.el + @echo " $^" + @$(EMACS_CLIENT) --eval "(byte-compile-file \"$^\")" > aux-log.out + +compile_log: + @echo "==========Compile Log==========" + $(EMACS_CLIENT) --eval '(progn (switch-to-buffer "*Compile-Log*") (write-file (concat "$(EMACS_D_DIR)/lisp/quiescent/Compile-Log.out"))))' + @cat Compile-Log.out + +start_emacs: + @echo "==========Starting Emacs to Download Any Extras==========" + if [ -x "xinit" ]; then xinit; else $(EMACS) --debug-init --eval '(save-buffers-kill-terminal)'; fi + @echo "==========COMPLETED SUCCESSFULLY! :D==========" + +clean: + @echo "==========Cleaning Old Installation==========" + rm -f $(EMACS_D_DIR)/lisp/quiescent/*.elc + rm -f $(EMACS_D_DIR)/lisp/wiki/*.elc + rm -rf $(EMACS_D_DIR)/elpa + +# TODO: Compile the whole of realgud when done: +# find . -name "*.el" -exec ~/wip/emacs/src/emacs-27.0.50.1 --batch --eval '(progn (package-initialize) (load-file "~/.emacs.d/elpa/realgud-20190504.1238/realgud.elc") (byte-compile-file "{}"))' \; diff --git a/lisp/quiescent/avy.el b/lisp/quiescent/avy.el new file mode 100644 index 0000000..9b907c9 --- /dev/null +++ b/lisp/quiescent/avy.el @@ -0,0 +1,63 @@ +;;; quiescent/avy --- Avy configuration and convenience functions -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'use-package) + +(use-package avy + :ensure t + :config (progn + (setq avy-timeout-seconds 0.1) + (global-set-key (kbd "s-x") #'quiescent-avy-super-jump) + (global-set-key (kbd "s-.") #'avy-goto-word-or-subword-1) + (global-set-key (kbd "s-c") #'quiescent-avy-copy-symbol) + (global-set-key (kbd "s-C") #'quiescent-avy-copy-sexp))) + +(defun quiescent-avy-super-jump () + "Jump to a point with avy and then find the definition." + (interactive) + (progn + (avy-goto-word-or-subword-1) + (xref-find-definitions (thing-at-point 'symbol)))) + +(defun quiescent-avy-copy-symbol () + "Copy a word subword or symbol using avy." + (interactive) + (save-excursion + (save-window-excursion + (avy-goto-word-or-subword-1) + (mark-sexp) + (kill-ring-save (region-beginning) (region-end)))) + (yank)) + +(defun quiescent-avy-copy-sexp () + "Copy an sexp using avy. +Goes backward up list and then copies the sexp." + (interactive) + (save-excursion + (save-window-excursion + (avy-goto-word-or-subword-1) + (backward-up-list) + (mark-sexp) + (kill-ring-save (region-beginning) (region-end)))) + (yank)) + +(defun quiescent-goto-char (&optional str) + "Perform an isearch-like session with STR. +If candidates are numbered as you go and if you type the number +of a candidate then you can jump to it." + (interactive) + (let ((char (read-string (concat "search: " (or str ""))))) + (if (member (aref char 0) '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)) + (quiescent-jump-to-candidate char str) + (let ((search-string (concat str char))) + (quiescent-highlight-matches search-string) + (call-interactively (quiescent-goto-char search-string)))))) + + +(define-key isearch-mode-map (kbd "C-'") 'avy-isearch) + +(provide 'quiescent/avy) +;;; avy ends here diff --git a/lisp/quiescent/bare-init.el b/lisp/quiescent/bare-init.el new file mode 100644 index 0000000..118fcb6 --- /dev/null +++ b/lisp/quiescent/bare-init.el @@ -0,0 +1,30 @@ +;;; bare-init --- A bare bones startup of emacs for use in compilation. -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'package) + +(setq package-archives + (quote (("gnu" . "http://elpa.gnu.org/packages/") + ("melpa" . "http://melpa.org/packages/")))) + +(load-file "~/.emacs.d/conf/system-vars.el") +(if (file-exists-p "~/.emacs.d/conf/system-conf.el") + (load-file "~/.emacs.d/conf/system-conf.el") + (warn "No system-conf.el file exists! Please configure variables in ~/.emacs.d/conf/system-vars.el and put them in ~/.emacs.d/conf/system-conf.el")) + +(package-initialize) +(package-refresh-contents) +(unless (package-installed-p 'use-package) + (package-install 'use-package nil)) +(unless (package-installed-p 'use-package-chords) + (package-install 'use-package-chords nil)) + +(require 'use-package-chords) + +(add-to-list 'load-path "~/.emacs.d/lisp") + +(provide 'bare-init) +;;; bare-init ends here diff --git a/lisp/quiescent/buffer-disjunction.el b/lisp/quiescent/buffer-disjunction.el new file mode 100644 index 0000000..f3085f2 --- /dev/null +++ b/lisp/quiescent/buffer-disjunction.el @@ -0,0 +1,37 @@ +;;; buffer-disjunction --- produce the lines which aren't in both of two given buffers -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defvar *buffer-disjunction-results-buffer-name* "*disjunction-results*" + "The name of the buffer to display disjunction results in.") + +(require 'cl-lib) + +(defun buffer-disjunction-lines-from-buffer (buffer) + "Produce the lines in BUFFER as a list of strings." + (with-current-buffer buffer + (split-string (buffer-substring (point-min) (point-max)) + "\n" + " "))) + +(defun buffer-disjunction-buffer-disjunction (this-buffer that-buffer) + "Produce lines which are not in both THIS-BUFFER and THAT-BUFFER. + + Results are displayed in a new buffer controlled by + `*buffer-disjunction-results-buffer-name*'." + (interactive "bThis buffer: \nbThat buffer:") + (let* ((these-lines (buffer-disjunction-lines-from-buffer this-buffer)) + (those-lines (buffer-disjunction-lines-from-buffer that-buffer)) + (results-buffer (get-buffer *buffer-disjunction-results-buffer-name*)) + (diff (mapcar (lambda (x) (concat x "\n")) + (cl-set-difference these-lines those-lines)))) + (switch-to-buffer (if (not (bufferp results-buffer)) + (generate-new-buffer *buffer-disjunction-results-buffer-name*) + results-buffer)) + (erase-buffer) + (mapc #'insert diff))) + +(provide 'quiescent/buffer-disjunction) +;;; buffer-disjunction ends here diff --git a/lisp/quiescent/buffer-set-difference.el b/lisp/quiescent/buffer-set-difference.el new file mode 100644 index 0000000..c99ca2f --- /dev/null +++ b/lisp/quiescent/buffer-set-difference.el @@ -0,0 +1,32 @@ +;;; buffer-set-difference --- produce the lines which are in the first given buffer but not the second -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defvar *buffer-set-difference-results-buffer-name* "*this-but-not-that-results*" + "The name of the buffer to display this-but-not-that results in.") + +(require 'quiescent/buffer-disjunction) + +(defun buffer-set-difference (this-buffer that-buffer) + "Produce lines which are in THIS-BUFFER but not THAT-BUFFER. + + Results are displayed in a new buffer controlled by + `*buffer-set-difference-results-buffer-name*'." + (interactive "bThis buffer: \nbThat buffer:") + (let* ((these-lines (buffer-disjunction-lines-from-buffer this-buffer)) + (those-lines (mapcar (lambda (x) (cons x t)) + (buffer-disjunction-lines-from-buffer that-buffer))) + (results-buffer (get-buffer *buffer-set-difference-results-buffer-name*)) + (diff (mapcar (lambda (x) (concat x "\n")) + (cl-remove-if (lambda (x) (assoc x those-lines)) + these-lines)))) + (switch-to-buffer (if (not (bufferp results-buffer)) + (generate-new-buffer *buffer-set-difference-results-buffer-name*) + results-buffer)) + (erase-buffer) + (mapc #'insert diff))) + +(provide 'quiescent/buffer-set-difference) +;;; buffer-set-difference ends here diff --git a/lisp/quiescent/code-explorer.el b/lisp/quiescent/code-explorer.el new file mode 100644 index 0000000..4b2f6b6 --- /dev/null +++ b/lisp/quiescent/code-explorer.el @@ -0,0 +1,48 @@ +;;; quiescent/code-explorer --- A code exploring interface -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'hydra) +(require 'quiescent/avy) + +(defhydra hydra-explore-code (:pre (global-auto-highlight-symbol-mode t) + :post (global-auto-highlight-symbol-mode -1) + :color amaranth) + " + Code Explorer + ^Move^ ^Jump^ ^Project^ + ^^^^^^^^--------------------------------------------------------- + _f_: forward s-exp _B_: pop mark _s_: project grep + _b_: backward s-exp _m_: mark point _g_: goto file + _n_: forward list _j_: xref jump + _p_: backward list _P_: prev file + _d_: down list _x_: super jump + _u_: up list _i_: imenu + + _q_: quit + " + ("f" forward-sexp nil) + ("b" backward-sexp nil) + ("n" forward-list nil) + ("p" backward-list nil) + ("d" down-list nil) + ("u" up-list nil) + ("P" (pop-global-mark) nil) + ("B" (pop-to-mark-command) nil) + ("m" (push-mark) nil) + ("j" xref-find-definitions nil) + ("s" quiescent-vc-git-grep nil) + ("x" quiescent-avy-super-jump nil) + ("i" quiescent-helm-semantic-or-imenu nil) + ("q" nil nil) + ("g" project-find-file nil)) +(key-chord-define-global "qc" 'hydra-explore-code/body) + +(defun quiescent-helm-semantic-or-imenu () + "Do `helm-semantic-or-imenu' with arg as 0." + (interactive) + (call-interactively #'helm-semantic-or-imenu)) + +(provide 'quiescent/code-explorer) diff --git a/lisp/quiescent/compile-on-guix.sh b/lisp/quiescent/compile-on-guix.sh new file mode 100755 index 0000000..b9fd608 --- /dev/null +++ b/lisp/quiescent/compile-on-guix.sh @@ -0,0 +1,3 @@ +#/run/current-system/profile/bin/bash + +EMACS="emacs --dump-file=/gnu/store/avj6wplznrxwxwrw3104cwfm1808v5yz-emacs-27-1e155dcc8dcbaed926a1574bc543d404d2859866/libexec/emacs/27.0.50/x86_64-pc-linux-gnu/.emacs.pdmp-real" make all diff --git a/lisp/quiescent/compile-on-nixos.sh b/lisp/quiescent/compile-on-nixos.sh new file mode 100755 index 0000000..c6e0241 --- /dev/null +++ b/lisp/quiescent/compile-on-nixos.sh @@ -0,0 +1,3 @@ +#!/run/current-system/sw/bin/bash + +EMACS='emacs-27.0.50.1 --dump-file ~/wip/emacs/src/emacs-27.0.50.1.pdmp' make all diff --git a/lisp/quiescent/cross-link-mode.el b/lisp/quiescent/cross-link-mode.el new file mode 100644 index 0000000..66dfcda --- /dev/null +++ b/lisp/quiescent/cross-link-mode.el @@ -0,0 +1,34 @@ +;;; quiescent/cross-link-mode --- link back to a document which symbols originated from -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defvar quiescent-cross-link-linked-buffer nil + "The buffer which this buffer is cross linked to.") + +(make-variable-buffer-local 'quiescent-cross-link-linked-buffer) + +(defun quiescent-cross-link () + "Cross link symbols in the current buffer to another buffer." + (interactive) + (let ((search-symbol (thing-at-point 'symbol))) + (when (not quiescent-cross-link-linked-buffer) + (call-interactively 'quiescent-cross-link-set-cross-linked-buffer)) + (pop-to-buffer quiescent-cross-link-linked-buffer) + (goto-char (point-min)) + (search-forward search-symbol))) + +(defun quiescent-coss-link-reset () + "Reset the linked buffer for current buffer." + (interactive) + (setq quiescent-cross-link-linked-buffer nil)) + +(defun quiescent-cross-link-set-cross-linked-buffer (linked-buffer-name) + "Set the LINKED-BUFFER-NAME of the buffer to link this buffer to." + (interactive "bEnter buffer name to link to: ") + (setq quiescent-cross-link-linked-buffer linked-buffer-name)) + +(provide 'quiescent/cross-link-mode) +;;; cross-link-mode ends here + diff --git a/lisp/quiescent/drawing-interface.el b/lisp/quiescent/drawing-interface.el new file mode 100644 index 0000000..eb96019 --- /dev/null +++ b/lisp/quiescent/drawing-interface.el @@ -0,0 +1,48 @@ +;;; quiescent/drawing-interface --- An interface for simple drawing in ASCII -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'hydra) + +(defhydra hydra-drawing (:color pink + :pre (overwrite-mode) + :post (progn (whitespace-cleanup) + (overwrite-mode -1))) + "Hydra draw" + ("f" (hydra-drawing-forward-maintaining-line) "forward") + ("b" backward-char "backward") + ("p" (hydra-drawing-move-maintaining-column (lambda () (forward-line -1))) "previous line") + ("n" (hydra-drawing-move-maintaining-column 'next-line) "next line") + ("q" nil "quit" :color blue)) +(global-set-key (kbd "C-c C-c d") 'hydra-drawing/body) + +(defun hydra-drawing-move-maintaining-column (move-action) + "Execute `MOVE-ACTION' while attempting to mainting column position." + (let* ((column-before) + (column-after)) + (progn + (setq column-before (current-column)) + (when (= (point) (point-max)) + (newline) + (backward-char)) + (funcall move-action) + (setq column-after (current-column)) + (while (< column-after column-before) + (insert " ") + (setq column-after (1+ column-after)))))) + +(defun hydra-drawing-forward-maintaining-line () + "Move forward on character attempting to maintain the current line." + (let* ((line-before (line-number-at-pos))) + (if (= (point) (point-max)) + (insert " ") + (progn (forward-char) + (when (/= line-before (line-number-at-pos)) + (progn + (backward-char) + (insert " "))))))) + +(provide 'quiescent/drawing-interface) +;;; drawing-interface ends here diff --git a/lisp/quiescent/editing.el b/lisp/quiescent/editing.el new file mode 100644 index 0000000..73e763b --- /dev/null +++ b/lisp/quiescent/editing.el @@ -0,0 +1,22 @@ +;;; editing --- Useful commands for editing text -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +;;; Stefan Monnier . It is the opposite of fill-paragraph +(defun unfill-paragraph (&optional region) + "Unfill REGION, otherwise paragraph at point. + +i.e. the reverse of fill paragraph." + (interactive (progn (barf-if-buffer-read-only) '(t))) + (let ((fill-column (point-max)) + ;; This would override `fill-column' if it's an integer. + (emacs-lisp-docstring-fill-column t)) + (fill-paragraph nil region))) + +;; Handy key definition +(define-key global-map "\M-Q" 'unfill-paragraph) + +(provide 'quiescent/editing) +;;; editing ends here diff --git a/lisp/quiescent/emacs-lisp.el b/lisp/quiescent/emacs-lisp.el new file mode 100644 index 0000000..b3bfd83 --- /dev/null +++ b/lisp/quiescent/emacs-lisp.el @@ -0,0 +1,512 @@ +;;; emacs-lisp --- Convenience, modes and config for editing Emacs Lisp -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(eval-when-compile + (require 'cl)) + +(require 'use-package) + +(require 'eros) + +(defun quiescent-remove-flex () + "Remove the `flex' completion style from completion styles." + (setq-local completion-styles '(basic partial-completion emacs22))) + +(add-hook 'emacs-lisp-mode-hook #'eros-mode) + +(defvar quiescent-edebug-previous-value nil + "The previous value from evaling the last sexp.") + +(defun quiescent-store-last-edebug-value (orig-fun prev-val) + "Execute ORIG-FUN which should be `edebug-compute-previous-result'. + +Store PREV-VAL in variable." + (progn + (setq quiescent-edebug-previous-value (edebug-unwrap* prev-val)) + (funcall orig-fun prev-val))) + +(advice-add #'edebug-compute-previous-result :around #'quiescent-store-last-edebug-value) + +(setq eros-overlays-use-font-lock t) + +(defun quiescent-font-lock-by-mode (major-mode) + "Font lock the current buffer by MAJOR-MODE." + (delay-mode-hooks (funcall major-mode)) + (font-lock-default-function 'major-mode) + (font-lock-default-fontify-region (point-min) + (point-max) + nil)) + +(defun quiescent-edebug-add-eros () + "Make edebug print eros statements when debugging." + (when quiescent-edebug-previous-value + (eros--make-result-overlay (with-temp-buffer + (insert (format "%s" quiescent-edebug-previous-value)) + (quiescent-font-lock-by-mode 'emacs-lisp-mode) + (buffer-string)) + :where (point) + :duration eros-eval-result-duration))) + +(advice-add #'edebug-previous-result :after #'quiescent-edebug-add-eros) + +;; Inspired by: +;; http://superuser.com/questions/669701/emacs-disable-some-minibuffer-messages +(defmacro quiescent-suppress-eval-output (&rest body) + "Supress the output of eval around BODY." + `(cl-flet ((silence (fun value &optional eval-last-sexp-arg-internal) value)) + (unwind-protect + (progn + (advice-add #'elisp--eval-last-sexp-print-value :around #'silence) + ,(car body)) + (advice-remove #'elisp--eval-last-sexp-print-value #'silence)))) + +(defun quiescent-el-live-eval () + "Evaluate the whole buffer and add overlays to each form." + (when (eq major-mode 'emacs-lisp-mode) + (save-excursion + (goto-char (point-min)) + (let ((message-log-max nil)) + (quiescent-suppress-eval-output + (while (quiescent-forward-sexp-w/o-error) + (ignore-errors (eros-eval-last-sexp nil))) + (ignore-errors (eros-eval-last-sexp nil))))))) + +;; Begin patches from: https://raw.githubusercontent.com/circularuins/.emacs.d/master/elisp/cl-indent-patches.el + + +;;; $Header: /home/rpg/emacs/RCS/cl-indent-patches.el,v 1.1 2003/06/30 15:37:28 rpg Exp $ + +;; Extentions for more cl compatible indenting +;; this was in cl-indent.el but it conflicted with the distribution + +;;; Load the distribution lisp-mode and cl-indent.el BEFORE loading this file. + +;;; Original version by rst +;;; Change Log +;;; 10/6/88 salem - Fixes for CL indentation style (CLtM as reference) +;;; 6/13/89 bromley - fixed common-lisp-indent-function to indent backquoted forms correctly. +;;; 6/15/89 bromley - removed bogus indentations for flet and labels + +(require 'cl-indent) + +(load-library "cl-indent") ; defines the common-lisp-indent-function properties +(cl-loop for symbol being the symbols + for cl-indent-rule = (get symbol 'common-lisp-indent-function) + for elisp-equivalent = (intern-soft (concat "cl-" (symbol-name symbol))) + when (and cl-indent-rule elisp-equivalent (fboundp elisp-equivalent)) + do (put elisp-equivalent 'common-lisp-indent-function cl-indent-rule)) + +(if (not (boundp 'lisp-indent-maximum-backtracking)) + (load-library "cl-indent")) + +(setq lisp-indent-function 'common-lisp-indent-function) + +(defun common-lisp-indent-function (indent-point state) + (let ((normal-indent (current-column))) + ;; Walk up list levels until we see something + ;; which does special things with subforms. + (let ((depth 0) + ;; Path describes the position of point in terms of + ;; list-structure with respect to contining lists. + ;; `foo' has a path of (0 4 1) in `((a b c (d foo) f) g)' + (path ()) + ;; set non-nil when somebody works out the indentation to use + calculated + (last-point indent-point) + ;; the position of the open-paren of the innermost containing list + (containing-form-start (elt state 1)) + ;; the column of the above + sexp-column) + ;; Move to start of innermost containing list + (goto-char containing-form-start) + (setq sexp-column (current-column)) + ;; Look over successively less-deep containing forms + (while (and (not calculated) + (< depth lisp-indent-maximum-backtracking)) + (let ((containing-sexp (point))) + (forward-char 1) + (parse-partial-sexp (point) indent-point 1 t) + ;; Move to the car of the relevant containing form + (let (tem function method) + (if (not (looking-at "\\sw\\|\\s_")) + ;; This form doesn't seem to start with a symbol + (setq function nil method nil) + (setq tem (point)) + (forward-sexp 1) + (setq function (downcase (buffer-substring tem (point)))) + (goto-char tem) + (setq tem (intern-soft function) + method (get tem 'common-lisp-indent-function)) + (cond ((and (null method) + (string-match ":[^:]+" function)) + ;; The pleblisp package feature + (setq function (substring function + (1+ (match-beginning 0))) + method (get (intern-soft function) + 'common-lisp-indent-function))) + ((and (null method)) + ;; backwards compatibility + (setq method (get tem 'lisp-indent-function))))) + (let ((n 0)) + ;; How far into the containing form is the current form? + (if (< (point) indent-point) + (while (condition-case () + (progn + (forward-sexp 1) + (if (>= (point) indent-point) + nil + (parse-partial-sexp (point) + indent-point 1 t) + (setq n (1+ n)) + t)) + (error nil)))) + (setq path (cons n path))) + + ;; backwards compatibility. + (cond ((null function)) + ((null method) + (if (null (cdr path)) + ;; (package prefix was stripped off above) + (setq method (cond ((string-match "\\`def" + function) + '(4 (&whole 4 &rest 1) &body)) + ((string-match "\\`\\(with\\|do\\)-" + function) + '(4 &body)))))) + ;; backwards compatibility. Bletch. + ((eq method 'defun) + (setq method '(4 (&whole 4 &rest 1) &body)))) + + (cond ((and (eql (char-after (1- containing-sexp)) ?\') ; patched to only do this for ' and not `. + (not (eql (char-after (- containing-sexp 2)) ?\#))) + ;; No indentation for "'(...)" elements + (setq calculated (1+ sexp-column))) + ((eql (char-after (1- containing-sexp)) ?\#) + ;; "#(...)" + (setq calculated (1+ sexp-column))) + ((null method)) + ((integerp method) + ;; convenient top-level hack. + ;; (also compatible with lisp-indent-function) + ;; The number specifies how many `distinguished' + ;; forms there are before the body starts + ;; Equivalent to (4 4 ... &body) + (setq calculated (cond ((cdr path) + normal-indent) + ((<= (car path) method) + ;; `distinguished' form + (list (+ sexp-column 4) + containing-form-start)) + ((= (car path) (1+ method)) + ;; first body form. + (+ sexp-column lisp-body-indent)) + (t + ;; other body form + normal-indent)))) + ((symbolp method) + (setq calculated (funcall method + path state indent-point + sexp-column normal-indent))) + (t + (setq calculated (lisp-indent-259 + method path state indent-point + sexp-column normal-indent))))) + + (goto-char containing-sexp) + (setq last-point containing-sexp) + (if (not calculated) + (condition-case () + (progn (backward-up-list 1) + (setq depth (1+ depth))) + (error (setq depth lisp-indent-maximum-backtracking)))))) + calculated))) + +(defun rst-indent-sexp (count) + "Indent each line of the sexp after point. With arg, indents +that many sexps. Redefined by rst to not reindent trailing comments +on lines." + (interactive "p") + (save-excursion + (let ((beginning (point))) + (forward-sexp count) + (indent-region beginning (point) nil)))) + +;;; Emacs-lisp backquote does exist (he finds out later) +;;; but it's pretty darn crude ... +(defmacro def-lisp-indentation (sym indentation-hook) + (list 'put + (list 'quote sym) + ''common-lisp-indent-function + (list 'quote indentation-hook))) + +(def-lisp-indentation block 1) +(def-lisp-indentation case 1) +(def-lisp-indentation catch 1) +(def-lisp-indentation ccase 1) +(def-lisp-indentation compiler-let 1) +(def-lisp-indentation cond 0) +(def-lisp-indentation ctypecase 1) +(def-lisp-indentation defconstant (13 13 2)) +(def-lisp-indentation define-setf-method defun) +(def-lisp-indentation defparameter (14 14 2)) +(def-lisp-indentation defsetf 3) +(def-lisp-indentation defstruct 1) +(def-lisp-indentation deftype defun) +(def-lisp-indentation defvar (8 4 2)) +(def-lisp-indentation do 2) +(def-lisp-indentation do-all-symbols 1) +(def-lisp-indentation do-external-symbols 1) +(def-lisp-indentation do-symbols 1) +(def-lisp-indentation dolist 1) +(def-lisp-indentation dotimes 1) +(def-lisp-indentation ecase 1) +(def-lisp-indentation etypecase 1) +(def-lisp-indentation eval-when 1) +;;the default one is better -rpg + ;(def-lisp-indentation macrolet 1) +(def-lisp-indentation multiple-value-bind 2) +(def-lisp-indentation multiple-value-prog1 0) +(def-lisp-indentation multiple-value-setq 2) +(def-lisp-indentation prog 1) +(def-lisp-indentation prog* 1) +(def-lisp-indentation prog1 0) +(def-lisp-indentation prog2 0) +(def-lisp-indentation progv 2) +(def-lisp-indentation tagbody 0) +(def-lisp-indentation typecase 1) +(def-lisp-indentation unless 1) +(def-lisp-indentation unwind-protect 1) +(def-lisp-indentation when 1) +(def-lisp-indentation with-input-from-string 1) +(def-lisp-indentation with-open-stream 1) +(def-lisp-indentation with-output-to-string 1) +(def-lisp-indentation handler-bind 1) + +;; TMC internal macros +(def-lisp-indentation letd 1) ;added by cal +(def-lisp-indentation letd* 1) ;added by cal +(def-lisp-indentation def-file-set 1) + + +;; CLOS +(def-lisp-indentation defclass (10 5 5 2)) +(def-lisp-indentation defgeneric defun) +(def-lisp-indentation define-method-combination (8 8 4 2)) +(def-lisp-indentation defmethod (11 4 2 2)) +;; the following isn't right, but could be fixed to be right +(def-lisp-indentation with-slots (12 12 3)) +(def-lisp-indentation make-instance (15 3)) + + +;; Symbolics +(def-lisp-indentation once-only 1) +(def-lisp-indentation condition-case (16 5 3)) +(def-lisp-indentation condition-case-if (18 18 5 3)) +(def-lisp-indentation defflavor (11 5 5 2)) ;; ?? + +;; *lisp +(def-lisp-indentation *defun defun) +(def-lisp-indentation *let 1) +(def-lisp-indentation let-vp-set 1) +(def-lisp-indentation *let* 1) +(def-lisp-indentation *cond 0) +(def-lisp-indentation with-css-saved 0) +(def-lisp-indentation do-for-selected-processors 1) +(def-lisp-indentation *all 0) +(def-lisp-indentation *when 1) +(def-lisp-indentation *unless 1) +(def-lisp-indentation *defvar (8 4 2)) +(def-lisp-indentation *with-vp-set 1) + +;; CM +(def-lisp-indentation with-any-vp-fields 1) +(def-lisp-indentation with-vp-fields 1) +(def-lisp-indentation with-vp-fields-in-cm 1) +(def-lisp-indentation with-vp-fields-for-read-or-write 1) +(def-lisp-indentation let-paris-stack 1) +(def-lisp-indentation let-paris-stack-vp-set 1) +(def-lisp-indentation without-safety-checking 0) +(def-lisp-indentation with-vp-set-and-flags-saved 0) +(def-lisp-indentation with-vp-set-saved 0) +(def-lisp-indentation with-flags-saved 1) +(def-lisp-indentation with-all-flags-saved 0) + +(def-lisp-indentation loop cl-indent-indent-loop-macro) + +(defun cl-indent-parse-state-depth (parse-state) + (car parse-state)) + +(defun cl-indent-parse-state-start (parse-state) + (car (cdr parse-state))) + +(defun cl-indent-parse-state-prev (parse-state) + (car (cdr (cdr parse-state)))) + +;; Regexps matching various varieties of loop macro keyword ... +(defvar cl-indent-body-introducing-loop-macro-keyword + "do\\|finally\\|initially" + "Regexp matching loop macro keywords which introduce body-forms") + +;; This is so "and when" and "else when" get handled right +;; (not to mention "else do" !!!) +(defvar cl-indent-prefix-loop-macro-keyword + "and\\|else" + "Regexp matching loop macro keywords which are prefixes") + +(defvar cl-indent-clause-joining-loop-macro-keyword + "and" + "Regexp matching 'and', and anything else there ever comes to be +like it ...") + +;; This is handled right, but it's incomplete ... +;; (It could probably get arbitrarily long if I did *every* iteration-path) +(defvar cl-indent-indented-loop-macro-keyword + "into\\|by\\|upto\\|downto\\|above\\|below\\|on\\|being\\|=\\|first\\|then\\|from\\|to" + "Regexp matching keywords introducing loop subclauses. Always indented two") + +(defvar cl-indent-indenting-loop-macro-keyword + "when\\|unless\\|if" + "Regexp matching keywords introducing conditional clauses. +Cause subsequent clauses to be indented") + +(defvar cl-indent-loop-macro-else-keyword "else") + +;;; Attempt to indent the loop macro ... + +(defun cl-indent-indent-loop-macro + (path parse-state indent-point sexp-column normal-indent) + (list (cl-indent-indent-loop-macro-1 parse-state indent-point) + (cl-indent-parse-state-start parse-state))) + +(defun cl-indent-indent-loop-macro-1 (parse-state indent-point) + (catch 'return-indentation + (save-excursion + + ;; Find first clause of loop macro, and use it to establish + ;; base column for indentation + + (goto-char (cl-indent-parse-state-start parse-state)) + (let ((loop-start-column (current-column))) + (cl-indent-loop-advance-past-keyword-on-line) + (if (eolp) + (progn + (forward-line 1) + (end-of-line) + + ;; If indenting first line after "(loop " + ;; cop out ... + + (if (<= indent-point (point)) + (throw 'return-indentation (+ 2 loop-start-column))) + (back-to-indentation))) + + (let* ((case-fold-search t) + (loop-macro-first-clause (point)) + (previous-expression-start (cl-indent-parse-state-prev parse-state)) + (default-value (current-column)) + (loop-body-p nil) + (loop-body-indentation nil) + (indented-clause-indentation (+ 2 default-value))) + ;; Determine context of this loop clause, starting with the + ;; expression immediately preceding the line we're trying to indent + (goto-char previous-expression-start) + + ;; Handle a body-introducing-clause which ends a line specially. + (if (looking-at cl-indent-body-introducing-loop-macro-keyword) + (let ((keyword-position (current-column))) + (setq loop-body-p t) + (setq loop-body-indentation + (if (cl-indent-loop-advance-past-keyword-on-line) + (current-column) + (back-to-indentation) + (if (/= (current-column) keyword-position) + (+ 2 (current-column)) + (- keyword-position 3))))) + + (back-to-indentation) + (if (< (point) loop-macro-first-clause) + (goto-char loop-macro-first-clause)) + + ;; If there's an "and" or "else," advance over it. + ;; If it is alone on the line, the next "cond" will treat it + ;; as if there were a "when" and indent under it ... + (let ((exit nil)) + (while (and (null exit) + (looking-at cl-indent-prefix-loop-macro-keyword)) + (if (null (cl-indent-loop-advance-past-keyword-on-line)) + (progn (setq exit t) + (back-to-indentation))))) + + ;; Found start of loop clause preceding the one we're trying to indent. + ;; Glean context ... + (cond + ((looking-at "(") + ;; We're in the middle of a clause body ... + (setq loop-body-p t) + (setq loop-body-indentation (current-column))) + ((looking-at cl-indent-body-introducing-loop-macro-keyword) + (setq loop-body-p t) + ;; Know there's something else on the line (or would + ;; have been caught above) + (cl-indent-loop-advance-past-keyword-on-line) + (setq loop-body-indentation (current-column))) + (t + (setq loop-body-p nil) + (if (or (looking-at cl-indent-indenting-loop-macro-keyword) + (looking-at cl-indent-prefix-loop-macro-keyword)) + (setq default-value (+ 2 (current-column)))) + (setq indented-clause-indentation (+ 2 (current-column))) + ;; We still need loop-body-indentation for "syntax errors" ... + (goto-char previous-expression-start) + (setq loop-body-indentation (current-column))))) + + ;; Go to first non-blank character of the line we're trying to indent. + ;; (if none, wind up poised on the new-line ...) + (goto-char indent-point) + (back-to-indentation) + (cond + ((looking-at "(") + ;; Clause body ... + loop-body-indentation) + ((or (eolp) (looking-at ";")) + ;; Blank line. If body-p, indent as body, else indent as + ;; vanilla clause. + (if loop-body-p + loop-body-indentation + default-value)) + ((looking-at cl-indent-indented-loop-macro-keyword) + indented-clause-indentation) + ((looking-at cl-indent-clause-joining-loop-macro-keyword) + (let ((stolen-indent-column nil)) + (forward-line -1) + (while (and (null stolen-indent-column) + (> (point) loop-macro-first-clause)) + (back-to-indentation) + (if (and (< (current-column) loop-body-indentation) + (looking-at "\\sw")) + (progn + (if (looking-at cl-indent-loop-macro-else-keyword) + (cl-indent-loop-advance-past-keyword-on-line)) + (setq stolen-indent-column + (current-column))) + (forward-line -1))) + (if stolen-indent-column + stolen-indent-column + default-value))) + (t default-value))))))) + +(defun cl-indent-loop-advance-past-keyword-on-line () + (forward-word 1) + (while (and (looking-at "\\s-") (not (eolp))) + (forward-char 1)) + (if (eolp) + nil + (current-column))) + + + +(provide 'quiescent/emacs-lisp) +;;; emacs-lisp ends here diff --git a/lisp/quiescent/eshell.el b/lisp/quiescent/eshell.el new file mode 100644 index 0000000..bcdc3bc --- /dev/null +++ b/lisp/quiescent/eshell.el @@ -0,0 +1,129 @@ +;;; quiescent/eshell --- Setup, convenience functions and configuration for eshell -*- lexical-binding: t; -*- + +;;; Commentary: +;; Jump around in eshell based on the most frequently used stuff. +;; Look up the fasd command. +;; https://developer.mozilla.org/en-US/docs/Mozilla/Tech/Places/Frecency_algorithm + +;;; Code: + +(require 'use-package) +(eval-and-compile + (when (package-installed-p 'use-package-chords) + (package-activate 'use-package-chords)) + (use-package use-package-chords + :ensure t)) +(require 'eshell) +(require 'em-prompt) +(require 'vc-git) + +(defconst quiescent-eshell-project-buffer-regexp "\*eshell .*\*" + "A regular expression which matches project eshell buffer names.") + +(defun quiescent-eshell () + "Create split current window and make bottom half an eshell instance." + (interactive) + (if (or (string-equal (buffer-name) eshell-buffer-name) + (string-match quiescent-eshell-project-buffer-regexp (buffer-name))) + (delete-window) + (quiescent-eshell-switch-to-and-change-dir))) + +(use-package em-smart + :chords ((",s" . quiescent-eshell)) + :config (progn + (setq eshell-where-to-jump 'begin) + (setq eshell-review-quick-commands nil) + (setq eshell-smart-space-goes-to-end t) + (add-hook 'eshell-mode-hook 'eshell-smart-initialize))) + +;; This is originally borrowed from Ben's journal, but has since been +;; heavily modified +;; http://www.blogbyben.com/2013/08/a-tiny-eshell-add-on-jump-to-shell.html +(defun quiescent-eshell-switch-to-and-change-dir () + "Switch to eshell and make sure we're in the directory the current buffer is in." + (interactive) + (let ((dir default-directory)) + ;; TODO create a project shell function + (pop-to-buffer "*popup-eshell*") + (eshell) + (goto-char (point-max)) + (unless (eq dir default-directory) + (cd dir) + (eshell-send-input) + (goto-char (point-max))))) + +;; Cool prompt from: https://www.reddit.com/r/emacs/comments/6f0rkz/my_fancy_eshell_prompt/ +(lambda () + (concat + (propertize "┌─[" 'face `(:foreground "green")) + (propertize (user-login-name) 'face `(:foreground "red")) + (propertize "@" 'face `(:foreground "green")) + (propertize (system-name) 'face `(:foreground "blue")) + (propertize "]──[" 'face `(:foreground "green")) + (propertize (format-time-string "%H:%M" (current-time)) 'face `(:foreground "yellow")) + (propertize "]──[" 'face `(:foreground "green")) + (propertize (concat (eshell/pwd)) 'face `(:foreground "white")) + (propertize "]\n" 'face `(:foreground "green")) + (propertize "└─>" 'face `(:foreground "green")) + (propertize (if (= (user-uid) 0) " # " " $ ") 'face `(:foreground "green")))) + +;; The following comes from the emacs wiki: +;; https://www.emacswiki.org/emacs/EshellPrompt +(defmacro with-face (str &rest properties) + "Propertise STR with PROPERTIES." + `(propertize ,str 'face (list ,@properties))) + +(defun shk-eshell-prompt () + "A fancy eshell prompt function." + (let ((header-bg "dark slate grey")) + (concat + (with-face (concat (eshell/pwd) " ") :background header-bg) + (with-face (format-time-string "(%Y-%m-%d %H:%M) " (current-time)) :background header-bg :foreground "#888") + (with-face + (or (ignore-errors (format "(%s)" (car (vc-git-branches)))) "") + :background header-bg) + (with-face "\n" :background header-bg) + (with-face user-login-name :foreground "#5180b3") + "@" + (with-face "localhost" :foreground "green") + (if (= (user-uid) 0) + (with-face " #" :foreground "red") + " $") + " "))) + +(defun quiescent-eshell-prompt () + "My eshell prompt. + +A combination of one from the wiki and one from reddit." + (concat + (with-face "┌─[" :foreground "#b2d7ff") + (with-face (user-login-name) :foreground "#5180b3") + (with-face "@" :foreground "light grey") + (with-face (system-name) :foreground "green") + (with-face "]──[" :foreground "#b2d7ff") + (with-face (format-time-string "%H:%M" (current-time)) :foreground "light grey") + (with-face "]──[" :foreground "#b2d7ff") + (with-face (eshell/pwd) :foreground "light grey") + (with-face "]──[" :foreground "#b2d7ff") + (with-face (or (car (vc-git-branches)) "unversioned") :foreground "light grey") + (with-face "]\n" :foreground "#b2d7ff") + (with-face "└->" :foreground "#b2d7ff") + (with-face (if (= (user-uid) 0) " # " " $ ") :foreground "light grey"))) + +(setq eshell-prompt-function 'quiescent-eshell-prompt) +(setq-default eshell-prompt-regexp "└-> \\$ ") + +(defun eshell-next-prompt (n) + "Move to end of Nth next prompt in the buffer. +See `eshell-prompt-regexp'." + (interactive "p") + (progn + (dotimes (_ (abs n)) + (if (> n 0) + (re-search-forward eshell-prompt-regexp nil t nil) + (re-search-backward eshell-prompt-regexp nil t nil))) + (when (< n 0) + (re-search-forward eshell-prompt-regexp nil t nil)))) + +(provide 'quiescent/eshell) +;;; eshell ends here diff --git a/lisp/quiescent/git.el b/lisp/quiescent/git.el new file mode 100644 index 0000000..128b88d --- /dev/null +++ b/lisp/quiescent/git.el @@ -0,0 +1,166 @@ +;;; git --- functions for use in git, especially via eshell -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'vc-git) + +(defun quiescent-current-branch-name () + "Produce the current branch name." + (car (vc-git-branches))) + +(defun quiescent-insert-current-branch-name () + "Print the current branch name at point." + (interactive) + (insert (quiescent-current-branch-name))) + +(require 'esh-mode) + +(defun quiescent-add-insert-branch-binding-eshell () + "Add the insert branch binding in eshell mode." + (define-key eshell-mode-map (kbd "C-c b") #'quiescent-insert-current-branch-name)) + +(add-hook 'eshell-mode-hook #'quiescent-add-insert-branch-binding-eshell) + +(defconst q-git-log-entry-parse-up-to-extra-details ".*Author: \\(.*\\)\nDate: \\(.*\\)\n\n \\(.*\\)$" + "A regular expression for parsing git log entries.") + +(defconst q-git-month-translation-alist '(("Jan" . "01") + ("Feb" . "02") + ("Mar" . "03") + ("Apr" . "04") + ("May" . "05") + ("Jun" . "06") + ("Jul" . "07") + ("Aug" . "08") + ("Sep" . "09") + ("Oct" . "10") + ("Nov" . "11") + ("Dec" . "12")) + "A transaltion alist for string months to ints.") + +(defun q-git-to-org-time-stamp (git-time-stamp) + "Convert GIT-TIME-STAMP into org format for time stamps." + (cl-labels ((month-to-int (month) + (cdr (assoc month q-git-month-translation-alist)))) + (pcase (split-string git-time-stamp " " t) + (`(,_ ,month ,day ,time ,year ,_) + (format "%s-%s-%02d %s" year (month-to-int month) (string-to-number day) time))))) + +(defun q-git-parse-log-entry (log-entry-text) + "Parse LOG-ENTRY-TEXT into a list structure." + (progn + (string-match q-git-log-entry-parse-up-to-extra-details log-entry-text) + (let ((author (substring log-entry-text (match-beginning 1) (match-end 1))) + (date (substring log-entry-text (match-beginning 2) (match-end 2))) + (summary (substring log-entry-text (match-beginning 3) (match-end 3))) + (detail (substring log-entry-text (match-end 3)))) + `(GIT-LOG ,author ,(q-git-to-org-time-stamp date) ,summary ,detail)))) + +(defun q-git-parse-log (log-lines) + "Parse LOG-LINES into a list of list structures." + (mapcar #'q-git-parse-log-entry (split-string log-lines "^commit" t "[ \n]"))) + +(defun q-grab-match (regex string &optional match-group) + "Match REGEX to STRING and then produce the matching text. +If MATCH-GROUP is supplied then produce that matching group." + (progn + (when (null match-group) + (setq match-group 0)) + (when (string-match regex string) + (substring string (match-beginning match-group) (match-end match-group))))) + +(defun q-push-hash (key value table) + "Define KEY to be a list updated with VALUE from the current list in TABLE. + +Creates a list with only value in it if there isn't one yet." + (puthash key (append (list value) (gethash key table)) table)) + +(defvar *q-git-augment-task* #'identity + "A function to augment a task entry when grouping by commiter then task.") + +(defun q-parse-string-field-by-regexp (regexp) + "Parse a string field from the current buffer using the first hit of REGEXP." + (q-grab-match regexp + (buffer-string) + 1)) + +(defvar *q-git-report-buffer* "*git-report*" + "The name of the buffer to wirte the report to.") + +(defvar *q-git-grouping-function* #'q-git-group-by-commiter-then-task + "The function which will perform grouping prior to writing the report.") +(defvar *q-git-printing-function* #'identity + "The function which will print the report. +Must be compatible with the output of the grouping function.") + +(defvar quiescent-patch-file-buffer-name "*patch-files*" + "The name of buffer to create a patch files in.") + +(defun quiescent-bad-commit-message (patch-file-path) + "Produce a list of the lines where bad commits are found. + +Search the file at PATCH-FILE-PATH for the invalid messages." + (with-temp-buffer + (let (start + end + bad) + (insert-file-contents patch-file-path) + (while (re-search-forward "Subject: \\[PATCH\\( [0-9]+/[0-9]+\\|\\)\\] " nil t nil) + (progn + (setq start (point)) + (setq end (re-search-forward "^---$")) + (narrow-to-region start end) + (goto-char (point-min)) + (unfill-paragraph) + (when (re-search-forward ".\\{79\\}" nil t nil) + (widen) + (push (line-number-at-pos) bad)) + (widen))) + bad))) + +(defun quiescent-bad-commits-for-patches-in-dir (dir) + "Produce a patches in DIR which have bad commit messages. + +A bad commit is defined as having more than 78 characters in any +of it's lines." + (interactive "DDirectory: ") + (let ((patch-files (directory-files dir nil "\\.patch$" t))) + (progn + (switch-to-buffer quiescent-patch-file-buffer-name) + (erase-buffer) + (dolist (patch-file patch-files) + (let ((bad-lines (quiescent-bad-commit-message patch-file))) + (when bad-lines + (insert patch-file "\n - " (mapconcat #'number-to-string bad-lines "\n - ") "\n"))))))) + +(defun quiescent-parse-month (month) + "Parse MONTH into it's integer representation." + (pcase (downcase month) + ("jan" 1) + ("feb" 2) + ("mar" 3) + ("apr" 4) + ("may" 5) + ("jun" 6) + ("jul" 7) + ("aug" 8) + ("sep" 9) + ("oct" 10) + ("nov" 11) + ("dec" 12))) + +(defun quiescent-parse-git-time-string (s) + "Parse S into an EMACS time list (SEC MINUTE HOUR DAY MONTH YEAR DOW DST UTCOFF)." + (pcase (split-string s " " t) + (`(,_ ,month ,day ,_ ,year) + `(0 0 0 ,(string-to-number day) ,(quiescent-parse-month month) ,(string-to-number year) 0 nil 0)))) + +(require 'use-package) + +(use-package git-timemachine + :ensure t) + +(provide 'quiescent/git) +;;; quiescent/git ends here diff --git a/lisp/quiescent/haskell.el b/lisp/quiescent/haskell.el new file mode 100644 index 0000000..242d355 --- /dev/null +++ b/lisp/quiescent/haskell.el @@ -0,0 +1,63 @@ +;;; quiescent/haskell --- Setup Haskell mode -*- lexical-binding: t; -*- + +;;; Commentary: + +;; Cabal packages to install: +;; - hslint +;; - hindent +;; - happy +;; - hasktags +;; - stylish-haskell +;; - present (didn't work though :/ and needed for :present in terminal) + +;; A lot of this comes from: +;; https://github.com/serras/emacs-haskell-tutorial/blob/master/tutorial.md + +;;; Code: + +(require 'use-package) + +(use-package haskell-mode + :ensure t + :config (add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)) + +(use-package intero + :ensure t + :config (add-hook 'haskell-mode-hook 'intero-mode)) + +(custom-set-variables '(haskell-tags-on-save t)) + +(defun quiescent-disable-flycheck-mode () + "Disable flycheck mode." + (flycheck-mode -1)) + +(add-hook 'haskell-debug-mode-hook #'quiescent-disable-flycheck-mode) +(add-hook 'haskell-debug-mode-hook #'quiescent-disable-flyspell-mode) + +;; Use xref as backup when interro can't find the definition +(defun quiescent-xref-backup-advice (f) + "Use `xref-find-definitions' as a backup to `interro-goto-definition'. + +Used as Advice around the `interro-goto-definition' function F." + (when (string-equal "Couldn't resolve to any modules." (funcall-interactively f)) + (funcall-interactively #'xref-find-definitions (xref--read-identifier "Find definitions of: ")))) + +(advice-add #'intero-goto-definition :around #'quiescent-xref-backup-advice) + +;; Haskell mode +(require 'async) +(defun haskell-mode-generate-tags (&optional and-then-find-this-tag) + "Generate tags using Hasktags. This is synchronous function. + +If optional AND-THEN-FIND-THIS-TAG argument is present it is used +with function `xref-find-definitions' after new table was +generated." + (interactive) + (let* ((dir (haskell-cabal--find-tags-dir)) + (command (haskell-cabal--compose-hasktags-command dir))) + (if (not command) + (error "Unable to compose hasktags command") + (async-start-process "hasktags" "*hask-tags-buffer*" (split-string command))))) + +(provide 'quiescent/haskell) +;;; haskell ends here diff --git a/lisp/quiescent/hippie-expand-function.el b/lisp/quiescent/hippie-expand-function.el new file mode 100644 index 0000000..69c5623 --- /dev/null +++ b/lisp/quiescent/hippie-expand-function.el @@ -0,0 +1,28 @@ +;;; quiescent/hippie-expand-function --- Expand the function at point with hippie expand -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defun quiescent-previous-char () + "Produce the character one before point." + (save-excursion + (backward-char) + (thing-at-point 'char t))) + +(defun quiescent-start-of-symbol () + "Produce the point at start of the symbol around point." + (save-excursion + (when (not (string-equal "." (quiescent-previous-char))) + (backward-sexp)) + (point))) + +(defun quiescent-end-of-symbol () + "Produce the points at the end of the symbol around point." + (save-excursion + ;; (when (not (= ?\ (thing-at-point 'char))) + ;; (forward-sexp)) + (point))) + +(provide 'quiescent/hippie-expand-function) +;;; hippie-expand-function ends here diff --git a/lisp/quiescent/install-on-mac.sh b/lisp/quiescent/install-on-mac.sh new file mode 100755 index 0000000..ae71098 --- /dev/null +++ b/lisp/quiescent/install-on-mac.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +make EMACS=/usr/local/Cellar/emacs-plus/HEAD-5510515/bin/emacs all diff --git a/lisp/quiescent/javascript.el b/lisp/quiescent/javascript.el new file mode 100644 index 0000000..c18b5d2 --- /dev/null +++ b/lisp/quiescent/javascript.el @@ -0,0 +1,227 @@ +;;; quiescent/javascript --- Setup for editing javascript -*- lexical-binding: t; -*- + +;;; Commentary: +;; Linters used in conjunction with flycheck (install them with npm +;; install -g): +;; +;; - jshint +;; - eslint +;; - jscs + +;; jscs needs a config file like so: +;; { +;; "preset": "google", +;; "requireCurlyBraces": null +;; } + +;; Debuggers require a global install of: +;; - babel: npm i -g babel-cli +;; - jest: npm i -g jest +;; - mocha: npm i -g mocha + +;;; Code: + +(require 'quiescent/haskell) + +(require 'use-package) + +(use-package js2-mode + :ensure t + :config (progn + (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode)) + (add-to-list 'interpreter-mode-alist '("node" . js2-mode)) + (define-key js2-mode-map (kbd "") #'quiescent-start-node-with-this-file) + (define-key js2-mode-map (kbd "") #'quiescent-start-mocha-with-this-file) + (define-key js2-mode-map (kbd "") #'quiescent-start-jest-with-this-file))) + +(require 'flycheck) + +;; Courtesy: http://daniel-bush.blogspot.co.za/2014/12/emacs-flycheck-and-jshint-and-other.html +(flycheck-define-checker javascript-jscs + "A JavaScript style checker using jscs. + +See URL `https://www.npmjs.com/package/jscs'." + :command ("jscs" "--reporter=checkstyle" + (config-file "--config" flycheck-jscsrc) + source) + :error-parser flycheck-parse-checkstyle + :modes (js-mode js2-mode js3-mode js-jsx-mode)) + +(flycheck-def-config-file-var flycheck-jscsrc javascript-jscs ".jscsrc" + :safe #'stringp) + +(add-to-list 'flycheck-checkers 'javascript-jscs t) +(flycheck-add-next-checker 'javascript-jshint '(t . javascript-jscs)) +(flycheck-add-next-checker 'javascript-jshint '(t . javascript-eslint)) + +(flycheck-add-mode 'javascript-jshint #'js-jsx-mode) + +(add-hook 'js2-mode-hook #'quiescent-select-jshint) +(add-hook 'js-jsx-mode-hook #'quiescent-select-eslint) + +(defun quiescent-select-eslint () + "Select JSHint as the linter in this buffer." + (interactive) + (flycheck-select-checker 'javascript-eslint)) + +(defun quiescent-select-jshint () + "Select JSHint as the linter in this buffer." + (interactive) + (flycheck-select-checker 'javascript-jshint)) + +(defconst quiescent-node-url-extracting-regexp "://.*127.0.0.1:\\([0-9]+\\)/\\(.*\\)" + "A regular expression which can extract the port and path of the node process for indium.") + +(defconst quiescent-node-buffer-failed "failed" + "A regular expression to match instances where the node buffer failed to start.") + +(defun quiescent-node-name-for-this-file () + "Compute the name of the node process buffer for this file." + (format "*node-%s*" (buffer-name))) + +(defvar quiescent-node-used-ports '() + "A list of the ports used to spin up indium interactive processes. + +TODO: derigester ports when the associated process dies.") + +(defun quiescent-random-node-port () + "Produce a random port for node to run on which isn't already in use." + (let (result) + (while (member (setq result (+ 8000 (random 1000))) + quiescent-node-used-ports)) + result)) + +(defun quiescent-start-node-with-this-file () + "Start node in the current buffer with the debugger." + (interactive) + (quiescent--start-process-with-this-file "node")) + +(defun quiescent-start-mocha-with-this-file () + "Start mocha in the current buffer with the debugger. + +Requires that there exists file /tests/.test.setup.js +which does any necessary prep for the test." + (interactive) + (quiescent--start-process-with-this-file + "mocha" + (list "--inspect" (format "%stests/.test.setup.js" + (projectile-project-root))))) + +;;(require 'quiescent/projectile) + +;; Left these here because I think that they might be useful +(defun quiescent-babel-process-name (buffer-name) + "Produce a babel process name for BUFFER-NAME." + (format "*babel-process-%s*" (buffer-name))) + +(defvar quiescent-use-babel-with-launched-files t + "Whether files should first be translated before being debugged.") + +(defun quiescent-babel-translate-this-file () + "Translate this file and store it at `.babel.js'. +Produces the new file name." + (let ((process-name (quiescent-babel-process-name (buffer-name))) + (babel-file-name (concat (file-name-sans-extension (buffer-file-name)) ".babel.js"))) + (start-process process-name process-name + "babel" "--preset" "es2015,react" (buffer-file-name) "-o" babel-file-name) + babel-file-name)) + +(defun quiescent-start-jest-with-this-file () + "Start jest in the current buffer with the debugger." + (interactive) + (quiescent--start-process-with-this-file + "babel-node" + (list "--harmony" + "--inspect" + (format "%snode_modules/jest/bin/jest.js" + (projectile-project-root)) + "--runInBand"))) + +(defun quiescent--start-process-with-this-file (process &optional args) + "Start PROCESS with `--inspect' on this buffer. + +Intention is for it to be used by indium mode. + +Supply the (optional) list of ARGS to that process." + (let* ((node-process-name (quiescent-node-name-for-this-file)) + (random-port (quiescent-random-node-port)) + (file-path (buffer-file-name)) + (node-process + (progn + (when (bufferp (get-buffer node-process-name)) + (with-current-buffer node-process-name (erase-buffer))) + (apply + #'start-process + node-process-name + node-process-name + process + (cons (format "--inspect-brk=%s" random-port) + (reverse (cons file-path + (reverse args))))))) + port + path) + (with-current-buffer node-process-name + (let ((buffer-text (progn + (while (string= "" (buffer-string)) + (sleep-for 0 100)) + (buffer-string)))) + (when (string-match quiescent-node-buffer-failed buffer-text) + (error "Couldn't start node process: %s" buffer-text)) + (string-match quiescent-node-url-extracting-regexp + buffer-text) + (setq port (substring buffer-text (match-beginning 1) (match-end 1)) + path (substring buffer-text (match-beginning 2) (match-end 2))))) + (indium-nodejs--connect "127.0.0.1" + port + path))) + +(defun quiescent-last-message () + "Produce the last message logged, as the last line in `*Messages*'." + (with-current-buffer "*Messages*" + (goto-char (point-max)) + (beginning-of-line) + (forward-line -1) + (buffer-substring (point) (progn (end-of-line) + (point))))) + +(defun quiescent-overlay-of-fun (orig-fun &rest args) + "Run ORIG-FUN with ARGS and create an overlay with it." + (progn + (apply orig-fun args) + (sleep-for 0 100) + (quiescent-haskell-result-overlay-at-point (quiescent-last-message)))) + +(advice-add #'indium-eval-last-node :around #'quiescent-overlay-of-fun) + +;; To start debugging, open the following URL in Chrome: +;; chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/a8ef938b-045a-4098-b50e-e1a18cf415c7 + +(use-package indium + :ensure t) + +(defun quiescent-is-js2-mode () + "Produce t if this buffer is in `js2-mode'." + (eq major-mode 'js2-mode)) + +(add-hook 'js2-mode-hook #'indium-interaction-mode) + +(use-package js2-refactor + :ensure t) + +(defun quiescent-pluse-ignoring-args (&rest args) + "Run `xref-pulse-momentarily' ignoring ARGS." + (xref-pulse-momentarily)) + +(advice-add #'js2-jump-to-definition :after #'quiescent-pluse-ignoring-args) + +(defun quiescent-xref-jump-when-point-doesnt-move (jump-fun &rest args) + "Execute JUMP-FUN with ARGS, jump with xref if it didn't move." + (let ((start (point))) + (ignore-errors (apply jump-fun args)) + (when (eql start (point)) + (xref-find-definitions (thing-at-point 'symbol))))) + +(advice-add #'js2-jump-to-definition :around #'quiescent-xref-jump-when-point-doesnt-move) + +(provide 'quiescent/javascript) +;;; javascript ends here diff --git a/lisp/quiescent/maven.el b/lisp/quiescent/maven.el new file mode 100644 index 0000000..d2ddd31 --- /dev/null +++ b/lisp/quiescent/maven.el @@ -0,0 +1,46 @@ +;;; quiescent/maven --- Functions for convenience while working with maven -*- lexical-binding: t; -*- + +;;; Commentary: + +;; If I'm writing a spring boot app in the future then the following +;; commands can be used to run it with profiles (local and default) +;; and to debug it with those same profiles respectively: +;; - mvn-backend spring-boot:run -Drun.profiles=local,default +;; - mvn-backend spring-boot:run -Drun.profiles=local,default -Drun.jvmArguments="-agentlib:jdwp=transport=dt_shmem,address=jdbconn,server=y,suspend=n" +;; +;; Attaching to a debug process in java is done as follows: +;; - jdb -attach + +;;; Code: + +(defun quiescent-nearest-pom-up () + "Goto the nearest pom file updwards from the current directory." + (interactive) + (let* ((start-directory (if (equal (file-relative-name (buffer-file-name) + default-directory) "pom.xml") + (quiescent-up-directory default-directory) + default-directory)) + (pom-dir (locate-dominating-file start-directory "pom.xml"))) + (when (not pom-dir) + (error "Pom not found")) + (find-file (concat pom-dir "pom.xml")))) + +(require 'subr-x) + +(defun quiescent-up-directory (dir) + "Produce the directory one up from DIR. + +Nil if root is supplied as DIR." + (string-join (reverse (cddr (reverse (split-string dir "/")))) "/")) + +(defun quiescent-maven-compile-on-nearest-pom-up (mvn-command) + "Find the nearest pom up from the current directory and run MVN-COMMAND." + (interactive "sMaven command to run: ") + (let ((starting-buffer (buffer-name))) + (progn + (quiescent-nearest-pom-up) + (compile (format "mvn -f %s %s" (buffer-file-name) mvn-command)) + (switch-to-buffer starting-buffer)))) + +(provide 'quiescent/maven) +;;; maven ends here diff --git a/lisp/quiescent/other-window.el b/lisp/quiescent/other-window.el new file mode 100644 index 0000000..5dfc8be --- /dev/null +++ b/lisp/quiescent/other-window.el @@ -0,0 +1,38 @@ +;;; other-window --- Convenience functions for manipulating the other window -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defmacro quiescent-in-other-buffer (&rest body) + "Execute BODY in the other window and return to the startindg window." + `(progn + (other-window 1) + ,@body + (select-window (previous-window)))) + +(defun quiescent-other-window-line-down () + "Move point one line down in the other window. + +Maintains the point in the current window." + (interactive) + (quiescent-in-other-buffer + (forward-line))) + +(defun quiescent-other-window-line-up () + "Move point one line up in the other window. + +Maintains the point in the current window." + (interactive) + (quiescent-in-other-buffer + (forward-line -1))) + +(defun quiescent-search-other-window () + "Search the other window and then pop back to the current one." + (interactive) + (progn + (quiescent-in-other-buffer + (isearch-forward)))) + +(provide 'quiescent/other-window) +;;; other-window ends here diff --git a/lisp/quiescent/sql.el b/lisp/quiescent/sql.el new file mode 100644 index 0000000..b205e3d --- /dev/null +++ b/lisp/quiescent/sql.el @@ -0,0 +1,78 @@ +;;; quiescent/sql --- Setup and convenience functions for editing sql -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defvar quiescent-sql-completion-candidates '("SELECT" "FROM" "WHERE" "ORDER" "GROUP" "BY" "UPDATE" "DELETE") + "A variable list of candidates to complete a sql symbol from.") + +(defvar quiescent-sql-original-completion-candidates '("SELECT" "FROM" "WHERE" "ORDER" "GROUP" "BY" "UPDATE" "DELETE") + "A fixed list of candidates to complete a sql symbol from.") + +(require 'thingatpt) +(require 'sql) + +(require 'use-package) + +(use-package flx + :ensure t) +(use-package flx-ido + :ensure t) + +(defun quiescent-fuzzy-completion (prefix completions) + "Complete the given PREFIX using a fixed list of COMPLETIONS. + +Part of this is ripped out of the implementation of flx's +ido matching and sorting. See `flx-ido-match-internal'." + (let ((flex-result (flx-flex-match prefix completions))) + (if (< (length flex-result) flx-ido-threshold) + (let* ((matches (cl-loop for item in flex-result + for string = (ido-name item) + for score = (flx-score string prefix flx-file-cache) + if score + collect (cons item score) + into matches + finally return matches))) + (mapcar 'car (delete-consecutive-dups + (sort matches + (lambda (x y) (> (cadr x) (cadr y)))) + t))) + (mapcar 'car flex-result)))) + +(defun quiescent-sql-completion () + "Complete PREFIX fuzzily using a fixed list of completions. + +The fixed list is read from a variable: +`quiescent-sql-completion-candidates', which ought to be set when +connecting to a given database." + (let* ((prefix (thing-at-point 'symbol)) + (prefix-bounds (bounds-of-thing-at-point 'symbol)) + (candidates + (if (looking-at "^") + (quiescent-fuzzy-completion prefix quiescent-sql-original-completion-candidates) + (quiescent-fuzzy-completion prefix quiescent-sql-completion-candidates)))) + (if (null candidates) + (progn (message "No candidates for completion found") + nil) + (list (car prefix-bounds) (cdr prefix-bounds) candidates)))) + +(defun quiescent-register-sql-completion () + "Register the sql completion function as a completion method in this buffer." + (progn + (setq completion-at-point-functions (list #'quiescent-sql-completion)) + (setq completion-ignore-case t))) + +(add-hook 'sql-mode-hook #'quiescent-register-sql-completion) + +(defun quiescent-describe-table-at-point () + "Desicribe the table at point." + (interactive) + (sql-send-string (format "DESCRIBE %s;" + (thing-at-point 'symbol t)))) + +(define-key sql-mode-map (kbd "C-c C-d") + #'quiescent-describe-table-at-point) + +(provide 'quiescent/sql) +;;; sql ends here diff --git a/lisp/quiescent/start-server.el b/lisp/quiescent/start-server.el new file mode 100644 index 0000000..abf89e7 --- /dev/null +++ b/lisp/quiescent/start-server.el @@ -0,0 +1,4 @@ +;;; -*- lexical-binding: t; -*- + +(require 'server) +(server-start) diff --git a/lisp/too-long-lines-mode/.gitattributes b/lisp/too-long-lines-mode/.gitattributes new file mode 100644 index 0000000..5876c38 --- /dev/null +++ b/lisp/too-long-lines-mode/.gitattributes @@ -0,0 +1 @@ +*.html linguist-vendored \ No newline at end of file diff --git a/lisp/too-long-lines-mode/.gitignore b/lisp/too-long-lines-mode/.gitignore new file mode 100644 index 0000000..c531d98 --- /dev/null +++ b/lisp/too-long-lines-mode/.gitignore @@ -0,0 +1 @@ +*.elc diff --git a/lisp/too-long-lines-mode/README.md b/lisp/too-long-lines-mode/README.md new file mode 100644 index 0000000..6aa4504 --- /dev/null +++ b/lisp/too-long-lines-mode/README.md @@ -0,0 +1,5 @@ +# Too-long-lines-mode + +

+ +

diff --git a/lisp/too-long-lines-mode/example.html b/lisp/too-long-lines-mode/example.html new file mode 100644 index 0000000..0fe8d3b --- /dev/null +++ b/lisp/too-long-lines-mode/example.html @@ -0,0 +1,16 @@ + + + + + + JQuery minified hiden example + + + + +
The minified jquery code above is hidden with too-long-line-mode.
+ + diff --git a/lisp/too-long-lines-mode/screenshots/example.png b/lisp/too-long-lines-mode/screenshots/example.png new file mode 100644 index 0000000000000000000000000000000000000000..d7c43136f927fcc49d50534a9b957c8d130f850d GIT binary patch literal 92274 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 +;; Version: 0 +;; Keywords: convenience files maint +;; Package-Requires: ((emacs "24.2")) +;; URL: https://github.com/rakete/too-long-lines-mode + +;; This file is part of too-long-lines-mode. + +;; too-long-lines-mode is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; too-long-lines-mode is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with too-long-lines-mode. If not, see . + +;;; Commentary: + +;; Emacs has a problem when displaying very long lines: it becomes unusable slow. +;; This global minor mode works around this problem by hiding very long lines and +;; replacing them with only a few of their first characters and a little info +;; blurp about how many characters were hidden. + +;; It works adding the function too-long-lines-hide into the find-file-hook so that +;; it is called as soon as a file is opened. + +;; too-long-lines-hide goes through all lines in a buffer, checks if the line +;; is longer then too-long-lines-threshold and if it is, it creates an overlay +;; with the lines replacement in its 'display property that is then displayed +;; instead of the very long line. + +;; To use this mode just require this file, configure too-long-lines-threshold +;; and too-long-lines-show-number-of-characters to your pleasing and call +;; too-long-lines-mode to enable the mode globally. + +;;; Code: +(defvar too-long-lines-threshold 10000 + "The threshold after which `too-long-lines-hide' cuts of a line and hides the rest.") + +(defvar too-long-lines-show-number-of-characters 30 + "How many characters of a line remain shown after it is hidden.") + +(defvar too-long-lines-hide-current-timer nil + "The current timer for `too-long-lines-run-with-idle-timer-in-special-buffers'. + +Do not set this manually, use `too-long-lines-idle-seconds' to specify the +idle seconds after which `too-long-lines-run-with-idle-timer-in-special-buffers' +runs `too-long-lines-hide'.") + +(defvar too-long-lines-idle-seconds 3 + "Set this to how many seconds emacs should be idle before +`too-long-lines-run-with-idle-timer-in-special-buffers' runs `too-long-lines-hide'.") + +(defvar too-long-lines-special-buffer-modes '(shell-mode) + "The modes which `too-long-lines-run-with-idle-timer-in-special-buffers' recognizes +as special buffers in which `too-long-lines-hide' should be run periodically.") + +(defun too-long-lines-hide (&optional beg end len) + "Hides lines that are longer then `too-long-lines-threshold'. + +It replaces too long lines with the first N number characters of the line as +configured in variable `too-long-lines-show-number-of-characters', and a little +info blurp about how many characters were hidden. + +See also `too-long-lines-threshold', `too-long-lines-show-number-of-characters', +`too-long-lines-hide-in-buffers' and `too-long-lines-show'." + (interactive) + (save-excursion + (goto-char (or beg (point-min))) + (let ((done nil)) + (while (not done) + (setq done (>= (line-end-position) (or end (point-max)))) + (let ((line-length (- (line-end-position) (line-beginning-position))) + (already-hidden nil)) + (when (> line-length too-long-lines-threshold) + (dolist (ov (overlays-in (line-beginning-position) (line-end-position))) + (if (and (overlay-get ov 'too-long-line) + (> (line-end-position) (overlay-end ov))) + (delete-overlay ov) + (setq already-hidden t) + )) + (unless already-hidden + (let ((ov (make-overlay (+ (line-beginning-position) too-long-lines-show-number-of-characters) (line-end-position) (current-buffer)))) + (overlay-put ov 'too-long-line t) + (overlay-put ov 'display (concat "... " (prin1-to-string (- line-length too-long-lines-show-number-of-characters)) " hidden characters")) + (overlay-put ov 'face '(:background "#ff0066")) + )))) + (when (eq (point) (goto-char (line-beginning-position 2))) + (setq done t)))))) + +(defun too-long-lines-hide-after-change-in-special-buffer (&optional beg end len) + "Run `too-long-lines-hide' after changes in current buffer + +But only if its mode is in `too-long-lines-special-buffer-modes'. Arguments +BEG, END and LEN are passed to `too-long-lines-hide'." + (when (cl-some (lambda (x) (eq major-mode x)) too-long-lines-special-buffer-modes) + (too-long-lines-hide beg end len))) + +(defun too-long-lines-hide-in-buffers (&optional buffers) + "Run `too-long-lines-hide' in multiple buffers. + +Argument BUFFERS is a list of buffers in which this function looks for too +long lines to hide, if it is not specified this function will look in all +visible buffers for too long lines to hide. + +See also `too-long-lines-threshold', `too-long-lines-show-number-of-characters', +`too-long-lines-hide' and `too-long-lines-show'." + (interactive) + (let ((buffers (or buffers + (cl-mapcan (lambda (buf) + (when (get-buffer-window buf) + (list buf))) + (buffer-list))))) + (cl-pushnew (current-buffer) buffers) + (dolist (buf buffers) + (unless (window-minibuffer-p (get-buffer-window buf)) + (with-current-buffer buf + (too-long-lines-hide (point-min) (point-max))))))) + +(defun too-long-lines-run-with-idle-timer-in-special-buffers () + "Periodically run `too-long-lines-hide' in buffers that are in a major-mode from +`loo-long-lines-special-buffer-modes'. + +See also `too-long-lines-idle-seconds'." + (when (not (eq too-long-lines-hide-current-timer nil)) + (cancel-timer too-long-lines-hide-current-timer) + (setq too-long-lines-hide-current-timer nil)) + (setq too-long-lines-hide-current-timer + (run-with-idle-timer too-long-lines-idle-seconds t + (lambda () + (let ((special-buffers '())) + (dolist (buffer (buffer-list)) + (when (cl-some (lambda (x) (eq (with-current-buffer buffer major-mode) x)) too-long-lines-special-buffer-modes) + (setq special-buffers (append special-buffers (list buffer))))) + (too-long-lines-hide-in-buffers special-buffers)))))) + +(defun too-long-lines-show () + "Restore all lines previously hidden by `too-long-lines-hide' in the current buffer." + (interactive) + (save-excursion + (goto-char (point-min)) + (let ((done nil)) + (while (not done) + (setq done (>= (line-end-position) (point-max))) + (dolist (ov (overlays-in (line-beginning-position) (line-end-position))) + (when (overlay-get ov 'too-long-line) + (delete-overlay ov))) + (when (eq (point) (goto-char (line-beginning-position 2))) + (setq done t)))))) + +;;;###autoload +(define-minor-mode too-long-lines-mode + "A minor that hides lines that are longer then a configurable threshold. + +See also `too-long-lines-hide'." + :global t + :init-value nil + :lighter " tll" + :keymap '() + (if too-long-lines-mode + (progn + (too-long-lines-hide) + (add-hook 'find-file-hook 'too-long-lines-hide) + (add-hook 'after-change-functions 'too-long-lines-hide-after-change-in-special-buffer)) + (progn + (remove-hook 'find-file-hook 'too-long-lines-hide) + (remove-hook 'after-change-functions 'too-long-lines-hide-after-change-in-special-buffer) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (too-long-lines-show))) + (when (timerp too-long-lines-hide-current-timer) + (cancel-timer too-long-lines-hide-current-timer) + (setq too-long-lines-hide-current-timer nil))))) + +(provide 'too-long-lines-mode) +;;; too-long-lines-mode.el ends here diff --git a/lisp/wiki/Makefile b/lisp/wiki/Makefile new file mode 100644 index 0000000..c933b52 --- /dev/null +++ b/lisp/wiki/Makefile @@ -0,0 +1,17 @@ +# TODO: grab these off of the wiki when there are updates (!!!!) :3 + +EMACS ?=emacs +EMACS_FLAGS=-Q -batch --no-init -f batch-byte-compile + +TARGETS=rotate-text.elc linkd.elc + +compile: $(TARGETS) + +all: clean compile + +%.elc: %.el + $(EMACS) $(EMACS_FLAGS) $^ + +clean: + rm -f *.elc + diff --git a/lisp/wiki/linkd.el b/lisp/wiki/linkd.el new file mode 100644 index 0000000..160277e --- /dev/null +++ b/lisp/wiki/linkd.el @@ -0,0 +1,1280 @@ +;;; linkd.el --- Make hypertext with active links in any buffer +;; +;; Filename: linkd.el +;; Description: Make hypertext with active links in any buffer +;; Author: David O'Toole +;; Additional code by Eduardo Ochs +;; Maintainer: Shaun Johnson +;; Copyright (C) 2007, David O'Toole. +;; Copyright (C) 2008-2009, Drew Adams. +;; Copyright (C) 2009, Shaun Johnson. +;; Created: Fri Mar 14 07:56:32 2008 (Pacific Daylight Time) +;; Version: $Id: linkd.el,v 1.64 2008/03/14 $ +;; Last-Updated: Sun Mar 7 11:48:30 2010 (-0800) +;; By: dradams +;; Update #: 629 +;; Package-Version: 0.9 +;; Website, original version: http://dto.github.com/notebook/linkd.html +;; URL: http://www.emacswiki.org/cgi-bin/wiki/linkd.el +;; URL: http://www.emacswiki.org/emacs/linkd.tar.gz +;; Keywords: hypermedia help +;; Compatibility: GNU Emacs 21.x, GNU Emacs 22.x +;; +;; Features that might be required by this library: +;; +;; `easymenu'. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Commentary: +;; +;; Make hypertext with active links in any buffer +;; +;; +;;(@* "Overview") ---------------------------------------------------- +;; +;; Linkd-mode is a major mode that automatically recognizes and +;; processes certain S-expressions, called "links", embedded in plain +;; text files. Links may be followed by invoking certain interactive +;; functions when point is on the link text. Links may also be +;; interpreted as marking up the surrounding text. Different types +;; of links have different behaviors when followed, and they may have +;; different interpretations as markup. +;; +;; With Linkd mode, you can do the following: +;; * Embed hyperlinks to files, webpages, or documentation into +;; any type of text file in any major mode. +;; * Delimit and name regions of text ("blocks") in these text files. +;; See (@> "Stars") +;; * Extract and send blocks to other programs for processing. +;; See (@> "Processing blocks") +;; * Identify and mark locations and concepts in source code. +;; See (@> "Tags") +;; * Embed active data objects ("datablocks") into text files. +;; See (@> "Datablocks") +;; * Convert Lisp source-code listings to LaTeX for publication. +;; See (@> "Exporting to LaTeX") +;; * Define new link behaviors. +;; +;; For detailed information about using linkd-mode, see the online +;; manual: http://dto.github.com/notebook/linkd.html. +;; +;; +;;(@* "TODO") -------------------------------------------------------- +;; +;; * Should have a proper history of link navigation, like in Info, +;; for forward and backward link following, instead of just saving +;; the previous location. +;; +;; * Should have a link follow behavior that takes you from @> to the +;; corresponding @*, not just to the next link (@* or @>). +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Change log: +;; +;; 2010/03/07 dadams +;; linkd-render-link: +;; Don't render unless the (@...) is really a function call. Thx to eeeickythump. +;; 2010/02/28 dadams +;; linkd-match: Incorporated bug fix from Emacs Wiki by eeeickythump: Ensure sexp is symbol. +;; Incorporated addition of autoloads by Daniel Hackney (from Emacs Wiki 2010-02-06). +;; 2009/03/12 sjohnson +;; Updated embedded URLs. +;; 2009/02/17 sjohnson +;; Removed test for linkd-mode from menu - un-needed. +;; 2009/02/16 dadams +;; linkd-html-export: Do nothing if htmlize.el is not available. +;; Show Linkd menu only in Linkd mode. +;; linkd-enable-linkd-mode-in-target: Added :tags +;; linkd-use-menu: Changed default value to t. +;; 2009/02/15 sjohnson +;; Added: linkd-use-menu, linkd-enable-linkd-mode-in-target, linkd-maybe-enable-in-target, +;; linkd-menu. +;; Restored require of easymenu - used now. +;; 2009/02/10 dadams +;; Renamed: linkd-insertion-schemes to linkd-type-keywords-alist, +;; linkd-export-formats to linkd-export-formats-alist. +;; Changed defvars to defcustoms: linkd-use-icons, linkd-icons-directory, +;; linkd-generic-regexp, linkd-type-keywords-alist, linkd-default-bullet-string, +;; linkd-star-search-string, linkd-block-file-name, linkd-shell-buffer-name, +;; linkd-export-heading-regexp, linkd-export-commentary-regexp, linkd-export-link-regexp, +;; linkd-export-formats-alist, linkd-file-handler-alist, linkd-wiki-extensions, +;; linkd-wiki-directory. +;; linkd-file-handler-alist: +;; Default value no longer nil - now covers .el files, find-library, finder-commentary. +;; @file: Treat :to also for the handler case (since handler just opens the file). +;; Turn on Linkd mode for the target file. +;; Removed: (require 'easymenu) - doesn't seem to be used. +;; 2008/04/18 dadams +;; linkd-overlay: +;; Put keymap property back on the overlay (for RET etc.). Thx to Shaun Johnson. +;; 2008/04/16 dadams +;; linkd-overlay: Add keymap property of linkd-overlay-map to the display property. +;; Remove keymap property from the overlay itself. +;; linkd-map: Removed linkd-follow-mouse binding to mouse-2. +;; 2008/03/21 dadams +;; linkd-back: Reset linkd-previous-point. +;; linkd-map: Bind mouse-2 here also, as workaround for Emacs bug. Remove when bug fixed. +;; 2008/03/14 dadams +;; linkd-follow-mouse: Go to the buffer of clicked window. +;; linkd(-overlay)-map: Bound linkd-follow-mouse to mouse-2 and linkd-back to mouse-4. +;; linkd-(enable|disable): +;; Ensure add/remove text props doesn't count as buffer modification. +;; linkd-overlay: Added mouse-face to links. +;; Renamed faces, to remove -face suffix and be more specific. +;; Removed all face variables - just use faces. +;; Changed face default definitions, to be less gaudy. Still needs work (dark/light bg). +;; linkd-send-block-to-shell: goto-char point-max instead of end-of-buffer. +;; Changed require cl to eval-when-compile require. +;; linkd-activate-datablock: Added missing right paren. Removed extra one elsewhere. +;; linkd-use-datablocks: defvar, not defun (!). +;; Collected defvars together and gave them doc strings. +;; Added doc strings, cleaned up doc strings (still some missing or unclear). +;; Use header2.el header. +;; Code cleanup (cosmetic). +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 3, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth +;; Floor, Boston, MA 02110-1301, USA. +;; +;; This file is not part of GNU Emacs. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Code: + +(eval-when-compile (require 'cl)) ;; block, case +(require 'easymenu) ;; easy-menu-define + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +;; (@* "Faces") ------------------------------------------------------ + +(defgroup linkd nil + "Hypertext links." + :prefix "linkd-" + :group 'convenience :group 'help + :link '(url-link :tag "Download" "http://www.emacswiki.org/cgi-bin/wiki/linkd.el") + :link '(url-link :tag "Download (with icons)" " http://www.emacswiki.org/emacs/linkd.tar.gz") + :link '(emacs-commentary-link :tag "Doc" "linkd")) + +(defface linkd-generic-link '((t (:foreground "blue"))) + "Face for linkd links." :group 'linkd :group 'faces) + +(defface linkd-generic-link-name '((t (:foreground "blue"))) + "Face for linkd links." :group 'linkd :group 'faces) + +(defface linkd-star `((t (:foreground ,(frame-parameter nil 'background-color)))) + "Face for star delimiters." :group 'linkd :group 'faces) + +(defface linkd-star-name '((t (:foreground "blue" :background "Pink"))) + "Face for star names." :group 'linkd :group 'faces) + +(defface linkd-tag `((t (:foreground ,(frame-parameter nil 'background-color)))) + "Face for tags." :group 'linkd :group 'faces) + +(defface linkd-tag-name '((t (:foreground "blue" :underline t))) + "Face for tag names." :group 'linkd :group 'faces) + +(defface linkd-icon '((t (:underline nil))) + "Face for icons." :group 'linkd :group 'faces) + +(defface linkd-wiki '((t (:foreground "FireBrick" :underline t))) + "Face for camel-case wiki links." :group 'linkd :group 'faces) + +(defface linkd-command '((t (:foreground "red" :underline t))) + "Face for command links." :group 'linkd :group 'faces) + + +;; (@* "User Options") ----------------------------------------------- + +(defcustom linkd-use-icons nil + "Non-nil means icons, instead of text bullets, are displayed for links." + :type 'boolean :group 'linkd) + +(defcustom linkd-icons-directory "~/.linkd-icons" "Directory where linkd's icons are kept." + :type 'directory :group 'linkd) + +(defcustom linkd-use-menu t + "Non-nil means show the Linkd menu in the menu bar." + :type 'boolean :group 'linkd) + +(defcustom linkd-enable-linkd-mode-in-target t + "Whether to turn on Linkd mode for the target of a @file link. +* t - turn linkd mode on unconditionally. + +* nil - don't turn linkd mode on. + +* A list of major mode symbols, Turn on linkd mode if the target + buffer's mode is in this list. + +* A function to be called in the context of the target buffer. + Turn on linkd mode if it returns a non-nil value." + :type '(choice + (const :tag "Turn on Linkd mode unconditionally" t) + (const :tag "Do not turn on Linkd mode" nil) + (repeat :tag "Modes to use Linkd" + (symbol :tag "Major mode for which to turn on Linkd mode")) + (function :tag "Turn on Linkd mode if this function returns non-nil")) + :group 'linkd) + +(defcustom linkd-generic-regexp (concat "\(" "@" "[^)]*\)") + "Regexp to find links." + :type 'regexp :group 'linkd) + +(defcustom linkd-type-keywords-alist '(("file" :file-name :to :display) + ("man" :page :to :display) + ("info" :file-name :node :to :display) + ("url" :file-name :display)) + "Alist of possible link types and their associated Linkd keywords. +Each key is a link type name. +Each value is a list of Linkd keywords to use for that type (key)." + :type '(alist + :key-type (string :tag "Link type") + :value-type (repeat (symbol :tag "Linkd keywords for this type"))) + :group 'linkd) + +(defcustom linkd-default-bullet-string "." + "Default string to use to display a bullet." + :type 'string :group 'linkd) + +(defcustom linkd-star-search-string (concat "\(" "\@\*") + "Regexp that matches a Linkd star." + :type 'string :group 'linkd) + +(defcustom linkd-block-file-name "~/.linkd-block" + "File where temporary block text is stored for external processing." + :type 'file :group 'linkd) + +(defcustom linkd-shell-buffer-name "*Linkd Shell*" + "Name of shell buffer used by Linkd." + :type 'string :group 'linkd) + +;; Used for export to LaTeX and HTML. +(defcustom linkd-export-heading-regexp (concat "(" "@\\* \"\\([^\"]*\\)\")") + "Regexp to match section headings in the buffer." + :type 'regexp :group 'linkd) + +;; Used for export to LaTeX and HTML. +(defcustom linkd-export-commentary-regexp "^;;" + "Regexp to match commentary lines in a buffer." + :type 'string :group 'linkd) + +;; Used for export to LaTeX and HTML. +;; Of course no regexp can correctly recognize matched parentheses. +;; But our links are always on a single line, so we can sort of make it work. +(defcustom linkd-export-link-regexp (concat "(" "@" ".*)$") + "Regexp to match Linkd links." + :type 'string :group 'linkd) + +;; Used for export to LaTeX and HTML. +(defcustom linkd-export-formats-alist '(("html" . linkd-html-export) + ("tex" . linkd-latex-export)) + "Alist of file extensions and associated export formats, for Linkd." + :type '(alist + :key-type (string :tag "File-name extension") + :value-type (symbol :tag "Export function")) + :group 'linkd) + +(defcustom linkd-file-handler-alist + '(("el" . (lambda (file-name) + (let ((curr-mode major-mode)) + (condition-case nil + (if (eq curr-mode 'finder-mode) + (condition-case nil + (finder-commentary file-name) + (error (find-library file-name))) + (find-library file-name)) + (error (find-file file-name))))))) + "Alist that maps file extensions to functions that open files. +Each such function should accept a file name as its argument." + :type '(alist + :key-type (string :tag "File extension (no period)") + :value-type (symbol :tag "Handler function for such files")) + :group 'linkd) + +(defcustom linkd-wiki-extensions '("linkd" "org" "el") + "List of file-name extensions to try, to look for a given wiki page." + :type '(repeat string) :group 'linkd) + +(defcustom linkd-wiki-directory "~/linkd-wiki" + "Default directory to look for wiki pages in." + :type 'directory :group 'linkd) + + +;; (@* "Internal Variables") ----------------------------------------- + +(defvar linkd-previous-buffer nil "Last buffer being shown.") + +(defvar linkd-previous-point nil "Value of point before link following.") + +;; We may attach keybindings to an overlay, so that the keybindings +;; are in effect whenever point is within the overlay. For rapid +;; navigation, we will eventually attach some quick single-character +;; commands to the links, using the following keymap: +(defvar linkd-overlay-map nil "Keymap for Linkd overlays.") +(unless linkd-overlay-map + (setq linkd-overlay-map (make-sparse-keymap)) + (define-key linkd-overlay-map (kbd "RET") 'linkd-follow-at-point) + ;; $$$$(define-key linkd-overlay-map [down-mouse-2] 'ignore) + (define-key linkd-overlay-map [mouse-2] 'linkd-follow-mouse) + (define-key linkd-overlay-map [mouse-4] 'linkd-back) + (define-key linkd-overlay-map (kbd "b") 'linkd-back) + (define-key linkd-overlay-map (kbd "l") 'linkd-back) + (define-key linkd-overlay-map (kbd "[") 'linkd-previous-link) + (define-key linkd-overlay-map (kbd "]") 'linkd-next-link)) + +(defvar linkd-process-block-function nil + "Function called by `linkd-process-block'. +Argument is the contents of the block around point as a string. +You can set this in the `Local Variables' section of a file.") +(make-variable-buffer-local 'linkd-process-block-function) + +(defvar linkd-use-datablocks nil "When non-nil, Linkd uses datablocks in the current buffer.") +(make-variable-buffer-local 'linkd-use-datablocks) + +(defvar linkd-datablocks-activated nil "When non-nil, Linkd activates datablocks.") +(make-variable-buffer-local 'linkd-datablocks-activated) + +;; Used for export to LaTeX. +(defvar linkd-latex-in-verbatim nil "Non-nil means we are inside a LaTeX verbatim section.") + +(defvar linkd-map nil "Keymap used by Linkd mode.") +(when (null linkd-map) + (setq linkd-map (make-sparse-keymap)) + (define-key linkd-map (kbd "C-c *") 'linkd-process-block) + (define-key linkd-map (kbd "C-c [") 'linkd-previous-link) + (define-key linkd-map (kbd "C-c ]") 'linkd-next-link) + (define-key linkd-map (kbd "C-c '") 'linkd-follow-at-point) + (define-key linkd-map [mouse-4] 'linkd-back) + (define-key linkd-map (kbd "C-c , b") 'linkd-back) + (define-key linkd-map (kbd "C-c , ,") 'linkd-insert-link) + (define-key linkd-map (kbd "C-c , t") 'linkd-insert-tag) + (define-key linkd-map (kbd "C-c , s") 'linkd-insert-star) + (define-key linkd-map (kbd "C-c , w") 'linkd-insert-wiki) + (define-key linkd-map (kbd "C-c , l") 'linkd-insert-lisp) + (define-key linkd-map (kbd "C-c , e") 'linkd-edit-link-at-point) + (define-key linkd-map (kbd "C-c , x") 'linkd-escape-datablock)) + +;; Linkd menu for menu bar. +(easy-menu-define linkd-menu linkd-map "Linkd" + '("Linkd" + :visible linkd-use-menu + ["Follow" linkd-follow-at-point :active (get-char-property (point) 'linkd)] + ["Back" linkd-back :active (get-char-property (point) 'linkd)] + ["Previous link" linkd-previous-link :active (get-char-property (point) 'linkd)] + ["Next link" linkd-next-link :active (get-char-property (point) 'linkd)] + ("Insert" + ["Tag" linkd-insert-tag] + ["Star" linkd-insert-star] + ["Link" linkd-insert-link]) + ["Edit" linkd-edit-link-at-point :active (get-char-property (point) 'linkd)])) + + +;; (@* "Versioning") ------------------------------------------------- + +;;;###autoload +(defun linkd-version () + "Display Linkd version." + (interactive) + ;; (message "$Id: linkd.el,v 1.63 2007/05/19 00:16:17 dto Exp dto $")) + (message "$Id: linkd.el,v 1.64 2008/03/14 $")) + + +;; (@* "Recognizing Links") ------------------------------------------ +;; +;; In working with Emacs' font-lock code to obtain automatic +;; recognition of a construct, one typically uses a regular expression +;; to match the construct. But recall that we are looking to match +;; S-expressions, which cannot be matched by any regular +;; expression. To overcome this difficulty, we can supply font-lock +;; with a function to perform the search, instead of a regular +;; expression. If this function uses the system's built-in Lisp +;; reader, we can then match proper S-expressions. +;; +;; Below is a function that Emacs' font-locking can use to find and +;; highlight links. See (@> "Fontlocking") below. + +(defun linkd-match (limit) + "Try to read link sexp between point and LIMIT. +Return non-nil if a link is found. Set match-data appropriately." + (let ((sexp nil)) + (when (search-forward (concat "(" "@") limit t) (backward-char 2)) + (let ((begin-point (point))) + (condition-case nil (setq sexp (read (current-buffer))) ((error nil))) + (when (and (symbolp (car-safe sexp)) + (string-match "@.*" (symbol-name (car-safe sexp)))) + (let ((begin-marker (make-marker)) + (end-marker (make-marker))) + (set-marker begin-marker begin-point) + (set-marker end-marker (point)) + (set-match-data (list begin-marker end-marker))) + t)))) + +;; Function to extract link data from plain text. It determines the +;; presence of a link by searching for the `linkd' text property, +;; instead of using the regular expression given above. This is +;; because of the way link rendering works. When the activation of +;; Linkd mode triggers fontification of a buffer containing links, the +;; links are matched by the font-locking code, and marked with the +;; `linkd' text property. All the other functions that deal with +;; links can then use the `linkd' text property, which is simpler than +;; using regexps throughout. See (@> "Rendering links with overlays") +;; and (@> "Fontlocking"). + +(defun linkd-link-at-point () + "Return link around point as a sexp. Return nil if no link found." + (when (get-char-property (point) 'linkd) + (save-excursion (read (current-buffer))))) + + +;; (@* "Following Links") -------------------------------------------- +;; +;; Each link is an S-expression. When this S-expression is evaluated, +;; the result is a property list whose keys represent possible user +;; actions, and whose values are functions to be invoked when the +;; corresponding key is chosen. To follow a link, we evaluate the +;; link's S-expression and invoke the function corresponding to the +;; `:follow' property in the resulting property list. +;; +;; The results of following a link will often change the currently +;; displayed buffer, so we remember which is the current buffer before +;; switching, and provide a function, `linkd-back', to return to the +;; old buffer. + +(defun linkd-follow (sexp) + "Follow the link represented by SEXP." + (let* ((plist (eval sexp)) + (follower (plist-get plist :follow))) + (when follower + ;; save current spot so that we can go back if needed + (setq linkd-previous-buffer (current-buffer)) + (setq linkd-previous-point (point)) + (funcall follower)))) + +;;;###autoload +(defun linkd-back () + "Return to the buffer being viewed before the last link was followed." + (interactive) + (when linkd-previous-buffer + (switch-to-buffer linkd-previous-buffer) + (let ((start (point))) + (goto-char linkd-previous-point) + (setq linkd-previous-point start)))) + +;;;###autoload +(defun linkd-follow-at-point () + "Follow the link at point." + (interactive) + (linkd-follow (linkd-link-at-point))) + +(defun linkd-follow-mouse (event) + "Follow the clicked link." + (interactive "e") + (when event + (select-window (posn-window (event-start event))) + (set-buffer (window-buffer (posn-window (event-start event)))) + (goto-char (posn-point (event-start event))) + ;;; $$$$ (beginning-of-line) + (linkd-follow (linkd-link-at-point)))) + +(defun linkd-maybe-enable-in-target () + "Conditionally enable linkd mode in the target of an @file link." + (when (or (and (booleanp linkd-enable-linkd-mode-in-target) + linkd-enable-linkd-mode-in-target) + (and (functionp linkd-enable-linkd-mode-in-target) + (funcall linkd-enable-linkd-mode-in-target)) + (and (listp linkd-enable-linkd-mode-in-target) + (memq major-mode linkd-enable-linkd-mode-in-target))) + (linkd-mode 1))) + +;; (@* "Navigating Links") ------------------------------------------- +;; +;; Instead of manually positioning point on each link, we can navigate +;; directly between links. The following interactive functions jump +;; from link to link. + +;;;###autoload +(defun linkd-next-link () + "Move point to the next link, if any." + (interactive) + (forward-char 1) + (let ((inhibit-point-motion-hooks nil)) + ;; get out of the current overlay if needed + (when (get-char-property (point) 'linkd) + (while (and (not (eobp)) (get-char-property (point) 'linkd)) + (goto-char (min (next-overlay-change (point)) + (next-single-char-property-change (point) 'linkd))))) + ;; now find the next linkd overlay + (while (and (not (eobp)) (not (get-char-property (point) 'linkd))) + (goto-char (min (next-overlay-change (point)) + (next-single-char-property-change (point) 'linkd)))))) + +;;;###autoload +(defun linkd-previous-link () + "Move point to the previous link, if any." + (interactive) + (let ((inhibit-point-motion-hooks nil)) + ;; get out of the current overlay if needed + (when (get-char-property (point) 'linkd) + (while (and (not (bobp)) (get-char-property (point) 'linkd)) + (goto-char (max (previous-overlay-change (point)) + (previous-single-char-property-change (point) 'linkd))))) + ;; now find the previous linkd overlay + (while (and (not (bobp)) (not (get-char-property (point) 'linkd))) + (goto-char (max (previous-overlay-change (point)) + (previous-single-char-property-change (point) 'linkd)))))) + + +;; (@* "Inserting and Editing Links Interactively") ------------------ +;; +;; It is not necessary to type the links manually. With these +;; functions, the user may create and edit links interactively. + +;;;###autoload +(defun linkd-insert-single-arg-link (type-string argument) + "Insert a link containing ARGUMENT." + (insert (if (not (string= "" argument)) + (format (concat "(" "@%s %S)") type-string argument) + (format (concat "(" "@%s)") type-string)))) + +;;;###autoload +(defun linkd-insert-tag (tag-name) + "Insert a tag." + (interactive "sTag name: ") + (linkd-insert-single-arg-link ">" tag-name)) + +;;;###autoload +(defun linkd-insert-star (star-name) + "Insert a star." + (interactive "sStar name: ") + (linkd-insert-single-arg-link "*" star-name)) + +;;;###autoload +(defun linkd-insert-wiki (wiki-name) + "Insert a wiki link." + (interactive "sWiki page: ") + (linkd-insert-single-arg-link "!" wiki-name)) + +;;;###autoload +(defun linkd-insert-lisp (sexp) + "Insert a Lisp sexp." + (interactive "xLisp expression: ") + (linkd-insert-single-arg-link "L" sexp)) + +;;;###autoload +(defun linkd-insert-link (&optional type current-values) + "Insert a link. +Optional arg TYPE is the link type. +Optional arg CURRENT-VALUES is a property list of current values." + (interactive) + (let* ((type (or type (completing-read "Link type: " linkd-type-keywords-alist))) + (keys (cdr (assoc type linkd-type-keywords-alist))) + (key (car keys)) + (link-args nil)) + (while key + ;; read an argument value + (let ((value (read-from-minibuffer (format "%S " key) (plist-get current-values key)))) + (when (not (string= "" value)) (setq link-args (plist-put link-args key value)))) + ;; next + (setq keys (cdr keys)) + (setq key (car keys))) + ;; format and insert the link + (insert (format (concat "(" "@%s %s)") type (mapconcat (lambda (sexp) (format "%S" sexp)) + link-args + " "))))) + +;;;###autoload +(defun linkd-edit-link-at-point () + "Edit the Linkd link at point." + (interactive) + (let ((link (linkd-link-at-point))) + (when link + (if (keywordp (car (cdr link))) + (save-excursion ; it's a general link. drop the @ sign + (linkd-insert-link (substring (format "%S" (car link)) 1) (cdr link))) + ;; it's a single-arg link + (let ((new-value (read-from-minibuffer "New value: " (car (cdr link))))) + (insert (format "%S" (list (car link) new-value))))) + ;; now erase old link + (re-search-backward linkd-generic-regexp) + (delete-region (match-beginning 0) (match-end 0))))) + + +;; (@* "Rendering Links with Overlays") ------------------------------ +;; +;; Emacs' overlays allow us to render a link onscreen in ways that make +;; the meaning of the link clearer. We can do this by hiding the somewhat +;; ugly link syntax, color-coding the text, and optionally by +;; displaying graphical icons to help in determining the type of link. +;; +;; This is one of the trickiest parts of linkd-mode, as the use of +;; overlays requires attention to detail in order for things to work +;; right. +;; +;; First some preliminary definitions. + +(defun linkd-insert (string) + "Insert STRING, removing its text properties." + (insert (substring-no-properties string))) + +;; The following utility function is our standard way of applying +;; linkd-style overlays to the text of a link. + +(defun linkd-overlay (beg end display-text + &optional display-face bullet-text bullet-face bullet-icon) + "Apply Linkd overlay to link text. +$$$$$ FIXME: document args." + (let ((overlay (make-overlay beg end))) + (overlay-put + overlay 'display (propertize display-text + 'face (or display-face 'linkd-generic-link-name) + 'keymap linkd-overlay-map)) ; add speed-navigation keys + (overlay-put overlay 'mouse-face 'highlight) + (overlay-put overlay 'linkd t) ; mark overlay so that we can find it later + (overlay-put overlay 'keymap linkd-overlay-map) ; add speed-navigation keys + (when bullet-text ; add bullet, if appropriate + (let* ((face (if (and bullet-icon linkd-use-icons) 'linkd-icon bullet-face)) + (b1 (if face (propertize bullet-text 'face face) bullet-text)) + (b2 (if (and bullet-icon linkd-use-icons) + (propertize b1 'display + `(image :file ,bullet-icon :type xpm :ascent center)) + b1))) + (overlay-put overlay 'before-string (concat b2 " ")))) + (overlay-put overlay 'evaporate t) + (overlay-put overlay 'modification-hooks ; defontify if the user edits the text + (list (lambda (ov foo beg end &rest ignore) + (delete-overlay ov) + (remove-text-properties (point-at-bol) (point-at-eol) + (list 'fontified nil + 'linkd-fontified nil + 'linkd nil))))))) + + +;; (@* "Decorating Links with Graphical Icons") ---------------------- +;; +;; I have drawn a set of 16x16 icons for use with linkd-mode. When the +;; icon feature is enabled, an appropriate icon is displayed to the +;; left of the link. +;; +;; The icons are included in the linkd download at: +;; http://www.emacswiki.org/emacs/linkd.tar.gz + +(defun linkd-icon (icon-name) + "Returns the name of the icon file for ICON-NAME." + (concat (file-name-as-directory linkd-icons-directory) "linkd-" icon-name ".xpm")) + +(defun linkd-file-icon (file-name) + "Choose an appropriate icon for FILE-NAME based on the name or extension. +Returns the file-name to the icon image file." + (let* ((dir (file-name-as-directory linkd-icons-directory)) + (icon (concat dir "linkd-file-" (file-name-extension file-name) ".xpm"))) + (if (file-exists-p icon) + icon + (concat dir "linkd-file-generic.xpm")))) + + +;; (@* "Stars") ------------------------------------------------------ +;; +;; Stars delimit (and optionally name) blocks of text. A block of text +;; is the region between one star and the next. We may think of blocks +;; as dividing a text file into sections. + +(defun @* (&optional star-name) + "$$$$$$$$$$$$ FIXME" + `(:follow + (lambda () (linkd-find-next-tag-or-star ,star-name)) + :render + (lambda (beg end) + (linkd-overlay + beg end + ,(if star-name star-name " ") ; leave space so fontified link won't disappear + ',(if star-name 'linkd-star-name 'default) + "*" 'linkd-star ,(linkd-icon "star"))))) + + +;; (@* "Tags") ------------------------------------------------------- +;; +;; Tags can be used to navigate within source code. You can mark +;; those parts of a program that relate to a given concept with a +;; `tag' link that names the concept. +;; +;; Following a `tag' link navigates to the next tag (or star) with the +;; same name, cycling to the beginning of the buffer when the end is +;; reached. You can think of following tag links as tracing a concept +;; through different parts of a program by jumping between related +;; pieces of code. + +(defun linkd-find-next-tag-or-star (name) + "Find next Linkd tag or star." + (let* ((regexp (concat "\(\@\\(\*\\|>\\) \"" name)) + (found-position + (save-excursion + (goto-char (point-at-eol)) + (if (re-search-forward regexp nil t) + (match-beginning 0) + (goto-char (point-min)) ; start over at the beginning of the buffer + (when (re-search-forward regexp nil t) (match-beginning 0)))))) + (when found-position (goto-char found-position)))) + +(defun @> (tag-name) + "$$$$$$$$ FIXME" + `(:follow + (lambda () (linkd-find-next-tag-or-star ,tag-name)) + :render + (lambda (beg end) (linkd-overlay beg end ,tag-name 'linkd-tag-name + ">" 'linkd-tag ,(linkd-icon "tag"))))) + + +;; (@* "Processing Blocks") ------------------------------------------ +;; +;; You can divide a text file into sections using stars, and then +;; selectively process certain of those blocks of text, perhaps with +;; an external program. You can use this facility to experiment with +;; such external programs or to develop interactive scripts. For +;; example, you can send a block of shell-script commands to a shell +;; window for immediate execution. +;; +;; The operation to be performed is determined by the value of the +;; buffer-local variable `linkd-process-block-function'. You can set +;; this to an appropriate value in a file's `Local Variables' section. + +(defun linkd-block-around-point () + "Return the block around point as a string." + (interactive) + (let ((beg (save-excursion + (search-backward linkd-star-search-string) (beginning-of-line) (point))) + (end (save-excursion (search-forward linkd-star-search-string) (point)))) + (buffer-substring-no-properties beg end))) + +(defun linkd-write-block-to-file (block-text) + "Write the BLOCK-TEXT to the file named by linkd-block-file-name." + (interactive) + (with-temp-buffer + (insert block-text) + (write-file linkd-block-file-name))) + +(defun linkd-process-block () + "Process the Linkd block around point." + (interactive) + (funcall linkd-process-block-function (linkd-block-around-point))) + +(defun linkd-send-block-to-shell (block-text) + "Send the Linkd block around point to the shell." + (interactive) + ;; create shell if needed, but not in this window + (unless (get-buffer-window linkd-shell-buffer-name) + (save-window-excursion (shell linkd-shell-buffer-name)) + (display-buffer linkd-shell-buffer-name)) + (linkd-write-block-to-file block-text) + (save-selected-window + (select-window (get-buffer-window linkd-shell-buffer-name)) + (goto-char (point-max)) + (insert (concat ". " linkd-block-file-name)) ; make the shell source the temp file + (call-interactively (key-binding "\r")))) + + +;; (@* "Datablocks") ------------------------------------------------- +;; +;; A datablock is an embedded object of a user-defined type. It +;; consists of a "type symbol" followed by a printed representation of +;; a Lisp object called the "embedded object". The type symbol is a +;; symbol whose `symbol-function' determines the appearance and +;; behavior of the region of the buffer that contains the embedded +;; object. By convention, a type symbol's name begins with a caret +;; (`^'). +;; +;; When a datablock is "activated", the embedded object is read from +;; the buffer and fed to the type symbol's function. This function +;; can temporarily replace the region with an interactive +;; representation of the embedded object, which can then be +;; manipulated by the user. The behavior of this representation may +;; be effected by various uses of Emacs' text properties. +;; +;; When a datablock is "deactivated", the interface is replaced with a +;; plain-text representation of the new embedded object. You can +;; arrange for the automatic activation and deactivation of datablocks +;; - for example, upon saving and loading files that contain them. +;; +;; Datablocks must be activated on a per-file basis via a `Local +;; Variables' section in the file. + +;; Function to extract the embedded object at point. +(defun linkd-datablock-object-at-point () + "Returns the Linkd datablock object at point." + (get-text-property (point) 'linkd-datablock-object)) + +;; A function to insert a template datablock. This is what you use to +;; create new datablocks with specified contents. + +(defun linkd-insert-datablock-template (&optional object) + "Insert a new datablock with OBJECT as the printed contents." + (insert (format "(^begin ^cell)\n%S\n(^end)" object))) + +;; This function governs the interaction of linkd-mode's datablock +;; system with the ``modules'' that implement various types of +;; embedded objects. First the type symbol and embedded object are +;; read in from the text. The function value of the module's type +;; symbol is obtained, and the embedded object is fed to the function +;; in order to activate or deactivate the datablock as needed. The +;; function is also passed some markers that delimit the region to +;; which the module should confine its rendering activity. + +(defun linkd-activate-datablock (action) + "When ACTION is :begin, activate the current datablock. When +ACTION is :end, deactivate the datablock." + (interactive) + (when (search-forward (concat "(^" "begin ") nil t) + ;; first read in the datablock + (let* ((type-symbol (read (current-buffer))) + (datablock-begin (match-beginning 0)) + (datablock-object (progn (forward-line) (read (current-buffer)))) + (datablock-end (progn (search-forward "(^end)") (match-end 0))) + (activate (symbol-function type-symbol))) + (goto-char datablock-begin) + (case action + (:begin ; insert markers; datablock display happens in between them + (let* ((inhibit-read-only t) + (beg (make-marker)) + (end (make-marker))) + (set-marker beg (save-excursion (goto-char datablock-begin) (point-at-eol))) + (set-marker end (save-excursion (goto-char datablock-end) (point-at-bol))) + ;; make the delimiters invisible + (add-text-properties datablock-begin beg '(invisible t)) + (add-text-properties end datablock-end '(invisible t)) + ;; start the datablock going, tell it what region it is to manage + (let ((object (funcall activate :begin datablock-object beg end))) + (when (null object) (error "Null object.")) + ;; save datablock details for later lookup + (add-text-properties beg end (list 'linkd-datablock-object object))) + ;; move into the region + (goto-char (+ 1 datablock-begin)) + (message "%S" (linkd-datablock-object-at-point)))) + (:end ; stop managing the region and write the sexp back + (forward-line) + (let ((object (funcall activate :end datablock-object)) + (inhibit-read-only t) + (inhibit-point-motion-hooks t)) + (delete-region datablock-begin datablock-end) + (insert (format (concat "(^" "begin %S)\n%S\n(^end)") type-symbol object)))))))) + +(defun linkd-begin-datablock () + "Begin a Linkd datablock." + (linkd-activate-datablock :begin)) + +(defun linkd-end-datablock () + "End a Linkd datablock." + (linkd-activate-datablock :end)) + +(defun linkd-escape-datablock () + "Find the previous datablock beginning." + (interactive) + (search-backward (concat "(" "^begin ")) + (forward-line -1)) + +(defun linkd-activate-all-datablocks () + "Activate all Linkd datablocks." + (interactive) + (when (and linkd-use-datablocks (not linkd-datablocks-activated)) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (linkd-begin-datablock) + (forward-line)) + (setq linkd-datablocks-activated t)))) + +(defun linkd-deactivate-all-datablocks () + "Deactivate all Linkd datablocks." + (interactive) + (when (and linkd-use-datablocks linkd-datablocks-activated) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (linkd-end-datablock) + (forward-line)) + (setq linkd-datablocks-activated nil)))) + + +;; (@* "Exporting to Other Formats") --------------------------------- +;; +;; Linkd supports export to LaTeX and HTML. What follows are some +;; functions basic to the export process. + +(defun linkd-export (export-function output-file-name) + "Export the current-buffer using EXPORT-FUNCTION and write + output to OUTPUT-FILE-NAME. EXPORT-FUNCTION should convert to + the output format, do any required postprocessing, and return + the buffer with the ouput." + (with-current-buffer (funcall export-function) + (write-file (expand-file-name output-file-name)))) + +;;;###autoload +(defun linkd-export-default () + "Export the current buffer with default settings to all available formats." + (interactive) + (dolist (format linkd-export-formats-alist) + (let* ((extension (car format)) + (output-file (concat (buffer-file-name) "." extension)) + (export-function (cdr format))) + (linkd-export export-function output-file)))) + +;; (@* "Exporting to LaTeX") ----------------------------------------- +;; +;; This section contains routines to transform Lisp source code files +;; into beautiful LaTeX documents in (roughly) the style of Donald +;; Knuth's "Literate Programming". To take advantage of this feature, +;; the source code to be transformed should contain alternating +;; regions of commentary and code, with appropriate star headings to +;; group these regions into document sections. +;; +;; FIXME: There is no such function: `linkd-latex-render' +;; +;; The interactive function `linkd-latex-render' transforms the source +;; code in a temporary buffer and writes the result to a corresponding +;; LaTeX file. Where tags appear in Commentary, they are prettified +;; in the LaTeX output. +;; +;; The purist might object that true literate programming requires a +;; tool capable of resequencing code fragments and performing macro +;; expansion, neither of which are implemented here. In response to +;; this objection I (David O'Toole) point out the following: (i) there +;; is little need for resequencing in a language like Lisp, where +;; declarations can be ordered more or less as you please; (ii) Lisp +;; already has a powerful macro expansion facility; and (iii) there is +;; no reason why a system that deviates somewhat from the +;; traditionally accepted definition of literate programming should +;; not still contribute to the writing of better programs. +;; +;; FIXME: No such `require' in this file: The `fancyvrb' package is +;; required. + +(defun linkd-latex-begin-verbatim () + "Insert LaTeX `Verbatim' start tag." + (setq linkd-latex-in-verbatim t) + (insert (concat "\\" "begin{Verbatim}[fontsize=\\small]\n"))) + +(defun linkd-latex-end-verbatim () + "Insert LaTeX `Verbatim' end tag." + (setq linkd-latex-in-verbatim nil) + (insert (concat "\\" "end{Verbatim}\n"))) + +(defun linkd-latex-do-section (title) + "Insert LaTeX section tag." + (insert (format "\\section{%s}\n" title))) + +(defun linkd-latex-toggle-verbatim () + "Insert LaTeX `Verbatim' begin or end tag, as needed." + (if linkd-latex-in-verbatim (linkd-latex-end-verbatim) (linkd-latex-begin-verbatim))) + +;;;###autoload +(defun linkd-latex-export () + "Render a buffer as a LaTeX book chapter." + (interactive) + (let* ((output-buffer (get-buffer-create "*linkd-litprog*")) + (source-buffer (current-buffer))) + (with-current-buffer output-buffer + (let ((linkd-use-datablocks nil)) + (delete-region (point-min) (point-max)) ; clean up any previous output + (insert-buffer-substring-no-properties source-buffer) ; make a copy of the source + ;; delete everything before first heading + (goto-char (point-min)) + (re-search-forward linkd-export-heading-regexp) + (previous-line) + (end-of-line) + (delete-region (point-min) (point)) + ;; now process each block in turn. + (while (and (not (eobp)) (re-search-forward linkd-export-heading-regexp nil nil)) + (let ((title (match-string 1))) + (delete-region (point-at-bol) (point-at-eol)) + (linkd-latex-do-section title) + (forward-line) + (block processing + (while (not (eobp)) + (cond ((string-match linkd-export-heading-regexp ; heading + (buffer-substring (point-at-bol) (point-at-eol))) + (when linkd-latex-in-verbatim (linkd-latex-end-verbatim)) + (return-from processing)) + ((looking-at linkd-export-commentary-regexp) ; commentary + ;; get rid of comment delimiter + (delete-region (match-beginning 0) (match-end 0)) + (when linkd-latex-in-verbatim (linkd-latex-end-verbatim))) + (t ; code + (when (null linkd-latex-in-verbatim) (linkd-latex-begin-verbatim)))) + (forward-line 1))) + (when linkd-latex-in-verbatim ; close verbatim environment + (linkd-latex-end-verbatim)))) + ;; render linkd's tags nicely + (let ((tag-regexp "\(\@> \"\\(.*\\)\")")) + (goto-char (point-min)) + (while (and (not (eobp)) (re-search-forward tag-regexp nil t)) + (replace-match (format "$\\\\Rightarrow ${\\\\bf %s}" (match-string 1))))) + (current-buffer))))) + + +;; (@* "Exporting to HTML") ------------------------------------------ +;; +;; This functionality is built on top of Hrvoje Niksic's htmlize.el: +;; http://fly.srk.fer.hr/~hniksic/emacs/htmlize.el + +(defun linkd-html-export () + "Convert the current buffer to HTML using htmlize.el and some +extra rules. Return the buffer." + (when (require 'htmlize nil t) + (let* ((source-buffer (current-buffer)) + (output-buffer (htmlize-buffer source-buffer))) + ;; now postprocess it + (with-current-buffer output-buffer + (goto-char (point-min)) + (let ((star-regexp + (concat "(" "@" "\\* \"\\(.*\\)\")")) + (sexp-regexp + (concat "(" "@" "[^ ].* \"\\(.*\\)\")"))) + (while (re-search-forward star-regexp nil t) + (replace-match + (concat " " + "\\1"))))) + ;; return the buffer + output-buffer))) + + +;; (@* "Links to Files") --------------------------------------------- +;; +;; Since Emacs works mainly with in text files, one of the most common +;; uses for a link is in navigating from one text file to another. +;; The following declarations define such file links. (Note how the +;; function `@file' returns the type of property list discussed in +;; section (@> "Following links"). +;; +;; You can also associate a Lisp function with each type of file, and +;; then arrange for the function to be used to open the file (instead +;; of visiting it within Emacs using `find-file'.) + +(defun @file (&rest p) + "$$$$$$$ FIXME" + (let ((file-name (plist-get p :file-name)) + (to (plist-get p :to)) + (display (plist-get p :display))) + `(:follow + (lambda () + (let ((handler (cdr (assoc (file-name-extension ,file-name) + linkd-file-handler-alist)))) + (if handler + (funcall handler ,file-name) + ;; default action is find-file + (find-file ,file-name)) + (when ,to + (beginning-of-buffer) + (search-forward ,to))) + (linkd-maybe-enable-in-target)) + :render + (lambda (beg end) + (linkd-overlay beg end ,(or display (concat file-name (if to (concat " : " to) ""))) + nil linkd-default-bullet-string nil + ,(linkd-file-icon file-name)))))) + + +;; (@* "Other Link Types") ------------------------------------------- +;; +;; Here are more examples of link type definitions. These link types +;; navigate to UNIX manual pages, GNU Info documentation, and to +;; webpages. + +(defun @man (&rest p) + "$$$$$$$$$$ FIXME" + (let ((page (plist-get p :page)) + (to (plist-get p :to)) + (display (plist-get p :display))) + `(:follow + (lambda () + (man ,page) + (when ,to + (beginning-of-buffer) + (search-forward ,to))) + :render + (lambda (beg end) + (linkd-overlay + beg end ,(or display (concat page " manual" (if to (concat " : " to) ""))) + nil linkd-default-bullet-string nil ,(linkd-icon "man")))))) + +(defun @info (&rest p) + "$$$$$$$$$$ FIXME" + (let ((file (plist-get p :file-name)) + (node (plist-get p :node)) + (to (plist-get p :to)) + (display (plist-get p :display))) + `(:follow + (lambda () + (info (concat "(" ,file ")" ,node)) + (when ,to + (beginning-of-buffer) + (search-forward ,to))) + :render + (lambda (beg end) + (linkd-overlay + beg end ,(or display (concat file " manual" (if to (concat " : " to) ""))) + 'linkd-generic-link-name linkd-default-bullet-string nil ,(linkd-icon "info")))))) + +(defun @url (&rest p) + "$$$$$$$$$$ FIXME" + (let ((file-name (plist-get p :file-name)) + (display (plist-get p :display))) + `(:follow + (lambda () + (browse-url ,file-name)) + :render + (lambda (beg end) + (linkd-overlay beg end ,(or display file-name) 'linkd-generic-link-name + linkd-default-bullet-string nil ,(linkd-icon "url")))))) + + +;; (@* "Lisp Links") ------------------------------------------------- + +(defun @L (sexp) + "$$$$$$$$$$ FIXME" + `(:follow + (lambda () + (message "%S" (eval ,sexp))) + :render + (lambda (beg end) + (linkd-overlay beg end ,(format "%S" sexp) 'linkd-command + linkd-default-bullet-string nil ,(linkd-icon "url"))))) + + +;; (@* "Wiki Features") ---------------------------------------------- +;; +;; When using Emacs, you typically build up a library of text files. +;; You can turn this collection into a hypertext wiki by inserting +;; wiki links from one file to another. Wiki names LookLikeThis. + +;;;###autoload +(defun linkd-wiki-find-page (page-name) + "Find Linkd wiki page named PAGE-NAME." + (interactive "s") + (let ((page-file + (block testing + (dolist (extension linkd-wiki-extensions) + (let ((test-filename (concat (file-name-as-directory linkd-wiki-directory) + page-name "." extension))) + (if (file-exists-p test-filename) + (return-from testing test-filename) + (return-from testing nil))))))) + (if page-file + (find-file page-file) + ;; otherwise, query the user which file extension to create + (let ((ext (completing-read "Create wiki page with extension: " linkd-wiki-extensions))) + (find-file + (concat (file-name-as-directory linkd-wiki-directory) page-name "." ext)))))) + +(defun @! (page) + "$$$$$$$$$$ FIXME" + `(:follow + (lambda () (linkd-wiki-find-page ,page)) + :render + (lambda (beg end) (linkd-overlay beg end ,page 'linkd-wiki)))) + + +;; (@* "Minor Mode for Linkd") --------------------------------------- +;; +;; When Linkd minor mode is active, links are displayed using +;; overlays, and keybindings are available for common Linkd functions. +;; The keybindings are in accord with the convention for minor-modes: +;; `C-c' followed by one of a set of reserved punctuation characters. + +(define-minor-mode linkd-mode + "Create or follow hypertext links. +These link navigation commands are available: + +\\\\[linkd-follow-at-point] - follow link under cursor +\\[linkd-follow-mouse] - follow clicked link +\\[linkd-back] - return to last link followed +\\[linkd-next-link] - go to next link in buffer +\\[linkd-previous-link] - go to previous link in buffer + +These key bindings are in effect on a link:\n +\\{linkd-overlay-map}These key bindings are effect everywhere:\n +\\{linkd-map}" + nil :lighter " Linkd" :keymap linkd-map (if linkd-mode (linkd-enable) (linkd-disable))) + +(defun linkd-enable () + "Enable Linkd mode." + (let ((modified-p (buffer-modified-p))) + (add-hook 'before-save-hook 'linkd-deactivate-all-datablocks :append :local) + (add-hook 'after-save-hook 'linkd-activate-all-datablocks :append :local) + (linkd-do-font-lock 'font-lock-add-keywords) + (font-lock-fontify-buffer) + (set-buffer-modified-p modified-p))) + +(defun linkd-disable () + "Disable Linkd mode." + (let ((modified-p (buffer-modified-p))) + (remove-hook 'before-save-hook 'linkd-deactivate-all-datablocks) + (remove-hook 'after-save-hook 'linkd-activate-all-datablocks) + ;; remove all linkd's overlays + (mapcar (lambda (overlay) + (when (get-text-property (overlay-start overlay) 'linkd-fontified) + (delete-overlay overlay))) + (overlays-in (point-min) (point-max))) + ;; remove font-lock rules, textprops, and then refontify the buffer + (linkd-do-font-lock 'font-lock-remove-keywords) + (remove-text-properties (point-min) (point-max) '(linkd-fontified)) + (font-lock-fontify-buffer) + (set-buffer-modified-p modified-p))) + + +;; (@* "Font-Locking") ----------------------------------------------- +;; +;; Each link type can execute arbitrary code to render itself. In the +;; typical case, we use `(linkd-overlay)' to render the link using +;; overlays and possibly icons. +;; See also (@> "Rendering links with overlays"). +;; +;; The following function invokes a link's rendering code. + +(defun linkd-render-link (beg end) + "Invoke a link's rendering code." + (unless (get-text-property beg 'linkd-fontified) + (save-excursion + (goto-char beg) + (let ((sexp (read (current-buffer)))) + ;; For a Linkd link, the sexp is always a list whose car is a function + ;; name that begins with `@'. + (when (and sexp (fboundp (car sexp))) + (add-text-properties beg (+ beg 1) (list 'linkd-fontified t)) + (let* ((plist (eval sexp)) + (renderer (plist-get plist :render))) + (unless renderer (error "No renderer for link.")) + (funcall renderer beg end))))))) + +;; Interface with the Emacs font-locking system. You can configure +;; `linkd-do-font-lock' to add or remove font-locking rules that cause +;; Linkd's links to be fontified. + +(defun linkd-do-font-lock (add-or-remove) + "Add or remove font-lock rules for Linkd." + (funcall add-or-remove nil `((linkd-match 0 (let ((beg (match-beginning 0)) + (end (match-end 0))) + (linkd-render-link beg end) + 'linkd-generic-link) + prepend)))) + +;;;;;;;;;;;;;;;;;;;;;;;;; + +(provide 'wiki/linkd) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; linkd.el ends here diff --git a/lisp/wiki/rotate-text.el b/lisp/wiki/rotate-text.el new file mode 100644 index 0000000..06c4cef --- /dev/null +++ b/lisp/wiki/rotate-text.el @@ -0,0 +1,224 @@ +;;; rotate-text.el --- cycle through words, symbols and patterns +;; +;; Copyright (C) 2009 Nikolaj Schumacher +;; +;; Author: Nikolaj Schumacher +;; Version: 0.1 +;; Keywords: abbrev, convenience, matching +;; URL: http://nschum.de/src/emacs/rotate-text/ +;; Compatibility: GNU Emacs 22.x, GNU Emacs 23.x +;; +;; This file is NOT part of GNU Emacs. +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 2 +;; of the License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;; +;; rotate-text allows you cycle through commonly interchanged text with a single +;; keystroke. For example, you can toggle between "frame-width" and +;; "frame-height", between "public", "protected" and "private" and between +;; "variable1", "variable2" through "variableN". +;; +;; Add the following to your .emacs: +;; +;; (add-to-list 'load-path "/path/to/rotate-text") +;; (autoload 'rotate-text "rotate-text" nil t) +;; (autoload 'rotate-text-backward "rotate-text" nil t) +;; +;; Customize the variables `rotate-text-patterns', `rotate-text-symbols' and +;; `rotate-text-words'. You can make buffer-local additions in +;; `rotate-text-local-patterns', `rotate-text-local-symbols' and +;; `rotate-text-local-words'. +;; +;; Use the commands `rotate-text' and `rotate-text-backward' to rotate the +;; text. +;; +;;; Change Log: +;; +;; 2009-04-13 (0.1) +;; Initial release. +;; +;;; Code: + +(eval-when-compile (require 'cl)) + +(add-to-list 'debug-ignored-errors "^Nothing to rotate$") + +(defgroup rotate-text nil + "Cycle through words, symbols and patterns." + :group 'abbrev + :group 'convenience + :group 'matching) + +(defcustom rotate-text-patterns + '(("\\_<[^-]\\(\\sw\\|\\s_\\)*[0-9]+" rotate-text-increment-number-in-symbol) + ("-?0x?[0-9a-fA-F]+" rotate-text-increment-hex-number) + ("-?[0-9]+" rotate-text-increment-number)) + "*Patterns and functions to rotate them. +Each entry is a list. Its first element should be the regular expression to +replace, the second element is a function. When rotating, it is called with the +matching text and an integer determining the rotation amount and direction." + :group 'rotate-text + :type '(repeat (list (string :tag "Regular expression") + (function :tag "Rotation function")))) + +(defcustom rotate-text-symbols '(("private" "protected" "public")) + "*List of symbol names to rotate. +Each element is a list of symbols that should be cycled through." + :group 'rotate-text + :type '(repeat (repeat :tag "Rotation group" (string :tag "Symbol")))) + +(defcustom rotate-text-words '(("width" "height") + ("left" "right" "top" "bottom")) + "*List of words to rotate. +Each element is a list of words that should be cycled through. Individual +segments in symbol names are recognized as words, i.e. windowWidth can be +replaced with windowHeight. +All entries must be in lower case. The case is determined by the rotated +text." + :group 'rotate-text + :type '(repeat (repeat :tag "Rotation group" (string :tag "Word")))) + +(defvar rotate-text-local-patterns nil + "*Buffer local additions to `rotate-text-patterns'.") +(make-variable-buffer-local 'rotate-text-local-patterns) + +(defvar rotate-text-local-symbols nil + "*Buffer local additions to `rotate-text-symbols'.") +(make-variable-buffer-local 'rotate-text-local-symbols) + +(defvar rotate-text-local-words nil + "*Buffer local additions to `rotate-text-words'.") +(make-variable-buffer-local 'rotate-text-local-words) + +;;; numbers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun rotate-text-increment-number (original arg &optional minimum) + (number-to-string (max (+ (string-to-number original) arg) + (or minimum most-negative-fixnum)))) + +(defun rotate-text-increment-hex-number (original arg) + (when (string-match "\\`-?\\(0x\\)" original) + (setq original (replace-match "" t t original 1))) + (let ((result (+ (string-to-number original 16) arg))) + (format "%s0x%x" (if (< result 0) "-" "") (abs result)))) + +(defun rotate-text-increment-number-in-symbol (original arg) + (when (string-match "[0-9]+" original) + (replace-match (rotate-text-increment-number (match-string 0 original) + arg 0) + t t original))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun rotate-text-replacement (replacements original dir) + "Find the replacement for ORIGINAL in REPLACEMENTS." + (save-match-data + (if (functionp (car replacements)) + ;; function + (if (and (< dir 0) (functionp (cadr replacements))) + (funcall (cadr replacements) original (- dir)) + (funcall (car replacements) original dir)) + ;; list + (let ((rest-pattern (member original replacements))) + (when rest-pattern + (car (nthcdr (mod (- dir (length rest-pattern)) (length replacements)) + replacements))))))) + +(defun rotate-text-match-at-point (regexp) + (save-excursion + (let ((pos (point))) + (goto-char (point-at-bol)) + (catch 'match + (while (re-search-forward regexp (1+ (point-at-eol)) t) + (and (>= pos (match-beginning 0)) + (<= pos (match-end 0)) + (throw 'match (match-string-no-properties 0)))))))) + +(defun rotate-text-symbol-at-point () + "Rotate the symbol at point." + (rotate-text-match-at-point "\\_<\\(\\s_\\|\\sw\\)+\\_>")) + +(defun rotate-text-word-at-point () + "Rotate the word at point." + (let ((case-fold-search nil)) + (or (rotate-text-match-at-point "\\(\\<\\|[[:upper:]]\\)[[:lower:]]+") + (rotate-text-match-at-point "\\<[[:upper:]]+")))) + +(defun rotate-text-match-case (original new) + "Match the case of ORIGINAL in NEW." + (let ((case-fold-search nil)) + (save-match-data + (cond + ((string-match "\\`[[:upper:]][[:lower:]]" original) (capitalize new)) + ((string-match "\\`[[:upper:]][[:upper:]]" original) (upcase new)) + (t new))))) + +(defvar rotate-text-last-offset nil) + +;;;###autoload +(defun rotate-text (arg) + "Rotate the text at point." + (interactive (list (if (consp current-prefix-arg) + -1 + (prefix-numeric-value current-prefix-arg)))) + (let ((pos (point)) + (offset 0) + match replacement) + (or ;; symbols + (when (setq match (rotate-text-symbol-at-point)) + (dolist (symbols (append rotate-text-local-symbols + rotate-text-symbols)) + (when (setq replacement + (rotate-text-replacement symbols match arg)) + (return t)))) + ;; words + (when (setq match (rotate-text-word-at-point)) + (dolist (words (append rotate-text-local-words + rotate-text-words)) + (when (setq replacement + (rotate-text-replacement words (downcase match) arg)) + (setq replacement (rotate-text-match-case match replacement)) + (return t)))) + ;; regexp + (dolist (pattern (append rotate-text-local-patterns + rotate-text-patterns)) + (when (setq match (rotate-text-match-at-point (car pattern))) + (setq replacement (rotate-text-replacement (cdr pattern) match arg)) + (return t))) + (error "Nothing to rotate")) + + (unless (eq last-command this-command) + (setq rotate-text-last-offset + (if (eq pos (match-end 0)) + 'end + (- pos (match-beginning 0))))) + + (replace-match replacement) + + (goto-char (if (eq rotate-text-last-offset 'end) + (match-end 0) + (min (+ (match-beginning 0) rotate-text-last-offset) + (match-end 0)))))) + +;;;###autoload +(defun rotate-text-backward (arg) + "Rotate the text at point backwards." + (interactive (list (if (consp current-prefix-arg) + -1 + (prefix-numeric-value current-prefix-arg)))) + (rotate-text (- arg))) + +(provide 'wiki/rotate-text) +;;; rotate-text.el ends here diff --git a/mis-recipes/org/to-docx/Makefile b/mis-recipes/org/to-docx/Makefile new file mode 100644 index 0000000..8cc05c0 --- /dev/null +++ b/mis-recipes/org/to-docx/Makefile @@ -0,0 +1,20 @@ +#______________________________________________________________________________ + + +DIRORG = $(shell dir *.org) + +DIRDOCX = $(DIRORG:.org=.docx) + +all: clean Makefile $(DIRDOCX) + +%.docx: %.org + pandoc -f org -t docx $^ -o $@ + echo $@ >> provide + +clean: + rm -f provide + +install-tools: + echo "Please install pandoc" + +.PHONY: all install-tools clean diff --git a/schemas.xml b/schemas.xml new file mode 100644 index 0000000..da1bc76 --- /dev/null +++ b/schemas.xml @@ -0,0 +1,4 @@ + + + + diff --git a/schemas/maven-v4_0_0.rnc b/schemas/maven-v4_0_0.rnc new file mode 100644 index 0000000..eabe178 --- /dev/null +++ b/schemas/maven-v4_0_0.rnc @@ -0,0 +1,461 @@ +default namespace = "http://maven.apache.org/POM/4.0.0" + +start = + notAllowed + | element project { + element parent { + element artifactId { \string }? + & element groupId { \string }? + & element version { \string }? + & element relativePath { \string }? + }? + & element modelVersion { \string }? + & element groupId { \string }? + & element artifactId { \string }? + & element packaging { \string }? + & element name { \string }? + & element version { \string }? + & element description { \string }? + & element url { \string }? + & element prerequisites { + element maven { \string }? + }? + & element issueManagement { + element system { \string }? + & element url { \string }? + }? + & element ciManagement { + element system { \string }? + & element url { \string }? + & element notifiers { + element notifier { + element type { \string }? + & element sendOnError { boolean }? + & element sendOnFailure { boolean }? + & element sendOnSuccess { boolean }? + & element sendOnWarning { boolean }? + & element address { \string }? + & element configuration { element6* }? + }* + }? + }? + & element inceptionYear { \string }? + & element mailingLists { + element mailingList { + element name { \string }? + & element subscribe { \string }? + & element unsubscribe { \string }? + & element post { \string }? + & element archive { \string }? + & element otherArchives { + element otherArchive { \string }* + }? + }* + }? + & element developers { + element developer { + element id { \string }? + & element name { \string }? + & element email { \string }? + & element url { \string }? + & element organization { \string }? + & element organizationUrl { \string }? + & element roles { + element role { \string }* + }? + & element timezone { \string }? + & element properties { element7* }? + }* + }? + & element contributors { + element contributor { + element name { \string }? + & element email { \string }? + & element url { \string }? + & element organization { \string }? + & element organizationUrl { \string }? + & element roles { + element role { \string }* + }? + & element timezone { \string }? + & element properties { element8* }? + }* + }? + & element licenses { + element license { + element name { \string }? + & element url { \string }? + & element distribution { \string }? + & element comments { \string }? + }* + }? + & element scm { + element connection { \string }? + & element developerConnection { \string }? + & element tag { \string }? + & element url { \string }? + }? + & element organization { + element name { \string }? + & element url { \string }? + }? + & element build { + element sourceDirectory { \string }? + & element scriptSourceDirectory { \string }? + & element testSourceDirectory { \string }? + & element outputDirectory { \string }? + & element testOutputDirectory { \string }? + & element extensions { + element extension { + element groupId { \string }? + & element artifactId { \string }? + & element version { \string }? + }* + }? + & element defaultGoal { \string }? + & element resources { + element resource { Resource }* + }? + & element testResources { + element testResource { Resource }* + }? + & element directory { \string }? + & element finalName { \string }? + & element filters { + element filter { \string }* + }? + & element pluginManagement { PluginManagement }? + & element plugins { + element plugin { Plugin }* + }? + }? + & element profiles { + element profile { + element id { \string }? + & element activation { + element activeByDefault { boolean }? + & element jdk { \string }? + & element os { + element name { \string }? + & element family { \string }? + & element arch { \string }? + & element version { \string }? + }? + & element property { + element name { \string }? + & element value { \string }? + }? + & element file { + element missing { \string }? + & element exists { \string }? + }? + }? + & element build { + element defaultGoal { \string }? + & element resources { + element resource { Resource }* + }? + & element testResources { + element testResource { Resource }* + }? + & element directory { \string }? + & element finalName { \string }? + & element filters { + element filter { \string }* + }? + & element pluginManagement { PluginManagement }? + & element plugins { + element plugin { Plugin }* + }? + }? + & element modules { + element module { \string }* + }? + & element repositories { + element repository { Repository }* + }? + & element pluginRepositories { + element pluginRepository { Repository }* + }? + & element dependencies { + element dependency { Dependency }* + }? + & element reports { element5* }? + & element reporting { Reporting }? + & element dependencyManagement { DependencyManagement }? + & element distributionManagement { DistributionManagement }? + & element properties { element2* }? + }* + }? + & element modules { + element module { \string }* + }? + & element repositories { + element repository { Repository }* + }? + & element pluginRepositories { + element pluginRepository { Repository }* + }? + & element dependencies { + element dependency { Dependency }* + }? + & element reports { element1* }? + & element reporting { Reporting }? + & element dependencyManagement { DependencyManagement }? + & element distributionManagement { DistributionManagement }? + & element properties { element0* }? + } +boolean = xsd:boolean +Dependency = + ((notAllowed + | element groupId { \string })? + & (notAllowed + | element artifactId { \string })? + & (notAllowed + | element version { \string })? + & (notAllowed + | element type { \string })? + & (notAllowed + | element classifier { \string })? + & (notAllowed + | element scope { \string })? + & (notAllowed + | element systemPath { \string })? + & (notAllowed + | element exclusions { + element exclusion { + element artifactId { \string }? + & element groupId { \string }? + }* + })? + & (notAllowed + | element optional { boolean })?), + empty +Resource = + ((notAllowed + | element targetPath { \string })? + & (notAllowed + | element filtering { boolean })? + & (notAllowed + | element directory { \string })? + & (notAllowed + | element includes { + element include { \string }* + })? + & (notAllowed + | element excludes { + element exclude { \string }* + })?), + empty +Repository = + ((notAllowed + | element releases { RepositoryPolicy })? + & (notAllowed + | element snapshots { RepositoryPolicy })? + & (notAllowed + | element id { \string })? + & (notAllowed + | element name { \string })? + & (notAllowed + | element url { \string })? + & (notAllowed + | element layout { \string })?), + empty +element9 = + element * { + mixed { + (element9 + | attribute * { text })* + } + } +element3 = + element * { + mixed { + (element3 + | attribute * { text })* + } + } +element5 = + element * { + mixed { + (element5 + | attribute * { text })* + } + } +element0 = + element * { + mixed { + (element0 + | attribute * { text })* + } + } +element8 = + element * { + mixed { + (element8 + | attribute * { text })* + } + } +element11 = + element * { + mixed { + (element11 + | attribute * { text })* + } + } +element4 = + element * { + mixed { + (element4 + | attribute * { text })* + } + } +element6 = + element * { + mixed { + (element6 + | attribute * { text })* + } + } +element10 = + element * { + mixed { + (element10 + | attribute * { text })* + } + } +element2 = + element * { + mixed { + (element2 + | attribute * { text })* + } + } +element1 = + element * { + mixed { + (element1 + | attribute * { text })* + } + } +element7 = + element * { + mixed { + (element7 + | attribute * { text })* + } + } +Plugin = + ((notAllowed + | element groupId { \string })? + & (notAllowed + | element artifactId { \string })? + & (notAllowed + | element version { \string })? + & (notAllowed + | element extensions { boolean })? + & (notAllowed + | element executions { + element execution { + element id { \string }? + & element phase { \string }? + & element goals { + element goal { \string }* + }? + & element inherited { \string }? + & element configuration { element9* }? + }* + })? + & (notAllowed + | element dependencies { + element dependency { Dependency }* + })? + & (notAllowed + | element goals { element10* })? + & (notAllowed + | element inherited { \string })? + & (notAllowed + | element configuration { element11* })?), + empty +Reporting = + ((notAllowed + | element excludeDefaults { boolean })? + & (notAllowed + | element outputDirectory { \string })? + & (notAllowed + | element plugins { + element plugin { + element groupId { \string }? + & element artifactId { \string }? + & element version { \string }? + & element inherited { \string }? + & element configuration { element4* }? + & element reportSets { + element reportSet { + element id { \string }? + & element configuration { element3* }? + & element inherited { \string }? + & element reports { + element report { \string }* + }? + }* + }? + }* + })?), + empty +\string = xsd:string +PluginManagement = + (notAllowed + | element plugins { + element plugin { Plugin }* + })?, + empty +DependencyManagement = + (notAllowed + | element dependencies { + element dependency { Dependency }* + })?, + empty +DistributionManagement = + ((notAllowed + | element repository { DeploymentRepository })? + & (notAllowed + | element snapshotRepository { DeploymentRepository })? + & (notAllowed + | element site { + element id { \string }? + & element name { \string }? + & element url { \string }? + })? + & (notAllowed + | element downloadUrl { \string })? + & (notAllowed + | element relocation { + element groupId { \string }? + & element artifactId { \string }? + & element version { \string }? + & element message { \string }? + })? + & (notAllowed + | element status { \string })?), + empty +RepositoryPolicy = + ((notAllowed + | element enabled { boolean })? + & (notAllowed + | element updatePolicy { \string })? + & (notAllowed + | element checksumPolicy { \string })?), + empty +DeploymentRepository = + ((notAllowed + | element uniqueVersion { boolean })? + & (notAllowed + | element id { \string })? + & (notAllowed + | element name { \string })? + & (notAllowed + | element url { \string })? + & (notAllowed + | element layout { \string })?), + empty diff --git a/snippets/emacs-lisp-mode/act b/snippets/emacs-lisp-mode/act new file mode 100644 index 0000000..44059da --- /dev/null +++ b/snippets/emacs-lisp-mode/act @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: activate-mode +# key: act +# -- +(defun quiescent-activate-${1:mode-name} () + ($1 1)) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/ar b/snippets/emacs-lisp-mode/ar new file mode 100644 index 0000000..464d861 --- /dev/null +++ b/snippets/emacs-lisp-mode/ar @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: aref +# key: ar +# -- +(aref ${1:array} ${2:idx})$0 \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/as b/snippets/emacs-lisp-mode/as new file mode 100644 index 0000000..164855c --- /dev/null +++ b/snippets/emacs-lisp-mode/as @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: aset +# key: as +# -- +(aset ${1:array} ${2:idx} ${3:newelt})$0 \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/d b/snippets/emacs-lisp-mode/d new file mode 100644 index 0000000..2b52a78 --- /dev/null +++ b/snippets/emacs-lisp-mode/d @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: defun-without-fluff +# key: d +# -- +(defun ${1:name} (${2:args}) + $0) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/diff b/snippets/emacs-lisp-mode/diff new file mode 100644 index 0000000..a43d339 --- /dev/null +++ b/snippets/emacs-lisp-mode/diff @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: cl-set-difference +# key: diff +# -- +(cl-set-difference ${1:remove-from} ${2:elems-to-remove} ${3:keywords}) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/for b/snippets/emacs-lisp-mode/for new file mode 100644 index 0000000..483c718 --- /dev/null +++ b/snippets/emacs-lisp-mode/for @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: cl-loop +# key: for +# -- +(cl-loop $0) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/get b/snippets/emacs-lisp-mode/get new file mode 100644 index 0000000..03b3cf9 --- /dev/null +++ b/snippets/emacs-lisp-mode/get @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: map-elt +# key: get +# -- +(map-elt ${1:map} ${2:key} ${3:default}) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/l b/snippets/emacs-lisp-mode/l new file mode 100644 index 0000000..a8a7276 --- /dev/null +++ b/snippets/emacs-lisp-mode/l @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: let +# key: l +# -- +(let* ((${1:name} ${2:value})) $0) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/ln b/snippets/emacs-lisp-mode/ln new file mode 100644 index 0000000..2a38218 --- /dev/null +++ b/snippets/emacs-lisp-mode/ln @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: split-lines-string +# key: ln +# -- +(split-string ${1:string} "\n" t " ") \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/m b/snippets/emacs-lisp-mode/m new file mode 100644 index 0000000..9b7cb7e --- /dev/null +++ b/snippets/emacs-lisp-mode/m @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: mapcar +# key: m +# -- +(cl-mapcar ${1:function} ${2:list})$0 \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/mh b/snippets/emacs-lisp-mode/mh new file mode 100644 index 0000000..6ce64d2 --- /dev/null +++ b/snippets/emacs-lisp-mode/mh @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: make-hash-table +# key: mh +# -- +(make-hash-table :test ${1:#'equal})$0 \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/mq b/snippets/emacs-lisp-mode/mq new file mode 100644 index 0000000..bd93c5e --- /dev/null +++ b/snippets/emacs-lisp-mode/mq @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: make-queue +# key: mq +# -- +(make-queue) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/mr b/snippets/emacs-lisp-mode/mr new file mode 100644 index 0000000..d49be8c --- /dev/null +++ b/snippets/emacs-lisp-mode/mr @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: mref +# key: mr +# -- +(aref (aref ${1:matrix} ${2:y}) ${3:x})$0 \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/ms b/snippets/emacs-lisp-mode/ms new file mode 100644 index 0000000..1d55c83 --- /dev/null +++ b/snippets/emacs-lisp-mode/ms @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: mset +# key: ms +# -- +(aset (aref ${1:matrix} ${2:y}) ${3:x} ${4:newelt})$0 \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/pc b/snippets/emacs-lisp-mode/pc new file mode 100644 index 0000000..3dc2826 --- /dev/null +++ b/snippets/emacs-lisp-mode/pc @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: pcase +# key: p +# -- +(pcase ${1:expression} + (${2:pattern} ${3:expression})$0) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/pl b/snippets/emacs-lisp-mode/pl new file mode 100644 index 0000000..8ffdda2 --- /dev/null +++ b/snippets/emacs-lisp-mode/pl @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: pcase-lambda +# key: pl +# -- +(pcase-lambda (${1:pattern}) + $0) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/put b/snippets/emacs-lisp-mode/put new file mode 100644 index 0000000..472adb1 --- /dev/null +++ b/snippets/emacs-lisp-mode/put @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: map-put +# key: put +# -- +(setf (map-elt ${1:map} ${2:key}) ${3:value}) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/r b/snippets/emacs-lisp-mode/r new file mode 100644 index 0000000..3a78ad1 --- /dev/null +++ b/snippets/emacs-lisp-mode/r @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: cl-remove-if +# key: r +# -- +(cl-remove-if ${1:predicate} ${2:list})$0 \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/rn b/snippets/emacs-lisp-mode/rn new file mode 100644 index 0000000..a271b75 --- /dev/null +++ b/snippets/emacs-lisp-mode/rn @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: cl-remove-if-not +# key: rn +# -- +(cl-remove-if-not ${1:predicate} ${2:list})$0 \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/sp b/snippets/emacs-lisp-mode/sp new file mode 100644 index 0000000..95641f5 --- /dev/null +++ b/snippets/emacs-lisp-mode/sp @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: split-string +# key: sp +# -- +(split-string ${1:string} ${2:separators} ${3:omit-nulls} ${4:trim}) \ No newline at end of file diff --git a/snippets/java-mode/cc b/snippets/java-mode/cc new file mode 100644 index 0000000..d3f356c --- /dev/null +++ b/snippets/java-mode/cc @@ -0,0 +1,10 @@ +# -*- mode: snippet -*- +# name: comment-class +# key: cc +# -- +/** + * ${1:Comment} + * @author ${user-full-name} + * @date `(quiescent-absolute-date-to-string (org-today))` + * TaskNumber: ${3:task#} + */$0 \ No newline at end of file diff --git a/snippets/java-mode/f b/snippets/java-mode/f new file mode 100644 index 0000000..5ee9044 --- /dev/null +++ b/snippets/java-mode/f @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: final-member +# key: f +# -- +final ${1:type} ${2:name};$0 \ No newline at end of file diff --git a/snippets/java-mode/get b/snippets/java-mode/get new file mode 100644 index 0000000..b9e3973 --- /dev/null +++ b/snippets/java-mode/get @@ -0,0 +1,11 @@ +# -*- mode: snippet -*- +# name: getter +# key: get +# -- +/** + * Get ${1:name}. + * @return $1 + */$0 +public ${2:type} get$1 () { + return $1; +} \ No newline at end of file diff --git a/snippets/java-mode/getset b/snippets/java-mode/getset new file mode 100644 index 0000000..d8ec8f2 --- /dev/null +++ b/snippets/java-mode/getset @@ -0,0 +1,21 @@ +# -*- mode: snippet -*- +# name: getter with setter & private variable +# key: getset +# -- +private ${2:type} $1; + + /** + * Get the value of $1. + * @return the value of $1. + */ + public $2 get${1:$$(kaushalmodi-capitalize-first-char yas-text)} () { + return $1; + } + + /** + * Set the value of $1. + * @param $1 value to set it to + */ + public void set${1:$$(kaushalmodi-capitalize-first-char yas-text)} (final $2 $1) { + this.$1 = $1; + } \ No newline at end of file diff --git a/snippets/java-mode/mc b/snippets/java-mode/mc new file mode 100644 index 0000000..ca9e5e1 --- /dev/null +++ b/snippets/java-mode/mc @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: method-comment +# key: mc +# -- +/** + * ${1:Comment} + */$0 \ No newline at end of file diff --git a/snippets/java-mode/p b/snippets/java-mode/p new file mode 100644 index 0000000..9702a65 --- /dev/null +++ b/snippets/java-mode/p @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: private +# key: p +# -- +private $0 \ No newline at end of file diff --git a/snippets/java-mode/s b/snippets/java-mode/s new file mode 100644 index 0000000..cdf0d3a --- /dev/null +++ b/snippets/java-mode/s @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: string +# key: s +# -- +String $0 \ No newline at end of file diff --git a/snippets/java-mode/set b/snippets/java-mode/set new file mode 100644 index 0000000..f8a9b8a --- /dev/null +++ b/snippets/java-mode/set @@ -0,0 +1,11 @@ +# -*- mode: snippet -*- +# name: Makes a setter +# key: set +# -- +/** + * Set the value of $1. + * @param $1 value to set it to + */ + public void set${1:$$(kaushalmodi-capitalize-first-char yas-text)} (final $2 $1) { + this.$1 = $1; + } diff --git a/snippets/js2-mode/asdeq b/snippets/js2-mode/asdeq new file mode 100644 index 0000000..29388bc --- /dev/null +++ b/snippets/js2-mode/asdeq @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: assertDeepEqual +# key: asdeq +# -- +assert.deepEqual($1, $2);$0 \ No newline at end of file diff --git a/snippets/js2-mode/aseq b/snippets/js2-mode/aseq new file mode 100644 index 0000000..c7a2d04 --- /dev/null +++ b/snippets/js2-mode/aseq @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: assertEqual +# key: aseq +# -- +assert.equal($1, $2);$0 \ No newline at end of file diff --git a/snippets/js2-mode/asprox b/snippets/js2-mode/asprox new file mode 100644 index 0000000..d03b173 --- /dev/null +++ b/snippets/js2-mode/asprox @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: assertApproximately +# key: asprox +# -- +assert.approximately(${1:value}, ${2:expected}, ${3:delta});$0 \ No newline at end of file diff --git a/snippets/js2-mode/cfn b/snippets/js2-mode/cfn new file mode 100644 index 0000000..add292d --- /dev/null +++ b/snippets/js2-mode/cfn @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: cfn +# key: cfn +# -- +export const $1 = ($0) => undefined; \ No newline at end of file diff --git a/snippets/js2-mode/chai b/snippets/js2-mode/chai new file mode 100644 index 0000000..1eaf6d3 --- /dev/null +++ b/snippets/js2-mode/chai @@ -0,0 +1,8 @@ +# -*- mode: snippet -*- +# name: chai +# key: chai +# -- +import chai from "chai"; + +const assert = chai.assert; +$0 \ No newline at end of file diff --git a/snippets/js2-mode/describe b/snippets/js2-mode/describe new file mode 100644 index 0000000..b832202 --- /dev/null +++ b/snippets/js2-mode/describe @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: describe +# key: desc +# -- +describe("$1", () => { + $0 +}); \ No newline at end of file diff --git a/snippets/js2-mode/i b/snippets/js2-mode/i new file mode 100644 index 0000000..86ccb7a --- /dev/null +++ b/snippets/js2-mode/i @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: import +# key: i +# -- +import { $1 } from "$2";$0 diff --git a/snippets/js2-mode/it b/snippets/js2-mode/it new file mode 100644 index 0000000..b183863 --- /dev/null +++ b/snippets/js2-mode/it @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: it +# key: it +# -- +it("$1", () => { + $0 +}); \ No newline at end of file diff --git a/snippets/js2-mode/lambda b/snippets/js2-mode/lambda new file mode 100644 index 0000000..57c46e4 --- /dev/null +++ b/snippets/js2-mode/lambda @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: lamba +# key: l +# -- +($1) => {$0} \ No newline at end of file diff --git a/snippets/js2-mode/lambda1 b/snippets/js2-mode/lambda1 new file mode 100644 index 0000000..455a19a --- /dev/null +++ b/snippets/js2-mode/lambda1 @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: lamba1 +# key: l1 +# -- +$1 => {$0} \ No newline at end of file diff --git a/snippets/js2-mode/log b/snippets/js2-mode/log new file mode 100644 index 0000000..752705b --- /dev/null +++ b/snippets/js2-mode/log @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: log +# key: log +# -- +console.log($0) \ No newline at end of file diff --git a/snippets/js2-mode/probe b/snippets/js2-mode/probe new file mode 100644 index 0000000..c191226 --- /dev/null +++ b/snippets/js2-mode/probe @@ -0,0 +1,11 @@ +# -*- mode: snippet -*- +# name: probe +# key: p +# -- +function probe(msg) { + if (!msg) msg = ""; + return function(x) { + console.log(msg, "\n", JSON.stringify(x, null, 2)); + return x; + }; +} \ No newline at end of file diff --git a/snippets/js2-mode/str b/snippets/js2-mode/str new file mode 100644 index 0000000..0ebd2dd --- /dev/null +++ b/snippets/js2-mode/str @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: stringify +# key: str +# -- +JSON.stringify($0) \ No newline at end of file diff --git a/snippets/js2-mode/th b/snippets/js2-mode/th new file mode 100644 index 0000000..fe99112 --- /dev/null +++ b/snippets/js2-mode/th @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: thunk +# key: th +# -- +() => {$0} \ No newline at end of file diff --git a/snippets/nxml-mode/dep b/snippets/nxml-mode/dep new file mode 100644 index 0000000..25775bf --- /dev/null +++ b/snippets/nxml-mode/dep @@ -0,0 +1,9 @@ +# -*- mode: snippet -*- +# name: maven-dependency +# key: dep +# -- + + ${1:group id} + ${2:artifact id} + ${3:version} +$0 \ No newline at end of file diff --git a/snippets/nxml-mode/id b/snippets/nxml-mode/id new file mode 100644 index 0000000..ae64bd3 --- /dev/null +++ b/snippets/nxml-mode/id @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: hibernate-id +# key: id +# -- + + + diff --git a/snippets/nxml-mode/otm b/snippets/nxml-mode/otm new file mode 100644 index 0000000..e013411 --- /dev/null +++ b/snippets/nxml-mode/otm @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: hibernate-one-to-many +# key: otm +# -- + + + \ No newline at end of file diff --git a/snippets/nxml-mode/prop b/snippets/nxml-mode/prop new file mode 100644 index 0000000..7ebeba4 --- /dev/null +++ b/snippets/nxml-mode/prop @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: hibernate-property +# key: prop +# -- + + + \ No newline at end of file diff --git a/snippets/nxml-mode/scp b/snippets/nxml-mode/scp new file mode 100644 index 0000000..1d509ef --- /dev/null +++ b/snippets/nxml-mode/scp @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: maven-dependency-scope +# key: scp +# -- +${1:scope}$0 diff --git a/snippets/scala-mode/describe b/snippets/scala-mode/describe new file mode 100644 index 0000000..47f620d --- /dev/null +++ b/snippets/scala-mode/describe @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: describe +# key: desc +# -- +describe("$1") { + $0 +} \ No newline at end of file diff --git a/snippets/scala-mode/it b/snippets/scala-mode/it new file mode 100644 index 0000000..bc2f54f --- /dev/null +++ b/snippets/scala-mode/it @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: it +# key: it +# -- +it("$1") { + $0 +} \ No newline at end of file diff --git a/snippets/scala-mode/log b/snippets/scala-mode/log new file mode 100644 index 0000000..0e375f4 --- /dev/null +++ b/snippets/scala-mode/log @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: log +# key: log +# -- +println($0) \ No newline at end of file diff --git a/snippets/scala-mode/probe b/snippets/scala-mode/probe new file mode 100644 index 0000000..3e36378 --- /dev/null +++ b/snippets/scala-mode/probe @@ -0,0 +1,8 @@ +# -*- mode: snippet -*- +# name: probe +# key: p +# -- +def probe(msg) = x => { + println(s"msg: $x") + x +} \ No newline at end of file diff --git a/snippets/scala-mode/test b/snippets/scala-mode/test new file mode 100644 index 0000000..13a86f4 --- /dev/null +++ b/snippets/scala-mode/test @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: test +# key: test +# -- +test("$1") { + $0 +} \ No newline at end of file diff --git a/snippets/sql-mode/br b/snippets/sql-mode/br new file mode 100644 index 0000000..cac50f2 --- /dev/null +++ b/snippets/sql-mode/br @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: break +# key br +# -- + +---------------------------------------------------------------------------------------------------- +-- \ No newline at end of file diff --git a/snippets/sql-mode/sel b/snippets/sql-mode/sel new file mode 100644 index 0000000..1d25be7 --- /dev/null +++ b/snippets/sql-mode/sel @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: select +# key sel +# -- +SELECT * +FROM ${1:Table-Name}$0 \ No newline at end of file diff --git a/snippets/sql-mode/selw b/snippets/sql-mode/selw new file mode 100644 index 0000000..5026f9c --- /dev/null +++ b/snippets/sql-mode/selw @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: selectw +# key sel +# -- +SELECT * +FROM ${1:Table-Name} a +WHERE a.$0; \ No newline at end of file diff --git a/snippets/sql-mode/up b/snippets/sql-mode/up new file mode 100644 index 0000000..70dc3de --- /dev/null +++ b/snippets/sql-mode/up @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: update +# key up +# -- +UPDATE ${1:table-name} +SET ${2:column} = ${3:value}$0 \ No newline at end of file diff --git a/snippets/typescript-mode/asdeq b/snippets/typescript-mode/asdeq new file mode 100644 index 0000000..29388bc --- /dev/null +++ b/snippets/typescript-mode/asdeq @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: assertDeepEqual +# key: asdeq +# -- +assert.deepEqual($1, $2);$0 \ No newline at end of file diff --git a/snippets/typescript-mode/aseq b/snippets/typescript-mode/aseq new file mode 100644 index 0000000..c7a2d04 --- /dev/null +++ b/snippets/typescript-mode/aseq @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: assertEqual +# key: aseq +# -- +assert.equal($1, $2);$0 \ No newline at end of file diff --git a/snippets/typescript-mode/describe b/snippets/typescript-mode/describe new file mode 100644 index 0000000..11bfc82 --- /dev/null +++ b/snippets/typescript-mode/describe @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: describe +# key: desc +# -- +describe('$1', () => { + $0 +}); \ No newline at end of file diff --git a/snippets/typescript-mode/i b/snippets/typescript-mode/i new file mode 100644 index 0000000..86ccb7a --- /dev/null +++ b/snippets/typescript-mode/i @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: import +# key: i +# -- +import { $1 } from "$2";$0 diff --git a/snippets/typescript-mode/it b/snippets/typescript-mode/it new file mode 100644 index 0000000..dd4893f --- /dev/null +++ b/snippets/typescript-mode/it @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: it +# key: it +# -- +it('$1', () => { + $0 +}); \ No newline at end of file diff --git a/snippets/typescript-mode/log b/snippets/typescript-mode/log new file mode 100644 index 0000000..752705b --- /dev/null +++ b/snippets/typescript-mode/log @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: log +# key: log +# -- +console.log($0) \ No newline at end of file diff --git a/snippets/typescript-mode/probe b/snippets/typescript-mode/probe new file mode 100644 index 0000000..9279928 --- /dev/null +++ b/snippets/typescript-mode/probe @@ -0,0 +1,11 @@ +# -*- mode: snippet -*- +# name: probe +# key: p +# -- +function probe(msg: string) { + if (!msg) msg = ""; + return (x: any) => { + console.log(msg, "\n", JSON.stringify(x, null, 2)); + return x; + }; +} \ No newline at end of file diff --git a/snippets/typescript-mode/str b/snippets/typescript-mode/str new file mode 100644 index 0000000..0ebd2dd --- /dev/null +++ b/snippets/typescript-mode/str @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: stringify +# key: str +# -- +JSON.stringify($0) \ No newline at end of file diff --git a/snippets/typescript-mode/tell b/snippets/typescript-mode/tell new file mode 100644 index 0000000..1e2c55a --- /dev/null +++ b/snippets/typescript-mode/tell @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: tell +# key: tell +# -- +${1:variable}: \${$1\} \ No newline at end of file diff --git a/startup.org b/startup.org new file mode 100644 index 0000000..110ec21 --- /dev/null +++ b/startup.org @@ -0,0 +1,5031 @@ +#+TITLE: Emacs Startup & Configuration +#+AUTHOR: Edward John Steere +#+EMAIL: edward dot steere at gmail dot com +#+LANGUAGE: en +#+STARTUP: showall + +* Introduction +This is the story of my Emacs configuration. It's as much a story of +how I learned to program as it is a setup for Emacs -- the timeless +editor. + +In this document you can expect to find: + - how I configure Emacs; + - the story of how I came to use particular parts of Emacs; + - links to other Emacs Lisp sources where I've defined features to + extend Emacs and the packages which I use; + +The document begins by outlining the way in which it is expected to be +used. Following this I detail the modifications I've made to it in +order to support he various environments on which I run it. It then +details modifications which I've made to all programming modes. +Alterations and additions to specific modes follow this. It then +moves into usages which don't relate to programming at all. Another +section details modifications made to the appearance of Emacs. + +The document concludes with some fluffy text about my configuration +and what I hope to achieve with it in the future. + +* A Note on the Literate Nature of this Document +This document is literate and interactive. It is, of course, best +viewed in Emacs itself. Not only because it'll mark up all of the +code and headings correctly, but also because it'll provide +interactive development features. You can edit the document and the +code contained directly, but it is recommended that you instead enter +code blocks with the =C-= binding. Repeating that +binding will return you to the document proper. While in this mode +you can execute the code as one usually would. + +While not editing a source code block using the =C-= +binding you can still execute the code block to alter the Lisp +environment. This is done with the binding =C-c C-c=. + +* Modifications to Support the Environment +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref modifications-to-support-the-environment :exports none + <> + <> + <> + <> + <> +#+END_SRC + +This section details the modifications which I've made to Emacs in +order to support the different environments which I run it on. + +I use a set of configurations which are specific to each machine which +I run Emacs on in order to optimise my work flows on each system. +This allows me to program quick tasks which help automate laborious +tasks on those machines and to integrate more tightly with each tool +set that I use. I load the configurations and tasks outside of this +file and I don't committing those files to my Emacs configuration +repository. In doing so the configuration can vary per platform. + +There are also modifications which are the same for the same type of +platform. i.e. I'll always have the =PATH= variable determined from +=bash= on Mac OS X. + +** OS X +On OS X there's a very irritating interaction between Emacs and the +system path. For some reason GUI applications aren't launched with +the same path as terminal applications. In order to fix this you can +use a package (ofc!) which sets the =exec-path= (what Emacs considers +to be the path) in an Emacs session to the same value as it would be +in Bash. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref exec-path-from-shell :lexical t + (use-package exec-path-from-shell + :ensure t + :init + (when (not (or (equal system-type 'windows-nt) + (equal system-type 'gnu/linux))) + (with-eval-after-load "exec-path-from-shell" + (exec-path-from-shell-initialize)))) +#+END_SRC + +** Controlling the Proxy +Sometimes I need control over environment variables so that I can +control third party binaries which Emacs plugs into. Configuring the +proxy and whether it's on or off is the first reason that I've needed +this feature. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref toggle-proxy :lexical t + (defvar quiescent-http-proxy "" + "The previous/current value of the http proxy variable.") + + (defvar quiescent-http-proxy-active "" + "Whether the proxy ought to be active right now.") + + (defun quiescent-toggle-proxy () + "Remember the proxy and then toggle it on and off when called." + (interactive) + (when (and (not (boundp 'queiescent-http-proxy)) + (not (string-equal quiescent-http-proxy ""))) + (progn + (message "First binding") + (setq quiescent-http-proxy (getenv "HTTP_PROXY")) + (setq quiescent-http-proxy-active t))) + (if quiescent-http-proxy-active + (progn + (message "Unsetting proxy") + (setenv "HTTP_PROXY" "") + (setq quiescent-http-proxy-active nil)) + (progn + (message "Setting proxy") + (setenv "HTTP_PROXY" quiescent-http-proxy) + (setq quiescent-http-proxy-active t)))) +#+END_SRC + +** Changing the Keyboard Layout +I pair a lot at work and I need to be able to switch layouts when +Emacs is running as the window manager. I've created a function to +switch layouts quickly. The function makes use of the =setxkbmap= +command. Usually I use =setxkbmap -layout "dvorak" -option +ctrl:nocaps= in my =.xinitrc= to make dvorak the layout when I boot +in. A small modification is needed to be able to toggle between +layouts. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref change-keyboard-layouts :lexical t + (defun quiescent-toggle-keyboard-layout () + "Toggle between US and Dvorak keyboard layouts." + (interactive) + (set-process-filter (start-process "*Check-Layout*" + nil + "setxkbmap" + "-query") + #'quiescent-handle-keyboard-query)) + + (defun quiescent-handle-keyboard-query (_ output) + "Use the output to decide whether to change to QWERTY or Dvorak." + (if (string-match-p "dvorak" output) + (quiescent-switch-to-layout "us") + (quiescent-switch-to-layout "dvorak"))) + + (defun quiescent-switch-to-layout (layout) + "Use setkbmap to change the keyboard layout to LAYOUT." + (start-process "*Change-Layouts*" + nil + "setxkbmap" "-layout" layout "-option" "ctrl:nocaps")) +#+END_SRC + +** Windows Bindings +The super key used to be a thing on old computers, especially those of +a "lispy" nature. It's really handy because it lets you bind shorter +key bindings by using it the same way you would meta or control. To +use it on Windows you need to tell Emacs to bypass Windows' usual +bindings. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref windows-super-key :exports none :lexical t + (if running-windows + (progn (setq w32-pass-lwindow-to-system nil) + (setq w32-lwindow-modifier 'super) + (message "System is running windows")) + (message "System is *NIX :)")) +#+END_SRC + +** System Specific Tasks +I like to define tasks which optimise my work flow on each computer I +use. By their nature these tasks couple tightly with the machine on +which they're run, therefore they wont usually work across machines. +I store these commands in a folder [[~/.emacs.d/system-specific-tasks/system-specific-tasks.el][inside my EMACS directory.]] + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref system-specific-tasks :exports none :lexical t + (use-package system-specific-tasks + :load-path "~/.emacs.d/system-specific-tasks/") +#+END_SRC + +** System Specific Config +I've put system specific settings in their own folder, including mail +settings and variables which I use throughout my setup to determine +whether I'm running a particular OS (mostly just for windows) and +where emacs should start. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref system-specific-config :exports none :lexical t + (use-package system-vars + :load-path "~/.emacs.d/conf") + + (use-package mail-settings + :load-path "~/.emacs.d/conf") +#+END_SRC + +* Programming Modifications Across the Board +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref programming-modifications-across-the-board :exports none + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + ;;<> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + ;;<> + <> + <> + <> + <> + ;;<> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> + <> +#+END_SRC +In this section I'll detail the modes and modifications to programming +in Emacs which don't pertain to any single language and instead modify +the entire environment. These modifications aren't listed in any +particular order and they include modifications to navigation and +similar commands which change the way that I move around my system +while programming. + +Many people swear by their IDE/Editors capability to complete what +they were typing. Usually this involves the use of some kind of drop +down box. I find that these tools add robustness, but actually slow +your typing down, because you have to wait for the list to appear, +read it, and then select the option you wanted. + +Aside from encouraging one not to learn what functions /actually do/ +before using them, this also slows down your editing. I've adopted +tools which instead allow me to quickly enter what I need to -- which +first requires that I know /what I should be entering/. + +It's no use keeping your hands in the correct typing positions the +whole time if it takes an age to move the point to areas of the +screen. If it takes ages to move about then you may as well take the +performance hit of using a mouse. Well EMACS has some power tools +which help you move to just about any visible point in at most about +four key strokes and non visible points in a small (but not +predictable) amount of time. + +The following two packages are about super fast navigation of the +point to visible portions of the screen. + +** No Tab Characters in Code! +There's no guarantee on the way that tabs render in different editors, +when printed or when viewed on the Internet. Spaces don't suffer from +this problem. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref no-tabs :lexical t + (setq-default indent-tabs-mode nil) + (setq tab-width 4) +#+END_SRC + +** Column Numbers +When I got going with C++ I was taught via the command line. One of +the things which compilers reported to you back then and today and so +has become very important is which column an error occurs +on. Confusingly, EMACS doesn't show column numbers by default. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref column-number-mode :lexical t + (column-number-mode 1) +#+END_SRC + +** Disabled Commands +Up and down casing a region is disabled by default (!?) This wont do! + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref disabled-commands :lexical t + (put 'upcase-region 'disabled nil) + (put 'downcase-region 'disabled nil) +#+END_SRC + +** Manipulating the Other Window +It's often useful to modify the other window without ever leaving this +window. The following library contains, to begin with, functions for +moving the point about in the other window. I'm thinking that this +library will end up being pretty big after a while. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref other-window :exports none :lexical t + (require 'quiescent/other-window) +#+END_SRC + +** Buffer Set Operations +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref buffer-set-operations :exports none :lexical t + <> + <> +#+END_SRC +This is a section where I'm creating set like operations to work on +buffers. The first one that I needed was disjunction -- to solve the +problem of which lines are not common to each buffer. I needed to +check whether I had deleted files while restructuring a project. Git +would have been able to tell me except that it definitely got confused +by some of the changes and didn't recognise them as moves/renames. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref buffer-disjunction :exports none :lexical t + (require 'quiescent/buffer-disjunction) +#+END_SRC + +Shortly after writing this I realised that I actually needed an +operation which is "all the lines which are in this buffer and not +that buffer." + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref this-but-not-that :exports none :lexical t + (require 'quiescent/buffer-set-difference) +#+END_SRC + +** Tiny Mode +Ever been coding and just wished that you could type a formatted +sequence of symbols quickly? Tiny does this in a minimal amount of +keystrokes by expanding a super brief syntax for formatted sequences. + +Tiny is a package by the excellent eLisp hacker abo-abo. It allows one +to create formatted sequences with a very concise syntax. See [[https://github.com/abo-abo/tiny][this +documentation]] for more. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref tiny-mode :exports none :lexical t + (use-package tiny + :ensure t + :config (tiny-setup-default)) +#+END_SRC + +** Hippie Expand +Emacs comes with a fast auto-guess-expansion system which expands the +word prior to point. Hippie expand is an extension of this mode which +provides a more sophisticated interface and can complete entire code +blocks. I just replace the default package (dabbrev) with Hippie +Expand to use this feature. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref hippie-expand :exports none :lexical t + (global-set-key [remap dabbrev-expand] 'hippie-expand) +#+END_SRC + +** Avy +Avy is a system designed to make it fast and easy to jump to any point +you can see in Emacs. It does so by prompting for a character and then +highlighting each visible occurrence with a letter or a sequence of +letters by completing the sequence of letters for the point you want +to jump to your point will move there. I've bound the avy command I +use all the time to =s-.=. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref avy :exports none :lexical t + (require 'quiescent/avy) +#+END_SRC + +** Marking and Popping +Marking and popping is the built in mechanism for moving about buffers +to places that you've been and did some kind of move from. I don't +think that enough of the large movement commands set the mark so to +begin with these settings will make that different. + +The first thing that I'm changing here is up-list. When operating on +a large list or an XML document moves the point a huge distance. So +the idea is to just make sure that the mark is set when doing going up +list so that I can get back to where I was. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref mark-and-pop :exports none :lexical t + (defun quiescent-push-mark-to-ring (&optional arg escape-string no-syntax-crossing) + "Double `push-mark' for pushing straight onto the ring. + Ignore ARG, ESCAPE-STRING and NO-SYNTAX-CROSSING, they're there + to make the advice work." + (ignore arg) + (ignore escape-string) + (ignore no-syntax-crossing) + (progn + (push-mark) + (push-mark))) + + (advice-add #'backward-up-list :before + #'quiescent-push-mark-to-ring) + + (advice-add #'nxml-backward-up-element :before + #'quiescent-push-mark-to-ring) +#+END_SRC + +I've found that sometimes popping the mark ring can result in the +point staying where it is. The same can sometimes be true of popping +the xref ring. I'd like both of those commands to ensure that the +point ends up moving. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref ensure-pop-moves :exports none :lexical t + (defun quiescent-keep-popping-til-moved (pop-function &rest args) + "Keep popping the mark until either the mark ring is empty or the point moved." + (interactive) + (progn + (while (and mark-ring + (= (point) (marker-position (car mark-ring)))) + (pop mark-ring)) + (apply pop-function args))) + + (advice-add #'pop-to-mark-command :around #'quiescent-keep-popping-til-moved) +#+END_SRC + +I've recently discovered that transient mark mode wasn't always the +way that the region worked. In fact there's almost always a region +even if it's not "active." It's the region between the mark and the +point. I like thinking this way, but you do need to be able to see +where the last marks were. I tried it out for a while and I think +that you lose more than you gain so I'm going back to the way it was +before but with the mark made visible, because that's still very +useful. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref visual-marks :exports none :lexical t + (use-package visible-mark + :ensure t + :config (progn + (global-visible-mark-mode 1) + (setq visible-mark-max 1) + (setq visible-mark-faces `(visible-mark-face1 visible-mark-face2)))) +#+END_SRC + +** Auto Highlight Symbol Mode +Sometimes it can be useful to see the other occurrences of a symbol in +the visible buffer. Auto Highlight Mode does this for you. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref auto-highlight-symbol :exports none :lexical t + (use-package auto-highlight-symbol + :ensure t) +#+END_SRC + +** Hydra +Hydra is an incredibly cool package which allows one to ``chop the +head off of large key bindings'' so to speak. Some packages have long +base key bindings, e.g. =C-c C-c C-x = where == is some postfix +key to select the final action in a package whose base key binding is +=C-c C-c C-x=. It takes ages to always hit the base binding so Hydra +would let one instead enter a Hydra (read state) at =C-c C-c C-x= +where you could hit any and it would be as if you had hit the base +binding too. + +Hydras are incredibly powerful and I wouldn't be able to explain all +there is here. Check out [[https://github.com/abo-abo/hydra/wiki/Emacs][this documentation for more info]]. I've added +Hydra to [[Emacs Package Management][my package management]]. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref hydra :exports none :lexical t + (use-package hydra + :ensure t + :config + (progn + <> + <> + )) +#+END_SRC + +*** My Own Hydras +Naturally one uses Hydra to create their own systems of chopping of +heads and heroic work saving lispery. What follows are the ones I've +made. + +**** Drawing +I also used hydra to create a simple psuedo ASCII drawing mode. It +keeps track of the line you're on and uses the normal moving key binds +without == to move maintaining the current column to give the +appearance of moving about on a canvas. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref hydra-drawing :exports none :lexical t + (require 'quiescent/drawing-interface) +#+END_SRC + + +**** Code Explorer +This is a hydra which I came up with a while ago and now I'm +resurrecting because previously the features which I wanted weren't +well supported by my Emacs at the time. I now see projects like +Ensime and LSP as being able to support all of it's features going +forward. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref code-explorer :exports none :lexical t + (require 'quiescent/code-explorer) +#+END_SRC + +** Window Jump +Sometimes I want to just move to a window nearby, i.e. not do a long +distance jump. For that I'm trying out window-jump to move my point +left/right/up/down in my windows. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref window-jump :exports none :lexical t + (use-package window-jump + :ensure t + :init (when (and (boundp 'key-chord-mode) + (not key-chord-mode)) + (key-chord-mode 1)) + :chords ((",u" . window-jump-up) + (",d" . window-jump-down) + (",l" . window-jump-left) + (",r" . window-jump-right))) +#+END_SRC + +** iSearch +iSearch is an incredibly powerful utility which turns searching into a +fast way of moving around a buffer among other things. I sometimes +want to kill the thing that is currently highlighted by iSearch. The +following snippet does just that and comes from the Emacs wiki. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref isearch-kill :exports none :lexical t + (defun quiescent-kill-isearch-match () + "Kill the current isearch match string and continue searching." + (interactive) + (kill-region isearch-other-end (point)) + (isearch-repeat-forward)) + + (define-key isearch-mode-map [(control k)] 'quiescent-kill-isearch-match) +#+END_SRC + +Something which I needed this morning was to move to the first hit in +the buffer. I decided to make it general :) + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref isearch-first-last-hit :exports none :lexical t + (defun quiescent-first-search-hit () + "Activate isearch from the start of the buffer using the existing string if it's present." + (interactive) + (progn + (let ((original-isearch-string (when isearch-mode isearch-string))) + (goto-char (point-min)) + (isearch-forward nil t) + (when original-isearch-string (isearch-yank-string original-isearch-string))))) + + (defun quiescent-last-search-hit () + "Activate isearch from the end of the buffer using the existing string if it's present." + (interactive) + (progn + (let ((original-isearch-string (when isearch-mode isearch-string))) + (goto-char (point-max)) + (isearch-backward nil t) + (when original-isearch-string (isearch-yank-string original-isearch-string))))) + + (defun quiescent-turn-off-key-chord-mode (&rest _) + "Turn off `key-chord-mode'." + (key-chord-mode -1)) + + (defun quiescent-turn-on-key-chord-mode (&rest _) + "Turn on `key-chord-mode'." + (key-chord-mode 1)) + + (advice-add #'isearch-mode :before #'quiescent-turn-off-key-chord-mode) + (advice-add #'isearch-done :after #'quiescent-turn-on-key-chord-mode) + + (define-key isearch-mode-map (kbd "M-<") #'quiescent-first-search-hit) + (define-key isearch-mode-map (kbd "M->") #'quiescent-last-search-hit) + + (global-set-key (kbd "M-s M-<") #'quiescent-first-search-hit) + (global-set-key (kbd "M-s M->") #'quiescent-last-search-hit) +#+END_SRC + +** Iy +In multiple cursors mode I miss the ability to invoke iSearch -- one +of the most important and used packages in my Emacs. The next best +thing is to either zap to, or move to a particular character. This is +what Iy mode will give you, and it's not only for multiple cursors +this is just the best case I could think of. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref iy-mods :exports none :lexical t + (defun quiescent-init-iy-hydra-forward () + "Start using the iy hydra with iy forward." + (interactive) + (call-interactively 'iy-go-to-char) + (quiescent-hydra-iy-go-to-char/body)) + + (defun quiescent-init-iy-hydra-backward () + "Start using the iy hydra with iy backward." + (interactive) + (call-interactively 'iy-go-to-char-backward) + (quiescent-hydra-iy-go-to-char/body)) + + (use-package iy-go-to-char + :ensure t) + + (global-set-key (kbd "s-d") #'quiescent-init-iy-hydra-forward) + (global-set-key (kbd "s-i") #'quiescent-init-iy-hydra-backward) + + (defhydra quiescent-hydra-iy-go-to-char (:color amaranth) + " + Iy go to character Hydra + ^Forward^ ^Backward^ + ^^^^^^^^------------------------------- + _f_: forward _b_: backward + _n_: continue forward _p_: continue backward + + _q_: quit + " + ("f" iy-go-to-char nil) + ("n" iy-go-to-char-continue nil) + ("b" iy-go-to-char-backward nil) + ("p" iy-go-to-char-continue-backward nil) + ("q" nil nil)) +#+END_SRC + +** Cross Link +I've recently come up with the idea of ad hoc cross linking buffers +together based on the symbol at point. The basic idea is that you +first invoke it in a buffer creating a link to another buffer on the +first invocation. It will remember the buffer it was first used with +and cross link to that buffer every time it is invoked from the +starting buffer, until a clear command is called. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref quiescent-cross-link :exports none :lexical t + (require 'quiescent/cross-link-mode) +#+END_SRC + +** Multiple Cursors +I've always heard that multiple cursors is a very good mode. Until +recently I thought that it might just be a clone of modes from +programs like IntelliJ and Sublime. What made me decide to try it out +in the end was a package I came across called Mark multiple. The idea +with this package is that you can mark multiple regions and mark further +regions based on what really marked and then apply edits to all the +marked regions. + +It turns out that this functionality is built into multiple cursors. I +generally do the kind of editing that people advertise from multiple +cursors using macros, but the ability to select further subregions +based on what's already selected is really something that goes beyond +macros. This is what made me decide to try it out. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref multiple-cursors :exports none :lexical t + (use-package multiple-cursors + :ensure t + :bind (("s-SPC" . mc/edit-lines) + ("C->" . mc/mark-next-like-this) + ("C-<" . mc/mark-previous-like-this) + ("C-c C-<" . mc/mark-all-like-this) + ("s->" . mc/skip-to-next-like-this) + ("s-<" . mc/skip-to-previous-like-this))) +#+END_SRC + +** IEdit Mode +There is also a widely used package which allows one to edit multiple +occurrences of a symbol in the current buffer. It's called IEdit mode +and it's available via ELPA. The difference between it and mc is that +it serves a narrower purpose and so can be lighter weight. i.e. if all +you want to do is edit and not run the same generic commands +everywhere that a symbol occurs then IEdit does that a bit more neatly +than mc. However IEdit isn't as generic as mc. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref iedit :exports none :lexical t + (use-package iedit + :ensure t + :bind ("M-s ," . iedit-mode)) +#+END_SRC + +** "Scroll" Through History +I just read XKCD #1806 and the tool tip says that it would be cool to +bind the scroll wheel to undo and redo /if a program could keep up +with it/. Well let's see if Emacs can! + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref hiscroll :exports none :lexical t + (define-minor-mode hiscroll-mode + "Toggle hiscroll-mode. + Interactively with no argument, this command toggles the mode. + A positive prefix argument enables the mode, any other prefix + argument disables it. From Lisp, argument omitted or nil enables + the mode, `toggle' toggles the state. + + When hiscroll-mode is enabled the mouse wheel will move the + current buffer through time (i.e. undo/redo while you scroll.)" + :init-value nil + :lighter " HiS" + :keymap + '(([mouse-5] . hiscroll-undo) + ([mouse-4] . hiscroll-redo)) + :group 'hiscroll) + + (defvar hiscroll-undo-direction 'UNDO + "The direction to go in history of changes for this buffer.") + + (defun hiscroll--correct-direction-and-go (desired-direction) + "Correct the direction of undoing to DESIRED-DIRECTION and then undo." + (progn + (when (not (eq hiscroll-undo-direction desired-direction)) + (setq last-command nil) + (setq hiscroll-undo-direction desired-direction)) + (undo))) + + (defun hiscroll-undo () + "Reverse the direction of history if necessary and then continue undoing." + (interactive) + (hiscroll--correct-direction-and-go 'UNDO)) + + (defun hiscroll-redo () + "Reverse the direction of history if necessary and then continue redoing." + (interactive) + (hiscroll--correct-direction-and-go 'REDO)) +#+END_SRC + +** Recursive Editing +I really like the idea of recursive editing sessions. I want to be +doing something look somewhere else and come back to where I was by +quitting that short session. I'm going to bind it to =s-R=. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref general-recursive-session :exports none :lexical t + (defun quiescent-general-recursive-edit () + "Enter a recursive editing session remembering the window config etc." + (interactive) + (save-excursion + (save-window-excursion + (recursive-edit)))) + + (global-set-key (kbd "s-R") #'quiescent-general-recursive-edit) +#+END_SRC + +** Composable +There is a concept in VIM that objects and actions are orthogonal. +i.e. what you can do can be done to all things. This is a powerful +idea because it means that you don't have to remember as many bindings +and if any new actions or objects are added they are automatically +actionable by any the other kind. A realisation that an Emacsen had +was that Emacs could easily support this idea because it already has +actions and objects built in and various commands for using them (just +not by the same name.) Importantly one can continue using Emacs the +same way even with composable turned on! + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref composable :exports none :lexical t + (use-package composable + :ensure t + :init (composable-mode) + :config (composable-mark-mode)) +#+END_SRC + +** Completion Setup +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref completion :exports none :lexical t + <> + <> + <> + <> +#+END_SRC + +I've long since abandoned this completion file and instead I'm going +to do it all from here with company mode -- which seems to be the best +supported completion framework. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref company-mode :exports none :lexical t + (use-package company + :ensure t + :init + (progn + (add-hook 'prog-mode-hook #'quiescent-activate-company-mode) + ;; TODO: this would be awesome if it had everything. + ;; (add-hook 'minibuffer-setup-hook #'quiescent-activate-company-mode) + (add-hook 'text-mode-hook #'quiescent-activate-company-mode)) + :config + (progn + (define-key company-active-map (kbd "C-'") #'company-complete-selection) + (define-key company-active-map (kbd "M-'") #'company-complete-selection) + (define-key company-active-map (kbd "C-.") #'company-select-next) + (define-key company-active-map (kbd "M-.") #'company-select-next) + (define-key company-active-map (kbd "C-,") #'company-select-previous) + (define-key company-active-map (kbd "M-,") #'company-select-previous) + (global-set-key (kbd "C-'") #'company-complete-selection) + (advice-add #'company-ispell :around #'quiescent-supress-message-around) + (setq company-idle-delay 0) + (setq company-tooltip-idle-delay 5) + (setq company-tooltip-limit 0) + (setq company-require-match nil) + (setq company-frontends '(company-preview-frontend)))) + + (defun quiescent-activate-company-mode () + "Activate company mode." + (company-mode 1)) + + (defun quiescent-supress-message-around (f &rest args) + "Supress messages when executing F with ARGS." + (let ((inhibit-message t)) + (funcall f args))) +#+END_SRC + +In JavaScript mode company tries to do completions with Semantic and +it can be slooow. I'm going to turn that off for now. It's also +immensely slow in Emacs Lisp mode inside of comments so I'm getting +rid of that too. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref company-no-semantic-in-jsx :exports none :lexical t + (defun quiescent-remove-semantic-backend () + "Remove the semantic backend for company." + (setq company-backends (delq 'company-semantic company-backends))) + + (add-hook 'js-jsx-mode-hook #'quiescent-remove-semantic-backend) + (add-hook 'emacs-lisp-mode-hook #'quiescent-remove-semantic-backend) +#+END_SRC + +In text mode I only want company to work from my flyspell dictionary +and then from dabbrev. I think that the other back ends are slowing +it down quite a bit. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref company-text-mode-backends :exports none :lexical t + (defun quiescent-company-text-mode-hook () + "Keep only the backends I want in `text-mode'." + (setq-local company-backends '(company-bbdb company-ispell company-dabbrev))) + + (add-hook 'text-mode-hook #'quiescent-company-text-mode-hook) +#+END_SRC + +It's useful to keep track of the most used completions per context +because in practice placing these at the top of the list tends to be a +good expericence. Company has a mode for that called +`company-statistics'. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref company-statistics :exports none :lexical t + (defun quiescent-activate-company-statistics () + "Activate `company-statistics-mode'." + (company-statistics-mode 1)) + + (use-package company-statistics + :ensure t + :config + (add-hook 'company-mode-hook #'quiescent-activate-company-statistics)) +#+END_SRC + +** Emacs Complete Lines +Redguardtoo has released a package which attempts to complete the +lines following point based on grep hits in the project. It looks +amazing for web dev so I'm going to give it a bash. + +It turns out though that it's not a package so I'm going to have to do +a manual install of it. I'll take a look a bit later. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref eacl :exports none :lexical t + (eval-and-compile + (when (package-installed-p 'ivy) + (package-activate 'ivy)) + (use-package ivy + :ensure t) + (load-file "~/.emacs.d/lisp/eacl/eacl.el")) +#+END_SRC + +** Pair Programming +I'm starting to do more and more pair programming and one of the +stumbling blocks which I have is that the person reading my code is +accustomed to a number of things. The first of which is that their +editor/IDE always has line numbers showing. The second is that +they'll almost certainly have the current line highlighted and +sometimes outlined. I've decided to create a mode based on the pair +programming which I'm doing write now and call it "Matt is Watching +Mode". + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref matt-is-watching :exports none :lexical t + (define-minor-mode global-matt-is-watching-mode + "Toggle global-matt-is-watching-mode. + Turns on some features which are useful for pair programming. In + particular: + - line numbers + - hilighting the current line" + :init-value nil + :lighter " MiW" + :global t + (if global-matt-is-watching-mode + (progn + (global-display-line-numbers-mode 1) + (global-hl-line-mode 1)) + (progn + (global-display-line-numbers-mode -1) + (global-hl-line-mode -1)))) +#+END_SRC + +** RealGUD +RealGUD is a package which aims to provide more debugging support to +Emacs than comes standard. I'm going to start using it for debugging +from within Emacs where GUD doesn't yet have support for a debugger. +What prompted me to use it was the apparent lack of support for pdb on +Windows. + +In order to start using it you must run the following command: +=M-x load-library RET realgud RET= + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref realgud :exports none :lexical t + (use-package realgud + :ensure t) +#+END_SRC + +** Cleaning up Files +A lot of the code which I work on is poorly formatted and has tabs in +it -_- To rectify this I've written a function which cleans up a file +in one fell swoop. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref clean-file :exports none :lexical t + (defun quiescent-clean-file () + "Clean the current file. + + Removes tabs, cleans up whitespace errors and indents." + (interactive) + (save-excursion + (progn + (mark-whole-buffer) + (untabify (region-beginning) (region-end)) + (whitespace-cleanup) + (indent-for-tab-command)))) +#+END_SRC + +** Compiling +Emacs includes a mode which makes compilation buffers more +interactive. I've made a couple of adjustments to it. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref compile :exports none :lexical t + (require 'compile) + + ;; Credit: http://stackoverflow.com/questions/13397737/ansi-coloring-in-compilation-mode + (ignore-errors + (require 'ansi-color) + (defun gavenkoa-colorize-compilation-buffer () + "Colerise the buffer in compilation mode." + (when (eq major-mode 'compilation-mode) + (ansi-color-apply-on-region compilation-filter-start (point-max)))) + (add-hook 'compilation-filter-hook 'gavenkoa-colorize-compilation-buffer)) + + (key-chord-define-global "xr" 'recompile) +#+END_SRC + +** Flycheck Mode +Flycheck mode provides interactive syntax checking through integration +with compilers and regular expression based syntax checkers (which it +has built in.) It checks files while your editing and, like Flyspell, +underlines mistakes. At the moment I use it everywhere, because I'm +yet to decide where I want it to be enabled and it seems pretty useful +so far. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref flycheck :exports none :lexical t + ;; TODO more carefully consider which modes need flycheck + (use-package flycheck + :ensure t + :init (progn + (add-hook 'prog-mode-hook #'quiescent-enable-flycheck-prog) + (add-hook 'latex-mode-hook #'quiescent-enable-flycheck) + (add-hook 'message-mode-hook #'quiescent-enable-flycheck) + (add-hook 'org-mode-hook #'quiescent-enable-flycheck) + (add-hook 'text-mode-hook #'quiescent-enable-flycheck) + (add-hook 'gfm-mode #'quiescent-enable-flycheck) + (add-hook 'markdown-mode-hook #'quiescent-enable-flycheck) + (add-hook 'LaTeX-mode-hook #'quiescent-enable-flycheck))) + + (defvar quiescent-modes-not-to-activate-flycheck-in '(haskell-mode emacs-lisp-mode) + "Modes in which flycheck should not be activated. + + Usually because of too much overhead in checking.") + + (defun quiescent-enable-flycheck-prog (&optional rest) + "Decide whether flycheck should be enabled in this prog mode. + + Ignore REST." + (when (not (member major-mode quiescent-modes-not-to-activate-flycheck-in)) + (flycheck-mode 1))) + + ;; From http://emacsist.com/10784 + (flycheck-define-checker proselint + "A linter for prose." + :command ("proselint" source-inplace) + :error-patterns + ((warning line-start (file-name) ":" line ":" column ": " + (id (one-or-more (not (any " ")))) + (message) line-end)) + :modes (text-mode markdown-mode gfm-mode message-mode latex-mode org-mode)) + + (add-to-list 'flycheck-checkers 'proselint) + + (defun quiescent-enable-flycheck (&optional rest) + "Enable flycheck mode. + + Ignore REST." + (flycheck-mode 1)) + + (defun quiescent-disable-flycheck (&optional rest) + "Disable flycheck mode. + + Ignore REST." + (flycheck-mode -1)) + + (defun quiescent-add-probable-include-dir-for-cpp () + "Add a probable include directory clang flycheck. + + We guess that the include dir is probably one up and into + `include'." + (setq flycheck-clang-include-path + (list (expand-file-name "../include/")))) + + (defun quiescent-set-flycheck-language-standard () + "Set the language standard for flycheck." + (setq flycheck-clang-language-standard "c++11")) + + (add-hook 'c++-mode-hook + #'quiescent-set-flycheck-language-standard) + (add-hook 'c++-mode-hook + #'quiescent-add-probable-include-dir-for-cpp) + (add-hook 'c-mode-hook + #'quiescent-add-probable-include-dir-for-cpp) +#+END_SRC + +** Flymake Mode +Flymake is a competitor to Flycheck which is: + - in core Emacs; + - was recently remade and vastly improved; + +I'd like to try using it over Flycheck because I have a feeling that +(especially considering the man who wrote it) it'll do a better job of +being fast, streamlined and that it'll make better use of current +Emacs features. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref flymake :exports none :lexical t + (require 'flymake) + + + ;; Emacs Lisp + + (add-hook 'emacs-lisp-mode-hook #'flymake-mode) + + + ;; Haskell + + ;; This is entirely based on the python definition of Flymake. + + (defgroup haskell-flymake nil + "Integration between Haskell and Flymake." + :group 'python + :link '(custom-group-link :tag "Flymake" flymake) + :version "26.1") + + (defcustom haskell-flymake-command '("stack" "--nix" "--verbosity" "silent" "runghc" "--" "--ghc-arg=-i" "--ghc-arg=-Wall") + "The external tool which will be used to compile haskell files." + :version "26.1" + :group 'haskell-flymake + :type '(repeat string)) + + (defcustom haskell-flymake-command-output-pattern + (list + "[a-zA-Z.]+:\\([0-9]+\\):\\([0-9]+\\): \\([a-zA-Z]+\\):\\([^/]*\\)" + 1 2 3 4) + "Specify how to parse the output of `haskell-flymake-command'." + :version "26.1" + :group 'haskell-flymake + :type '(list regexp + (integer :tag "Line's index") + (integer :tag "Column's index") + (integer :tag "Type's index") + (integer :tag "Message's index"))) + + (defcustom haskell-flymake-msg-alist + '(("^error$" . :error) + ("^warning$" . :warning)) + "Alist to associate diagnostic types to their corresponding types." + :version "26.1" + :group 'haskell-flymake + :type `(alist :key-type (regexp) + :value-type (symbol))) + + (defvar-local haskell--flymake-proc nil) + + (defconst haskell-flymake-ignore-main-error-string + "Variable not in scope: main :: IO a0" + "A string which should be ignored if found.") + + (defun haskell--flymake-parse-output (source proc report-fn) + "Collect diagnostics from the tools output line by line. + + This is a straight copy from the `python-mode' method -- which + makes me think that this method of doing things could be more + general." + (let ((rx (nth 0 haskell-flymake-command-output-pattern)) + (lineidx (nth 1 haskell-flymake-command-output-pattern)) + (colidx (nth 2 haskell-flymake-command-output-pattern)) + (typeidx (nth 3 haskell-flymake-command-output-pattern)) + (msgidx (nth 4 haskell-flymake-command-output-pattern))) + (with-current-buffer (process-buffer proc) + (goto-char (point-min)) + (when (search-forward-regexp haskell-flymake-ignore-main-error-string nil t) + (forward-line -1) + (delete-region (point) (point-max))) + (goto-char (point-min)) + (cl-loop + while (search-forward-regexp rx nil t) + for msg = (match-string msgidx) + for (beg . end) = (flymake-diag-region + source + (string-to-number + (match-string lineidx)) + (and colidx + (match-string colidx) + (string-to-number + (match-string colidx)))) + for type = (or (and typeidx + (match-string typeidx) + (assoc-default + (match-string typeidx) + haskell-flymake-msg-alist + #'string-match)) + (assoc-default msg + haskell-flymake-msg-alist + #'string-match) + :error) + collect (flymake-make-diagnostic + source beg end type msg) + into diags + finally (funcall report-fn diags))))) + + (defun haskell-flymake (report-fn &rest _args) + "Flymake backend for Haskell. + This backend uses `haskell-flymake-command' (which see) to launch a process + that is passed the current buffer's content via stdin. + REPORT-FN is Flymake's callback function." + (unless (executable-find (car haskell-flymake-command)) + (error "Cannot find a suitable checker")) + + (when (process-live-p haskell--flymake-proc) + (kill-process haskell--flymake-proc)) + + (let ((source (current-buffer))) + (save-restriction + (widen) + (setq haskell--flymake-proc + (make-process + :name "haskell-flymake" + :noquery t + :connection-type 'pipe + :buffer (generate-new-buffer " *haskell-flymake*") + :command haskell-flymake-command + :sentinel + (lambda (proc _event) + (when (eq 'exit (process-status proc)) + (unwind-protect + (when (with-current-buffer source'' + (eq proc haskell--flymake-proc)) + (haskell--flymake-parse-output source proc report-fn)) + (kill-buffer (process-buffer proc))))))) + (process-send-region haskell--flymake-proc (point-min) (point-max)) + (process-send-eof haskell--flymake-proc)))) + + (defun haskell-init-flymake () + "Init flymake in the current buffer." + (advice-add #'haskell-flymake-init :around #'ignore) + (add-hook 'flymake-diagnostic-functions #'haskell-flymake nil t) + (flymake-mode)) + + ;(add-hook 'haskell-mode-hook #'haskell-init-flymake) + +#+END_SRC + +** Hide Show Mode +Emacs has a built in mode for hiding and showing source code blocks in +various languages. I find it useful in java when classes get very big +and you want to see a summary of the methods without the noise, or in +XML where you're interested in the elements at a particular depth. + +I toggle between show and hide using a custom snippet I got from the +emacs wiki. + +By default show-hide doesn't support folding in HTML and XML very +well. I use a snippet from the Emacs wiki to solve that. + +I've chosen a few language modes to load show hide in. + +#+BEGIN_SRC emacs-lisp :tangle no :noweb yes :noweb-ref hide-show :exports none :lexical t + (defun toggle-hiding (column) + "Hide or show the sexp at COLUMN (defaulted to the point). + + Requires that `hs-minor-mode' is enabled." + (interactive "P") + (if hs-minor-mode + (if (condition-case nil + (hs-toggle-hiding) + (error t)) + (hs-show-all)) + (toggle-selective-display column))) + + ;; Fix XML folding + (add-to-list 'hs-special-modes-alist + (list 'nxml-mode + "\\|]*[^/]>" + "\\|]*[^/]>" + "