├── snippets ├── js2-mode │ └── .yas-parents ├── rjsx-mode │ └── .yas-parents ├── web-mode │ └── .yas-parents ├── clojure-mode │ ├── div │ ├── div-class │ ├── cljsbuild │ └── defproject ├── sh-mode │ ├── if │ ├── ife │ ├── case │ ├── trap │ └── getopts ├── js-mode │ ├── sp.yasnippet │ ├── ss.yasnippet │ ├── r.yasnippet │ ├── cc.yasnippet │ ├── rr.yasnippet │ ├── gdp.yasnippet │ ├── gis.yasnippet │ ├── cwm.yasnippet │ ├── cwum.yasnippet │ ├── cdm.yasnippet │ ├── cwrp.yasnippet │ ├── cwu.yasnippet │ ├── scu.yasnippet │ ├── cdu.yasnippet │ ├── rcc.yasnippet │ └── fc.yasnippet ├── emacs-lisp-mode │ ├── autoload-cookie │ ├── use-package │ └── use-package-straight └── markdown-mode │ ├── journal │ └── frontmatter ├── screenshot.png ├── bin ├── dark-mode-notifier ├── applescript-helper ├── install-parinfer-rust-library ├── update-emacs ├── install-utilities ├── dark-mode-notifier.swift ├── install-emacs-plus ├── build-utils └── build-emacs ├── .gitignore ├── eshell └── alias ├── LICENSE ├── lisp ├── log-mode.el ├── polymode-setup.el ├── buttonize.el ├── anychange.el ├── m-mail.el ├── highlight-things.el ├── tagger.el ├── exec-path-from-shell.el ├── fiat-color.el └── ldap-mode.el ├── TODO.org ├── early-init.el ├── README.org └── TODO.org_archive /snippets/js2-mode/.yas-parents: -------------------------------------------------------------------------------- 1 | js-mode -------------------------------------------------------------------------------- /snippets/rjsx-mode/.yas-parents: -------------------------------------------------------------------------------- 1 | js-mode -------------------------------------------------------------------------------- /snippets/web-mode/.yas-parents: -------------------------------------------------------------------------------- 1 | js-mode -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnewt/dotemacs/HEAD/screenshot.png -------------------------------------------------------------------------------- /bin/dark-mode-notifier: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnewt/dotemacs/HEAD/bin/dark-mode-notifier -------------------------------------------------------------------------------- /snippets/clojure-mode/div: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: div 3 | # key: d 4 | # -- 5 | (d/div {} 6 | $0) -------------------------------------------------------------------------------- /snippets/sh-mode/if: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: if 3 | # key: if 4 | # -- 5 | if ${1:cond}; then 6 | $0 7 | fi -------------------------------------------------------------------------------- /snippets/clojure-mode/div-class: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: div-class 3 | # key: d. 4 | # -- 5 | (d/div {:className "$1"}$0) -------------------------------------------------------------------------------- /snippets/js-mode/sp.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#setProps 3 | # key: sp 4 | # -- 5 | this.setProps({$0}); 6 | -------------------------------------------------------------------------------- /snippets/js-mode/ss.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#setState 3 | # key: ss 4 | # -- 5 | this.setState({$0}); 6 | -------------------------------------------------------------------------------- /snippets/emacs-lisp-mode/autoload-cookie: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: autoload-cookie 3 | # key: alc 4 | # -- 5 | ;;;###autoload 6 | -------------------------------------------------------------------------------- /snippets/js-mode/r.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#render 3 | # key: r 4 | # -- 5 | render: function() { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/emacs-lisp-mode/use-package: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: use-package 3 | # key: up 4 | # -- 5 | (use-package ${1:package-name} 6 | $0) -------------------------------------------------------------------------------- /snippets/js-mode/cc.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React.createClass 3 | # key: cc 4 | # -- 5 | React.createClass({ 6 | $0 7 | }); 8 | -------------------------------------------------------------------------------- /snippets/js-mode/rr.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React.render 3 | # key: rr 4 | # -- 5 | React.render( 6 | $1, 7 | $0 8 | ); 9 | -------------------------------------------------------------------------------- /snippets/sh-mode/ife: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: ife 3 | # key: ife 4 | # -- 5 | if ${1:cond}; then 6 | $0 7 | else 8 | 9 | fi 10 | -------------------------------------------------------------------------------- /snippets/js-mode/gdp.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#getDefaultProps 3 | # key: gdp 4 | # -- 5 | getDefaultProps: function() { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/js-mode/gis.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#getInitialState 3 | # key: gis 4 | # -- 5 | getInitialState: function() { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/js-mode/cwm.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#componentWillMount 3 | # key: cwm 4 | # -- 5 | componentWillMount: function() { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/js-mode/cwum.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#componentWillUnmount 3 | # key: cwum 4 | # -- 5 | componentWillUnmount: function() { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/sh-mode/case: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: case 3 | # key: case 4 | # -- 5 | case ${1:thing} in 6 | ${2:option1}) 7 | ${0:thing_to_do} 8 | ;; 9 | esac -------------------------------------------------------------------------------- /snippets/js-mode/cdm.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#componentDidMount 3 | # key: cdm 4 | # -- 5 | componentDidMount: function(${1:rootNode}) { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/js-mode/cwrp.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#componentWillReceiveProps 3 | # key: cwrp 4 | # -- 5 | componentWillReceiveProps: function(${1:nextProps}) { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/js-mode/cwu.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#componentWillUpdate 3 | # key: cwu 4 | # -- 5 | componentWillUpdate: function(${1:nextProps}, ${2:nextState}) { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/js-mode/scu.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#shouldComponentUpdate 3 | # key: scu 4 | # -- 5 | shouldComponentUpdate: function(${1:nextProps}, ${2:nextState}) { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/emacs-lisp-mode/use-package-straight: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: use-package-straight 3 | # key: ups 4 | # -- 5 | :straight 6 | (:type ${1:git} :host ${2:github} :repo "${3:user/repo}") 7 | $0) -------------------------------------------------------------------------------- /snippets/js-mode/cdu.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#componentDidUpdate 3 | # key: cdu 4 | # -- 5 | componentDidUpdate: function(${1:prevProps}, ${2:prevState}, ${3:rootNode}) { 6 | $0 7 | } 8 | -------------------------------------------------------------------------------- /snippets/sh-mode/trap: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: trap 3 | # key: trap 4 | # -- 5 | ${1:touch /tmp/temp_file} 6 | 7 | function cleanup { 8 | ${0:rm -rf /tmp/temp_file} 9 | } 10 | 11 | trap cleanup EXIT 12 | -------------------------------------------------------------------------------- /snippets/markdown-mode/journal: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: journal 3 | # key: journal 4 | # expand-env: ((journal-date (file-name-base buffer-file-name))) 5 | # -- 6 | --- 7 | title: `journal-date` 8 | date: `journal-date` 9 | number: $1 10 | --- 11 | $0 -------------------------------------------------------------------------------- /snippets/sh-mode/getopts: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: getopts 3 | # key: getopts 4 | # -- 5 | while getopts ":$1" opt; do 6 | case $opt in 7 | -o) 8 | $0 9 | ;; 10 | *) 11 | echo "usage: $(basename \$0) [+-f} [--] ARGS..." 12 | exit 2 13 | esac 14 | done 15 | shift $(expr $OPTIND - 1) 16 | OPTIND=1 17 | -------------------------------------------------------------------------------- /snippets/js-mode/rcc.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#classComponent 3 | # key: rcc 4 | # expand-env: ((name (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))) 5 | # -- 6 | import React from 'react'; 7 | 8 | export default class ${1:`name`} extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | } 12 | render() { 13 | return ( 14 | $0 15 | ); 16 | } 17 | } -------------------------------------------------------------------------------- /bin/applescript-helper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # https://www.emacswiki.org/emacs/AppleScript 3 | 4 | if [ "$1" = "-d" ]; then 5 | RANDOMFILE="applescript${RANDOM}.scpt" 6 | cat /dev/stdin >"/tmp/$RANDOMFILE" 7 | osadecompile "/tmp/$RANDOMFILE" 8 | rm "/tmp/$RANDOMFILE" 9 | else 10 | RANDOMFILE="applescript${RANDOM}.scpt" 11 | osacompile -o "/tmp/$RANDOMFILE" 12 | cat "/tmp/$RANDOMFILE" 13 | rm "/tmp/$RANDOMFILE" 14 | fi 15 | -------------------------------------------------------------------------------- /snippets/js-mode/fc.yasnippet: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: React#functionComponent 3 | # key: fc 4 | # -- 5 | import React from 'react'; 6 | import PropTypes from 'prop-types'; 7 | 8 | const propTypes = {}; 9 | 10 | const defaultProps = {}; 11 | 12 | const ${1:Component} = (props) => {}; 13 | 14 | ${1:Component}.propTypes = propTypes; 15 | ${1:Component}.defaultProps = defaultProps; 16 | 17 | export default ${1:Component}; 18 | -------------------------------------------------------------------------------- /bin/install-parinfer-rust-library: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Build and install parinfer-rust library. 3 | 4 | cd ~/code 5 | [[ -d parinfer-rust ]] || git clone https://github.com/eraserhd/parinfer-rust.git 6 | cd ~/code/parinfer-rust 7 | git pull 8 | brew install rust 9 | cargo build --release --features emacs 10 | mkdir -vp ~/.emacs.d/parinfer-rust 11 | cp -v target/release/libparinfer_rust.dylib ~/.emacs.d/parinfer-rust/parinfer-rust-darwin.so 12 | -------------------------------------------------------------------------------- /snippets/clojure-mode/cljsbuild: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: cljsbuild 3 | # key: cljsbuild 4 | # -- 5 | :cljsbuild {:builds [{:source-paths ["src" "dev"] 6 | :compiler {:output-to "target/classes/public/app.js" 7 | :output-dir "target/classes/public/out" 8 | :optimizations :none 9 | :recompile-dependents true 10 | :source-map true}}]} -------------------------------------------------------------------------------- /snippets/markdown-mode/frontmatter: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: frontmatter 3 | # key: frontmatter 4 | # -- 5 | --- 6 | title: ` 7 | (mapconcat 8 | (lambda(s) (concat (upcase (substring s 0 1)) (downcase (substring s 1)))) 9 | (split-string (file-name-base) "-") 10 | " ")` 11 | date: ` 12 | (destructuring-bind (month day year) (calendar-current-date) 13 | (concat (format "%4i" year) 14 | "-" 15 | (format "%02i" month) 16 | "-" 17 | (format "%02i" day)))` 18 | --- 19 | 20 | $0 21 | -------------------------------------------------------------------------------- /snippets/clojure-mode/defproject: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: defproject 3 | # key: project 4 | # -- 5 | (defproject ${1:`(file-name-base (f-parent (buffer-file-name)))`} "0.1.0-SNAPSHOT" 6 | :description "$2" 7 | :url "$3" 8 | :license {:name "GNU General Public License" 9 | :url "http://www.gnu.org/licenses/gpl.html"} 10 | :jvm-opts ["-XX:MaxPermSize=256m"] 11 | :dependencies [[org.clojure/clojure "1.7.0"]] 12 | :profiles {:dev {:plugins [] 13 | :dependencies [] 14 | :source-paths ["dev"]}}) 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .lsp-session-v* 2 | /.mc-lists.el 3 | .python-environments 4 | /.cache 5 | /.persistent-scratch 6 | /bookmarks 7 | /custom.el 8 | /fiat-config 9 | /git 10 | /history 11 | /logview-cache.extmap 12 | /nov-places 13 | /org-wunderlist 14 | /packages 15 | /private.el 16 | /recentf 17 | /request 18 | /semanticdb 19 | /src 20 | /srecode-map.el 21 | /transient 22 | /undohist 23 | /url 24 | /var 25 | /.last-package-update-day 26 | /package-quickstart.el 27 | /image-dired/ 28 | /thumbs/ 29 | /tldr/ 30 | /calc.el 31 | /projects 32 | /eln-cache/ 33 | /.dap-breakpoints 34 | /org-persist/ 35 | /emacs/ 36 | -------------------------------------------------------------------------------- /bin/update-emacs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | emacs_dir="$HOME/.emacs.d" 4 | 5 | if [ -e "#$emacs_dir/.git" ] && [ -n "$(git -C "$emacs_dir" status --porcelain)" ]; then 6 | echo "Pulling changes from the current remote..." 7 | git -C "$HOME" pull 8 | else 9 | echo "Not pulling becase no clean repo was found at $emacs_dir." 10 | fi 11 | 12 | if [ -f "$emacs_dir/bin/setup" ]; then 13 | echo "Building a new version of Emacs..." 14 | "$emacs_dir/bin/setup" 15 | else 16 | echo "Emacs build script not found, skipping" 17 | fi 18 | 19 | # echo "Updating Emacs packages..." 20 | # emacs --batch --load "$emacs_dir/early-init.el" --load "$emacs_dir/init.el" \ 21 | # --funcall update-emacs-packages-sync 22 | -------------------------------------------------------------------------------- /eshell/alias: -------------------------------------------------------------------------------- 1 | alias ll ls -lh $* 2 | alias su eshell/su $* 3 | alias sudo eshell/sudo $* 4 | alias ... ../.. $* 5 | alias .... ../../.. $* 6 | alias df df -h $* 7 | alias en emacsclient -c --no-wait $* 8 | alias g git $* 9 | alias ga git add $* 10 | alias gc git commit $* 11 | alias gcm git commit -m $* 12 | alias glog git log --pretty=format:"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset" --abbrev-commit $* 13 | alias gp git push $* 14 | alias gpl git pull $* 15 | alias grep grep --color=auto $* 16 | alias gs git status $* 17 | alias hide-hidden-files defaults write com.apple.finder AppleShowAllFiles -bool false; and killall Finder $* 18 | alias ln ln -v $* 19 | alias mkdir mkdir -p $* 20 | alias show-hidden-files defaults write com.apple.finder AppleShowAllFiles -bool true; and killall Finder $* 21 | alias public-ip wget -qO- https://diagnostic.opendns.com/myip && echo 22 | alias mergepdf /System/Library/Automator/CombinePDFPages.action/Contents/Resources/join.py $* 23 | alias la ls -Alh $* 24 | alias l ls $* 25 | alias lt ls -Alhtr $* 26 | alias lsd *ls -d * 27 | -------------------------------------------------------------------------------- /bin/install-utilities: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Install support utilities for various Emacs modes and packages. 3 | # Read init.el for every line that begins like ";; > " and prompt to run those commands. 4 | 5 | set -euo pipefail 6 | 7 | # Prompt user to confirm 8 | prompt_confirm() { 9 | local confirm 10 | while true; do 11 | read -p "$* [y/N]: " confirm 12 | case $confirm in 13 | [yY]*) return 0 ;; 14 | [nN]*) return 1 ;; 15 | esac 16 | done 17 | } 18 | 19 | # Run command and read into the specified variable. 20 | # $1: variable name 21 | # All other args are the command 22 | into_array() { 23 | local array_var="$1" 24 | shift 25 | IFS=$'\n' read -r -d '' -a "$array_var" < <($@ && printf '\0') 26 | } 27 | 28 | # Display and execute the command 29 | exe() { 30 | echo "> $*" 31 | "$@" 32 | } 33 | 34 | into_array commands awk -F'> ' '/^ *;; +>/ {print $2}' "$HOME/.emacs.d/init.el" 35 | 36 | printf "%s\n" "${commands[@]}" 37 | echo 38 | if prompt_confirm "Run the above commands?"; then 39 | echo "Running commands..." 40 | for command in "${commands[@]}"; do 41 | exe $command 42 | done 43 | fi 44 | -------------------------------------------------------------------------------- /bin/dark-mode-notifier.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env swift 2 | // Compile with: 3 | // swiftc dark-mode-notifier.swift -o dark-mode-notifier 4 | 5 | import Cocoa 6 | 7 | @discardableResult 8 | func shell(_ args: String...) -> Int32 { 9 | let task = Process() 10 | task.launchPath = "/usr/bin/env" 11 | task.arguments = args 12 | task.launch() 13 | task.waitUntilExit() 14 | return task.terminationStatus 15 | } 16 | 17 | func updateEmacsTheme() { 18 | let isDark = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" 19 | let emacsen = NSRunningApplication.runningApplications( 20 | withBundleIdentifier: "org.gnu.Emacs") 21 | 22 | if emacsen.count > 0 { 23 | print("Notifying Emacs that macOS Dark Mode is:", isDark) 24 | shell("emacsclient", "-e", isDark ? "(fiat-nox)" : "(fiat-lux)") 25 | print() 26 | } 27 | } 28 | 29 | updateEmacsTheme() 30 | 31 | DistributedNotificationCenter.default.addObserver( 32 | forName: Notification.Name("AppleInterfaceThemeChangedNotification"), 33 | object: nil, 34 | queue: nil) { (notification) in 35 | updateEmacsTheme() 36 | } 37 | 38 | // Enter cocoa run loop and start listening for notifyd events 39 | NSApplication.shared.run() 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /bin/install-emacs-plus: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Build or rebuild an Emacs-Plus installation 3 | # https://github.com/d12frosted/homebrew-emacs-plus 4 | 5 | set -euo pipefail 6 | 7 | # Prompt user to confirm 8 | prompt_confirm() { 9 | local confirm 10 | while true; do 11 | read -p "$* [y/N]: " confirm 12 | case $confirm in 13 | [yY]*) return 0 ;; 14 | [nN]*) return 1 ;; 15 | esac 16 | done 17 | } 18 | 19 | emacs="emacs-plus@31" 20 | options="" 21 | destination="/Applications" 22 | dependencies="gcc libgccjit jq gnutls librsvg libxml2 little-cms2 tree-sitter" 23 | 24 | install-parinfer-rust-library 25 | brew install $dependencies || brew upgrade $dependencies 26 | brew tap d12frosted/emacs-plus 27 | brew uninstall $emacs || true 28 | brew install $emacs $options 29 | # If it's not a symlink, it's best that this error out and we can figure it out 30 | # manually. 31 | rm "$destination/Emacs.app" || true 32 | # Copied from the `homebrew-emacs-plus` install instructions. 33 | osascript -e "tell application \"Finder\" to make alias file to posix file \"$(brew --prefix $emacs)/Emacs.app\" at POSIX file \"$destination\" with properties {name:\"Emacs.app\"}" 34 | # ln -s "$(brew --prefix $emacs)/Emacs.app" "$destination" 35 | prompt_confirm "Remove $HOME/.emacs.d/{eln-cache,straight}?" && 36 | rm -rf $HOME/.emacs.d/{eln-cache,straight} 37 | 38 | echo "Restart Emacs for changes to take effect" 39 | -------------------------------------------------------------------------------- /lisp/log-mode.el: -------------------------------------------------------------------------------- 1 | ;;; log-mode.el --- Log File Mode -*- lexical-binding: t -*- 2 | 3 | ;; Author: Matthew Newton 4 | ;; Maintainer: Matthew Newton 5 | ;; Version: 0.1 6 | ;; Package-Requires: () 7 | ;; Homepage: https://github.com/mnewt/log-mode 8 | ;; Keywords: log 9 | 10 | 11 | ;; This file is not part of GNU Emacs 12 | 13 | ;; This file is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation; either version 3, or (at your option) 16 | ;; any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; For a full copy of the GNU General Public License 24 | ;; see . 25 | 26 | 27 | ;;; Commentary: 28 | 29 | ;; Major mode for viewing log files 30 | 31 | ;;; Code: 32 | 33 | (defvar log-mode-hook nil) 34 | 35 | (defvar log-mode-map 36 | (let ((map (make-sparse-keymap))) 37 | (define-key map ?k #'keep-lines) 38 | (define-key map ?f #'flush-lines))) 39 | 40 | ;;;###autoload 41 | (add-to-list 'auto-mode-alist '("\\.log\\'" . log-mode)) 42 | 43 | (defconst log-font-lock-keywords-1 44 | `((,(regexp-opt '("error" "ERROR")) . '(:foreground "red")) 45 | (,(regexp-opt '("WARNING" "warn" "WARN" "warning")) . '(:foreground "yellow")) 46 | (,(regexp-opt '("info" "INFO" "information" "INFORMATION")) . '(:foreground "white")) 47 | (,(regexp-opt '("debug" "DEBUG")) . '(:foreground "cyan")) 48 | (,(regexp-opt '("trace" "TRACE")) . '(:foreground "purple")))) 49 | 50 | (defconst log-font-lock-keywords-2 51 | (append log-font-lock-keywords-1 52 | (list ("\<[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}T?[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}[.0-9]*?Z\>" . font-lock-comment-face)))) 53 | 54 | (defvar log-font-lock-keywords log-font-lock-keywords-2) 55 | 56 | ;;;###autoload 57 | (define-derived-mode log-mode fundamental-mode "Log" 58 | "Major mode for viewing log files." 59 | (set (make-local-variable 'font-lock-defaults '(log-font-lock-keywords)))) 60 | 61 | (provide 'log-mode) 62 | 63 | ;;; log-mode.el ends here 64 | -------------------------------------------------------------------------------- /TODO.org: -------------------------------------------------------------------------------- 1 | #+TITLE: TODO 2 | 3 | * TODO Add native Emacs 29.1 =tree-sitter= support 4 | 5 | * TODO Add mode-line `-f` for `auto-revert-tail-mode`. 6 | 7 | * TODO Try Corfu 8 | 9 | * TODO =consult-line= should start on the next matching line, in order 10 | 11 | * TODO Remove =hydra= to =/lisp= 12 | 13 | * TODO Make changes to d12frosted/emacs-plus [0/3] 14 | 15 | ** TODO Install C source files 16 | #+begin_src ruby 17 | prefix.install "src" 18 | #+end_src 19 | 20 | ** TODO Don't compress source files 21 | #+begin_src 22 | --without-compress-install 23 | #+end_src 24 | 25 | ** TODO Compile with cores 26 | #+begin_src ruby 27 | system "make", "-j", `sysctl -n hw.ncpu` 28 | #+end_src 29 | 30 | ** TODO Compiler optimization settings 31 | #+begin_src ruby 32 | ENV.append_to_cflags %w[ 33 | -Ofast 34 | -march=native 35 | -pipe 36 | -falign-functions=64 37 | -fomit-frame-pointer 38 | -funit-at-a-time 39 | -fforce-addr 40 | -mfpmath=sse 41 | -fno-finite-math-only 42 | -fstack-check 43 | ].join(" ") 44 | #+end_src 45 | 46 | * TODO Create =dired-do-backup= 47 | Like =dired-do-copy= but renames the file on the other end with a timestamp. 48 | 49 | * TODO Submit patch to Org for =org-do-emphasis-faces= 50 | 51 | * TODO eshell-ls-decorated-name [1/3] 52 | 53 | ** DONE Fix it so =ls= to a different directory embeds correct links (it currently assumes files are relative to =default-directory=) 54 | 55 | ** TODO Extend =eshell-ls-decorated-name= to color output like =dired-rainbow= 56 | 57 | ** TODO Submit upstream 58 | 59 | * TODO Make =dired-rainbow-listing= into a package 60 | 61 | * TODO =counsel-term= stuff 62 | 63 | ** TODO Make =counsel-term-cd= into =counsel-dired-jump= 64 | 65 | ** TODO =counsel-term-cd= 66 | https://github.com/tautologyclub/counsel-term/pull/2 67 | [[file:git/counsel-term/counsel-term.el::(defun%20counsel-term-cd-function%20(str)][counsel-term-cd-function]] 68 | 69 | * TODO Send PR for =helpful-goto-face= 70 | 71 | * TODO Font-lock source code blocks in Info pages 72 | 73 | * TODO =flash-thing= 74 | 75 | * TODO Implement =bat= in Elisp =highlight-things.el= 76 | 77 | * DONE Fix =dired-listing-human-readable= or whatever it's called 78 | 79 | * DONE =exec-path-from-shell-setenv= should set =eshell-path-env= in each Eshell buffer 80 | Because it's a buffer local variable 81 | -------------------------------------------------------------------------------- /early-init.el: -------------------------------------------------------------------------------- 1 | ;;; early-init.el --- Emacs early init -*- lexical-binding: t; -*- 2 | 3 | ;;; Commentary: 4 | 5 | ;; Emacs 27 introduced early-init.el, which is run before init.el, before 6 | ;; package and UI initialization happens. 7 | 8 | ;;; Code: 9 | 10 | (defconst emacs-start-time (current-time)) 11 | 12 | ;; Uncomment this to debug. 13 | ;; (setq init-file-debug t) 14 | ;; (setq messages-buffer-max-lines 100000) 15 | 16 | ;; If an `.el' file is newer than its corresponding `.elc', load the `.el'. 17 | (setq load-prefer-newer t) 18 | 19 | ;; Set Garbage Collection threshold to 1GB during startup. `gcmh' will clean 20 | ;; things up later. 21 | (setq gc-cons-threshold 1073741824 22 | gc-cons-percentage 0.6) 23 | 24 | ;; Write any customizations to a temp file so they are discarded. 25 | (setq custom-file (make-temp-file "custom-" nil ".el")) 26 | 27 | ;; Faster to disable these here (before they've been initialized) 28 | (push '(menu-bar-lines . 0) default-frame-alist) 29 | (push '(tool-bar-lines . 0) default-frame-alist) 30 | (push '(vertical-scroll-bars) default-frame-alist) 31 | 32 | ;; Give the frame basic coloring while waiting for the theme to load. The main 33 | ;; purpose of this is to not blind me when it's dark by flashing a screen full 34 | ;; of white. These colors are from doom-one. 35 | (set-face-attribute 'default nil :background "#282c34" :foreground "#bbc2cf") 36 | ;; Default frame settings. This is actually maximized, not full screen. 37 | (push '(fullscreen . maximized) initial-frame-alist) 38 | (push '(ns-transparent-titlebar . t) default-frame-alist) 39 | 40 | ;; Resizing the Emacs frame can be a terribly expensive part of changing the 41 | ;; font. By inhibiting this, we easily halve startup times with fonts that are 42 | ;; larger than the system default. 43 | (setq frame-inhibit-implied-resize t 44 | frame-resize-pixelwise t) 45 | 46 | ;; Ignore X resources; its settings would be redundant with the other settings 47 | ;; in this file and can conflict with later config (particularly where the 48 | ;; cursor color is concerned). 49 | (advice-add #'x-apply-session-resources :override #'ignore) 50 | 51 | ;; These are good notes on optimizing startup performance: 52 | ;; https://github.com/hlissner/doom-emacs/wiki/FAQ#how-is-dooms-startup-so-fast 53 | 54 | ;; Unset `file-name-handler-alist' too (temporarily). Every file opened and 55 | ;; loaded by Emacs will run through this list to check for a proper handler for 56 | ;; the file, but during startup, it won’t need any of them. 57 | (defvar file-name-handler-alist-old file-name-handler-alist) 58 | (setq file-name-handler-alist nil) 59 | (add-hook 'emacs-startup-hook 60 | (lambda () 61 | (setq file-name-handler-alist file-name-handler-alist-old))) 62 | 63 | ;; Disable `package' in favor of `straight'. 64 | (setq package-enable-at-startup nil) 65 | 66 | (provide 'early-init) 67 | 68 | ;;; early-init.el ends here 69 | 70 | -------------------------------------------------------------------------------- /lisp/polymode-setup.el: -------------------------------------------------------------------------------- 1 | ;;; polymode-setup.el --- Set up Polymode -*- lexical-binding: t -*- 2 | 3 | ;;; Commentary: 4 | 5 | ;; commentary 6 | 7 | ;;; Code: 8 | 9 | (use-package polymode 10 | :config 11 | (with-no-warnings 12 | ;; js 13 | (define-hostmode poly-js-hostmode 14 | :mode 'js-mode) 15 | (define-innermode poly-js-graphql-innermode 16 | :mode 'graphql-mode 17 | :head-matcher "graphql[ \t\n]*(?`" 18 | :tail-matcher "`" 19 | :head-mode 'host 20 | :tail-mode 'host) 21 | ;; FIXME: Causes bad things to happen in the `js-mode' buffer. 22 | (define-polymode poly-js-mode 23 | :hostmode 'poly-js-hostmode 24 | :innermodes '(poly-js-graphql-innermode)) 25 | 26 | ;; web 27 | (define-hostmode poly-web-hostmode 28 | :mode 'web-mode) 29 | (define-innermode poly-web-svg-innermode 30 | :mode 'nxml-mode 31 | :head-matcher "" 33 | :head-mode 'inner 34 | :tail-mode 'inner) 35 | (define-polymode poly-web-mode 36 | :hostmode 'poly-web-hostmode 37 | :innermodes '(poly-web-svg-innermode)) 38 | 39 | ;; restclient 40 | (define-hostmode poly-restclient-hostmode 41 | :mode 'restclient-mode) 42 | (define-innermode poly-restclient-elisp-root-innermode 43 | :mode 'emacs-lisp-mode 44 | :head-mode 'host 45 | :tail-mode 'host) 46 | (define-innermode poly-restclient-elisp-single-innermode 47 | poly-restclient-elisp-root-innermode 48 | :head-matcher "^:[^ ]+ :=" 49 | :tail-matcher "\n") 50 | (define-innermode poly-restclient-elisp-multi-innermode 51 | poly-restclient-elisp-root-innermode 52 | :head-matcher "^:[^ ]+ := <<" 53 | :tail-matcher "^#$") 54 | (define-polymode poly-restclient-mode 55 | :hostmode 'poly-restclient-hostmode 56 | :innermodes '(poly-restclient-elisp-single-innermode 57 | poly-restclient-elisp-multi-innermode)) 58 | 59 | ;; applescript 60 | (defun match-string-delimiter (ahead) 61 | "Match the delimiter of a string, forward if AHEAD is positive. 62 | Backward if AHEAD is negative." 63 | (let ((re "[^\\]\"")) 64 | (when (or (looking-at re) 65 | (if (> ahead 0) 66 | (re-search-forward re) 67 | (re-search-backward re))) 68 | (cons (match-beginning 0) (match-end 0))))) 69 | 70 | (define-innermode poly-emacs-lisp-apples-innermode 71 | :mode 'apples-mode 72 | :head-matcher "do-applescript\s-*.*\"" 73 | :tail-matcher #'match-string-delimiter) 74 | (define-polymode poly-emacs-lisp-mode 75 | :hostmode 'poly-emacs-lisp-hostmode 76 | :innermodes '(poly-emacs-lisp-apples-innermode))) 77 | 78 | ;; ;; WIP 79 | ;; ;; (define-innermode poly-emacs-lisp-Info-innermode 80 | ;; ;; :mode 'Info-mode 81 | ;; ;; :) 82 | 83 | :hook 84 | (js-mode-hook . poly-js-mode) 85 | ;; (rjsx-mode-hook . poly-rjsx-mode) 86 | (web-mode-hook . poly-web-mode) 87 | (restclient-mode-hook . poly-restclient-mode)) 88 | 89 | (use-package poly-markdown 90 | :hook 91 | (markdown-mode-hook . poly-markdown-mode)) 92 | 93 | 94 | (provide 'polymode-setup) 95 | 96 | ;;; polymode-setup.el ends here 97 | -------------------------------------------------------------------------------- /lisp/buttonize.el: -------------------------------------------------------------------------------- 1 | ;;; buttonize --- Make a button from a url, address, whatever -*- lexical-binding: t -*- 2 | 3 | ;; Author: Matthew Newton 4 | ;; Maintainer: Matthew Newton 5 | ;; Version: 0.1 6 | ;; Package-Requires: ((emacs "24.3")) 7 | ;; Homepage: https://github.com/mnewt/buttonize 8 | ;; Keywords: reference, tools, docs 9 | 10 | 11 | ;; This file is not part of GNU Emacs 12 | 13 | ;; This file is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation; either version 3, or (at your option) 16 | ;; any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; For a full copy of the GNU General Public License 24 | ;; see . 25 | 26 | 27 | ;;; Commentary: 28 | 29 | ;; This package makes buttons out of whatever can be matched using font locking 30 | ;; rules. It is primarily intended to be a replacement for `goto-address-mode', 31 | ;; but simpler and more flexible. 32 | 33 | ;; `goto-address-mode' couldn't be extended because it assumes there are exactly 34 | ;; two types of interesting things: URLs and email addresses. Turns out there 35 | ;; are more. 36 | 37 | ;;; Code: 38 | 39 | 40 | ;;; Variables 41 | 42 | (defface buttonize-1 43 | '((((class color) (background light)) (:foreground "#1f5bff" :underline t)) 44 | (((class color) (background dark)) (:foreground "#6faaff" :underline t))) 45 | "First choice face for a button." 46 | :group 'buttonize) 47 | 48 | (defface buttonize-2 49 | '((((class color) (background light)) (:foreground "#00dd22" :underline t)) 50 | (((class color) (background dark)) (:foreground "#d7ff87" :underline t))) 51 | "Second choice face for a button." 52 | :group 'buttonize) 53 | 54 | (defface buttonize-3 55 | `((((class color) (background light)) (:foreground "#555" :underline t)) 56 | (((class color) (background dark)) (:foreground "#eee" :underline t))) 57 | "Third choice face for a button." 58 | :group 'buttonize) 59 | 60 | (defvar buttonize-mode-keywords nil 61 | "Font Lock Keywords for `buttonize-mode'.") 62 | 63 | 64 | ;;; Customizeable Features 65 | 66 | (defvar buttonize-regexp-url 67 | (concat (regexp-opt '("file:/" "ftp://" "git://" "http://" "https://" "magnet:" 68 | "sftp://" "sip:" "sips:" "smb://" "sms:" "tel:")) 69 | thing-at-point-url-path-regexp)) 70 | 71 | (defcustom buttonize-keywords 72 | `((,buttonize-regexp-url buttonize-1 buttonize-os-open-url-function)) 73 | 74 | 75 | "List of elements in the form: 76 | 77 | (REGEXP FACE &optional FUNCTION PARAMETERS MODES) 78 | 79 | REGEXP is the REGEXP to match. 80 | 81 | FACE is the FACE to apply. 82 | 83 | FUNCTION is the function to call when the button is activated. 84 | 85 | PARAMETERS is a list of function parameters passed directly the 86 | function. 87 | 88 | MODES, if non-nil, is a list of modes in which to apply the 89 | keyword. If it is nil, then the keyword applies to all modes in 90 | which `buttonize-mode' is activated." 91 | :group 'buttonize) 92 | 93 | 94 | ;;; Functions 95 | 96 | (defun buttonize--add-keywords () 97 | "Add keywords when activating `buttonize-mode'" 98 | (cl-loop ((regexp face function parameters modes) buttonize-keywords))) 99 | 100 | 101 | ;;; Commands 102 | 103 | 104 | ;;; Mode 105 | 106 | (define-minor-mode buttonize-mode 107 | "Create buttons around URLs, addresses, and the like." 108 | nil 109 | nil 110 | buttonize-mode-map 111 | (if buttonize-mode 112 | (buttonize---add-keywords) 113 | (buttonize--remove-keywords)) 114 | ;; As of Emacs 24.4, `font-lock-flush' should be used instead of 115 | ;; `font-lock-fontify-buffer'. 116 | (if (fboundp 'font-lock-flush) 117 | (font-lock-flush) 118 | (when font-lock-mode 119 | (with-no-warnings 120 | (font-lock-fontify-buffer))))) 121 | 122 | (define-button-type 'buttonize 'action #'buttonize-button-action 'face 'buttonize-1) 123 | 124 | (provide 'eg) 125 | 126 | ;;; eg.el ends here 127 | -------------------------------------------------------------------------------- /lisp/anychange.el: -------------------------------------------------------------------------------- 1 | ;;; anychange.el --- Go to changes in any open buffer -*- lexical-binding: t -*- 2 | 3 | ;; Author: Matthew Newton 4 | ;; Maintainer: Matthew Newton 5 | ;; Version: 0.1 6 | ;; Package-Requires: () 7 | ;; Homepage: https://gitlab.com/mnewt/anychange 8 | ;; Keywords: emacs 9 | 10 | 11 | ;; This file is not part of GNU Emacs 12 | 13 | ;; This file is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation; either version 3, or (at your option) 16 | ;; any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; For a full copy of the GNU General Public License 24 | ;; see . 25 | 26 | 27 | ;;; Commentary: 28 | 29 | ;; commentary 30 | 31 | ;;; Code: 32 | 33 | (defcustom anychange-minimum-distance 100 34 | "Minium number of characters between the last change and the 35 | current one before we record a new change." 36 | :type 'number 37 | :group 'anychange) 38 | . 39 | (defvar anychange-positions-list '() 40 | "List of changes.") 41 | 42 | (defvar anychange-position 0 43 | "Current position in the list.") 44 | 45 | (defun anychange--minimum-distance-p (a b) 46 | "Return `t' if A is at least the minimum distance from B." 47 | (>= (abs (- a b)) anychange-minimum-distance)) 48 | 49 | (defun anychange--get-nth-position (n) 50 | "Return the N-th position from `anychange-positions-list'." 51 | (cdr (nth n anychange-positions-list))) 52 | 53 | (defun anychange--get-nth-situation (n) 54 | "Return relevant attributes from the N-th position in `anychange-positions-list'. 55 | 56 | They are returned as a list. 57 | " 58 | (if-let* ((change (nth n anychange-positions-list)) 59 | (name (car change)) 60 | (position (cdr change)) 61 | (buffer-or-file (or (get-buffer name) (when (file-exists-p name) 62 | (find-file-noselect name)))) 63 | (window (get-buffer-window buffer-or-file))) 64 | (list position buffer-or-file window) 65 | (list nil nil nil))) 66 | 67 | (defun anychange-go (&optional direction) 68 | "According to DIRECTION, go `forward' or `backward' on 69 | `anychange-positions-list' and find the change that is is at 70 | least `anychange-minimum-distance' characters away." 71 | (interactive "P") 72 | (setq direction (or direction 1)) 73 | (when current-prefix-arg (setq direction (- direction))) 74 | (setq n (+ anychange-position direction)) 75 | (while (and (< n (length anychange-positions-list)) 76 | (not (anychange--minimum-distance-p (point) (anychange--get-nth-position n)))) 77 | (setq n (+ n (if (natnump direction) 1 -1)))) 78 | (destructuring-bind (position buffer-or-file window) (anychange--get-nth-situation n) 79 | (if buffer-or-file 80 | (progn 81 | (cond 82 | (window (select-window window)) 83 | ((buffer-live-p buffer-or-file-name) (switch-to-buffer buffer-or-file)) 84 | ((file-exists-p buffer-or-file) (find-file buffer-or-file))) 85 | (goto-char position) 86 | (message "anychange number %d: buffer: %s, position %d" 87 | anychange-position buffer-or-file position) 88 | (incf anychange-position)) 89 | (message "anychange: Couldn't find the buffer or file: %d" 90 | buffer-or-file)))) 91 | 92 | (defalias 'anychange-go-forward #'anychange-go 93 | "Go forward through the positions list.") 94 | 95 | (defun anychange-go-backward () 96 | "Go backward through the positions list." 97 | (interactive "P") 98 | (if current-prefix-arg (anychange-go 1) (anychange-go -1))) 99 | 100 | (defun anychange-buffer-change-hook (beg end len) 101 | (setq anychange-position 0) 102 | (let ((buf (or (buffer-file-name) (buffer-name))) 103 | (previous-position (when anychange-positions-list 104 | (cdar anychange-positions-list)))) 105 | (message "buf: %s" buf) 106 | (if previous-position 107 | (message "previous-position: %d"previous-position) 108 | (message "No previous position")) 109 | (when (and buf (not (string-match-p "\\`\\(?: ?\\*\\)" buf)) 110 | (or (not previous-position) 111 | (>= (abs (- end previous-position)) anychange-minimum-distance))) 112 | (setq anychange-positions-list 113 | (cons 114 | (cons buf end) 115 | (subseq anychange-positions-list anychange-position))) 116 | (message "anychange: %s : %d" buf end)))) 117 | 118 | (defvar anychange-mode-map 119 | (let ((map (make-sparse-keymap))) 120 | (define-key map (kbd "C-c '") #'anychange-go-forward) 121 | (define-key map (kbd "C-c ;") #'anychange-go-backward) 122 | map) 123 | "Keymap used when `anychange-mode' is active.") 124 | 125 | ;;;###autoload 126 | (define-minor-mode anychange-mode 127 | "Toggle `anychange-mode': Track changes across all buffers and 128 | then navigate back to them." 129 | :global t 130 | :lighter " Ⓐ" 131 | :keymap anychange-mode-map 132 | :group 'anychange 133 | (if anychange-mode 134 | (add-hook 'after-change-functions #'anychange-buffer-change-hook) 135 | (remove-hook 'after-change-functions #'anychange-buffer-change-hook))) 136 | 137 | (provide 'anychange) 138 | 139 | (setq anychange-position 0 140 | anychange-positions-list nil) 141 | 142 | ;;; anychange.el ends here 143 | . 144 | -------------------------------------------------------------------------------- /bin/build-utils: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Homebrew packages to install. 4 | # DO_BREW_PACKAGES=() 5 | 6 | # Homebrew casks to install. 7 | # DO_BREW_CASKS=() 8 | 9 | # Shared libraries. Their paths are found from homebrew and added to environment 10 | # variables. 11 | # DO_LIBS=() 12 | 13 | # Repository URL to fetch, if the repo does not exist already. 14 | # DO_REPO_URL="" 15 | 16 | # Repository directory to use, if the repo does not exist already. Defaults to 17 | # the repo name. 18 | DO_REPO_DIRECTORY="${DO_REPO_DIRECTORY:-$(basename ${DO_REPO_URL%.git})}" 19 | 20 | # Repository branch or ref to checkout. 21 | DO_REPO_BRANCH="${DO_REPO_BRANCH:-master}" 22 | 23 | # Options to pass to the configure script. 24 | # DO_CONFIGURE_OPTS=() 25 | 26 | # Print the given arguments out in a nice heading 27 | do_heading() { 28 | printf "\n\033[38;5;013m * %s \033[0m \n\n" "$*" 29 | } 30 | 31 | # Return exit code 0 if $1 is the same as any of the rest of the arguments 32 | contains() { 33 | local e match="$1" 34 | shift 35 | for e in "$@"; do [ "$e" = "$match" ] && return 0; done 36 | return 1 37 | } 38 | 39 | # Print the number of CPU cores on the local machine 40 | do_how_many_cores() { 41 | case "$(uname)" in 42 | Darwin) 43 | sysctl -n hw.ncpu 44 | ;; 45 | Linux) 46 | awk '/^processor/ {++n} END {print n}' /proc/cpuinfo 47 | ;; 48 | esac 49 | } 50 | 51 | # Ensure the given homebrew packages are installed and up to date 52 | # brew_ensure [ cask ] dep1 [ dep2 ] [ ... ] 53 | do_brew_ensure() { 54 | do_heading "Ensuring Homebrew packages..." 55 | echo "$@" 56 | local brew_type installed required missing outdated upgrade 57 | brew_type="$1" 58 | shift 59 | 60 | # List installed packages 61 | installed=($(brew list $brew_type -q)) 62 | # strip off the "@version" part, e.g. "python@3.9" becomes "python" 63 | for i in "${!installed[@]}"; do 64 | installed[$i]="${installed[$i]%%@*}" 65 | done 66 | 67 | # List missing packages (required but not installed) 68 | required=("$@") 69 | missing=() 70 | for p in "${required[@]}"; do 71 | contains "$p" "${installed[@]}" || missing+=("$p") 72 | done 73 | 74 | # Install missing packages 75 | if [ -n "${missing[*]:-}" ]; then 76 | echo "Installing packages: ${missing[*]}" 77 | brew install $brew_type "${missing[@]}" 78 | fi 79 | 80 | # List of outdated packages 81 | outdated="$(brew outdated $brew_type -q)" 82 | upgrade=() 83 | for p in "${required[@]}"; do 84 | contains "$p" "${outdated[@]}" && upgrade+=("$p") 85 | done 86 | 87 | # Upgrade out outdated packages 88 | if [ -n "${upgrade[*]:-}" ]; then 89 | echo "Upgrading packages: ${upgrade[*]}" 90 | brew upgrade $brew_type "${upgrade[@]}" 91 | fi 92 | } 93 | 94 | # Fetch the source repository 95 | do_fetch() { 96 | local url="${1:-$DO_REPO_URL}" 97 | local target="${2:-$DO_REPO_DIRECTORY}" 98 | local base="$DO_BASE_DIR" 99 | local ref="${3:-$DO_REPO_BRANCH}" 100 | 101 | do_heading "Fetching source for $target..." 102 | echo "Source directory will be $base/$target" 103 | 104 | mkdir -p "$base" 105 | pushd "$base" 106 | 107 | [ -d "$base/$target" ] || git clone "$url" "$target" 108 | pushd "$base/$target" 109 | 110 | git fetch 111 | git checkout "$ref" 112 | git clean -dxf 113 | git pull --ff-only origin "$DO_REPO_BRANCH" 114 | } 115 | 116 | # Prepare environment variables 117 | do_vars() { 118 | CFLAGS="${CFLAGS:--Ofast -march=native -pipe -falign-functions=64 -fomit-frame-pointer -funit-at-a-time -fforce-addr -mfpmath=sse -fno-finite-math-only -fstack-check}" 119 | CPPFLAGS="${CPPFLAGS:-}" 120 | LDFLAGS="${LDFLAGS:--Wl}" 121 | PKG_CONFIG_PATH="${PKG_CONFIG_PATH:-}" 122 | 123 | for dir in "${DO_LIBS[@]}"; do 124 | # If it's not a directory, it's a homebrew package--use its directory 125 | # prefix. 126 | [ -d "$dir" ] || dir="$(brew --prefix $dir)" 127 | 128 | CPPFLAGS="${CPPFLAGS} -I${dir}/include" 129 | LDFLAGS="${LDFLAGS} -L${dir}/lib" 130 | PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${dir}/lib/pkgconfig" 131 | done 132 | 133 | printf "\nCOMPILATION VARIABLES:\n\n" 134 | 135 | export CFLAGS 136 | printf "CFLAGS:\n%s\n" "$CFLAGS" 137 | export CPPFLAGS 138 | printf "CPPFLAGS:\n%s\n" "$CPPFLAGS" 139 | export LDFLAGS 140 | printf "LDFLAGS:\n%s\n" "$LDFLAGS" 141 | export PKG_CONFIG_PATH 142 | printf "PKG_CONFIG_PATH:\n%s\n" "$PKG_CONFIG_PATH" 143 | } 144 | 145 | # Perform the build 146 | do_build() { 147 | local target="${1:-$DO_REPO_DIRECTORY}" 148 | 149 | do_heading "Building $target..." 150 | 151 | pushd "$DO_BASE_DIR/$target" 152 | 153 | make distclean 154 | [ -f ./autogen.sh ] && ./autogen.sh 155 | echo ./configure "${DO_CONFIGURE_OPTS[*]}" 156 | ./configure "${DO_CONFIGURE_OPTS[@]}" 157 | make -j $DO_CORES 158 | 159 | popd 160 | } 161 | 162 | # Perform the installation 163 | do_install() { 164 | local target="${1:-$DO_REPO_DIRECTORY}" 165 | 166 | do_heading "Installing $target..." 167 | 168 | pushd "$DO_BASE_DIR/$target" 169 | 170 | # Even though we move the binary ourselves, we still need to install to get 171 | # the Emacs Lisp and C source files in the right place. 172 | make -j $DO_CORES install 173 | } 174 | 175 | # Do: ensure, fetch, vars, build, install 176 | do_all() { 177 | # brew install "$HOME/.emacs.d/gcc.rb" --build-from-source 178 | do_brew_ensure --formula "${DO_BREW_PACKAGES[@]}" 179 | do_brew_ensure --cask "${DO_BREW_CASKS[@]}" 180 | 181 | do_fetch 182 | do_vars 183 | do_build 184 | do_install 185 | } 186 | 187 | DO_CORES=${DO_CORES:-$((2 * $(do_how_many_cores)))} 188 | 189 | DO_BASE_DIR="${DO_BASE_DIR:-$HOME/code}" 190 | 191 | DO_REPO_BRANCH="${DO_REPO_BRANCH:-master}" 192 | 193 | DO_DIRECTORY="${DO_DIRECTORY:-$(basename "$DO_REPO_URL")}" 194 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: An Emacs Configuration 2 | 3 | * About 4 | This is my =~/.emacs.d= directory. My primary mode of operating is macOS GUI so things work best on that. It is also compatible (but will have more bugs) with running from a terminal, Linux, and Windows. 5 | 6 | #+CAPTION: Emacs frame with README and Magit windows 7 | [[file:screenshot.png]] 8 | 9 | * Goals 10 | 11 | ** Always fast and responsive. 12 | Nothing should produce unnecessary lag, waiting, or clunkiness. Remote use cases like [[https://www.gnu.org/software/tramp/][TRAMP]] and =ssh= have been ironed out through heavy use. 13 | 14 | ** The appearance should be functional and attractive. 15 | For maximum concentration and ergonomics, there should be no extraneous information. Strip away any distracting lines or unnecessary UI elements. 16 | 17 | ** Optimize startup time. 18 | Slow startup is a barrier to improving this config and it just makes me feel bad. On my machine Emacs starts it up in about 0.8 seconds. 19 | 20 | ** All interfaces should be uniform. 21 | [[https://github.com/abo-abo/swiper][ivy]] and [[http://company-mode.github.io/][company]] are the two main ways to find stuff. 22 | 23 | ** Use normal Emacs key bindings, enhance where appropriate. 24 | Then mix in the most common macOS key bindings so things like =s-c= and =s-w= work like in the rest of the OS. I work heavily with macOS applications like Mail, Calendar, Messages, MS Outlook, Firefox. So it's important that the basic key bindings in those applications have a translation in Emacs. Keep things as consistent and idiomatic as possible while merging these two disparate binding systems. 25 | 26 | ** Extensive customization but no excess. 27 | If I'm not using a package, it's out. But I guess I use a lot of packages. About 180 at last count. Dependencies bump it up to about 210. 28 | 29 | ** Custom Emacs build. 30 | Check out the =setup= script. It builds Emacs from the latest master to enjoy benefits such as performance improvements, better macOS compatibility, image viewing, modules like [[https://github.com/akermu/emacs-libvterm][libvterm]] and [[https://github.com/politza/pdf-tools][pdf-tools]], and new features like =js-jsx-mode= and Jansson support (see [[https://raw.githubusercontent.com/emacs-mirror/emacs/master/etc/NEWS][GNU Emacs NEWS]], or better yet, =M-x view-emacs-news=). 31 | 32 | ** Reproducible and portable configuration 33 | It should work the same way on each machine out of the box with as little fuss as possible. 34 | 35 | ** Frequent updates. 36 | I update frequently and often pick up new functionality from Emacs master. 37 | 38 | ** Utilize [[https://github.com/jwiegley/use-package/tree/master][use-package]] and [[https://github.com/raxod502/straight.el][straight.el]] for as much configuration as possible. 39 | Defer loading non-core packages until they are needed. Straight is an amazing package management system that does everything better than package.el. 40 | 41 | * Features 42 | 43 | ** A nice mode-line. 44 | It's a customized version of [[https://gitlab.com/jessieh/mood-line][mood-line]]. 45 | 46 | ** Custom theming system. 47 | Quickly (and completely!) switch between light and dark, or any other themes. A key part of this is realizing that many packages have dependencies on the theme so it is not nearly enough to call =load-theme=. So there is some additional logic to automatically change as much as possible and use a hook to do the rest. 48 | 49 | ** Editing 50 | Highlights include 51 | - [[http://www.dr-qubit.org/undo-tree.html][undo-tree]] 52 | - [[https://github.com/magnars/multiple-cursors.el][Multiple cursors]] 53 | - [[https://github.com/DogLooksGood/parinfer-mode][Parinfer]] 54 | - [[https://github.com/Fuco1/smartparens][Smartparens]] with lots of customization 55 | 56 | ** Environment support. 57 | macOS doesn't hand its applications much in terms of standard UNIX environment configuration. I have a customized setup to initialize the environment by running =bash --login= and pulling in its environment variables. The details are handled in my non-Emacs [[https://github.com/mnewt/dotfiles][dotfiles]]. 58 | 59 | ** Eshell. 60 | There is nothing like Eshell. It is truly amazing the way it enables powerful new ways to handle remote systems administration. There are extensive customizations to make it seamless with the rest of the environment and UI. 61 | 62 | ** File operations and Dired. 63 | Make the uniquely powerful Dired more comfortable, informative, and reliable. Heavily reliant on the awesome [[https://github.com/Fuco1/dired-hacks][dired-hacks]] collection. 64 | 65 | ** Persistence. 66 | Manage window configuration and buffer persistence using [[https://github.com/nex3/perspective-el][perspective]], undo, command completions, recent files, etc. 67 | 68 | ** Navigation. 69 | Some innovative intra- and extra- buffer navigation strategies. Check out =winner-wrong-window= for an example. 70 | 71 | ** Version control. 72 | [[https://magit.vc/][Magit]] and more. But mostly magit. 73 | 74 | ** Language support. 75 | - [[https://www.gnu.org/software/bash/][Bash]] and [[https://en.wikipedia.org/wiki/Unix_shell#Bourne_shell][friends]] 76 | - [[https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/][C#]] by way of [[https://github.com/OmniSharp/omnisharp-emacs][omnisharp]] 77 | - [[https://clojure.org/][Clojure]] and [[https://clojurescript.org/][Clojurescript]] 78 | - [[https://lisp-lang.org/][Common Lisp]] 79 | - [[https://www.docker.com/][Docker]] 80 | - [[https://www.gnu.org/software/emacs/manual/html_mono/eintr.html][Emacs Lisp]] of course, with many enhancements. 81 | - [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]] 82 | - [[https://www.lua.org/][Lua]] 83 | - [[https://orgmode.org/][Org]] customizations 84 | - [[https://www.python.org/][Python]] 85 | - [[https://www.ruby-lang.org/][Ruby]] 86 | - [[https://schemers.org/][Scheme]] (mostly [[https://call-cc.org/][CHICKEN]]) 87 | - [[http://web-mode.org/][Web]], Javascript and [[https://reactjs.org/][React]] development 88 | 89 | ** Other nice things 90 | - Automatic code formatting, mostly with [[https://github.com/raxod502/apheleia][Apheleia]] 91 | - [[https://www.flycheck.org/en/latest/][flycheck]] 92 | - Log viewing - Docker, systemd, and others 93 | - Nested language support with [[https://github.com/aaronbieber/fence-edit.el][fence-edit]] 94 | 95 | * Supported Emacs versions 96 | Attempts have been made to make this config compatible with 27+. However, they are half-hearted and some features are 28+ or maybe even 29+ only. 97 | 98 | * Install 99 | 1. Clone the repo: 100 | #+begin_src sh 101 | git clone https://github.com/mnewt/dotemacs.git ~/.emacs.d 102 | #+end_src 103 | 2. Install Emacs 104 | You can install Emacs using your method of choice but I build it from source: 105 | #+begin_src sh 106 | ~/.emacs.d/bin/build-emacs 107 | #+end_src 108 | 3. Start Emacs. The first run will install lots of stuff so it will take a few minutes. 109 | 110 | ** Emacs Build 111 | You can build Emacs with support for compiling packages to machine code using [[https://akrl.sdf.org/gccemacs.html][GCCEmacs]]. As of Emacs 28, this is an optional feature that seems to work pretty well. Currently, I'm using [[https://github.com/d12frosted/homebrew-emacs-plus][homebrew-emacs-plus]]. 112 | 113 | See the [[file:bin/install-emacs-plus][install-emacs-plus]] script for how I build Emacs on macOS. 114 | 115 | ** TODO 116 | See [[file:TODO.org][TODO.org]]. 117 | 118 | ** License 119 | The Free Software Foundation may control certain pieces of this by virtue of them being contributed to Emacs or a package in ELPA. The rest is basically in the public domain. See the LICENSE file for details. 120 | -------------------------------------------------------------------------------- /bin/build-emacs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | DO_BASE_DIR="$HOME/code" 6 | 7 | DO_REPO_URL="https://git.savannah.gnu.org/git/emacs.git" 8 | 9 | DO_REPO_DIRECTORY="emacs" 10 | 11 | DO_REPO_BRANCH="feature/native-comp" 12 | 13 | DO_APP_LOCATION="$HOME/Emacs.app" 14 | 15 | DO_BREW_PACKAGES=( 16 | # Build dependencies 17 | autoconf 18 | cairo 19 | cmake 20 | gcc 21 | libgccjit 22 | gnupg 23 | gnutls 24 | # The macOS build uses the Cocoa image library 25 | # imagemagick 26 | jansson 27 | librsvg 28 | libvterm 29 | libxml2 30 | pkg-config 31 | tree-sitter 32 | sqlite 33 | 34 | # Runtime dependencies 35 | coreutils 36 | texinfo 37 | ripgrep 38 | fd 39 | node 40 | python 41 | shfmt 42 | ) 43 | 44 | DO_BREW_CASKS=( 45 | mactex 46 | ) 47 | 48 | DO_LIBS=( 49 | $(xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr 50 | coreutils 51 | gcc 52 | libgccjit 53 | giflib 54 | gnutls 55 | jansson 56 | jpeg 57 | libtiff 58 | libxml2 59 | ) 60 | 61 | DO_CONFIGURE_OPTS=( 62 | --config-cache 63 | --without-pop 64 | --with-ns 65 | --disable-ns-self-contained 66 | --with-cairo 67 | # --with-imagemagick 68 | --with-modules 69 | --with-nativecomp 70 | --with-xml2 71 | --with-gnutls 72 | --with-json 73 | --without-compress-install 74 | --program-transform-name='s/^ctags$/ctags.emacs/' 75 | ) 76 | 77 | # native-comp optimization 78 | export BYTE_COMPILE_EXTRA_FLAGS="--eval '(setq comp-speed 2)'" 79 | # Ensure we can find the Homebrew installed version of libgccjit 80 | export LIBRARY_PATH="$(brew --prefix libgccjit)/lib/gcc/10/" 81 | 82 | # Print the given arguments out in a nice heading 83 | do_heading() { 84 | printf "\n\033[38;5;013m * %s \033[0m \n\n" "$*" 85 | } 86 | 87 | # Return exit code 0 if $1 is the same as any of the rest of the arguments 88 | contains() { 89 | local e match="$1" 90 | shift 91 | for e in "$@"; do [ "$e" = "$match" ] && return 0; done 92 | return 1 93 | } 94 | 95 | # Print the number of CPU cores on the local machine 96 | do_how_many_cores() { 97 | case "$(uname)" in 98 | Darwin) 99 | sysctl -n hw.ncpu 100 | ;; 101 | Linux) 102 | awk '/^processor/ {++n} END {print n}' /proc/cpuinfo 103 | ;; 104 | esac 105 | } 106 | 107 | # Ensure the given homebrew packages are installed and up to date 108 | # brew_ensure [ cask ] dep1 [ dep2 ] [ ... ] 109 | do_brew_ensure() { 110 | do_heading "Ensuring Homebrew packages..." 111 | echo "$@" 112 | local brew_type installed required missing outdated upgrade 113 | brew_type="$1" 114 | shift 115 | 116 | # List installed packages 117 | installed=($(brew list $brew_type -q)) 118 | # strip off the "@version" part, e.g. "python@3.9" becomes "python" 119 | for i in "${!installed[@]}"; do 120 | installed[$i]="${installed[$i]%%@*}" 121 | done 122 | 123 | # List missing packages (required but not installed) 124 | required=("$@") 125 | missing=() 126 | for p in "${required[@]}"; do 127 | contains "$p" "${installed[@]}" || missing+=("$p") 128 | done 129 | 130 | # Install missing packages 131 | if [ -n "${missing[*]:-}" ]; then 132 | echo "Installing packages: ${missing[*]}" 133 | brew install $brew_type "${missing[@]}" 134 | fi 135 | 136 | # List of outdated packages 137 | outdated="$(brew outdated $brew_type -q)" 138 | upgrade=() 139 | for p in "${required[@]}"; do 140 | contains "$p" "${outdated[@]}" && upgrade+=("$p") 141 | done 142 | 143 | # Upgrade out outdated packages 144 | if [ -n "${upgrade[*]:-}" ]; then 145 | echo "Upgrading packages: ${upgrade[*]}" 146 | brew upgrade $brew_type "${upgrade[@]}" 147 | fi 148 | } 149 | 150 | # Fetch the source repository 151 | do_fetch() { 152 | local url="${1:-$DO_REPO_URL}" 153 | local target="${2:-$DO_REPO_DIRECTORY}" 154 | local base="$DO_BASE_DIR" 155 | local ref="${3:-$DO_REPO_BRANCH}" 156 | 157 | do_heading "Fetching source for $target..." 158 | echo "Source directory will be $base/$target" 159 | 160 | mkdir -p "$base" 161 | pushd "$base" 162 | 163 | [ -d "$base/$target" ] || git clone "$url" "$target" 164 | pushd "$base/$target" 165 | 166 | git fetch 167 | git checkout "$ref" 168 | git clean -dxf 169 | git pull --ff-only origin "$DO_REPO_BRANCH" 170 | } 171 | 172 | # Prepare environment variables 173 | do_vars() { 174 | CFLAGS="${CFLAGS:--Ofast -march=native -pipe -falign-functions=64 -fomit-frame-pointer -funit-at-a-time -fforce-addr -mfpmath=sse -fno-finite-math-only -fstack-check}" 175 | CPPFLAGS="${CPPFLAGS:-}" 176 | LDFLAGS="${LDFLAGS:--Wl}" 177 | PKG_CONFIG_PATH="${PKG_CONFIG_PATH:-}" 178 | 179 | for dir in "${DO_LIBS[@]}"; do 180 | # If it's not a directory, it's a homebrew package--use its directory 181 | # prefix. 182 | [ -d "$dir" ] || dir="$(brew --prefix $dir)" 183 | 184 | CPPFLAGS="${CPPFLAGS} -I${dir}/include" 185 | LDFLAGS="${LDFLAGS} -L${dir}/lib" 186 | PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${dir}/lib/pkgconfig" 187 | done 188 | 189 | printf "\nCOMPILATION VARIABLES:\n\n" 190 | 191 | export CFLAGS 192 | printf "CFLAGS:\n%s\n" "$CFLAGS" 193 | export CPPFLAGS 194 | printf "CPPFLAGS:\n%s\n" "$CPPFLAGS" 195 | export LDFLAGS 196 | printf "LDFLAGS:\n%s\n" "$LDFLAGS" 197 | export PKG_CONFIG_PATH 198 | printf "PKG_CONFIG_PATH:\n%s\n" "$PKG_CONFIG_PATH" 199 | } 200 | 201 | # Perform the build 202 | do_build() { 203 | local target="${1:-$DO_REPO_DIRECTORY}" 204 | 205 | do_heading "Building $target..." 206 | 207 | pushd "$DO_BASE_DIR/$target" 208 | 209 | make distclean 210 | [ -f ./autogen.sh ] && ./autogen.sh 211 | echo ./configure "${DO_CONFIGURE_OPTS[*]}" 212 | ./configure "${DO_CONFIGURE_OPTS[@]}" 213 | make -j $DO_CORES 214 | 215 | popd 216 | } 217 | 218 | # Install Emacs from the build directory 219 | do_install() { 220 | local target="${1:-$DO_REPO_DIRECTORY}" 221 | do_heading "Installing $target..." 222 | 223 | # Ensure the directory to which we will install Emacs exists and has the correct 224 | # permissions set. 225 | local libexec=/usr/local/libexec/emacs 226 | if [ ! -d $libexec ]; then 227 | sudo mkdir -p $libexec 228 | chown $USER $libexec 229 | fi 230 | 231 | pushd "$DO_BASE_DIR/$target" 232 | 233 | # Even though we move the binary ourselves, we still need to install to get 234 | # the Emacs Lisp and C source files in the right place. We need `ginstall` 235 | # because the `-D` option is not available in the macOS BSD version. 236 | sudo make INSTALL_PROGRAM="$(which ginstall) -c" -j $DO_CORES install 237 | 238 | # Back up the old version of the app bundle, if it exists. 239 | [ -d "$DO_APP_LOCATION" ] && mv "$DO_APP_LOCATION" "$DO_APP_LOCATION.bak" 240 | 241 | # Move Emacs.app to its final location. 242 | mv "nextstep/Emacs.app" "$DO_APP_LOCATION" 243 | 244 | # Copy native compiled ELisp into Emacs.app, which is where it is expected by 245 | # the configure and compilation process. 246 | cp -R "native-lisp" "$DO_APP_LOCATION/Contents/" 247 | 248 | # Add annotations to Emacs.app to request access to special directories. 249 | plist="$DO_APP_LOCATION/Contents/Info.plist" 250 | sudo defaults write "$plist" NSDesktopFolderUsageDescription \ 251 | -string "Emacs requires permission to access the Desktop folder." 252 | sudo defaults write "$plist" NSDocumentsFolderUsageDescription \ 253 | -string "Emacs requires permission to access the Documents folder." 254 | sudo defaults write "$plist" NSDownloadsFolderUsageDescription \ 255 | -string "Emacs requires permission to access the Downloads folder." 256 | sudo defaults write "$plist" NSRemovableVolumesUsageDescription \ 257 | -string "Emacs requires permission to access files on Removable Volumes." 258 | # This will convert the Info.plist from xml representation to the binary form. 259 | # Now we convert it back. 260 | sudo plutil -convert xml1 "$plist" 261 | 262 | echo "Emacs is installed. See README.org for notes on fixing the menu." 263 | 264 | popd 265 | } 266 | 267 | DO_CORES=$((2 * $(do_how_many_cores))) 268 | 269 | do_brew_ensure --formula "${DO_BREW_PACKAGES[@]}" 270 | do_brew_ensure --cask "${DO_BREW_CASKS[@]}" 271 | 272 | do_fetch 273 | do_vars 274 | do_build 275 | do_install 276 | -------------------------------------------------------------------------------- /lisp/m-mail.el: -------------------------------------------------------------------------------- 1 | ;;; m-mail.el --- My mail config -*- lexical-binding: t -*- 2 | 3 | ;; Author: Matthew Sojourner Newton 4 | ;; Maintainer: Matthew Sojourner Newton 5 | ;; Version: version 6 | ;; Package-Requires: (dependencies) 7 | ;; Homepage: homepage 8 | ;; Keywords: keywords 9 | 10 | 11 | ;; This file is not part of GNU Emacs 12 | 13 | ;; This file is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation; either version 3, or (at your option) 16 | ;; any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; For a full copy of the GNU General Public License 24 | ;; see . 25 | 26 | 27 | ;;; Commentary: 28 | 29 | ;; My mail config 30 | 31 | ;;; Code: 32 | 33 | (use-package mu4e 34 | :disabled 35 | :ensure nil 36 | :load-path "/usr/local/share/emacs/site-lisp/mu4e" 37 | :custom 38 | ;; Tell Emacs to send using `mu4e' 39 | (mail-user-agent 'mu4e-user-agent) 40 | ;; Don't save message to Sent Messages. The sending server saves the message. 41 | (mu4e-sent-messages-behavior 'delete) 42 | ;; Allow for updating mail using 'U' in the main view: 43 | (mu4e-get-mail-command "mbsync -aq") 44 | ;; Non-nil value retrieves mail and updates the database 45 | (mu4e-update-interval 1800) 46 | ;; Enable inline images in message view. 47 | (mu4e-view-show-images t) 48 | ;; Prevent duplicate UID issues with mbsync 49 | (mu4e-change-filenames-when-moving t) 50 | ;; Start with the first (default) context; 51 | ;; default is to ask-if-none (ask when there's no context yet, and none match) 52 | (mu4e-context-policy 'pick-first) 53 | ;; Use fancy unicode chars for marks and threads 54 | (mu4e-use-fancy-chars nil) 55 | (mu4e-attachment-dir "~/Downloads") 56 | (mu4e-compose-dont-reply-to-self t) 57 | ;; convert org mode to HTML automatically 58 | (org-mu4e-convert-to-html t) 59 | (mu4e-headers-fields `((:human-date . 12) 60 | (:flags . 5) 61 | (:from-or-to . 20) 62 | (:subject . nil))) 63 | (mu4e-view-show-images t) 64 | 65 | :config 66 | (use-package smtpmail 67 | :ensure nil 68 | :custom 69 | ;; Setting `smtp-queue-mail' to t prevents messages from being sent unless 70 | ;; they are explicitly flushed. nil is the default. 71 | ;; TODO: Queue messages and periodically flush them. 72 | (smtpmail-queue-mail nil) 73 | (send-mail-function 'async-smtpmail-send-it) 74 | (message-send-mail-function 'async-smtpmail-send-it) 75 | 76 | :config 77 | (defvar mail-queue-directory (expand-file-name "~/.mail/queue/") 78 | "Mail queue for SMTP outgoing mail messages.") 79 | (setq smtpmail-queue-dir (expand-file-name "cur" mail-queue-directory)) 80 | 81 | (shell-command (format "mu mkdir %s" mail-queue-directory)) 82 | (shell-command (format "touch %s" (expand-file-name ".noindex" mail-queue-directory)))) 83 | 84 | 85 | (add-to-list 'mu4e-headers-actions 86 | '("Browse message" . mu4e-action-view-in-browser) t) 87 | (add-to-list 'mu4e-view-actions 88 | '("Browse message" . mu4e-action-view-in-browser) t) 89 | 90 | (defun org-capture-mu4e () 91 | (interactive) 92 | "Capture a TODO item via email." 93 | (org-capture nil "m")) 94 | 95 | (use-package mu4e-maildirs-extension 96 | :config 97 | (mu4e-maildirs-extension)) 98 | 99 | ;; Attachments from dired 100 | ;; http://www.djcbsoftware.nl/code/mu/mu4e/Dired.html#Dired 101 | 102 | ;; make the `gnus-dired-mail-buffers' function also work on 103 | ;; message-mode derived modes, such as mu4e-compose-mode 104 | (defun gnus-dired-mail-buffers () 105 | "Return a list of active-bg message buffers." 106 | (require 'gnus-dired) 107 | (let (buffers) 108 | (save-current-buffer 109 | (dolist (buffer (buffer-list t)) 110 | (set-buffer buffer) 111 | (when (and (derived-mode-p 'message-mode) 112 | (null message-sent-message-via)) 113 | (push (buffer-name buffer) buffers)))) 114 | (nreverse buffers))) 115 | 116 | (setq gnus-dired-mail-mode 'mu4e-user-agent) 117 | 118 | (defvar mu4e-last-window nil 119 | "The last mu4e window. 120 | 121 | Used by `mu4e-toggle'.") 122 | 123 | (defun mu4e-toggle (arg) 124 | "Toggle the display of `mu4e'." 125 | (interactive "P") 126 | (let ((r "*mu4e-")) 127 | (if (string-match-p r (buffer-name)) 128 | (progn (setq mu4e-last-window (buffer-name)) 129 | (mapc #'bury-buffer (filter-buffers-by-name r)) 130 | (previous-buffer)) 131 | (if arg 132 | (call-interactively #'mu4e) 133 | (if mu4e-last-window 134 | (switch-to-buffer mu4e-last-window) 135 | (mu4e-headers-search (mu4e-get-bookmark-query ?u))))))) 136 | 137 | (defun mu4e-update-mail-and-index-in-background (arg) 138 | "Run `mu4e-update-mail-and-index' in the background." 139 | (interactive "P") 140 | (mu4e-update-mail-and-index (not arg))) 141 | 142 | (defun mu4e-mark-execute-all-no-confirm (arg) 143 | "Run `mu4e-mark-execute-all' with no confirmation." 144 | (interactive "P") 145 | (mu4e-mark-execute-all (not arg))) 146 | 147 | (defun mbsync-start-imap-watch () 148 | "Start the imap-watch daemon." 149 | (interactive) 150 | (call-process "imap-watch" nil (get-buffer-create "*imap-watch*"))) 151 | 152 | (defhydra hydra-mu4e-headers (:color blue :hint nil) 153 | " 154 | ^General^ | ^Search^ | _!_ read | _#_ deferred | ^Switches^ 155 | -^^----------+-^^-----------------| _?_ unread | _%_ pattern |-^^------------------ 156 | _n_ next | _s_ search | _r_ refile | _&_ custom | _O_ sorting 157 | _p_ prev | _S_ edit prev qry | _u_ unmk | _+_ flag | _P_ threading 158 | _]_ n unred | _/_ narrow search | _U_ unmk * | _-_ unflag | _Q_ full-search 159 | _[_ p unred | _b_ search bkmk | _d_ trash | _T_ thr | _V_ skip dups 160 | _y_ sw view | _B_ edit bkmk | _D_ delete | _t_ subthr | _W_ include-related 161 | _R_ reply | _{_ previous qry | _m_ move |-^^-------------+-^^------------------ 162 | _C_ compose | _}_ next query | _a_ action | _|_ thru shl | _`_ update, reindex 163 | _F_ forward | _C-+_ show more | _A_ mk4actn | _H_ help | _;_ context-switch 164 | _o_ org-cap | _C--_ show less | _*_ *thing | _q_ quit hdrs | _j_ jump2maildir" 165 | 166 | ;; general 167 | ("n" mu4e-headers-next) 168 | ("p" mu4e-headers-previous) 169 | ("[" mu4e-select-next-unread) 170 | ("]" mu4e-select-previous-unread) 171 | ("y" mu4e-select-other-view) 172 | ("R" mu4e-compose-reply) 173 | ("C" mu4e-compose-new) 174 | ("F" mu4e-compose-forward) 175 | ("o" org-capture-mu4e) 176 | 177 | ;; search 178 | ("s" mu4e-headers-search) 179 | ("S" mu4e-headers-search-edit) 180 | ("/" mu4e-headers-search-narrow) 181 | ("b" mu4e-headers-search-bookmark) 182 | ("B" mu4e-headers-search-bookmark-edit) 183 | ("{" mu4e-headers-query-prev) 184 | ("}" mu4e-headers-query-next) 185 | ("C-+" mu4e-headers-split-view-grow) 186 | ("C--" mu4e-headers-split-view-shrink) 187 | 188 | ;; mark stuff 189 | ("!" mu4e-headers-mark-for-read) 190 | ("?" mu4e-headers-mark-for-unread) 191 | ("r" mu4e-headers-mark-for-refile) 192 | ("u" mu4e-headers-mark-for-unmark) 193 | ("U" mu4e-mark-unmark-all) 194 | ("d" mu4e-headers-mark-for-trash) 195 | ("D" mu4e-headers-mark-for-delete) 196 | ("m" mu4e-headers-mark-for-move) 197 | ("a" mu4e-headers-action) 198 | ("A" mu4e-headers-mark-for-action) 199 | ("*" mu4e-headers-mark-for-something) 200 | 201 | ("#" mu4e-mark-resolve-deferred-marks) 202 | ("%" mu4e-headers-mark-pattern) 203 | ("&" mu4e-headers-mark-custom) 204 | ("+" mu4e-headers-mark-for-flag) 205 | ("-" mu4e-headers-mark-for-unflag) 206 | ("t" mu4e-headers-mark-subthread) 207 | ("T" mu4e-headers-mark-thread) 208 | 209 | ;; miscellany 210 | ("q" mu4e~headers-quit-buffer) 211 | ("H" mu4e-display-manual) 212 | ("|" mu4e-view-pipe) 213 | 214 | ;; switches 215 | ("O" mu4e-headers-change-sorting) 216 | ("P" mu4e-headers-toggle-threading) 217 | ("Q" mu4e-headers-toggle-full-search) 218 | ("V" mu4e-headers-toggle-skip-duplicates) 219 | ("W" mu4e-headers-toggle-include-related) 220 | 221 | ;; more miscellany 222 | ("`" mu4e-update-mail-and-index) 223 | (";" mu4e-context-switch) 224 | ("j" mu4e~headers-jump-to-maildir) 225 | 226 | ("." nil)) 227 | 228 | :hook 229 | (mu4e-main-mode-hook . mu4e-maildirs-extension) 230 | (dired-mode-hook . turn-on-gnus-dired-mode) 231 | (mu4e-compose-mode-hook . turn-off-auto-fill) 232 | 233 | 234 | :bind 235 | ("s-m" . mu4e-toggle) 236 | ("M-m m" . mu4e) 237 | (:map mu4e-main-mode-map 238 | ("G" . mu4e-update-mail-and-index-in-background)) 239 | (:map mu4e-headers-mode-map 240 | ("G" . mu4e-update-mail-and-index-in-background) 241 | ("x" . mu4e-mark-execute-all-no-confirm) 242 | ("." . hydra-mu4e-headers/body)) 243 | (:map mu4e-view-mode-map 244 | ("" . mu4e~view-browse-url-from-binding) 245 | ("" . shr-next-link) 246 | ("" . shr-previous-link))) 247 | 248 | (provide 'm-mail) 249 | 250 | ;;; m-mail.el ends here 251 | -------------------------------------------------------------------------------- /lisp/highlight-things.el: -------------------------------------------------------------------------------- 1 | ;;; highlight-things.el --- Highlight interesting things -*- lexical-binding: t -*- 2 | 3 | ;; Author: Matthew Newton 4 | ;; Maintainer: Matthew Newton 5 | ;; Version: 0.0.1 6 | ;; Package-Requires: (dash "1.0") 7 | ;; Homepage: homepage 8 | ;; Keywords: font-lock 9 | 10 | 11 | ;; This file is not part of GNU Emacs 12 | 13 | ;; This file is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation; either version 3, or (at your option) 16 | ;; any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; For a full copy of the GNU General Public License 24 | ;; see . 25 | 26 | 27 | ;;; Commentary: 28 | 29 | ;; Ideas to filter Eshell output: 30 | ;; 1. Eshell feeds each line of program output to 31 | ;; `eshell-preoutput-filter-functions'. This is easy but there is no obvious way 32 | ;; to handle multi-line semantics. 33 | ;; 2. Highlight region from `(eshell-beginning-of-output)' to 34 | ;; `(eshell-end-of-output)'. See https://emacs.stackexchange.com/a/5408. 35 | ;; 3. Maybe use `org-src-font-lock-fontify-block'. 36 | 37 | ;; The downsides to #2 and #3 are that the output is not highlighted until after 38 | ;; the command finishes. 39 | 40 | ;; See https://www.emacswiki.org/emacs?SampleMode for mode boilerplate. Note 41 | ;; that we are purposefully not using a derived mode because this is not an 42 | ;; interactive mode and we don't want to do useless, user-oriented stuff on a 43 | ;; temporary buffer. We are going for the most minimal mode possible and it is 44 | ;; only intended to be used programmatically so we will eschew the normal 45 | ;; boilerplate and mode features like hooks and maps. 46 | 47 | ;; * TODO: Add a bunch of defcustom's for font-face's of various matches. 48 | ;; * TODO: Key in `hlt-keywords-commands' should be a regexp, not a string. 49 | ;; * Implement this in the appropriate functions. 50 | ;; * TODO: log file 51 | ;; * TODO: Minor mode for netutils 52 | ;; * TODO: Keywords: Emacs Lisp functions 53 | ;; * TODO: Keywords: Eshell aliases 54 | ;; * TODO: Minor mode replace `hl-todo' 55 | ;; * TODO: Highlight `shell' output 56 | ;; * TODO: Highlight Eshell command line while it's being typed 57 | ;; * TODO: Test/support font-lock's matching groups. Do we already support it? 58 | 59 | ;;; Code: 60 | 61 | (defgroup hlt nil 62 | "Highlight things in buffers." 63 | :prefix "hlt-" 64 | :group 'emacs 65 | :link '(url-link "https://gitlab.com/mnewt/highlight-things")) 66 | 67 | (defcustom hlt-face-property-type 'face 68 | "The property type to use when adding color properties to a string. Normally, you would want this to be 'face, but `font-lock' can overwrite that. If that is happening, try setting this to 'font-lock-face to trick `font-lock' into leaving the coloring alone." 69 | :type '(symbol) 70 | :group 'hlt 71 | :options '(face font-lock-face)) 72 | 73 | (defcustom hlt-keyword-executable 74 | `(,(concat "\\<" 75 | (regexp-opt (-remove (apply-partially #'string-prefix-p ".") 76 | (-distinct (mapcan #'directory-files exec-path))) 77 | t) 78 | "\\>") 79 | font-lock-keyword-face) 80 | "Executables found in $PATH." 81 | :type '(list) 82 | :group 'hlt) 83 | 84 | (defcustom hlt-keyword-eshell-builtin 85 | (let ((builtins '())) 86 | (mapatoms (lambda (x) 87 | (and (fboundp x) 88 | (string-prefix-p "eshell/" (symbol-name x)) 89 | (add-to-list 'builtins (substring (symbol-name x) 7))))) 90 | builtins) 91 | "Eshell built in functions, which are just Emacs functions with the `eshell/' prefix" 92 | :type '(list) 93 | :group 'hlt) 94 | 95 | ;; (defcustom hlt-keyword-eshell-alias 96 | ;; (message (eshell/alias))) 97 | 98 | (defcustom hlt-keyword-ipv4-address 99 | (list "\\b\\(?:\\(?:25[0-5]\\|2[0-4][0-9]\\|[01]?[0-9][0-9]?\\)\\.\\)\\{3\\}\\(?:25[0-5]\\|2[0-4][0-9]\\|[01]?[0-9][0-9]?\\)\\b" 100 | font-lock-string-face) 101 | "IPv4 address." 102 | :type '(list) 103 | :group 'hlt) 104 | 105 | (defcustom hlt-keyword-mac-address 106 | (list "\\b[0-9a-f]\\{2\\}\\(?::[0-9a-f]\\{2\\}\\)\\{5\\}\\b" 107 | font-lock-string-face) 108 | "MAC address." 109 | :type '(list) 110 | :group 'hlt) 111 | 112 | (defcustom hlt-keyword-ipv6-address 113 | (list "\\b[0-9a-f]\\{4\\}\\(?:::?[0-9a-f]\\{1,4\\}\\)+\\b" 114 | font-lock-string-face) 115 | "IPv6 address." 116 | :type '(list) 117 | :group 'hlt) 118 | 119 | (defcustom hlt-keyword-hostname 120 | (list "\\b\\(?:\\b[[:alnum:]]+\\.\\)+\\(?:[[:alnum:]]+\\)\\b" 121 | font-lock-string-face) 122 | "Hostname." 123 | :type '(list) 124 | :group 'hlt) 125 | 126 | (defcustom hlt-keyword-human-readable-number 127 | (list "\\b[0-9.]+[kKmMgGtT]\\b" 128 | font-lock-builtin-face) 129 | "Human readable number, like the file size in `ls -h'." 130 | :type '(list) 131 | :group 'hlt) 132 | 133 | (defcustom hlt-keywords-default '() 134 | "Keywords which are used when the current command does not have 135 | a definition in `hlt-keywords-commands'" 136 | :type '(list) 137 | :group 'hlt) 138 | 139 | (defcustom hlt-keywords-common '() 140 | "Keywords which are used for all commands." 141 | :type '(list) 142 | :group 'hlt) 143 | 144 | (defcustom hlt-keywords-commands 145 | `(("df" ((,(regexp-opt '("Filesystem" "Size" "Used" "Avail" "Use%" "Mounted on") t) font-lock-keyword-face) 146 | ,hlt-keyword-human-readable-number)) 147 | ("ping" (,hlt-keyword-ipv4-address ,hlt-keyword-ipv6-address ,hlt-keyword-hostname))) 148 | "Alist. Keyws are strings representing the currently running 149 | command. Values are regexp/font-face pairs in standard 150 | font-lock format." 151 | :type '(list) 152 | :group 'hlt) 153 | 154 | (defvar-local hlt--current-program nil 155 | "The current program to use for `hlt-mode'.") 156 | 157 | (defun hlt--get-keywords (command) 158 | "Return keywords for specified COMMAND, or default ones if none 159 | are defined in `hlt-keywords-commands'." 160 | (or 161 | (cadr (assoc command hlt-keywords-commands)) 162 | hlt-keywords-default)) 163 | 164 | (defun hlt-fontify-region (start end &optional keywords) 165 | "Like `font-lock-fontify-keywords-region' only can be run 166 | without an activated mode." 167 | ;; Blatantly lie to `font-lock', telling it things have already been set up. 168 | (let ((font-lock-set-defaults t) 169 | (font-lock-keywords (or keywords hlt-keywords-default))) 170 | (font-lock-fontify-keywords-region start end))) 171 | 172 | (defun hlt-fontify-string (string &optional keywords) 173 | "Use `hlt-keywords-default' to fontify the STRING, sort of 174 | like `font-lock-fontify-keywords-region' would, only simpler." 175 | (dolist (pair (or keywords hlt-keywords-default)) 176 | (let ((re (car pair)) 177 | (face (cdr pair)) 178 | (start 0)) 179 | (while (string-match re string start) 180 | (add-text-properties (match-beginning 0) (match-end 0) 181 | `(,hlt-face-property-type ,face) string) 182 | (setq start (match-end 0))))) 183 | string) 184 | 185 | (defun hlt-fontify-string-for-command (command string) 186 | "Like `hlt-fontify-string' but does special stuff for the 187 | specified COMMAND. If COMMAND is nil then fontify using `hlt-keywords-default'" 188 | (hlt-fontify-string string 189 | (append hlt-keywords-common 190 | (hlt--get-keywords command)))) 191 | 192 | (defun hlt-eshell-preoutput-filter (string) 193 | "Add this to `eshell-preoutput-filter-functions' to highlight Eshell output." 194 | (hlt-fontify-string-for-command eshell-last-command-name string)) 195 | 196 | ;; (defun hlt-fontify-text (text) 197 | ;; (with-temp-buffer 198 | ;; (erase-buffer) 199 | ;; (insert text) 200 | ;; ;; TODO: Maybe replace `font-lock.el' machinery with our own, for speed. 201 | ;; (setq major-mode 'hlt-mode) 202 | ;; (set (make-local-variable 'font-lock-defaults) '(hlt-keywords-default)) 203 | ;; (font-lock-default-function 'hlt-mode) 204 | ;; ;; I can't think of any use cases for syntactic fontification so we skip it. 205 | ;; (font-lock-fontify-keywords-region (point-min) (point-max) nil) 206 | ;; (if hlt-use-font-lock-face 207 | ;; (hlt-replace-face-with-font-lock-face (buffer-string)) 208 | ;; (buffer-string)))) 209 | 210 | ;; (defun hlt-replace-face-with-font-lock-face (text) 211 | ;; "Replace property 'face with 'font-lock-face so that font-lock 212 | ;; functions don't strip it." 213 | ;; (let ((pos 0)) 214 | ;; (while (setq next (next-single-property-change pos 'face text)) 215 | ;; (put-text-property pos next 'font-lock-face (get-text-property pos 'face text) text) 216 | ;; (setq pos next)) 217 | ;; (add-text-properties 0 (length text) '(fontified t) text) 218 | ;; text)) 219 | 220 | (defvar hlt-mode-map (make-sparse-keymap) 221 | "Keymap for `hlt-mode'.") 222 | 223 | ;;;###autoload 224 | (define-minor-mode hlt-mode 225 | "Highlight things." 226 | :lighter "" 227 | :keymap hlt-mode-map 228 | :group 'hlt 229 | (if hlt-mode 230 | (font-lock-add-keywords nil (hlt--get-keywords hlt--current-command) t) 231 | (font-lock-remove-keywords nil (hlt--get-keywords hlt--current-command))) 232 | (when font-lock-mode 233 | (if (and (fboundp 'font-lock-flush) 234 | (fboundp 'font-lock-ensure)) 235 | (save-restriction 236 | (widen) 237 | (font-lock-flush) 238 | (font-lock-ensure)) 239 | (with-no-warnings 240 | (font-lock-fontify-buffer))))) 241 | 242 | ;;;###autoload 243 | (defun hlt-mode () 244 | "Major mode for highlighting things. It is not intended to be 245 | used interactively, only programmatically." 246 | (kill-all-local-variables) 247 | (set (make-local-variable 'font-lock-defaults) '(hlt-keywords-default)) 248 | (setq major-mode 'hlt-mode)) 249 | 250 | (add-hook 'eshell-preoutput-filter-functions 'hlt-eshell-preoutput-filter) 251 | ;; (remove-hook 'eshell-preoutput-filter-functions 'hlt-eshell-preoutput-filter) 252 | 253 | (provide 'highlight-things) 254 | 255 | ;;; highlight-things.el ends here 256 | -------------------------------------------------------------------------------- /lisp/tagger.el: -------------------------------------------------------------------------------- 1 | ;;; tagger.el --- Organize Org Files by Tag -*- lexical-binding: t -*- 2 | 3 | ;; Author: Matthew Sojourner Newton 4 | ;; Maintainer: Matthew Sojourner Newton 5 | ;; Version: 0.1 6 | ;; Package-Requires: ((emacs "25.1") (spinner "1.0.0")) 7 | ;; Homepage: https://github.com/mnewt/tagger 8 | ;; Keywords: docs files hypermedia outlines 9 | 10 | 11 | ;; This file is not part of GNU Emacs 12 | 13 | ;; This file is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation; either version 3, or (at your option) 16 | ;; any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; For a full copy of the GNU General Public License 24 | ;; see . 25 | 26 | 27 | ;;; Commentary: 28 | 29 | ;; Browse and find Org files by tag, with a loose definition of tag. 30 | 31 | ;;; Code: 32 | 33 | (require 'org) 34 | (require 'spinner) 35 | 36 | (defgroup tagger nil 37 | "Search in Org files by tag." 38 | :group 'org 39 | :prefix "tagger-") 40 | 41 | (defcustom tagger-list-functions 42 | '(tagger-list-org-tags 43 | tagger-list-org-headlines 44 | tagger-list-org-file-names) 45 | "List of functions which produce a list of tags." 46 | :type '(repeat symbol)) 47 | 48 | (defcustom tagger-tag-line-regexp 49 | "^(?:\*+|#\+FILETAGS:|#\+filetags:)[\t\s]+.*:[a-z]+:" 50 | "Regexp matching a line with tags on it. 51 | 52 | Note that this regexp is passed to ripgrep." 53 | :type 'string) 54 | 55 | (defvar-local tagger--entries (make-hash-table :test #'equal) 56 | "Internal representation of tag entries.") 57 | 58 | (defvar-local tagger--tag-buffer nil 59 | "The main tag listing buffer.") 60 | 61 | (defvar-local tagger--tag nil 62 | "The current tag for the `tagger-line-list-mode' buffer.") 63 | 64 | (defvar-local tagger--lines nil 65 | "The current line entries for the `tagger-line-list-mode' buffer.") 66 | 67 | ;; TODO: Eliminate this. 68 | (defvar tagger-link-keymap 69 | (let ((map (make-sparse-keymap))) 70 | (define-key map (kbd "RET") #'tagger-follow-link) 71 | (define-key map (kbd "") #'tagger-follow-link) 72 | (define-key map [mouse-1] #'tagger-follow-link) 73 | map) 74 | "Keys in effect when point is over a link in Tagger.") 75 | 76 | (defun tagger--help-echo (_window _object pos) 77 | "Display `help-echo' for WINDOW OBJECT and POS." 78 | (when-let ((button (button-at pos))) 79 | (message "tag: %s" (button-get button :link)))) 80 | 81 | (defun tagger-tag-button-action (button) 82 | "The default action for BUTTON type `tagger-tag'." 83 | (tagger-list-lines (button-get button :tag))) 84 | 85 | (define-button-type 'tagger-tag 86 | :help-echo #'tagger--help-echo 87 | :action #'tagger-tag-button-action) 88 | 89 | (defun tagger--add-tag-line (tag file line) 90 | "Add TAG, FILE, and LINE to the tagger entries. 91 | 92 | Also update `tabulated-list-entries' and refresh the display. 93 | 94 | This function is intended to be invoked in a Tagger buffer." 95 | (puthash tag 96 | (cons (list file line) (gethash tag tagger--entries)) 97 | tagger--entries) 98 | (setf (alist-get tag tabulated-list-entries nil nil 'equal) 99 | (list (vector `(,tag :type tagger-tag :tag ,tag) 100 | (number-to-string (length (gethash tag tagger--entries)))))) 101 | (tabulated-list-print 'remember-pos)) 102 | 103 | (defun tagger-refresh-tags () 104 | "Recompute the list of tags for the Tagger buffer." 105 | (spinner-start) 106 | (setq tagger--entries (make-hash-table :test #'equal) 107 | tabulated-list-entries nil) 108 | (tagger--list-ripgrep)) 109 | 110 | (defun tagger-tags () 111 | "Return a list of tags." 112 | (with-current-buffer "*Tagger*" 113 | (let (keys) 114 | (maphash (lambda (key _value) (push key keys)) tagger--entries) 115 | keys))) 116 | 117 | (defun tagger-find-file (filename line) 118 | "Find FILENAME and open it at LINE." 119 | (message "tagger-find-file") 120 | (find-file filename) 121 | (goto-char (point-min)) 122 | (forward-line line)) 123 | 124 | (defun tagger-link-button-action (button) 125 | "The action for BUTTON type `tagger-link'." 126 | (tagger-find-file (button-get button :file) (button-get button :line))) 127 | 128 | (define-button-type 'tagger-link 129 | :help-echo #'tagger-help-echo 130 | :action #'tagger-link-button-action) 131 | 132 | (defun tagger-refresh-lines () 133 | "Recompute the list of lines for the Tagger Lines buffer." 134 | (setq tagger--lines 135 | (gethash tagger--tag (buffer-local-value 'tagger--entries (get-buffer "*Tagger*")))) 136 | (setq tabulated-list-entries 137 | (mapcar (lambda (entry) 138 | (cl-destructuring-bind (file line) entry 139 | (list entry 140 | (vector `(,(file-name-nondirectory file) 141 | :type tagger-link 142 | :file ,file 143 | :line ,line) 144 | "" (number-to-string line))))) 145 | tagger--lines)) 146 | (tabulated-list-print 'remember-pos)) 147 | 148 | (defun tagger--grep-file-and-line-number () 149 | "Get the file name and line number at point. 150 | 151 | The expected format is like that from 'grep -n'. 152 | 153 | After parsing, leave point at the start of the line contents." 154 | (beginning-of-line) 155 | (list (buffer-substring-no-properties (point) (1- (search-forward ":"))) 156 | (string-to-number 157 | (buffer-substring-no-properties (point) (1- (search-forward ":")))))) 158 | 159 | (defun tagger--grep-org-line () 160 | "Process one line of grep results. 161 | 162 | Results are expected to be in the current buffer, with each line 163 | in the format: 164 | 165 | :: 166 | 167 | Return a list of the form: 168 | (\"tag\" \"/path/to/file\" ) 169 | 170 | Return nil if there is no complete line at point, or there are no 171 | tags on the line. 172 | 173 | Note that a line is considered complete if it has a newline after 174 | it." 175 | ;; Only process a line if it is complete. 176 | (when (char-after (line-end-position)) 177 | (cl-destructuring-bind (file-name line-number) (tagger--grep-file-and-line-number) 178 | (let (tags) 179 | (while (search-forward-regexp ":\\([[:word:]]+\\):" (line-end-position) 'noerror) 180 | (push (buffer-substring-no-properties (match-beginning 1) (match-end 1)) tags) 181 | (backward-char)) 182 | (search-forward "\n") 183 | (mapcar (lambda (tag) (list tag file-name line-number)) tags))))) 184 | 185 | (defun tagger--async-filter-grep-org (proc string) 186 | "Process output STRING from PROC `tagger--list-ripgrep'." 187 | (when (buffer-live-p (process-buffer proc)) 188 | (with-current-buffer (process-buffer proc) 189 | (save-excursion 190 | ;; Insert the text, advancing the process marker. 191 | (goto-char (process-mark proc)) 192 | (insert string) 193 | (set-marker (process-mark proc) (point))) 194 | ;; Add results to the list, starting where we left off. 195 | (let (entries) 196 | (while (setq entries (tagger--grep-org-line)) 197 | (with-current-buffer (process-get proc :tagger-buffer) 198 | (dolist (entry entries) 199 | (tagger--add-tag-line (car entry) (cadr entry) (caddr entry)))))) 200 | (goto-char (process-mark proc))))) 201 | 202 | (defun tagger--async-sentinel (proc event) 203 | "Process output for PROC after EVENT." 204 | (if (equal event "finished\n") 205 | (with-current-buffer (process-get proc :tagger-buffer) 206 | (spinner-stop)) 207 | (message "Tagger received signal: %s" (string-trim event)))) 208 | 209 | (defun tagger--list-ripgrep () 210 | "Find tags and update the Tagger buffer." 211 | (let ((buffer (get-buffer-create " *tagger-list-ripgrep*")) 212 | proc) 213 | (with-current-buffer buffer 214 | (erase-buffer)) 215 | (setq proc 216 | (make-process 217 | :name "tagger-list-ripgrep" 218 | :buffer buffer 219 | :command `("rg" 220 | "--color=never" 221 | "--no-heading" 222 | "--with-filename" 223 | "(?:\\*+|#\\+FILETAGS:|#\\+filetags:)[\\t\\s]+.*:[a-z]+:" 224 | "-g" 225 | "*.org" 226 | ,(expand-file-name org-directory)) 227 | :noquery t 228 | :filter #'tagger--async-filter-grep-org 229 | :sentinel #'tagger--async-sentinel)) 230 | (process-put proc :tagger-buffer (get-buffer "*Tagger*")))) 231 | 232 | (defun tagger-string-number-greaterp (a b) 233 | "Return non-nil if A is greater than B." 234 | (> (string-to-number (aref (cadr a) 1)) 235 | (string-to-number (aref (cadr b) 1)))) 236 | 237 | (define-derived-mode tagger-tag-list-mode tabulated-list-mode "Tagger Tags" 238 | "Major mode for listing tags." 239 | (setq tabulated-list-format [("tag" 12 t) 240 | ("number" 7 tagger-string-number-greaterp (:right-align t))] 241 | tabulated-list-padding 2 242 | tabulated-list-sort-key '("tag" . nil)) 243 | (tabulated-list-init-header) 244 | (add-hook 'tabulated-list-revert-hook #'tagger-refresh-tags nil t)) 245 | 246 | ;;;###autoload 247 | (defun tagger () 248 | "Display a list of tags." 249 | (interactive) 250 | (switch-to-buffer (get-buffer-create "*Tagger*")) 251 | (tagger-tag-list-mode) 252 | (tagger-refresh-tags)) 253 | 254 | (define-derived-mode tagger-line-list-mode tabulated-list-mode "Tagger Lines" 255 | "Major mode for listing tagged lines." 256 | (setq tabulated-list-format [("file" 20 t) 257 | ("modified" 10 t) 258 | ("line" 50 t)] 259 | tabulated-list-padding 2 260 | tabulated-list-sort-key '("file" . nil)) 261 | (tabulated-list-init-header) 262 | (add-hook 'tabulated-list-revert-hook #'tagger-refresh-lines nil t)) 263 | 264 | ;;;###autoload 265 | (defun tagger-list-lines (tag) 266 | "Display a list of lines for a TAG." 267 | (interactive (list (completing-read "List lines for tag: " (tagger-tags)))) 268 | (switch-to-buffer (get-buffer-create (format "*Tagger Tag: %s*" tag))) 269 | (tagger-line-list-mode) 270 | (setq tagger--tag tag) 271 | (tagger-refresh-lines)) 272 | 273 | (provide 'tagger) 274 | 275 | ;;; tagger.el ends here 276 | -------------------------------------------------------------------------------- /lisp/exec-path-from-shell.el: -------------------------------------------------------------------------------- 1 | ;;; exec-path-from-shell.el --- Get environment variables such as $PATH from the shell -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2012-2014 Steve Purcell 4 | 5 | ;; Author: Steve Purcell 6 | ;; Keywords: unix, environment 7 | ;; URL: https://github.com/purcell/exec-path-from-shell 8 | ;; Package-Version: 2.2 9 | ;; Package-Requires: ((emacs "24.4")) 10 | 11 | ;; This file is not part of GNU Emacs. 12 | 13 | ;; This file is free software: you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation, either version 3 of the License, or 16 | ;; (at your option) any later version. 17 | 18 | ;; This file is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with this file. If not, see . 25 | 26 | ;;; Commentary: 27 | 28 | ;; On OS X (and perhaps elsewhere) the $PATH environment variable and 29 | ;; `exec-path' used by a windowed Emacs instance will usually be the 30 | ;; system-wide default path, rather than that seen in a terminal 31 | ;; window. 32 | 33 | ;; This library allows the user to set Emacs' `exec-path' and $PATH 34 | ;; from the shell path, so that `shell-command', `compile' and the 35 | ;; like work as expected. 36 | 37 | ;; It also allows other environment variables to be retrieved from the 38 | ;; shell, so that Emacs will see the same values you get in a terminal. 39 | 40 | ;; If you use a non-POSIX-standard shell like "tcsh" or "fish", your 41 | ;; shell will be asked to execute "sh" as a subshell in order to print 42 | ;; out the variables in a format which can be reliably parsed. "sh" 43 | ;; must be a POSIX-compliant shell in this case. 44 | 45 | ;; Note that shell variables which have not been exported as 46 | ;; environment variables (e.g. using the "export" keyword) may not be 47 | ;; visible to `exec-path-from-shell'. 48 | 49 | ;; Installation: 50 | 51 | ;; ELPA packages are available on Marmalade and MELPA. Alternatively, 52 | ;; place this file on a directory in your `load-path', and explicitly 53 | ;; require it. 54 | 55 | ;; Usage: 56 | ;; 57 | ;; (require 'exec-path-from-shell) ;; if not using the ELPA package 58 | ;; (exec-path-from-shell-initialize) 59 | ;; 60 | ;; Customize `exec-path-from-shell-variables' to modify the list of 61 | ;; variables imported. 62 | ;; 63 | ;; If you use your Emacs config on other platforms, you can instead 64 | ;; make initialization conditional as follows: 65 | ;; 66 | ;; (when (memq window-system '(mac ns)) 67 | ;; (exec-path-from-shell-initialize)) 68 | ;; 69 | ;; Alternatively, you can use `exec-path-from-shell-copy-envs' or 70 | ;; `exec-path-from-shell-copy-env' directly, e.g. 71 | ;; 72 | ;; (exec-path-from-shell-copy-env "PYTHONPATH") 73 | 74 | ;;; Code: 75 | 76 | ;; Satisfy the byte compiler 77 | (eval-when-compile (require 'eshell)) 78 | (require 'cl-lib) 79 | (require 'json) 80 | 81 | (defgroup exec-path-from-shell nil 82 | "Make Emacs use shell-defined values for $PATH etc." 83 | :prefix "exec-path-from-shell-" 84 | :group 'environment) 85 | 86 | (defcustom exec-path-from-shell-variables 87 | '("PATH" "MANPATH") 88 | "List of environment variables which are copied from the shell." 89 | :type '(repeat (string :tag "Environment variable")) 90 | :group 'exec-path-from-shell) 91 | 92 | (defcustom exec-path-from-shell-warn-duration-millis 500 93 | "Print a warning if shell execution takes longer than this many milliseconds." 94 | :type 'integer) 95 | 96 | (defcustom exec-path-from-shell-shell-name nil 97 | "If non-nil, use this shell executable. 98 | Otherwise, use either `shell-file-name' (if set), or the value of 99 | the SHELL environment variable." 100 | :type '(choice 101 | (file :tag "Shell executable") 102 | (const :tag "Use `shell-file-name' or $SHELL" nil)) 103 | :group 'exec-path-from-shell) 104 | 105 | (defvar exec-path-from-shell-debug nil 106 | "Display debug info when non-nil.") 107 | 108 | (defun exec-path-from-shell--double-quote (s) 109 | "Double-quote S, escaping any double-quotes already contained in it." 110 | (concat "\"" (replace-regexp-in-string "\"" "\\\\\"" s) "\"")) 111 | 112 | (defun exec-path-from-shell--shell () 113 | "Return the shell to use. 114 | See documentation for `exec-path-from-shell-shell-name'." 115 | (or 116 | exec-path-from-shell-shell-name 117 | shell-file-name 118 | (getenv "SHELL") 119 | (error "SHELL environment variable is unset"))) 120 | 121 | (defcustom exec-path-from-shell-arguments 122 | (let ((shell (exec-path-from-shell--shell))) 123 | (if (string-match-p "t?csh$" shell) 124 | (list "-d") 125 | (if (string-match-p "fish" shell) 126 | (list "-l") 127 | (list "-l" "-i")))) 128 | "Additional arguments to pass to the shell. 129 | 130 | The default value denotes an interactive login shell." 131 | :type '(repeat (string :tag "Shell argument")) 132 | :group 'exec-path-from-shell) 133 | 134 | (defun exec-path-from-shell--debug (msg &rest args) 135 | "Print MSG and ARGS like `message', but only if debug output is enabled." 136 | (when exec-path-from-shell-debug 137 | (apply 'message msg args))) 138 | 139 | (defun exec-path-from-shell--nushell-p (shell) 140 | "Return non-nil if SHELL is nu." 141 | (string-match-p "nu$" shell)) 142 | 143 | (defun exec-path-from-shell--standard-shell-p (shell) 144 | "Return non-nil iff SHELL supports the standard ${VAR-default} syntax." 145 | (not (string-match-p "\\(fish\\|nu\\|t?csh\\)$" shell))) 146 | 147 | (defmacro exec-path-from-shell--warn-duration (&rest body) 148 | "Evaluate BODY and warn if execution duration exceeds a time limit. 149 | The limit is given by `exec-path-from-shell-warn-duration-millis'." 150 | (let ((start-time (cl-gensym)) 151 | (duration-millis (cl-gensym))) 152 | `(let ((,start-time (current-time))) 153 | (prog1 154 | (progn ,@body) 155 | (let ((,duration-millis (* 1000.0 (float-time (time-subtract (current-time) ,start-time))))) 156 | (if (> ,duration-millis exec-path-from-shell-warn-duration-millis) 157 | (message "Warning: exec-path-from-shell execution took %dms. See the README for tips on reducing this." ,duration-millis) 158 | (exec-path-from-shell--debug "Shell execution took %dms" ,duration-millis))))))) 159 | 160 | (defun exec-path-from-shell-printf (str &optional args) 161 | "Return the result of printing STR in the user's shell. 162 | 163 | Executes the shell as interactive login shell. 164 | 165 | STR is inserted literally in a single-quoted argument to printf, 166 | and may therefore contain backslashed escape sequences understood 167 | by printf. 168 | 169 | ARGS is an optional list of args which will be inserted by printf 170 | in place of any % placeholders in STR. ARGS are not automatically 171 | shell-escaped, so they may contain $ etc." 172 | (let* ((printf-bin (or (executable-find "printf") "printf")) 173 | (printf-command 174 | (concat (shell-quote-argument printf-bin) 175 | " '__RESULT\\000" str "\\000__RESULT' " 176 | (mapconcat #'exec-path-from-shell--double-quote args " "))) 177 | (shell (exec-path-from-shell--shell)) 178 | (shell-args (append exec-path-from-shell-arguments 179 | (list "-c" 180 | (if (exec-path-from-shell--standard-shell-p shell) 181 | printf-command 182 | (concat "sh -c " (shell-quote-argument printf-command))))))) 183 | (with-temp-buffer 184 | (exec-path-from-shell--debug "Invoking shell %s with args %S" shell shell-args) 185 | (let ((exit-code (exec-path-from-shell--warn-duration 186 | (apply #'call-process shell nil t nil shell-args)))) 187 | (exec-path-from-shell--debug "Shell printed: %S" (buffer-string)) 188 | (unless (zerop exit-code) 189 | (error "Non-zero exit code from shell %s invoked with args %S. Output was:\n%S" 190 | shell shell-args (buffer-string)))) 191 | (goto-char (point-min)) 192 | (if (re-search-forward "__RESULT\0\\(.*\\)\0__RESULT" nil t) 193 | (match-string 1) 194 | (error "Expected printf output from shell, but got: %S" (buffer-string)))))) 195 | 196 | (defun exec-path-from-shell-getenvs--nushell (names) 197 | "Use nushell to get the value of env vars with the given NAMES. 198 | 199 | Execute the shell according to `exec-path-from-shell-arguments'. 200 | The result is a list of (NAME . VALUE) pairs." 201 | (let* ((shell (exec-path-from-shell--shell)) 202 | (expr (format "[ %s ] | to json" 203 | (string-join 204 | (mapcar (lambda (name) 205 | (format "$env.%s?" (exec-path-from-shell--double-quote name))) 206 | names) 207 | ", "))) 208 | (shell-args (append exec-path-from-shell-arguments (list "-c" expr)))) 209 | (with-temp-buffer 210 | (exec-path-from-shell--debug "Invoking shell %s with args %S" shell shell-args) 211 | (let ((exit-code (exec-path-from-shell--warn-duration 212 | (apply #'call-process shell nil t nil shell-args)))) 213 | (exec-path-from-shell--debug "Shell printed: %S" (buffer-string)) 214 | (unless (zerop exit-code) 215 | (error "Non-zero exit code from shell %s invoked with args %S. Output was:\n%S" 216 | shell shell-args (buffer-string)))) 217 | (goto-char (point-min)) 218 | (let ((json-array-type 'list) 219 | (json-null :null)) 220 | (let ((values (json-read-array)) 221 | result) 222 | (while values 223 | (let ((value (car values))) 224 | (push (cons (car names) 225 | (unless (eq :null value) 226 | (if (listp value) 227 | (string-join value path-separator) 228 | value))) 229 | result)) 230 | (setq values (cdr values) 231 | names (cdr names))) 232 | result))))) 233 | 234 | (defun exec-path-from-shell-getenvs (names) 235 | "Get the environment variables with NAMES from the user's shell. 236 | 237 | Execute the shell according to `exec-path-from-shell-arguments'. 238 | The result is a list of (NAME . VALUE) pairs." 239 | (when (file-remote-p default-directory) 240 | (error "You cannot run exec-path-from-shell from a remote buffer (Tramp, etc.)")) 241 | (if (exec-path-from-shell--nushell-p (exec-path-from-shell--shell)) 242 | (exec-path-from-shell-getenvs--nushell names) 243 | (let* ((random-default (md5 (format "%s%s%s" (emacs-pid) (random) (current-time)))) 244 | (dollar-names (mapcar (lambda (n) (format "${%s-%s}" n random-default)) names)) 245 | (values (split-string (exec-path-from-shell-printf 246 | (mapconcat #'identity (make-list (length names) "%s") "\\000") 247 | dollar-names) "\0"))) 248 | (let (result) 249 | (while names 250 | (prog1 251 | (let ((value (car values))) 252 | (push (cons (car names) 253 | (unless (string-equal random-default value) 254 | value)) 255 | result)) 256 | (setq values (cdr values) 257 | names (cdr names)))) 258 | result)))) 259 | 260 | (defun exec-path-from-shell-getenv (name) 261 | "Get the environment variable NAME from the user's shell. 262 | 263 | Execute the shell as interactive login shell, have it output the 264 | variable of NAME and return this output as string." 265 | (cdr (assoc name (exec-path-from-shell-getenvs (list name))))) 266 | 267 | (defun exec-path-from-shell-setenv (name value) 268 | "Set the value of environment var NAME to VALUE. 269 | Additionally, if NAME is \"PATH\" then also update the 270 | variables `exec-path' and `eshell-path-env'." 271 | (setenv name value) 272 | (when (string-equal "PATH" name) 273 | (setq exec-path (append (parse-colon-path value) (list exec-directory))) 274 | ;; `eshell-path-env' is a buffer local variable, so change its default 275 | ;; value. 276 | (setq-default eshell-path-env value))) 277 | 278 | ;;;###autoload 279 | (defun exec-path-from-shell-copy-envs (names) 280 | "Set the environment variables with NAMES from the user's shell. 281 | 282 | As a special case, if the variable is $PATH, then the variables 283 | `exec-path' and `eshell-path-env' are also set appropriately. 284 | The result is an alist, as described by 285 | `exec-path-from-shell-getenvs'." 286 | (let ((pairs (exec-path-from-shell-getenvs names))) 287 | (mapc (lambda (pair) 288 | (exec-path-from-shell-setenv (car pair) (cdr pair))) 289 | pairs))) 290 | 291 | ;;;###autoload 292 | (defun exec-path-from-shell-copy-env (name) 293 | "Set the environment variable $NAME from the user's shell. 294 | 295 | As a special case, if the variable is $PATH, then the variables 296 | `exec-path' and `eshell-path-env' are also set appropriately. 297 | Return the value of the environment variable." 298 | (interactive "sCopy value of which environment variable from shell? ") 299 | (cdar (exec-path-from-shell-copy-envs (list name)))) 300 | 301 | ;;;###autoload 302 | (defun exec-path-from-shell-initialize () 303 | "Initialize environment from the user's shell. 304 | 305 | The values of all the environment variables named in 306 | `exec-path-from-shell-variables' are set from the corresponding 307 | values used in the user's shell." 308 | (interactive) 309 | (exec-path-from-shell-copy-envs exec-path-from-shell-variables)) 310 | 311 | 312 | (provide 'exec-path-from-shell) 313 | 314 | ;; Local Variables: 315 | ;; coding: utf-8 316 | ;; indent-tabs-mode: nil 317 | ;; require-final-newline: t 318 | ;; checkdoc-minor-mode: t 319 | ;; End: 320 | 321 | ;;; exec-path-from-shell.el ends here 322 | -------------------------------------------------------------------------------- /lisp/fiat-color.el: -------------------------------------------------------------------------------- 1 | ;;; fiat-color.el --- Fiat Lux! -*- lexical-binding: t -*- 2 | 3 | ;;; Commentary: 4 | 5 | ;; Fiat Lux, Fiat Nox, Fiat Color! 6 | ;; This is a system that does a few different things to try to make Emacs more 7 | ;; beautiful and colorful. 8 | 9 | ;;; Code: 10 | 11 | (require 'color) 12 | (require 'dash) 13 | 14 | 15 | 16 | ;;; Custom 17 | 18 | (defcustom fiat-theme nil 19 | "The theme to load by default. 20 | 21 | Non-nil\: Load this theme when calling `fiat-theme' without args. 22 | 23 | nil\: Read the last set theme from disk." 24 | :group 'fiat 25 | :type 'symbol) 26 | 27 | (defcustom fiat-theme-fallback 'adwaita 28 | "The theme to load if nothing is configured." 29 | :group 'fiat 30 | :type 'symbol) 31 | 32 | (defcustom fiat-source-faces 33 | '(default mode-line-emphasis mode-line-highlight compilation-mode-line-fail) 34 | "The faces fiat-theme uses to style the mode-line." 35 | :group 'fiat 36 | :type 'list) 37 | 38 | (defcustom fiat-status nil 39 | "The mode to choose when `fiat' is called." 40 | :group 'fiat 41 | :type 'symbol 42 | :options '(nil lux nox)) 43 | 44 | (defcustom fiat-nox-theme 'tango-dark 45 | "The theme activated when calling 'fiat-nox'." 46 | :group 'fiat 47 | :type 'symbol) 48 | 49 | (defcustom fiat-lux-theme 'tango 50 | "The theme activated when calling `fiat-lux'." 51 | :group 'fiat 52 | :type 'symbol) 53 | 54 | (defcustom fiat-show-flycheck nil 55 | "When non-nil, show flycheck status in mode-line." 56 | :group 'fiat 57 | :type 'boolean) 58 | 59 | (defcustom fiat-show-line-and-column nil 60 | "When non-nil, show line number and column in mode-line." 61 | :group 'fiat 62 | :type 'boolean) 63 | 64 | (defcustom fiat-themes (mapcar #'list (custom-available-themes)) 65 | "Alist where car is the theme and cdr can be: 66 | 67 | * A function to run after loading the theme. 68 | * An alist specifying additional arguments. Possible arguments: 69 | ** hook - A function, as above. 70 | ** specs 71 | ** mouse-color 72 | 73 | TODO: Give examples." 74 | :group 'fiat 75 | :type 'list) 76 | 77 | (defcustom fiat-specs-common nil 78 | "List of default face specs to apply when a theme is activated. 79 | This list is for specs that are common to all themes and do not 80 | require any kind of dynamic evaluation (e.g. configuring one face 81 | to inherit from another). For dynamic configurations, add an 82 | alist as the cdr of the alist entry in `fiat-themes'. The 83 | attributes specified in `fiat-themes' overrides these. 84 | 85 | For details on face specs see `defface'." 86 | :group 'fiat 87 | :type 'list) 88 | 89 | (defcustom fiat-config-file 90 | (expand-file-name "fiat-config" user-emacs-directory) 91 | "Fiat settings file. 92 | 93 | It is used to load the last used settings without the need for 94 | `desktop' or a similar external tool." 95 | :group 'fiat 96 | :type 'string) 97 | 98 | (defcustom fiat-save-variables '(fiat-theme fiat-status) 99 | "A list of variables to be saved to disk." 100 | :group 'fiat 101 | :type '(repeat symbol)) 102 | 103 | (defcustom fiat-before-theme-hook nil 104 | "Called from `fiat-theme' before changing themes." 105 | :group 'fiat 106 | :type 'hook) 107 | 108 | (defcustom fiat-after-theme-hook nil 109 | "Called from `fiat-theme' after changing themes." 110 | :group 'fiat 111 | :type 'hook) 112 | 113 | (defcustom fiat-eyebrowse-format "%d:%s" 114 | "Format string for eyebrowse mode-line information." 115 | :group 'fiat 116 | :type 'string) 117 | 118 | (defvar fiat-choose-history nil 119 | "History list for `fiat-theme-choose'.") 120 | 121 | (defvar fiat-selected-window (frame-selected-window) 122 | "Selected window.") 123 | 124 | (defvar fiat--old-mode-line-format nil 125 | "Save the old `mode-line-format'.") 126 | 127 | 128 | ;;; Functions 129 | 130 | (defun fiat--set-selected-window () 131 | "Set the variable `fiat-selected-window' appropriately. 132 | This is used to determine whether the current window is active." 133 | (unless (minibuffer-window-active-p (frame-selected-window)) 134 | (setq fiat-selected-window (frame-selected-window)) 135 | (force-mode-line-update))) 136 | 137 | ;; Executes after a window (not a buffer) has been created, deleted, or moved. 138 | (add-hook 'window-configuration-change-hook #'fiat--set-selected-window) 139 | 140 | ;; Executes after the window manager requests that the user's events 141 | ;; be directed to a different frame. 142 | (advice-add 'handle-switch-frame :after #'fiat--set-selected-window) 143 | 144 | ;; Executes after the `buffer-list' changes. 145 | (add-hook 'buffer-list-update-hook #'fiat--set-selected-window) 146 | 147 | (defun fiat-window-active-p () 148 | "Return whether the current window is active." 149 | (eq fiat-selected-window (selected-window))) 150 | 151 | ;; TODO: Fix `alist-get-all', then fix its dependents. 152 | (defun alist-get-all (key alist &optional default testfn) 153 | "Return a list of *all* the elements of ALIST with matching KEY. 154 | 155 | Modeled on `alist-get', which only returns the first match. 156 | 157 | DEFAULT returns a default value if nothing matches. 158 | 159 | REMOVE is not implemented on account of I don't care and it's 160 | dumb. 161 | 162 | TESTFN is an equality function, *not* an alist function as with 163 | `alist-get'. Default is `eq'." 164 | (let* ((testfn (or testfn #'eq)) 165 | (matches (seq-filter 166 | (lambda (e) (funcall testfn key (car e))) 167 | alist))) 168 | (if matches 169 | (car (mapcar #'cadr matches)) 170 | default))) 171 | 172 | (defun get-attribute (object propname attribute name) 173 | "Get the value of NAME from OBJECT. 174 | 175 | PROPNAME, ATTRIBUTE, and NAME are symbols which drill down to 176 | access the individual Alist element we are after. 177 | 178 | See `get' and `fiat-theme-attribute'." 179 | (let ((name (if (stringp name) (intern name) name))) 180 | (cl-some (lambda (e) (when (and (eq attribute (car e)) (eq name (cadr e))) 181 | (car (cdr (cdr (cdr e)))))) 182 | (get (car object) propname)))) 183 | 184 | (defun fiat-theme-attribute (attribute name) 185 | "Get the ATTRIBUTE identified by NAME from the current theme settings.) 186 | 187 | Example usage 188 | 189 | \(fiat-theme-face-attribute 'default :background\) 190 | 191 | Note that unlike `face-attribute', which gets the *current* 192 | attribute value as displayed on the screen, this function gets 193 | the attribute as specified in the *original* theme settings. This 194 | is useful when you switch themes and want to calculate new faces 195 | derived from existing ones." 196 | (get-attribute custom-enabled-themes 'theme-settings attribute name)) 197 | 198 | (defun fiat-theme-face (face) 199 | "Get the FACE from the current theme. 200 | 201 | See `fiat-theme-attribute'." 202 | (fiat-theme-attribute 'theme-face face)) 203 | 204 | (defun fiat-theme-face-attribute (face attribute) 205 | "Get the ATTRIBUTE of the FACE from the current theme. 206 | 207 | See `fiat-theme-attribute'." 208 | (plist-get (face-spec-choose (fiat-theme-face face)) attribute)) 209 | 210 | (defun fiat-theme-get-value (value) 211 | "Get the VALUE from the current theme. 212 | 213 | See `fiat-theme-attribute'." 214 | (fiat-theme-attribute 'theme-value value)) 215 | 216 | (defun fiat-search-theme-attrs (regexp) 217 | "Return the attributes in the current theme which match the REGEXP." 218 | (seq-filter (lambda (e) (string-match-p regexp (symbol-name (cadr e)))) 219 | (get (car custom-enabled-themes) 'theme-settings))) 220 | 221 | (defun fiat-color-blend (color1 color2 alpha) 222 | "Blends COLOR1 onto COLOR2 with ALPHA. 223 | COLOR1 and COLOR2 should be color names (e.g. \"white\") or RGB 224 | triplet strings (e.g. \"#ff12ec\"). 225 | Alpha should be a float between 0 and 1. 226 | 227 | Stolen from solarized." 228 | (apply #'color-rgb-to-hex 229 | (-zip-with (lambda (it other) 230 | (+ (* alpha it) (* other (- 1 alpha)))) 231 | (color-name-to-rgb color1) 232 | (color-name-to-rgb color2)))) 233 | 234 | ;; TODO: Move this to user config. 235 | (defun fiat-generate-theme-specs () 236 | "Automatically generate theme specs the supplied faces. 237 | 238 | See also `fiat-specs-common'. Advise or override this function 239 | to customize further." 240 | (let* ((active-bg (fiat-theme-face-attribute 'default :background)) 241 | (active-fg (fiat-theme-face-attribute 'default :foreground)) 242 | ;; (highlight-fg (fiat-theme-face-attribute 'highlight :foreground)) 243 | (inactive-bg (fiat-color-blend active-bg active-fg 0.95))) 244 | ;; (inactive-fg (fiat-color-blend active-bg active-fg 0.4))) 245 | `((default ((t :background ,inactive-bg))) 246 | (fiat-inactive-window ((t :background ,inactive-bg))) 247 | (window-highlight-focused-window ((t :background ,active-bg))) 248 | (fringe ((t :background ,inactive-bg))) 249 | (vertical-border ((t :foreground ,inactive-bg))) 250 | (mode-line ((t :box nil 251 | :underline nil))) 252 | ;; :height 1.1 253 | ;; :background ,(fiat-color-blend active-bg active-fg 0.8) 254 | ;; :foreground ,(fiat-color-blend active-fg active-bg 0.9)))) 255 | ;; (mode-line-emphasis ((t :background ,(fiat-color-blend active-bg active-fg 0.7) 256 | ;; :foreground ,active-fg))) 257 | ;; (mode-line-highlight ((t :background ,highlight-fg 258 | ;; :foreground ,active-bg))) 259 | ;; (mode-line-buffer-id ((t :background ,(fiat-color-blend active-bg active-fg 0.2) 260 | ;; :foreground ,inactive-bg 261 | ;; :bold t))) 262 | ;; (compilation-mode-line-fail ((t :inherit highlight))) 263 | (mode-line-inactive ((t :box nil 264 | :underline nil))) 265 | ;; :height 1.1 266 | ;; :background ,(fiat-color-blend inactive-bg inactive-fg 0.8) 267 | ;; :foreground ,(fiat-color-blend active-fg active-bg 0.8)))) 268 | (sp-show-pair-match-face ((t :inherit highlight 269 | :foreground nil 270 | :background nil)))))) 271 | 272 | (defun fiat--save-config () 273 | "Save the configuration to disk." 274 | (with-temp-file fiat-config-file 275 | (prin1 (mapcar (lambda (v) (cons v (symbol-value v))) fiat-save-variables) 276 | (current-buffer)))) 277 | 278 | (defun fiat--load-config () 279 | "Load the configuration from disk." 280 | (when (file-exists-p fiat-config-file) 281 | (with-temp-buffer 282 | (insert-file-contents fiat-config-file) 283 | (goto-char (point-min)) 284 | (cl-loop for (symbol . value) in (read (current-buffer)) do 285 | (set symbol value))))) 286 | 287 | (defun fiat-render-mode-line (left right) 288 | "Return a string string concatenating LEFT and RIGHT. 289 | 290 | Insert spaces between the two so that the string is 291 | `window-total-width' columns wide." 292 | (let* ((left (apply #'concat "" left)) 293 | (right (apply #'concat "" right)) 294 | (reserve (length right))) 295 | (concat left 296 | " " 297 | (propertize " " 298 | 'display `((space :align-to (- right ,reserve)))) 299 | right))) 300 | 301 | (defun fiat-ml-concat (strings &optional separator outside) 302 | "Concatenate the given list of STRINGS. 303 | Optionally interpose with SEPARATOR and surround with OUTSIDE." 304 | (let* ((separator (or separator " ")) 305 | (outside (when outside separator)) 306 | (inside (string-join (cl-remove-if-not (lambda (s) (and s (> (length s) 0))) 307 | strings) 308 | separator))) 309 | (when (> (length inside) 0) 310 | (concat outside inside outside)))) 311 | 312 | (declare-function tramp-file-name-host 'tramp) 313 | (declare-function tramp-dissect-file-name 'tramp) 314 | 315 | (defun fiat-ml-remote-hostname () 316 | "Return the remote hostname for the current buffer. 317 | Return nil if the buffer is local." 318 | (when (file-remote-p default-directory) 319 | (format " %s " (tramp-file-name-host (tramp-dissect-file-name default-directory))))) 320 | 321 | (declare-function term-in-char-mode 'term) 322 | (declare-function term-in-line-mode 'term) 323 | 324 | (defun fiat-ml-term () 325 | "Return the input mode for the buffer if in `term-mode'. 326 | Return nil if otherwise." 327 | (when (eq major-mode 'term-mode) 328 | (cond 329 | ((term-in-char-mode) " [char] ") 330 | ((term-in-line-mode) " [line] ")))) 331 | 332 | (defun fiat-ml-evil () 333 | "Return a mode line string indicating `evil-mode' status. 334 | Return nil if `evil-mode' is not active." 335 | (when (bound-and-true-p evil-state) 336 | (cl-case evil-state 337 | (normal (propertize " NORMAL " 'face 338 | `(:foreground 339 | "black" 340 | :background 341 | ,(aref (fiat-theme-get-value 'ansi-color-names-vector) 2)))) 342 | (insert (propertize " INSERT " 'face 343 | `(:foreground 344 | "white" 345 | :background 346 | ,(aref (fiat-theme-get-value 'ansi-color-names-vector) 4)))) 347 | (t (propertize " EVIL " 'face 348 | `(:foreground 349 | ,(aref (fiat-theme-get-value 'ansi-color-names-vector) 0) 350 | :background 351 | ,(aref (fiat-theme-get-value 'ansi-color-names-vector) 7))))))) 352 | 353 | (declare-function eyebrowse--get 'eyebrowse) 354 | 355 | (defun fiat-eyebrowse-modeline () 356 | "Return a mode line string with status for `eyebrowse-mode'." 357 | (when (bound-and-true-p eyebrowse-mode) 358 | (let ((current-slot (eyebrowse--get 'current-slot)) 359 | (border (propertize " " 'face 'mode-line-emphasis))) 360 | (concat border 361 | (string-join 362 | (mapcar (lambda (wc) 363 | (let* ((number (car wc)) 364 | (name (car (cdr (cdr wc)))) 365 | (label (if (string-blank-p name) 366 | (number-to-string number) 367 | (format fiat-eyebrowse-format number name)))) 368 | (if (= number current-slot) 369 | (propertize label 'face 'mode-line-buffer-id) 370 | (propertize label 'face 'mode-line-emphasis)))) 371 | (eyebrowse--get 'window-configs)) 372 | border) 373 | border)))) 374 | 375 | (defun when-propertize (exp &rest properties) 376 | "Evaluate EXP, which should return a string or nil.. 377 | Propertize the result with the specified PROPERTIES." 378 | (when exp (apply #'propertize exp properties))) 379 | 380 | (defmacro fiat-make-toggle (variable) 381 | "Make a command to toggle a VARIABLE." 382 | `(defun ,(intern (concat (symbol-name variable) "-toggle")) (arg) 383 | ,(format "Toggle the variable %s between `nil' and `t'." variable) 384 | (interactive "P") 385 | (setq ,variable 386 | (if arg 387 | (read-from-minibuffer (format "New value for variable %s: " 388 | ,variable) 389 | nil nil t) 390 | (not ,variable))))) 391 | 392 | (defun fiat-mode-line--enable () 393 | "Enable `fiat-mode-line-mode'." 394 | (fiat-make-toggle fiat-show-flycheck) 395 | (fiat-make-toggle fiat-show-line-and-column) 396 | (setq fiat--old-mode-line-format mode-line-format) 397 | (setq-default 398 | mode-line-format 399 | '((:eval 400 | (if (fiat-window-active-p) 401 | (fiat-render-mode-line 402 | ;; left 403 | (list 404 | ;; (fiat-ml-evil) 405 | (when-propertize (fiat-ml-remote-hostname) 'face 'highlight) 406 | (propertize (concat " " (buffer-name) " ") 'face 'mode-line-buffer-id) 407 | (when (buffer-modified-p) " •") 408 | " " 409 | (format-mode-line mode-name) 410 | " " 411 | (when (memq major-mode '(compilation-mode shell-mode)) 412 | (format-mode-line mode-line-process)) 413 | (when (bound-and-true-p which-function-mode) 414 | (format-mode-line which-func-format))) 415 | ;; right 416 | (list 417 | (when-propertize (fiat-ml-term) 'face 'mode-line-emphasis) 418 | (when (and fiat-show-flycheck 419 | (bound-and-true-p flycheck-mode)) 420 | (concat (substring (flycheck-mode-line-status-text) 1) " ")) 421 | (when (bound-and-true-p workgroups-mode) 422 | (wg-mode-line-string)) 423 | (fiat-eyebrowse-modeline) 424 | (when-propertize 425 | (fiat-ml-concat 426 | (list "" 427 | (when (bound-and-true-p parinfer-mode) 428 | (if (eq 'paren parinfer--mode) "()" "=>")) 429 | (when (buffer-narrowed-p) "n") 430 | (when (bound-and-true-p hs-minor-mode) "hs") 431 | (when (bound-and-true-p outline-minor-mode) "O")) 432 | " " 433 | t) 434 | 'face 'mode-line-buffer-id) 435 | (when fiat-show-line-and-column 436 | (propertize (format-mode-line " %l:%c ") 'face 'mode-line-buffer-id)))) 437 | (fiat-render-mode-line 438 | ;; left 439 | (list 440 | " " 441 | (buffer-name) 442 | " " 443 | (when (buffer-modified-p) " •") 444 | " " 445 | (when (memq major-mode '(compilation-mode shell-mode)) 446 | (substring-no-properties 447 | (format-mode-line mode-line-process)))) 448 | ;; right 449 | nil)))))) 450 | 451 | (defun fiat-mode-line--disable () 452 | "Disable `fiat-mode-line-mode'." 453 | (setq-default mode-line-format fiat--old-mode-line-format) 454 | (fmakunbound 'fiat-show-flycheck-toggle) 455 | (fmakunbound 'fiat-show-line-and-column-toggle)) 456 | 457 | 458 | ;;; Commands 459 | 460 | ;;;###autoload 461 | (defun fiat-theme (&optional theme) 462 | "Switch the current Emacs theme to THEME. 463 | 464 | Handle some housekeeping that comes with switching themes. Set 465 | face specs specific to the theme. Having done that try to prevent 466 | Emacs from barfing fruit salad on the screen." 467 | (setq theme (or theme 468 | fiat-theme 469 | (progn (fiat--load-config) fiat-theme) 470 | fiat-theme-fallback)) 471 | (if theme 472 | (progn 473 | (mapc #'funcall fiat-before-theme-hook) 474 | (custom-set-variables '(custom-enabled-themes nil)) 475 | (load-theme theme t) 476 | (let* ((opts (alist-get theme fiat-themes))) 477 | (setq opts (append opts `((specs ,(fiat-generate-theme-specs))))) 478 | ;; Feed face specs to `custom-set-faces' in reverse because last write 479 | ;; wins. We do it this way so additional specs can be specified when 480 | ;; adding the theme to `fiat-themes'. 481 | (apply #'custom-set-faces 482 | (append fiat-specs-common (reverse (alist-get-all 'specs opts)))) 483 | (let-alist opts 484 | (set-mouse-color 485 | (cond 486 | ((boundp 'mouse-color) mouse-color) 487 | ((equal 'dark (frame-parameter nil 'background-mode)) "white") 488 | (t "black"))) 489 | (when (boundp 'hook) (mapc #'funcall hook)))) 490 | (setq fiat-theme theme) 491 | (fiat--save-config) 492 | (mapc #'funcall fiat-after-theme-hook)) 493 | (user-error "No theme specified in args, `fiat-theme', or `fiat-config-file'"))) 494 | 495 | ;;;###autoload 496 | (defun fiat-theme-choose (theme) 497 | "Interactively choose a THEME from `fiat-themes' and activate it." 498 | (interactive (list (completing-read "Load custom theme: " 499 | (mapcar #'car fiat-themes) 500 | nil t nil 501 | 'fiat-choose-history))) 502 | (fiat-theme (intern theme))) 503 | 504 | ;;;###autoload 505 | (define-minor-mode fiat-mode-line-mode 506 | "Make a mode line whose aesthetics are inspired by vim's lightline." 507 | :group 'fiat 508 | :lighter nil 509 | :global t 510 | (if fiat-mode-line-mode 511 | (fiat-mode-line--enable) 512 | (fiat-mode-line--disable))) 513 | 514 | ;;;###autoload 515 | (defun fiat-lux () 516 | "Let the Emacs and OS themes be switched to light mode." 517 | (interactive) 518 | (setq fiat-status 'lux) 519 | (fiat-theme 'spacemacs-light)) 520 | 521 | ;;;###autoload 522 | (defun fiat-nox () 523 | "Let the Emacs and OS themes be switched to dark mode." 524 | (interactive) 525 | (setq fiat-status 'nox) 526 | (fiat-theme 'spacemacs-dark)) 527 | 528 | ;;;###autoload 529 | (defun fiat () 530 | "Let the Emacs and OS themes be toggled." 531 | (interactive) 532 | (if (eq fiat-status 'nox) (fiat-lux) (fiat-nox))) 533 | 534 | ;; TODO: Make it work reliably. Current problems: 535 | ;; [ ] Top of window and vertical border are in `default' not `fringe' color. 536 | ;; [ ] It often changes only after a couple seconds...? 537 | (defface fiat-inactive-window '((t :inherit fringe)) 538 | "Face to use for default on inactive windows." 539 | :group 'theme) 540 | 541 | (defun fiat-highlight-selected-window () 542 | "Highlight the selected window with a different background color." 543 | (let ((s (selected-window)) 544 | (m (minibuffer-selected-window))) 545 | (walk-windows (lambda (w) 546 | (unless (eq w fiat-selected-window) 547 | (with-current-buffer (window-buffer w) 548 | (buffer-face-set 'fiat-inactive-window))))) 549 | (with-current-buffer (window-buffer s) (buffer-face-set 'default)) 550 | (with-current-buffer (window-buffer m) (buffer-face-set 'default)))) 551 | 552 | (defun fiat-highlight-selected-window--update () 553 | "Update face colors after theme change." 554 | (let ((inactive-bg (fiat-theme-face-attribute 'fringe :background))) 555 | (set-face-attribute 'default :background inactive-bg) 556 | (set-face-attribute 'fringe :background inactive-bg) 557 | (set-face-attribute 'vertical-border :background inactive-bg))) 558 | 559 | (define-minor-mode fiat-highlight-selected-window-mode 560 | "Toggle highlighting of the selected window." 561 | :group 'theme 562 | :lighter nil 563 | :global t 564 | (if fiat-highlight-selected-window-mode 565 | (progn 566 | (add-hook 'fiat-after-theme-hook #'fiat-highlight-selected-window--update) 567 | (add-hook 'buffer-list-update-hook #'fiat-highlight-selected-window)) 568 | (remove-hook 'buffer-list-update-hook #'fiat-highlight-selected-window))) 569 | 570 | (provide 'fiat-color) 571 | 572 | ;;; fiat-color.el ends here 573 | -------------------------------------------------------------------------------- /lisp/ldap-mode.el: -------------------------------------------------------------------------------- 1 | ;;; ldap-mode.el --- major modes for editing LDAP schema and LDIF files 2 | 3 | ;; Copyright (C) 2004 Free Software Foundation, Inc. 4 | 5 | ;; Author: Dave Love 6 | ;; Keywords: data 7 | ;; $Revision: 1.2 $ 8 | ;; URL: http://www.loveshack.ukfsn.org/emacs 9 | 10 | ;; This file is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 2, or (at your option) 13 | ;; any later version. 14 | 15 | ;; This file is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with GNU Emacs; see the file COPYING. If not, write to 22 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 23 | ;; Boston, MA 02111-1307, USA. 24 | 25 | ;;; Commentary: 26 | 27 | ;; LDAP schema is defined in RFC 2252. 28 | ;; LDIF is defined in RFC 2849. 29 | ;; Both sorts of file are defined to use UTF-8 strings and so should 30 | ;; presumably use UTF-8 as the file coding system. 31 | 32 | ;; The RFC ASN.1 description of schemas isn't actually the syntax you 33 | ;; need to put in OpenLDAP schema files -- each object or attribute 34 | ;; definition is prefixed by an option name. See 35 | ;; `ldap-convert-asn1-schema' for conversion. 36 | 37 | ;; This file isn't called ldap.el, since that's part of EUDC. 38 | 39 | ;;; Code: 40 | 41 | (eval-when-compile 42 | ;; Emacs <= 21.3 doesn't expand rx at compile time and has a single arg. 43 | (unless (equal "" (macroexpand '(rx ""))) 44 | (defmacro rx (&rest regexps) 45 | (if (cdr regexps) 46 | (rx-to-string `(and ,@regexps) t) 47 | (rx-to-string (car regexps) t)))) 48 | (defvar imenu-use-markers)) 49 | 50 | ;;;; LDAP 51 | 52 | (defconst ldap-mode-syntax-table 53 | (let ((table (make-syntax-table))) 54 | (modify-syntax-entry ?# "<" table) 55 | (modify-syntax-entry ?\n ">" table) 56 | (modify-syntax-entry ?\" "." table) 57 | (modify-syntax-entry ?' "\"" table) 58 | table)) 59 | 60 | (defconst ldap-font-lock-keywords 61 | `(,(rx 62 | word-start 63 | (or 64 | ;; §S4.2 in RFC 2252 ("NAME" is done below.) 65 | "DESC" "OBSOLETE" "SUP" "EQUALITY" "ORDERING" "SUBSTR" "SYNTAX" 66 | "SINGLE-VALUE" "COLLECTIVE" "NO-USER-MODIFICATION" "USAGE" 67 | ;; §S4.4 68 | "ABSTRACT" "STRUCTURAL" "AUXILIARY" "MUST" "MAY" 69 | ;; §S4.5 70 | "APPLIES" 71 | ;; §6.11 72 | "AUX" "NOT" 73 | ;; These are OpenLDAP option names occurring in schema files. 74 | ;; (Schema elements are part of the syntax of slapd.conf, but 75 | ;; the schemas are normally kept in separate, included files.) 76 | ;; slapd.conf(5) doesn't say the option names are 77 | ;; case-insensitive, but the distributed schema files contain 78 | ;; both "objectclass" and "objectClass", for instance. 79 | "attributetype" "objectclass" "attributeType" "objectClass" 80 | "ditcontentrule" "ditContentRule" 81 | "objectIdentifier" "objectidentifier") 82 | word-end) 83 | (,(rx word-start "NAME" word-end) ; highlighting defined name 84 | (0 'font-lock-keyword-face) 85 | (ldap-match-string-and-skip 86 | (skip-chars-forward "^'(") nil 87 | ;; If we fontified strings this would need to specify overriding. 88 | (1 'font-lock-variable-name-face))) 89 | (,(rx line-start (or "objectIdentifier" "objectidentifier") 90 | (1+ blank) (group (1+ word))) 91 | (1 'font-lock-type-face)) ; highlight defined oid name 92 | (,(rx word-start (group "include") (1+ blank) (group (1+ not-newline))) 93 | (1 'font-lock-keyword-face) (2 'font-lock-string-face)) 94 | ;; §4.2 95 | (,(rx word-start (or "userApplications" "directoryOperation" 96 | "distributedOperation" "dSAOperation") 97 | word-end) 98 | (0 'font-lock-constant-face)))) 99 | 100 | (defconst ldap-syntactic-keywords 101 | `((,(rx not-newline (group "#")) 1 ".")) 102 | "Give `#' punctuation syntax when not at line beginning.") 103 | 104 | (defconst ldap-string-regexp 105 | (rx "'" (group (1+ (or "\\'" (not (any ?'))))) "'")) 106 | 107 | (defun ldap-match-string-and-skip (limit) 108 | "Fontification anchored match function. 109 | Deals with fontifying contents of a string or list of strings." 110 | (condition-case () 111 | (if (and 112 | (if (fboundp 'syntax-ppss) 113 | (not (syntax-ppss-context (syntax-ppss))) ; check if in comment 114 | t) 115 | (re-search-forward ldap-string-regexp limit t)) 116 | (skip-chars-forward " \t\n")) 117 | (error nil))) 118 | 119 | (defvar ldap-is-openldap nil 120 | "Non-nil means the buffer is assumed to visit an OpenLDAP schema file.") 121 | (make-variable-buffer-local 'ldap-is-openldap) 122 | 123 | (defun ldap-create-index () 124 | "`imenu-create-index-function' for LDAP schema." 125 | ;; Look for `NAME 'name'' or `NAME ( 'name' 'name' ...' not in comments. 126 | (let (case-fold-search attributes objects oids) 127 | (save-excursion 128 | (save-restriction 129 | (widen) 130 | (goto-char (point-max)) 131 | (while (re-search-backward "\\" nil t) 132 | (unless (save-match-data ; check if in comment 133 | (if (fboundp 'syntax-ppss) 134 | (syntax-ppss-context (syntax-ppss)) 135 | (save-excursion 136 | (back-to-indentation) 137 | (eq ?# (char-after))))) 138 | (save-excursion 139 | (goto-char (match-end 0)) 140 | (skip-chars-forward " \t\n(") 141 | (let ((pos (if imenu-use-markers (point-marker) (point))) 142 | (attr (save-excursion 143 | (save-match-data 144 | (beginning-of-defun) 145 | (if ldap-is-openldap (forward-sexp)) 146 | (not (ldap-object-def-p)))))) 147 | (while (looking-at ldap-string-regexp) 148 | (save-excursion 149 | (let ((elt (cons (match-string-no-properties 1) pos))) 150 | (if attr 151 | (push elt attributes) 152 | (push elt objects)))) 153 | (goto-char (match-end 0)) 154 | (skip-chars-forward " \t")))))))) 155 | (save-excursion 156 | (save-restriction 157 | (widen) 158 | (goto-char (point-max)) 159 | (let ((case-fold-search t)) 160 | (while (re-search-backward 161 | (rx line-start "objectidentifier" (1+ blank) 162 | (group (1+ word))) 163 | nil t) 164 | (push (cons (match-string-no-properties 1) 165 | (if imenu-use-markers 166 | (copy-marker (match-beginning 1)) 167 | (match-beginning 1))) 168 | oids))))) 169 | (list (if oids (cons "objectidentifiers" oids)) 170 | (if objects (cons "objectclasses" objects)) 171 | (if attributes (cons "attributetypes" attributes))))) 172 | 173 | (defconst ldap-defun-regexp 174 | (rx line-start (or "attributetype" "objectclass") word-end)) 175 | 176 | (define-skeleton ldap-attr-skeleton 177 | "Insert OpenLDAP attribute definition skeleton." 178 | "Name? " "attributetype ( " _ "\n NAME '" str "' " \n _ > ")\n") 179 | 180 | (define-skeleton ldap-obj-skeleton 181 | "Insert OpenLDAP object definition skeleton." 182 | "Name? " "objectclass ( " _ "\n NAME '" str "' " \n _ > ")\n") 183 | 184 | (define-skeleton ldap-change-skeleton 185 | "Insert LDIF change skeleton." 186 | nil "dn: " _ "\nchangetype: " 187 | (completing-read "Change type? " 188 | '(("add") ("delete") ("modify") ("modrdn") ("moddn"))) 189 | ?\n _ ": " _ "\n-\n\n") 190 | 191 | (defvar ldap-mode-map 192 | (let ((map (make-keymap))) 193 | (define-key map "\C-c\C-a" #'ldap-attr-skeleton) 194 | (define-key map "\C-c\C-o" #'ldap-obj-skeleton) 195 | map)) 196 | 197 | (defun ldap-openldap-p () 198 | "Return non-nil if this appears to be an OpenLDAP schema file. 199 | The criterion is the presence of `attributetype' or `objectclass' options." 200 | (save-excursion 201 | (save-restriction 202 | (widen) 203 | (goto-char 1) 204 | (let ((case-fold-search t)) 205 | (re-search-forward (rx line-start 206 | (or "attributetype" "objectclass") 207 | word-end) 208 | 10000 t))))) 209 | 210 | ;; Fixme: Do something better for indentation than `indent-relative'? 211 | 212 | 213 | ;;;### (autoloads nil "ldap-mode" "ldap-mode.el" (0 0 0 0)) 214 | ;;; Generated autoloads from ldap-mode.el 215 | ;;;###autoload 216 | (define-derived-mode ldap-mode fundamental-mode "LDAP" 217 | "Major mode for editing LDAP schema. 218 | 219 | Attributetype and objectclass definitions are treated as defuns. 220 | The mode decides whether or not to treat the buffer as OpenLDAP 221 | schema according to whether it uses the `attributetype' or 222 | `objectclass' keywords to label the definitions. (OpenLDAP 223 | schema definitions are assumed to be collected into separate 224 | files for inclusion into slapd.conf.) 225 | 226 | Imenu, outline and skeleton support are provided. See also 227 | command `ldap-convert-asn1-schema' `ldap-convert-ldif-schema'. 228 | 229 | \\{ldap-mode-map}" 230 | (setq ldap-is-openldap (ldap-openldap-p)) 231 | (set (make-local-variable 'font-lock-defaults) 232 | '(ldap-font-lock-keywords 233 | nil nil nil nil 234 | (font-lock-syntactic-keywords . ldap-syntactic-keywords) 235 | ;; Don't highlight strings. 236 | (font-lock-syntactic-face-function 237 | . (lambda (state) 238 | (if (nth 4 state) font-lock-comment-face))))) 239 | (set (make-local-variable 'comment-start) "# ") ; Only valid at BOL. 240 | (when ldap-is-openldap 241 | (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil) 242 | (setq defun-prompt-regexp 243 | (rx line-start 244 | (or "attributetype" "objectclass" "attributeType" "objectClass") 245 | (+ blank)))) 246 | (set (make-local-variable 'parse-sexp-lookup-properties) t) 247 | (set (make-local-variable 'imenu-create-index-function) #'ldap-create-index) 248 | (set (make-local-variable 'outline-regexp) ldap-defun-regexp) 249 | (set (make-local-variable 'outline-level) (lambda () 1))) 250 | 251 | (autoload 'ldap-mode "ldap-mode" "\ 252 | Major mode for editing LDAP schema. 253 | 254 | Attributetype and objectclass definitions are treated as defuns. 255 | The mode decides whether or not to treat the buffer as OpenLDAP 256 | schema according to whether it uses the `attributetype' or 257 | `objectclass' keywords to label the definitions. (OpenLDAP 258 | schema definitions are assumed to be collected into separate 259 | files for inclusion into slapd.conf.) 260 | 261 | Imenu, outline and skeleton support are provided. See also 262 | command `ldap-convert-asn1-schema' `ldap-convert-ldif-schema'. 263 | 264 | \\{ldap-mode-map} 265 | 266 | \(fn)" t nil) 267 | 268 | ;;;; Schema conversion 269 | 270 | (defun ldap-convert-asn1-schema (&optional start end) 271 | "Convert schema from ASN.1, as used in RFCs, to one suitable for OpenLDAP. 272 | 273 | Operates on the contents of the current buffer. 274 | In Transient Mark mode, if the mark is active, operate on the contents 275 | of the region. Otherwise, operate from point to the end of the buffer. 276 | 277 | Basically replaces lines like 278 | 279 | ( ... 280 | 281 | with 282 | 283 | attributetype ( ... 284 | 285 | or 286 | 287 | objectclass ( ... 288 | 289 | depending on whether they describe objects or attributes. Regions of 290 | the text outside object or attribute definitions are converted to 291 | comments. 292 | 293 | If is of the form ., it is converted to 294 | :. The former is apparently an old usage and 295 | IPlanet-ism eliminated in RFC 2252. The latter is an OpenLDAP OID 296 | macro use which requires the macro to be defined using 297 | `objectidentifier'." 298 | (interactive (list (if (and transient-mark-mode mark-active) 299 | (region-beginning)) 300 | (if (and transient-mark-mode mark-active) 301 | (region-end)))) 302 | (let ((start (or start (point))) 303 | (end (or end (point-max))) 304 | (comment-start-pos start) 305 | (bol)) 306 | (save-excursion 307 | (save-restriction 308 | (narrow-to-region start end) 309 | (let ((case-fold-search nil)) 310 | (goto-char start) 311 | (while (re-search-forward ; find start of data 312 | (rx line-start (* blank) "(" (1+ blank) 313 | (? (and (group (and letter (* alphanumeric))) ".")) 314 | (group (1+ (any "0-9.")))) 315 | nil t) 316 | 317 | (beginning-of-line) 318 | (setq bol (point-marker)) 319 | ;; Comment-out commentary. 320 | (if (> (point) comment-start-pos) 321 | (save-excursion 322 | (save-match-data 323 | (comment-region comment-start-pos (point))))) 324 | ;; Operate on the data. 325 | (let ((obj (ldap-object-def-p))) 326 | (save-match-data 327 | (replace-match (if obj "objectClass" "attributeType") t)) 328 | (insert " ( ") 329 | (if (match-beginning 1) 330 | (insert (match-string 1) ":")) 331 | (insert (match-string 2))) 332 | (goto-char bol) 333 | (forward-sexp 2) 334 | (skip-chars-forward " \t\n") 335 | (setq comment-start-pos (point))) 336 | (skip-chars-forward " \t\n") 337 | (unless (eobp) 338 | (comment-region (point) (point-max)))) 339 | (ldap-openldap-p))))) 340 | 341 | (defun ldap-convert-ldif-schema (&optional start end) 342 | "Convert schema from LDIF, as used by Netscape, to one suitable for OpenLDAP. 343 | 344 | Operates on the contents of the current buffer. 345 | In Transient Mark mode, if the mark is active, operate on the contents 346 | of the region. Otherwise, operate from point to the end of the buffer. 347 | 348 | Basically replaces lines like 349 | 350 | attributeTypes: ( ... 351 | objectClasses: ( ... 352 | 353 | with 354 | 355 | attributetype ( ... 356 | 357 | or 358 | 359 | objectclass ( ... 360 | 361 | If is of the form ., it is converted to 362 | :. The former is apparently an old usage and 363 | IPlanet-ism eliminated in RFC 2252. The latter is an OpenLDAP OID 364 | macro use which requires the macro to be defined using 365 | `objectidentifier'. 366 | 367 | You likely want to use \\[ldap-mode] on the buffer after this command." 368 | (interactive (list (if (and transient-mark-mode mark-active) 369 | (region-beginning)) 370 | (if (and transient-mark-mode mark-active) 371 | (region-end)))) 372 | (let ((start (or start (point))) 373 | (end (or end (point-max))) 374 | (comment-start-pos start) 375 | (bol)) 376 | (save-excursion 377 | (save-restriction 378 | (narrow-to-region start end) 379 | (let ((case-fold-search nil)) 380 | (goto-char start) 381 | (flush-lines "^dn: *cn=schema") 382 | (while (re-search-forward (rx line-start (group "attributeTypes:") 383 | (* blank) "(" (* blank) 384 | (? (group (and letter (* alphanumeric)) 385 | (group ".")))) 386 | nil t) 387 | (save-match-data 388 | (replace-match "attributetype " t t nil 1)) 389 | (if (match-beginning 2) 390 | (replace-match ":" t t nil 3))) 391 | (goto-char (point-min)) 392 | (while (re-search-forward (rx line-start (group "objectClasses:") 393 | (* blank) "(" (* blank) 394 | (? (group (and letter (* alphanumeric)) 395 | (group ".")))) 396 | nil t) 397 | (save-match-data 398 | (replace-match "objectclass " t t nil 1)) 399 | (if (match-beginning 2) 400 | (replace-match ":" t t nil 3))) 401 | 402 | 403 | (while (re-search-forward ; find start of data 404 | (rx line-start (* blank) "(" (1+ blank) 405 | (? (and (group (and letter (* alphanumeric))) ".")) 406 | (group (1+ (any "0-9.")))) 407 | nil t))) 408 | (ldap-openldap-p))))) 409 | 410 | (defun ldap-object-def-p () 411 | "Return non-nil if current definition is of an object, not an attribute. 412 | Assumes point is before the opening paren (and possible intervening 413 | whitespace)." 414 | (save-match-data 415 | ;; Look for keyword indicating an object definition. Should 416 | ;; really move via sexps in case a value might match. 417 | (re-search-forward 418 | (rx word-start 419 | (or "STRUCTURAL" "ABSTRACT" "AUXILIARY" "MUST" "MAY") 420 | word-end) 421 | ;; forward-sexp could only fail with invalid data, I think. 422 | (save-excursion (forward-sexp) (point)) t))) 423 | 424 | ;;;; LDIF 425 | 426 | (defconst ldif-mode-syntax-table 427 | (let ((table (make-syntax-table))) 428 | (modify-syntax-entry ?# "<" table) ; actually only comment at BOL 429 | (modify-syntax-entry ?\n ">" table) 430 | (modify-syntax-entry ?\" "." table) 431 | table)) 432 | 433 | (eval-and-compile 434 | (defconst ldif-continued-line-regexp 435 | '(group (1+ not-newline) (* (and "\n" (1+ blank) (1+ not-newline)))))) 436 | 437 | (if (default-boundp 'font-lock-extra-managed-props) 438 | ;; This only works in Emacs 22, where we have 439 | ;; `font-lock-extra-managed-props', though it could probably be 440 | ;; made to work in 21.3, at least after a fashion. 441 | (progn 442 | (defgroup ldap-mode () 443 | "Editing LDAP schema and LDIF" 444 | ;; Fixme: add to ldap group too? 445 | :group 'data) 446 | 447 | (defface ldif-decoded 448 | '((t :inherit underline)) 449 | "Face indicating decoded base64 data. 450 | This is merged with the normal font-lock face for the attribute data." 451 | :group 'faces 452 | :group 'ldap-mode) 453 | 454 | (defcustom ldif-font-lock-decode t 455 | "Non-nil means that Font Lock displays base64 data decoded. 456 | This applies to all DN attributes. It may also apply to other 457 | attributes -- see `ldif-font-lock-decode-all'. 458 | 459 | This feature only works in Emacs 22 and newer." 460 | :type 'boolean 461 | :group 'ldap-mode) 462 | 463 | (defcustom ldif-font-lock-decode-all nil 464 | "Non-nil means Font Lock displays base64 data decoded for all attributes. 465 | This only applies if `ldif-font-lock-decode' is non-nil. This 466 | decoding depends on Emacs being able to decode the data as UTF-8. 467 | It may be wrong if the data are actually binary, not text. 468 | Turning this on may be quite expensive." 469 | :type 'boolean 470 | :group 'ldap-mode)) 471 | 472 | ;; else (Emacs 21) just bind variables used below 473 | (defconst ldif-font-lock-decode nil) 474 | (defconst ldif-font-lock-decode-all nil)) 475 | 476 | (defcustom ldif-attribute-face nil 477 | "Face with which to highlight LDIF object attributes other than the DN. 478 | The default is not to highlight." 479 | :group 'ldap-mode 480 | :type '(choice face (const :tag "No highlight" nil))) 481 | 482 | (defconst ldif-font-lock-keywords 483 | `((,(rx line-start (group "dn") "::" (* blank) 484 | (eval ldif-continued-line-regexp)) 485 | (1 'font-lock-keyword-face) 486 | (2 (if ldif-font-lock-decode 487 | ;; Decode the base64-encoded utf-8 as a display string. 488 | (list 'face '(:inherit (font-lock-variable-name-face 489 | ldif-decoded)) 490 | 'help-echo "base64-encoded" 491 | 'display (decode-coding-string (base64-decode-string 492 | (match-string 2)) 493 | 'utf-8)) 494 | 'font-lock-variable-name-face))) 495 | ;; Keep this below the `dn::' case. 496 | (,(rx line-start (group (or "dn" "add" "delete" "replace")) ":" 497 | (* blank) (eval ldif-continued-line-regexp)) 498 | (1 'font-lock-keyword-face) 499 | (2 'font-lock-variable-name-face)) 500 | (,(rx line-start (group "changetype") ":" (* blank) 501 | (eval ldif-continued-line-regexp)) 502 | (1 'font-lock-keyword-face) (2 'font-lock-constant-face)) 503 | (,(rx line-start (group "-") (* blank) line-end) 504 | (1 'font-lock-warning-face)) ; make hyphen as strong as possible 505 | (,(rx line-start (group "version") ":" (* blank) (group (1+ word))) 506 | (1 'font-lock-keyword-face)) 507 | ;; I'm not sure it's useful to highlight all the attribute names. 508 | ;; Let's try to display decoded base64, though. We don't know 509 | ;; whether an attribute is textual, i.e. whether it should be 510 | ;; decoded, though we could keep a list of attributes known to 511 | ;; have text syntax (e.g. `ldap-attribute-syntaxes-alist'). 512 | ;; Instead, just test whether the data are valid UTF-8. 513 | (,(rx line-start (1+ (not (any ?:))) "::" (* blank) 514 | (eval ldif-continued-line-regexp)) 515 | (1 (or (and ldif-font-lock-decode ldif-font-lock-decode-all 516 | (let* ((data (base64-decode-string (match-string 1))) 517 | (coding-category-utf-8 'utf-8) 518 | (coding-category-list '(coding-category-utf-8)) 519 | (coding (detect-coding-string data)) 520 | utf-8) 521 | (dolist (c coding) 522 | (if (memq c '(utf-8 utf-8-unix utf-8-dos utf-8-mac 523 | undecided undecided-unix 524 | undecided-dos undecided-mac)) 525 | (setq utf-8 t))) 526 | (if utf-8 527 | (list 'face 'ldif-decoded 528 | 'display (decode-coding-string data 'utf-8) 529 | 'help-echo "base64-encoded")))) 530 | nil))) 531 | (,(rx line-start (group (not (any ?\ )) (1+ (not (any ?:)))) ":") 532 | (1 ldif-attribute-face)))) 533 | 534 | ;; Fixme: for `dn::', decode the name from base64/utf-8 535 | (defconst ldif-generic-expression 536 | `((nil ,(rx line-start "dn:" (? ":") (* blank) 537 | (eval ldif-continued-line-regexp)) 538 | 1)) 539 | "`imenu-generic-expression' for LDIF.") 540 | 541 | ;;;###autoload 542 | (define-derived-mode ldif-mode fundamental-mode "LDIF" 543 | "Major mode for editing LDAP LDIF files. 544 | Imenu and outline support is provided. There are no special 545 | keybindings. Font-Lock support includes decoding some 546 | base64-encoded attributes -- see `ldif-decoded'." 547 | (set (make-local-variable 'font-lock-defaults) 548 | `(ldif-font-lock-keywords 549 | nil nil nil nil 550 | (font-lock-syntactic-keywords . ldap-syntactic-keywords) 551 | (font-lock-extra-managed-props . (display help-echo)) 552 | (font-lock-multiline . t))) 553 | (set (make-local-variable 'parse-sexp-lookup-properties) t) 554 | (set (make-local-variable 'comment-start) "# ") ; Only valid at BOL. 555 | (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil) 556 | (set (make-local-variable 'imenu-generic-expression) 557 | ldif-generic-expression) 558 | (set (make-local-variable 'parse-sexp-lookup-properties) t)) 559 | 560 | (autoload 'ldif-mode "ldap-mode" "\ 561 | Major mode for editing LDAP LDIF files. 562 | Imenu and outline support is provided. There are no special 563 | keybindings. Font-Lock support includes decoding some 564 | base64-encoded attributes -- see `ldif-decoded'. 565 | 566 | \(fn)" t nil) 567 | 568 | ;;;###autoload 569 | (add-to-list 'auto-mode-alist '("\\.ldif\\'" . ldif-mode) t) 570 | 571 | (add-to-list 'auto-mode-alist '("\\.ldif\\'" . ldif-mode) t) 572 | ;;;###autoload 573 | (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "ldap-mode" '("ldap-" "ldif-"))) 574 | 575 | ;;;*** 576 | 577 | (modify-coding-system-alist 'file "\\.ldif\\'" 'utf-8) 578 | 579 | (modify-coding-system-alist 'file "\\.ldif\\'" 'utf-8) 580 | 581 | 582 | (defun ldif-decode-base64 () 583 | "Decode base64-encoded UTF-8 data for attribute at point. 584 | Also change the double colon after the attribute name to a single one. 585 | An error is signalled if the attribute data aren't base64-encoded UTF-8. 586 | Note that this loses the significance of encoded newlines and leading 587 | whitespace." 588 | (interactive "*") 589 | (save-excursion 590 | (beginning-of-line) 591 | (while (and (not (bobp)) (eq ?\ (char-after))) 592 | (forward-line -1)) 593 | (if (looking-at (rx line-start (1+ word) (group "::" (* blank)) 594 | (eval ldif-continued-line-regexp))) 595 | (let* ((data (base64-decode-string (match-string 2))) 596 | (coding-category-utf-8 'utf-8) 597 | (coding-category-list '(coding-category-utf-8)) 598 | (coding (detect-coding-string data)) 599 | utf-8) 600 | (dolist (c coding) 601 | (if (memq c '(utf-8 utf-8-unix utf-8-dos utf-8-mac 602 | undecided undecided-unix undecided-dos 603 | undecided-mac)) 604 | (setq utf-8 t))) 605 | (unless utf-8 606 | (error "Not UTF-8 data")) 607 | (save-match-data 608 | (save-restriction 609 | (narrow-to-region (match-beginning 2) (match-end 2)) 610 | (replace-match (decode-coding-string 611 | (base64-decode-string (match-string 2)) 612 | 'utf-8) 613 | t t nil 2) 614 | (goto-char (point-min)) 615 | (while (search-forward "\n" nil t) 616 | (replace-match "")))) 617 | (replace-match ": " t t nil 1)) 618 | (error "Not at base64-encoded attribute")))) 619 | 620 | (defun ldif-base64-encode () 621 | "Convert the attribute data to base64-encoded UTF-8 if possible. 622 | Line continuations are removed. See also `ldif-base64-encode-region'." 623 | (interactive "*") 624 | (save-excursion 625 | (beginning-of-line) 626 | (while (and (not (bobp)) (eq ?\ (char-after))) 627 | (forward-line -1)) 628 | (unless (looking-at (rx line-start (1+ word) ":" (* blank) 629 | (eval ldif-continued-line-regexp))) 630 | (error "Not at attribute")) 631 | (save-restriction 632 | (narrow-to-region (match-beginning 1) (match-end 1)) 633 | (let ((coding (find-coding-systems-region (point-min) (point-max)))) 634 | (unless (or (memq 'undecided coding) 635 | (memq 'utf-8 coding) 636 | (memq 'mule-utf-8 coding)) 637 | (error "Can't encode the data as UTF-8"))) 638 | (goto-char (point-min)) 639 | ;; Remove line continuations. 640 | (while (re-search-forward "\n[ \t]" nil t) 641 | (replace-match "")) 642 | (encode-coding-region (point-min) (point-max) 'utf-8) 643 | (base64-encode-region (point-min) (point-max)) 644 | (goto-char (point-min)) 645 | (while (zerop (forward-line)) 646 | (insert ?\ )) 647 | (goto-char (point-min)) 648 | (insert ?:)))) 649 | 650 | (defun ldif-base64-encode-region (beg end) 651 | "Encode the region as base64-encoded UTF-8. 652 | This doesn't remove LDIF line continuations in the region, 653 | c.f. `ldif-base64-encode'. It does insert a leading space on 654 | each line of the result so that it may be used as attribute data. 655 | See also `ldif-base64-encode'." 656 | (interactive "*r") 657 | (save-excursion 658 | (save-restriction 659 | (narrow-to-region beg end) 660 | (let ((coding (find-coding-systems-region (point-min) (point-max)))) 661 | (cond ((memq 'undecided coding)) 662 | ((or (memq 'utf-8 coding) 663 | (memq 'mule-utf-8 coding)) 664 | (encode-coding-region (point-min) (point-max) 'utf-8)) 665 | (t (error "Can't encode the data as UTF-8")))) 666 | (base64-encode-region (point-min) (point-max)) 667 | (goto-char (point-min)) 668 | (insert ?\ ) 669 | (while (= 0 (forward-line)) 670 | (insert ?\ ))))) 671 | 672 | 673 | (provide 'ldap-mode) 674 | ;;; ldap-mode.el ends here 675 | -------------------------------------------------------------------------------- /TODO.org_archive: -------------------------------------------------------------------------------- 1 | # -*- mode: org -*- 2 | 3 | 4 | Archived entries from file /Users/mn/TODO.org 5 | 6 | 7 | * DONE Automatically create/update =TAGS= file when =xref-find-definitions= is called 8 | :PROPERTIES: 9 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 10 | :ARCHIVE_FILE: ~/TODO.org 11 | :ARCHIVE_OLPATH: Dotfiles TODO List 12 | :ARCHIVE_CATEGORY: TODO 13 | :ARCHIVE_TODO: DONE 14 | :END: 15 | - State "DONE" from "WIP" [2018-10-03 Wed 07:24] 16 | How does =xref-find-definitions decide whether or not to use =TAGS= file? It doesn't seem to use it for elisp. 17 | I think I will not do this for now 18 | 19 | Archived entries from file /Users/mn/TODO.org 20 | 21 | 22 | * DONE Ugh, broke Windows Emacs again. [4/4] 23 | :PROPERTIES: 24 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 25 | :ARCHIVE_FILE: ~/TODO.org 26 | :ARCHIVE_OLPATH: Dotfiles TODO List 27 | :ARCHIVE_CATEGORY: TODO 28 | :ARCHIVE_TODO: DONE 29 | :END: 30 | - State "DONE" from "WIP" [2018-10-03 Wed 07:23] 31 | ** DONE sly - Make it compatible or else don't load it on Windows 32 | - State "DONE" from "TODO" [2018-09-08 Sat 22:57] 33 | ** DONE Remove =mac-key-mode-map= references in main config file. Time to properly clean it up? 34 | - State "DONE" from "TODO" [2018-09-08 Sat 18:31] 35 | ** DONE Fix Eshell 36 | - State "DONE" from "TODO" [2018-09-08 Sat 22:57] 37 | ** DONE Make super key bindings work with Windows 38 | - State "DONE" from "WIP" [2018-10-03 Wed 07:23] 39 | 40 | Archived entries from file /Users/mn/TODO.org 41 | 42 | 43 | * DONE Try emacs-geeknote fork 44 | :PROPERTIES: 45 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 46 | :ARCHIVE_FILE: ~/TODO.org 47 | :ARCHIVE_OLPATH: Dotfiles TODO List 48 | :ARCHIVE_CATEGORY: TODO 49 | :ARCHIVE_TODO: DONE 50 | :END: 51 | - State "DONE" from "WIP" [2018-10-03 Wed 07:22] 52 | https://github.com/vxe/emacs-geeknote 53 | I give up 54 | 55 | Archived entries from file /Users/mn/TODO.org 56 | 57 | 58 | * DONE Fix modeline sometimes getting stuck on old colors when theme is switched 59 | :PROPERTIES: 60 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 61 | :ARCHIVE_FILE: ~/TODO.org 62 | :ARCHIVE_OLPATH: Dotfiles TODO List 63 | :ARCHIVE_CATEGORY: TODO 64 | :ARCHIVE_TODO: DONE 65 | :END: 66 | - State "DONE" from "WIP" [2018-10-02 Tue 21:45] 67 | 68 | Archived entries from file /Users/mn/TODO.org 69 | 70 | 71 | * DONE Org sorting 72 | :PROPERTIES: 73 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 74 | :ARCHIVE_FILE: ~/TODO.org 75 | :ARCHIVE_OLPATH: Dotfiles TODO List 76 | :ARCHIVE_CATEGORY: TODO 77 | :ARCHIVE_TODO: DONE 78 | :END: 79 | - State "DONE" from "WIP" [2018-10-02 Tue 21:44] 80 | Read all suggestions 81 | https://emacs.stackexchange.com/questions/9585/org-how-to-sort-headings-by-todo-and-then-by-priority 82 | 83 | Archived entries from file /Users/mn/TODO.org 84 | 85 | 86 | * DONE Backup changes to saved files and restore them with a diff view 87 | :PROPERTIES: 88 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 89 | :ARCHIVE_FILE: ~/TODO.org 90 | :ARCHIVE_OLPATH: Dotfiles TODO List 91 | :ARCHIVE_CATEGORY: TODO 92 | :ARCHIVE_TODO: DONE 93 | :END: 94 | - State "DONE" from "TODO" [2018-10-02 Tue 21:35] 95 | See comments from NateEag and github-alphapapa in this thread: 96 | https://www.reddit.com/r/emacs/comments/9hfo51/idea_periodically_save_file_changes_to_cache_file/ 97 | 98 | Archived entries from file /Users/mn/TODO.org 99 | 100 | 101 | * DONE Make =C-k= in =ivy-switch-buffer= kill the buffer, refresh the list, and stay in =ivy-switch-buffer=. 102 | :PROPERTIES: 103 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 104 | :ARCHIVE_FILE: ~/TODO.org 105 | :ARCHIVE_OLPATH: Dotfiles TODO List 106 | :ARCHIVE_CATEGORY: TODO 107 | :ARCHIVE_TODO: DONE 108 | :END: 109 | - State "DONE" from "TODO" [2018-10-02 Tue 21:40] 110 | Can't seem to get this to work but the workaround is to use =C-o a k= 111 | 112 | Archived entries from file /Users/mn/TODO.org 113 | 114 | 115 | * DONE Try ivy features [2/2] 116 | :PROPERTIES: 117 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 118 | :ARCHIVE_FILE: ~/TODO.org 119 | :ARCHIVE_OLPATH: Dotfiles TODO List 120 | :ARCHIVE_CATEGORY: TODO 121 | :ARCHIVE_TODO: DONE 122 | :END: 123 | - State "DONE" from "TODO" [2018-10-02 Tue 02:08] 124 | ** DONE ivy-hydra 125 | - State "DONE" from "WIP" [2018-10-02 Tue 01:54] 126 | ** DONE ivy-overlay 127 | - State "DONE" from "WIP" [2018-10-02 Tue 02:08] 128 | 129 | Archived entries from file /Users/mn/TODO.org 130 | 131 | 132 | * DONE fix rubygems-update issue 133 | :PROPERTIES: 134 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 135 | :ARCHIVE_FILE: ~/TODO.org 136 | :ARCHIVE_OLPATH: Dotfiles TODO List 137 | :ARCHIVE_CATEGORY: TODO 138 | :ARCHIVE_TODO: DONE 139 | :END: 140 | 141 | Archived entries from file /Users/mn/TODO.org 142 | 143 | 144 | * DONE Migrate fish plugin manager to fisher 145 | :PROPERTIES: 146 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 147 | :ARCHIVE_FILE: ~/TODO.org 148 | :ARCHIVE_OLPATH: Dotfiles TODO List 149 | :ARCHIVE_CATEGORY: TODO 150 | :ARCHIVE_TODO: DONE 151 | :END: 152 | 153 | Archived entries from file /Users/mn/TODO.org 154 | 155 | 156 | * DONE aliases: move to ~ and make compatible with fish, bash, zsh so 157 | :PROPERTIES: 158 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 159 | :ARCHIVE_FILE: ~/TODO.org 160 | :ARCHIVE_OLPATH: Dotfiles TODO List 161 | :ARCHIVE_CATEGORY: TODO 162 | :ARCHIVE_TODO: DONE 163 | :END: 164 | they all use the same aliases file 165 | 166 | Archived entries from file /Users/mn/TODO.org 167 | 168 | 169 | * DONE do the same for private.fish 170 | :PROPERTIES: 171 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 172 | :ARCHIVE_FILE: ~/TODO.org 173 | :ARCHIVE_OLPATH: Dotfiles TODO List 174 | :ARCHIVE_CATEGORY: TODO 175 | :ARCHIVE_TODO: DONE 176 | :END: 177 | 178 | Archived entries from file /Users/mn/TODO.org 179 | 180 | 181 | * DONE fix arch sed error 182 | :PROPERTIES: 183 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 184 | :ARCHIVE_FILE: ~/TODO.org 185 | :ARCHIVE_OLPATH: Dotfiles TODO List 186 | :ARCHIVE_CATEGORY: TODO 187 | :ARCHIVE_TODO: DONE 188 | :END: 189 | 190 | Archived entries from file /Users/mn/TODO.org 191 | 192 | 193 | * DONE install.sh: colorize output 194 | :PROPERTIES: 195 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 196 | :ARCHIVE_FILE: ~/TODO.org 197 | :ARCHIVE_OLPATH: Dotfiles TODO List 198 | :ARCHIVE_CATEGORY: TODO 199 | :ARCHIVE_TODO: DONE 200 | :END: 201 | 202 | Archived entries from file /Users/mn/TODO.org 203 | 204 | 205 | * DONE Fix bash_prompt 206 | :PROPERTIES: 207 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 208 | :ARCHIVE_FILE: ~/TODO.org 209 | :ARCHIVE_OLPATH: Dotfiles TODO List 210 | :ARCHIVE_CATEGORY: TODO 211 | :ARCHIVE_TODO: DONE 212 | :END: 213 | 214 | Archived entries from file /Users/mn/TODO.org 215 | 216 | 217 | * DONE Migrate zlogin to bash aliases 218 | :PROPERTIES: 219 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 220 | :ARCHIVE_FILE: ~/TODO.org 221 | :ARCHIVE_OLPATH: Dotfiles TODO List 222 | :ARCHIVE_CATEGORY: TODO 223 | :ARCHIVE_TODO: DONE 224 | :END: 225 | 226 | Archived entries from file /Users/mn/TODO.org 227 | 228 | 229 | * DONE Port start-ssh-agent.fish to bash and zsh 230 | :PROPERTIES: 231 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 232 | :ARCHIVE_FILE: ~/TODO.org 233 | :ARCHIVE_OLPATH: Dotfiles TODO List 234 | :ARCHIVE_CATEGORY: TODO 235 | :ARCHIVE_TODO: DONE 236 | :END: 237 | 238 | Archived entries from file /Users/mn/TODO.org 239 | 240 | 241 | * DONE Vim and Emacs updates shouldn't make repo dirty -- don't store packages themselves in repo 242 | :PROPERTIES: 243 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 244 | :ARCHIVE_FILE: ~/TODO.org 245 | :ARCHIVE_OLPATH: Dotfiles TODO List 246 | :ARCHIVE_CATEGORY: TODO 247 | :ARCHIVE_TODO: DONE 248 | :END: 249 | 250 | Archived entries from file /Users/mn/TODO.org 251 | 252 | 253 | * DONE Catch-22 for running =fisher= for first time: It both requires and installs =bass= 254 | :PROPERTIES: 255 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 256 | :ARCHIVE_FILE: ~/TODO.org 257 | :ARCHIVE_OLPATH: Dotfiles TODO List 258 | :ARCHIVE_CATEGORY: TODO 259 | :ARCHIVE_TODO: DONE 260 | :END: 261 | 262 | Archived entries from file /Users/mn/TODO.org 263 | 264 | 265 | * DONE Fix tmux line length bug in =fish_prompt= -- probably not stripping all control characters out when measuring the line length 266 | :PROPERTIES: 267 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 268 | :ARCHIVE_FILE: ~/TODO.org 269 | :ARCHIVE_OLPATH: Dotfiles TODO List 270 | :ARCHIVE_CATEGORY: TODO 271 | :ARCHIVE_TODO: DONE 272 | :END: 273 | (=bash_prompt= does not have this problem) 274 | 275 | Archived entries from file /Users/mn/TODO.org 276 | 277 | 278 | * DONE prompt - direnv 279 | :PROPERTIES: 280 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 281 | :ARCHIVE_FILE: ~/TODO.org 282 | :ARCHIVE_OLPATH: Dotfiles TODO List 283 | :ARCHIVE_CATEGORY: TODO 284 | :ARCHIVE_TODO: DONE 285 | :END: 286 | 287 | Archived entries from file /Users/mn/TODO.org 288 | 289 | 290 | * DONE bash prompt sometimes exibits weird behavior 291 | :PROPERTIES: 292 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 293 | :ARCHIVE_FILE: ~/TODO.org 294 | :ARCHIVE_OLPATH: Dotfiles TODO List 295 | :ARCHIVE_CATEGORY: TODO 296 | :ARCHIVE_TODO: DONE 297 | :END: 298 | Moving the start of the prompt to the middle of the line or even the previous line. Guessing there are special characters not being printed properly, or maybe the normal escape sequence isn't clearing everything 299 | 300 | Archived entries from file /Users/mn/TODO.org 301 | 302 | 303 | * DONE Make tmux status bar more like vim's 304 | :PROPERTIES: 305 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 306 | :ARCHIVE_FILE: ~/TODO.org 307 | :ARCHIVE_OLPATH: Dotfiles TODO List 308 | :ARCHIVE_CATEGORY: TODO 309 | :ARCHIVE_TODO: DONE 310 | :END: 311 | (https://github.com/edkolev/tmuxline.vim) 312 | 313 | Archived entries from file /Users/mn/TODO.org 314 | 315 | 316 | * DONE Finish emacs tmuxline-ification 317 | :PROPERTIES: 318 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 319 | :ARCHIVE_FILE: ~/TODO.org 320 | :ARCHIVE_OLPATH: Dotfiles TODO List 321 | :ARCHIVE_CATEGORY: TODO 322 | :ARCHIVE_TODO: DONE 323 | :END: 324 | 325 | Archived entries from file /Users/mn/TODO.org 326 | 327 | 328 | * DONE Make =PS1= fallback look like =bash_prompt= 329 | :PROPERTIES: 330 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 331 | :ARCHIVE_FILE: ~/TODO.org 332 | :ARCHIVE_OLPATH: Dotfiles TODO List 333 | :ARCHIVE_CATEGORY: TODO 334 | :ARCHIVE_TODO: DONE 335 | :END: 336 | 337 | Archived entries from file /Users/mn/TODO.org 338 | 339 | 340 | * DONE Only update packages lists if =--save= flag is set so that repo stays clean after =update= [3/3] 341 | :PROPERTIES: 342 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 343 | :ARCHIVE_FILE: ~/TODO.org 344 | :ARCHIVE_OLPATH: Dotfiles TODO List 345 | :ARCHIVE_CATEGORY: TODO 346 | :ARCHIVE_TODO: DONE 347 | :END: 348 | - [X] Move all fish config to plugin 349 | - [X] =update-fish=: problematic because fisher updates 350 | ~/.config/fish/functions/* 351 | - [X] All the rest 352 | 353 | Archived entries from file /Users/mn/TODO.org 354 | 355 | 356 | * DONE Better manage vim and nvim configs 357 | :PROPERTIES: 358 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 359 | :ARCHIVE_FILE: ~/TODO.org 360 | :ARCHIVE_OLPATH: Dotfiles TODO List 361 | :ARCHIVE_CATEGORY: TODO 362 | :ARCHIVE_TODO: DONE 363 | :END: 364 | 365 | Archived entries from file /Users/mn/TODO.org 366 | 367 | 368 | * DONE BUG: Is =./install -f= removing dirs during =link_children=? 369 | :PROPERTIES: 370 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 371 | :ARCHIVE_FILE: ~/TODO.org 372 | :ARCHIVE_OLPATH: Dotfiles TODO List 373 | :ARCHIVE_CATEGORY: TODO 374 | :ARCHIVE_TODO: DONE 375 | :END: 376 | 377 | Archived entries from file /Users/mn/TODO.org 378 | 379 | 380 | * DONE Remove noise from atom status bar 381 | :PROPERTIES: 382 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 383 | :ARCHIVE_FILE: ~/TODO.org 384 | :ARCHIVE_OLPATH: Dotfiles TODO List 385 | :ARCHIVE_CATEGORY: TODO 386 | :ARCHIVE_TODO: DONE 387 | :END: 388 | 389 | Archived entries from file /Users/mn/TODO.org 390 | 391 | 392 | * DONE Get rid of =basic.vim=? No, it has some nice settings. 393 | :PROPERTIES: 394 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 395 | :ARCHIVE_FILE: ~/TODO.org 396 | :ARCHIVE_OLPATH: Dotfiles TODO List 397 | :ARCHIVE_CATEGORY: TODO 398 | :ARCHIVE_TODO: DONE 399 | :END: 400 | 401 | Archived entries from file /Users/mn/TODO.org 402 | 403 | 404 | * DONE with_color escape sequences sometimes dont' work over ssh/tmux. should also be way faster 405 | :PROPERTIES: 406 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 407 | :ARCHIVE_FILE: ~/TODO.org 408 | :ARCHIVE_OLPATH: Dotfiles TODO List 409 | :ARCHIVE_CATEGORY: TODO 410 | :ARCHIVE_TODO: DONE 411 | :END: 412 | 413 | Archived entries from file /Users/mn/TODO.org 414 | 415 | 416 | * DONE BUG: update-python on arch overwrites packages that are managed by pacman, causing pacman updates to fail 417 | :PROPERTIES: 418 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 419 | :ARCHIVE_FILE: ~/TODO.org 420 | :ARCHIVE_OLPATH: Dotfiles TODO List 421 | :ARCHIVE_CATEGORY: TODO 422 | :ARCHIVE_TODO: DONE 423 | :END: 424 | 425 | Archived entries from file /Users/mn/TODO.org 426 | 427 | 428 | * DONE BUG: On interactive login: 429 | :PROPERTIES: 430 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 431 | :ARCHIVE_FILE: ~/TODO.org 432 | :ARCHIVE_OLPATH: Dotfiles TODO List 433 | :ARCHIVE_CATEGORY: TODO 434 | :ARCHIVE_TODO: DONE 435 | :END: 436 | =set: Tried to change the read-only variable '_'= Has something to dowith adding "/usr/local/opt/coreutils/libexec/gnubin" to PATH 437 | 438 | Archived entries from file /Users/mn/TODO.org 439 | 440 | 441 | * DONE BUG: Error message is displayed from prompt when tmux is not in path 442 | :PROPERTIES: 443 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 444 | :ARCHIVE_FILE: ~/TODO.org 445 | :ARCHIVE_OLPATH: Dotfiles TODO List 446 | :ARCHIVE_CATEGORY: TODO 447 | :ARCHIVE_TODO: DONE 448 | :END: 449 | 450 | Archived entries from file /Users/mn/TODO.org 451 | 452 | 453 | * DONE Move prompt to its own plugin/repo - teleprompt 454 | :PROPERTIES: 455 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 456 | :ARCHIVE_FILE: ~/TODO.org 457 | :ARCHIVE_OLPATH: Dotfiles TODO List 458 | :ARCHIVE_CATEGORY: TODO 459 | :ARCHIVE_TODO: DONE 460 | :END: 461 | 462 | Archived entries from file /Users/mn/TODO.org 463 | 464 | 465 | * DONE BUG: =install=: Don't create files with literal name of '*' in created directories (=link-children=) - only happens when specifying source path on command line? 466 | :PROPERTIES: 467 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 468 | :ARCHIVE_FILE: ~/TODO.org 469 | :ARCHIVE_OLPATH: Dotfiles TODO List 470 | :ARCHIVE_CATEGORY: TODO 471 | :ARCHIVE_TODO: DONE 472 | :END: 473 | 474 | Archived entries from file /Users/mn/TODO.org 475 | 476 | 477 | * DONE BUG: Why does it always link .parinfer-file-extentions.txt? 478 | :PROPERTIES: 479 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 480 | :ARCHIVE_FILE: ~/TODO.org 481 | :ARCHIVE_OLPATH: Dotfiles TODO List 482 | :ARCHIVE_CATEGORY: TODO 483 | :ARCHIVE_TODO: DONE 484 | :END: 485 | 486 | Archived entries from file /Users/mn/TODO.org 487 | 488 | 489 | * DONE BUG: =inf-clojure= sets =comint-send-input= on every shell 490 | :PROPERTIES: 491 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 492 | :ARCHIVE_FILE: ~/TODO.org 493 | :ARCHIVE_OLPATH: Dotfiles TODO List 494 | :ARCHIVE_CATEGORY: TODO 495 | :ARCHIVE_TODO: DONE 496 | :END: 497 | 498 | Archived entries from file /Users/mn/TODO.org 499 | 500 | 501 | * DONE BUG: dired+ often doesn't work or doesn't work completely (details not hidden and/or highlighting not working) 502 | :PROPERTIES: 503 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 504 | :ARCHIVE_FILE: ~/TODO.org 505 | :ARCHIVE_OLPATH: Dotfiles TODO List 506 | :ARCHIVE_CATEGORY: TODO 507 | :ARCHIVE_TODO: DONE 508 | :END: 509 | 510 | Archived entries from file /Users/mn/TODO.org 511 | 512 | 513 | * DONE Emacs: macos proced doesn't show all columns 514 | :PROPERTIES: 515 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 516 | :ARCHIVE_FILE: ~/TODO.org 517 | :ARCHIVE_OLPATH: Dotfiles TODO List 518 | :ARCHIVE_CATEGORY: TODO 519 | :ARCHIVE_TODO: DONE 520 | :END: 521 | 522 | Archived entries from file /Users/mn/TODO.org 523 | 524 | 525 | * DONE Get =diff-hl= to work 526 | :PROPERTIES: 527 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 528 | :ARCHIVE_FILE: ~/TODO.org 529 | :ARCHIVE_OLPATH: Dotfiles TODO List 530 | :ARCHIVE_CATEGORY: TODO 531 | :ARCHIVE_TODO: DONE 532 | :END: 533 | 534 | Archived entries from file /Users/mn/TODO.org 535 | 536 | 537 | * DONE Get =gist= to work 538 | :PROPERTIES: 539 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 540 | :ARCHIVE_FILE: ~/TODO.org 541 | :ARCHIVE_OLPATH: Dotfiles TODO List 542 | :ARCHIVE_CATEGORY: TODO 543 | :ARCHIVE_TODO: DONE 544 | :END: 545 | 546 | Archived entries from file /Users/mn/TODO.org 547 | 548 | 549 | * DONE tramp: add hostname to mode-line when connected via tramp 550 | :PROPERTIES: 551 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 552 | :ARCHIVE_FILE: ~/TODO.org 553 | :ARCHIVE_OLPATH: Dotfiles TODO List 554 | :ARCHIVE_CATEGORY: TODO 555 | :ARCHIVE_TODO: DONE 556 | :END: 557 | 558 | Archived entries from file /Users/mn/TODO.org 559 | 560 | 561 | * DONE Re-enable company-mode in tramp and optimize 562 | :PROPERTIES: 563 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 564 | :ARCHIVE_FILE: ~/TODO.org 565 | :ARCHIVE_OLPATH: Dotfiles TODO List 566 | :ARCHIVE_CATEGORY: TODO 567 | :ARCHIVE_TODO: DONE 568 | :END: 569 | 570 | Archived entries from file /Users/mn/TODO.org 571 | 572 | 573 | * DONE Move any requires in =init.el= to use-package 574 | :PROPERTIES: 575 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 576 | :ARCHIVE_FILE: ~/TODO.org 577 | :ARCHIVE_OLPATH: Dotfiles TODO List 578 | :ARCHIVE_CATEGORY: TODO 579 | :ARCHIVE_TODO: DONE 580 | :END: 581 | 582 | Archived entries from file /Users/mn/TODO.org 583 | 584 | 585 | * DONE Add =eless= to aliases 586 | :PROPERTIES: 587 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 588 | :ARCHIVE_FILE: ~/TODO.org 589 | :ARCHIVE_OLPATH: Dotfiles TODO List 590 | :ARCHIVE_CATEGORY: TODO 591 | :ARCHIVE_TODO: DONE 592 | :END: 593 | 594 | Archived entries from file /Users/mn/TODO.org 595 | 596 | 597 | * DONE Move =path.fish= to =env= 598 | :PROPERTIES: 599 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 600 | :ARCHIVE_FILE: ~/TODO.org 601 | :ARCHIVE_OLPATH: Dotfiles TODO List 602 | :ARCHIVE_CATEGORY: TODO 603 | :ARCHIVE_TODO: DONE 604 | :END: 605 | 606 | Archived entries from file /Users/mn/TODO.org 607 | 608 | 609 | * DONE install / install.settings should use arrays, not lists, for variables 610 | :PROPERTIES: 611 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 612 | :ARCHIVE_FILE: ~/TODO.org 613 | :ARCHIVE_OLPATH: Dotfiles TODO List 614 | :ARCHIVE_CATEGORY: TODO 615 | :ARCHIVE_TODO: DONE 616 | :END: 617 | 618 | Archived entries from file /Users/mn/TODO.org 619 | 620 | 621 | * DONE BUG: =ignore= in =install.settings= is getting ignored 622 | :PROPERTIES: 623 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 624 | :ARCHIVE_FILE: ~/TODO.org 625 | :ARCHIVE_OLPATH: Dotfiles TODO List 626 | :ARCHIVE_CATEGORY: TODO 627 | :ARCHIVE_TODO: DONE 628 | :END: 629 | 630 | Archived entries from file /Users/mn/TODO.org 631 | 632 | 633 | * DONE Move to bare git repo 634 | :PROPERTIES: 635 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 636 | :ARCHIVE_FILE: ~/TODO.org 637 | :ARCHIVE_OLPATH: Dotfiles TODO List 638 | :ARCHIVE_CATEGORY: TODO 639 | :ARCHIVE_TODO: DONE 640 | :END: 641 | (https://developer.atlassian.com/blog/2016/02/best-way-to-store-dotfiles-git-bare-repo/) 642 | 643 | Archived entries from file /Users/mn/TODO.org 644 | 645 | 646 | * DONE Talk about eshell in README 647 | :PROPERTIES: 648 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 649 | :ARCHIVE_FILE: ~/TODO.org 650 | :ARCHIVE_OLPATH: Dotfiles TODO List 651 | :ARCHIVE_CATEGORY: TODO 652 | :ARCHIVE_TODO: DONE 653 | :END: 654 | 655 | Archived entries from file /Users/mn/TODO.org 656 | 657 | 658 | * DONE Move README to org format 659 | :PROPERTIES: 660 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 661 | :ARCHIVE_FILE: ~/TODO.org 662 | :ARCHIVE_OLPATH: Dotfiles TODO List 663 | :ARCHIVE_CATEGORY: TODO 664 | :ARCHIVE_TODO: DONE 665 | :END: 666 | 667 | Archived entries from file /Users/mn/TODO.org 668 | 669 | 670 | * DONE Finish source 671 | :PROPERTIES: 672 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 673 | :ARCHIVE_FILE: ~/TODO.org 674 | :ARCHIVE_OLPATH: Dotfiles TODO List 675 | :ARCHIVE_CATEGORY: TODO 676 | :ARCHIVE_TODO: DONE 677 | :END: 678 | 679 | Archived entries from file /Users/mn/TODO.org 680 | 681 | 682 | * DONE Make bare repo setup compatible with magit and projectile. Could use a `link' and `unlink' function to create a temporary $HOME/.git file. See example: https://github.com/magit/magit/issues/460#issuecomment-36139308 and https://gitorious.org/magit/skangas-detached-worktree#more 683 | :PROPERTIES: 684 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 685 | :ARCHIVE_FILE: ~/TODO.org 686 | :ARCHIVE_OLPATH: Dotfiles TODO List 687 | :ARCHIVE_CATEGORY: TODO 688 | :ARCHIVE_TODO: DONE 689 | :END: 690 | 691 | Archived entries from file /Users/mn/TODO.org 692 | 693 | 694 | * DONE Make yanking indentation work on a newline inside a form in parinfer (mac-key-mode's =clipboard-yank-and-indent= needs to work with parinfer's =parinfer-smart-yank:yank=. 695 | :PROPERTIES: 696 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 697 | :ARCHIVE_FILE: ~/TODO.org 698 | :ARCHIVE_OLPATH: Dotfiles TODO List 699 | :ARCHIVE_CATEGORY: TODO 700 | :ARCHIVE_TODO: DONE 701 | :END: 702 | Maybe advise the latter? Could turn on parinfer-paren-mode while yanking) 703 | 704 | Archived entries from file /Users/mn/TODO.org 705 | 706 | 707 | * DONE Update readme with new non-bare repo setup 708 | :PROPERTIES: 709 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 710 | :ARCHIVE_FILE: ~/TODO.org 711 | :ARCHIVE_OLPATH: Dotfiles TODO List 712 | :ARCHIVE_CATEGORY: TODO 713 | :ARCHIVE_TODO: DONE 714 | :END: 715 | 716 | Archived entries from file /Users/mn/TODO.org 717 | 718 | 719 | * DONE Fix Windows problems 720 | :PROPERTIES: 721 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 722 | :ARCHIVE_FILE: ~/TODO.org 723 | :ARCHIVE_OLPATH: Dotfiles TODO List 724 | :ARCHIVE_CATEGORY: TODO 725 | :ARCHIVE_TODO: DONE 726 | :END: 727 | 728 | Archived entries from file /Users/mn/TODO.org 729 | 730 | 731 | * DONE eshell should have a way to cd relative to tramp path 732 | :PROPERTIES: 733 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 734 | :ARCHIVE_FILE: ~/TODO.org 735 | :ARCHIVE_OLPATH: Dotfiles TODO List 736 | :ARCHIVE_CATEGORY: TODO 737 | :ARCHIVE_TODO: DONE 738 | :END: 739 | Example: 740 | pwd: =/sshx:host:/some/path= 741 | =cd :/other/path= => =/sshx:host:/other/path= 742 | 743 | Archived entries from file /Users/mn/TODO.org 744 | 745 | 746 | * DONE Fix yank-pop deleting a bunch more than the previous yank :bug: 747 | :PROPERTIES: 748 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 749 | :ARCHIVE_FILE: ~/TODO.org 750 | :ARCHIVE_OLPATH: Dotfiles TODO List 751 | :ARCHIVE_CATEGORY: TODO 752 | :ARCHIVE_TODO: DONE 753 | :END: 754 | What are repro steps? Does this happen only when there are characters on 755 | the line after point? 756 | 757 | Thinking we should use a temp buffer to build the yanked text using parinfer 758 | as normal, then insert it at point. 759 | 760 | All that sounded great, but when I removed the kludgey advice I had created, 761 | everything seemed to work. Not sure what changed but super. 762 | 763 | Archived entries from file /Users/mn/TODO.org 764 | 765 | 766 | * DONE Make solarized theme colors work with eshell 767 | :PROPERTIES: 768 | :ARCHIVE_TIME: 2018-10-03 Wed 07:49 769 | :ARCHIVE_FILE: ~/TODO.org 770 | :ARCHIVE_OLPATH: Dotfiles TODO List 771 | :ARCHIVE_CATEGORY: TODO 772 | :ARCHIVE_TODO: DONE 773 | :END: 774 | 775 | Archived entries from file /Users/mn/TODO.org 776 | 777 | 778 | * DONE Fix org-mode " 901 | :PROPERTIES: 902 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 903 | :ARCHIVE_FILE: ~/TODO.org 904 | :ARCHIVE_OLPATH: Dotfiles TODO List 905 | :ARCHIVE_CATEGORY: TODO 906 | :ARCHIVE_TODO: DONE 907 | :END: 908 | 909 | Archived entries from file /Users/mn/TODO.org 910 | 911 | 912 | * DONE Advise =eshell/cd= such that paths starting with "/" and "~" are interpreted relative to the remote host [2/2] 913 | :PROPERTIES: 914 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 915 | :ARCHIVE_FILE: ~/TODO.org 916 | :ARCHIVE_OLPATH: Dotfiles TODO List 917 | :ARCHIVE_CATEGORY: TODO 918 | :ARCHIVE_TODO: DONE 919 | :END: 920 | - State "DONE" from "WIP" [2018-08-22 Wed 09:01] 921 | ** DONE cd 922 | - State "DONE" from "TODO" [2018-08-22 Wed 08:47] 923 | ** DONE e, ee 924 | - State "DONE" from "TODO" [2018-08-22 Wed 09:01] 925 | 926 | Archived entries from file /Users/mn/TODO.org 927 | 928 | 929 | * DONE "s-n" should create a new scratch buffer 930 | :PROPERTIES: 931 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 932 | :ARCHIVE_FILE: ~/TODO.org 933 | :ARCHIVE_OLPATH: Dotfiles TODO List 934 | :ARCHIVE_CATEGORY: TODO 935 | :ARCHIVE_TODO: DONE 936 | :END: 937 | 938 | Archived entries from file /Users/mn/TODO.org 939 | 940 | 941 | * DONE Add git-ls-files command 942 | :PROPERTIES: 943 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 944 | :ARCHIVE_FILE: ~/TODO.org 945 | :ARCHIVE_OLPATH: Dotfiles TODO List 946 | :ARCHIVE_CATEGORY: TODO 947 | :ARCHIVE_TODO: DONE 948 | :END: 949 | https://stackoverflow.com/questions/24993868/how-do-you-list-tracked-files-git-ls-files-in-magit#25227438 950 | 951 | Archived entries from file /Users/mn/TODO.org 952 | 953 | 954 | * DONE Add to =org-todo-keywords= and =org-todo-keyword-faces= 955 | :PROPERTIES: 956 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 957 | :ARCHIVE_FILE: ~/TODO.org 958 | :ARCHIVE_OLPATH: Dotfiles TODO List 959 | :ARCHIVE_CATEGORY: TODO 960 | :ARCHIVE_TODO: DONE 961 | :END: 962 | - State "DONE" from "WIP" [2018-08-19 Sun 08:29] 963 | ** "WIP" 964 | ** "NEXT" 965 | 966 | Archived entries from file /Users/mn/TODO.org 967 | 968 | 969 | * DONE How to manage package version pinning in =straight.el=? 970 | :PROPERTIES: 971 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 972 | :ARCHIVE_FILE: ~/TODO.org 973 | :ARCHIVE_OLPATH: Dotfiles TODO List 974 | :ARCHIVE_CATEGORY: TODO 975 | :ARCHIVE_TODO: DONE 976 | :END: 977 | - State "DONE" from "TODO" [2018-08-22 Wed 15:39] 978 | 979 | Archived entries from file /Users/mn/TODO.org 980 | 981 | 982 | * DONE Package regression: clojure-mode: =end-of-defun= skips two top level forms 983 | :PROPERTIES: 984 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 985 | :ARCHIVE_FILE: ~/TODO.org 986 | :ARCHIVE_OLPATH: Dotfiles TODO List 987 | :ARCHIVE_CATEGORY: TODO 988 | :ARCHIVE_TODO: DONE 989 | :END: 990 | - State "DONE" from "DONE" [2018-08-22 Wed 15:39] 991 | 992 | Archived entries from file /Users/mn/TODO.org 993 | 994 | 995 | * DONE Package regression: org-mode: easy templates don't expand 996 | :PROPERTIES: 997 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 998 | :ARCHIVE_FILE: ~/TODO.org 999 | :ARCHIVE_OLPATH: Dotfiles TODO List 1000 | :ARCHIVE_CATEGORY: TODO 1001 | :ARCHIVE_TODO: DONE 1002 | :END: 1003 | - State "DONE" from "TODO" [2018-08-22 Wed 15:40] 1004 | 1005 | Archived entries from file /Users/mn/TODO.org 1006 | 1007 | 1008 | * DONE w3m easily view buffer in appropriate mode 1009 | :PROPERTIES: 1010 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1011 | :ARCHIVE_FILE: ~/TODO.org 1012 | :ARCHIVE_OLPATH: Dotfiles TODO List 1013 | :ARCHIVE_CATEGORY: TODO 1014 | :ARCHIVE_TODO: DONE 1015 | :END: 1016 | - State "DONE" from "TODO" [2018-08-22 Wed 20:53] 1017 | Such as displaying a markdown file in `markdown-mode`. Maybe just copy the buffer to another buffer and change mode based on the original buffer's file name? 1018 | NOTE: This isn't completely done, but =language-detection= does most of what we want. 1019 | 1020 | Archived entries from file /Users/mn/TODO.org 1021 | 1022 | 1023 | * DONE Add indicators to modeline for: [3/3] 1024 | :PROPERTIES: 1025 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1026 | :ARCHIVE_FILE: ~/TODO.org 1027 | :ARCHIVE_OLPATH: Dotfiles TODO List 1028 | :ARCHIVE_CATEGORY: TODO 1029 | :ARCHIVE_TODO: DONE 1030 | :END: 1031 | - State "DONE" from "TODO" [2018-08-23 Thu 12:56] 1032 | ** DONE outline-minor-mode 1033 | - State "DONE" from "TODO" [2018-08-23 Thu 11:57] 1034 | ** DONE hs-minor-mode 1035 | - State "DONE" from "TODO" [2018-08-23 Thu 12:56] 1036 | ** DONE Narrowing 1037 | - State "DONE" from "TODO" [2018-08-23 Thu 12:56] 1038 | 1039 | Archived entries from file /Users/mn/TODO.org 1040 | 1041 | 1042 | * DONE =ssh= Can't run commands at all 1043 | :PROPERTIES: 1044 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1045 | :ARCHIVE_FILE: ~/TODO.org 1046 | :ARCHIVE_OLPATH: Dotfiles TODO List 1047 | :ARCHIVE_CATEGORY: TODO 1048 | :ARCHIVE_TODO: DONE 1049 | :END: 1050 | - State "DONE" from "TODO" [2018-08-26 Sun 08:47] 1051 | Error: =Wrong type argument, stringp, nil= 1052 | 1053 | Archived entries from file /Users/mn/TODO.org 1054 | 1055 | 1056 | * DONE Make function to search =~/org= 1057 | :PROPERTIES: 1058 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1059 | :ARCHIVE_FILE: ~/TODO.org 1060 | :ARCHIVE_OLPATH: Dotfiles TODO List 1061 | :ARCHIVE_CATEGORY: TODO 1062 | :ARCHIVE_TODO: DONE 1063 | :END: 1064 | - State "DONE" from "TODO" [2018-08-30 Thu 11:56] 1065 | "C-c s" 1066 | 1067 | Archived entries from file /Users/mn/TODO.org 1068 | 1069 | 1070 | * DONE Fix tldr.el 1071 | :PROPERTIES: 1072 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1073 | :ARCHIVE_FILE: ~/TODO.org 1074 | :ARCHIVE_OLPATH: Dotfiles TODO List 1075 | :ARCHIVE_CATEGORY: TODO 1076 | :ARCHIVE_TODO: DONE 1077 | :END: 1078 | - State "DONE" from "TODO" [2018-08-30 Thu 11:58] 1079 | Apparently the issue can be worked around by first running =tldr-update-docs= 1080 | 1081 | Archived entries from file /Users/mn/TODO.org 1082 | 1083 | 1084 | * DONE Fix Eshell / tramp tab completion adding a space after the file name 1085 | :PROPERTIES: 1086 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1087 | :ARCHIVE_FILE: ~/TODO.org 1088 | :ARCHIVE_OLPATH: Dotfiles TODO List 1089 | :ARCHIVE_CATEGORY: TODO 1090 | :ARCHIVE_TODO: DONE 1091 | :END: 1092 | - State "DONE" from "TODO" [2018-09-05 Wed 13:20] 1093 | - State "DONE" from "WIP" [2018-08-23 Thu 15:16] 1094 | This is a result of my config. Maybe the Eshell completion packages? 1095 | It's either: 1096 | ** DONE bash-completion 1097 | - State "DONE" from "TODO" [2018-09-05 Wed 13:19] 1098 | ** DONE fish-completion 1099 | - State "DONE" from "TODO" [2018-09-05 Wed 13:20] 1100 | Yes, it was fish-completion 1101 | 1102 | Archived entries from file /Users/mn/TODO.org 1103 | 1104 | 1105 | * DONE Organize or even get rid of mac-key-mode 1106 | :PROPERTIES: 1107 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1108 | :ARCHIVE_FILE: ~/TODO.org 1109 | :ARCHIVE_OLPATH: Dotfiles TODO List 1110 | :ARCHIVE_CATEGORY: TODO 1111 | :ARCHIVE_TODO: DONE 1112 | :END: 1113 | - State "DONE" from "TODO" [2018-09-08 Sat 18:31] 1114 | 1115 | Archived entries from file /Users/mn/TODO.org 1116 | 1117 | 1118 | * DONE Finish =org-todo-cycle= 1119 | :PROPERTIES: 1120 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1121 | :ARCHIVE_FILE: ~/TODO.org 1122 | :ARCHIVE_OLPATH: Dotfiles TODO List 1123 | :ARCHIVE_CATEGORY: TODO 1124 | :ARCHIVE_TODO: DONE 1125 | :END: 1126 | - State "DONE" from "TODO" [2018-10-01 Mon 03:49] 1127 | This is =org=shiftleft= 1128 | 1129 | Archived entries from file /Users/mn/TODO.org 1130 | 1131 | 1132 | * DONE Create binding for =org-todo= -> TODO 1133 | :PROPERTIES: 1134 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1135 | :ARCHIVE_FILE: ~/TODO.org 1136 | :ARCHIVE_OLPATH: Dotfiles TODO List 1137 | :ARCHIVE_CATEGORY: TODO 1138 | :ARCHIVE_TODO: DONE 1139 | :END: 1140 | - State "DONE" from "WIP" [2018-10-01 Mon 03:44] 1141 | 1142 | Archived entries from file /Users/mn/TODO.org 1143 | 1144 | 1145 | * DONE Try moody 1146 | :PROPERTIES: 1147 | :ARCHIVE_TIME: 2018-10-03 Wed 07:50 1148 | :ARCHIVE_FILE: ~/TODO.org 1149 | :ARCHIVE_OLPATH: Dotfiles TODO List 1150 | :ARCHIVE_CATEGORY: TODO 1151 | :ARCHIVE_TODO: DONE 1152 | :END: 1153 | - State "DONE" from "TODO" [2018-10-01 Mon 03:21] 1154 | 1155 | * DONE =shell= doesn't output anything! 1156 | :PROPERTIES: 1157 | :ARCHIVE_TIME: 2018-10-09 Tue 17:19 1158 | :ARCHIVE_FILE: ~/TODO.org 1159 | :ARCHIVE_CATEGORY: TODO 1160 | :ARCHIVE_TODO: DONE 1161 | :END: 1162 | - State "DONE" from "NEXT" [2018-10-06 Sat 18:04] 1163 | 1164 | * DONE Make ssh with =dtach= actually work 1165 | :PROPERTIES: 1166 | :ARCHIVE_TIME: 2019-05-15 Wed 09:14 1167 | :ARCHIVE_FILE: ~/TODO.org 1168 | :ARCHIVE_CATEGORY: TODO 1169 | :ARCHIVE_TODO: DONE 1170 | :END: 1171 | - State "DONE" from "WIP" [2018-11-18 Sun 18:04] 1172 | See https://github.com/Ambrevar/dotfiles/blob/master/.emacs.d/lisp/package-eshell-detach.el 1173 | 1174 | * DONE ~man~ should select the window 1175 | :PROPERTIES: 1176 | :ARCHIVE_TIME: 2019-05-15 Wed 09:15 1177 | :ARCHIVE_FILE: ~/TODO.org 1178 | :ARCHIVE_CATEGORY: TODO 1179 | :ARCHIVE_TODO: DONE 1180 | :END: 1181 | 1182 | * DONE ~man~ should be able to search manpages on remote system (default to this when using tramp?) 1183 | :PROPERTIES: 1184 | :ARCHIVE_TIME: 2019-05-15 Wed 09:15 1185 | :ARCHIVE_FILE: ~/TODO.org 1186 | :ARCHIVE_CATEGORY: TODO 1187 | :ARCHIVE_TODO: DONE 1188 | :END: 1189 | https://emacs.stackexchange.com/questions/20524/can-emacs-eshell-show-remote-man-pages-in-tramp-mode?rq=1 1190 | https://stackoverflow.com/questions/22957137/how-to-make-emacs-man-work-over-tramp#22962342 1191 | 1192 | * DONE Make command to tail a file [2/2] 1193 | :PROPERTIES: 1194 | :ARCHIVE_TIME: 2019-05-15 Wed 09:15 1195 | :ARCHIVE_FILE: ~/TODO.org 1196 | :ARCHIVE_CATEGORY: TODO 1197 | :ARCHIVE_TODO: DONE 1198 | :END: 1199 | ** DONE command 1200 | ** DONE key binding in dired 1201 | 1202 | * DONE WIP Emacs recoll 1203 | :PROPERTIES: 1204 | :ARCHIVE_TIME: 2019-05-15 Wed 09:15 1205 | :ARCHIVE_FILE: ~/TODO.org 1206 | :ARCHIVE_CATEGORY: TODO 1207 | :ARCHIVE_TODO: DONE 1208 | :END: 1209 | No longer using recoll--use spotlight instead 1210 | https://oremacs.com/2015/07/27/counsel-recoll/ 1211 | https://opensourceprojects.eu/p/recoll1/tickets/61/ 1212 | 1213 | * DONE Get tldr to work 1214 | :PROPERTIES: 1215 | :ARCHIVE_TIME: 2019-05-15 Wed 09:15 1216 | :ARCHIVE_FILE: ~/TODO.org 1217 | :ARCHIVE_CATEGORY: TODO 1218 | :ARCHIVE_TODO: DONE 1219 | :END: 1220 | - State "DONE" from "WIP" [2018-11-18 Sun 18:04] 1221 | 1222 | * DONE Eshell =rm= hangs when pressing =RET= 1223 | :PROPERTIES: 1224 | :ARCHIVE_TIME: 2019-05-15 Wed 09:15 1225 | :ARCHIVE_FILE: ~/TODO.org 1226 | :ARCHIVE_CATEGORY: TODO 1227 | :ARCHIVE_TODO: TODO 1228 | :END: 1229 | - State "DONE" from "TODO" [2019-08-10 Sat 09:40] 1230 | - State "DONE" from "TODO" [2019-08-10 Sat 09:39] 1231 | Actually, it is working, it just takes a long time and gives zero feedback. This is especially problematic when working over slow links and performing bulk operations. Can we fix this? 1232 | ** Takes too long 1233 | ** Gives zero feedback (doesn't even go to new line when pressing =RET= until it is completely done) 1234 | ** Outputs =t= for each file deleted. It should give the same feedback as =*rm=. 1235 | 1236 | * DONE Find and squash causes of unresponsiveness in remote contexts [2/2] 1237 | :PROPERTIES: 1238 | :ARCHIVE_TIME: 2019-05-15 Wed 09:16 1239 | :ARCHIVE_FILE: ~/TODO.org 1240 | :ARCHIVE_CATEGORY: TODO 1241 | :ARCHIVE_TODO: DONE 1242 | :END: 1243 | ** DONE Editing 1244 | ** DONE Eshell 1245 | 1246 | * DONE =copy-region-other-window= and =move-region-other-window= should also copy the line if no region is selected 1247 | :PROPERTIES: 1248 | :ARCHIVE_TIME: 2019-05-15 Wed 09:16 1249 | :ARCHIVE_FILE: ~/TODO.org 1250 | :ARCHIVE_CATEGORY: TODO 1251 | :ARCHIVE_TODO: DONE 1252 | :END: 1253 | 1254 | * Takes too long 1255 | :PROPERTIES: 1256 | :ARCHIVE_TIME: 2019-05-15 Wed 09:20 1257 | :ARCHIVE_FILE: ~/TODO.org 1258 | :ARCHIVE_OLPATH: Eshell =rm= hangs when pressing =RET= 1259 | :ARCHIVE_CATEGORY: TODO 1260 | :END: 1261 | 1262 | * Gives zero feedback (doesn't even go to new line when pressing =RET= until it is completely done) 1263 | :PROPERTIES: 1264 | :ARCHIVE_TIME: 2019-05-15 Wed 09:20 1265 | :ARCHIVE_FILE: ~/TODO.org 1266 | :ARCHIVE_OLPATH: Eshell =rm= hangs when pressing =RET= 1267 | :ARCHIVE_CATEGORY: TODO 1268 | :END: 1269 | 1270 | * Outputs =t= for each file deleted. It should give the same feedback as =*rm=. 1271 | :PROPERTIES: 1272 | :ARCHIVE_TIME: 2019-05-15 Wed 09:20 1273 | :ARCHIVE_FILE: ~/TODO.org 1274 | :ARCHIVE_OLPATH: Eshell =rm= hangs when pressing =RET= 1275 | :ARCHIVE_CATEGORY: TODO 1276 | :END: 1277 | 1278 | * DONE Re-compile with new changes 1279 | :PROPERTIES: 1280 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1281 | :ARCHIVE_FILE: ~/TODO.org 1282 | :ARCHIVE_OLPATH: Mojave issues 1283 | :ARCHIVE_CATEGORY: TODO 1284 | :ARCHIVE_TODO: DONE 1285 | :END: 1286 | - State "DONE" from "WIP" [2018-10-02 Tue 21:33] 1287 | 1288 | * DONE Inserting characters is slow (try holding down a key--it doesn't keep up with repeat rate) 1289 | :PROPERTIES: 1290 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1291 | :ARCHIVE_FILE: ~/TODO.org 1292 | :ARCHIVE_OLPATH: Mojave issues 1293 | :ARCHIVE_CATEGORY: TODO 1294 | :ARCHIVE_TODO: DONE 1295 | :END: 1296 | - State "DONE" from "WIP" [2018-11-18 Sun 18:05] 1297 | This seems to be fixed for most buffers but Org buffers are still terrible 1298 | 1299 | * DONE Scrolling is slow 1300 | :PROPERTIES: 1301 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1302 | :ARCHIVE_FILE: ~/TODO.org 1303 | :ARCHIVE_OLPATH: Mojave issues 1304 | :ARCHIVE_CATEGORY: TODO 1305 | :ARCHIVE_TODO: DONE 1306 | :END: 1307 | - State "DONE" from "WIP" [2018-11-18 Sun 18:05] 1308 | 1309 | * DONE For the user 1310 | :PROPERTIES: 1311 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1312 | :ARCHIVE_FILE: ~/TODO.org 1313 | :ARCHIVE_OLPATH: Get Eshell/tramp to cache and/or save passwords 1314 | :ARCHIVE_CATEGORY: TODO 1315 | :ARCHIVE_TODO: DONE 1316 | :END: 1317 | 1318 | * DONE For sudo/sudoers - alias for su/sudo? 1319 | :PROPERTIES: 1320 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1321 | :ARCHIVE_FILE: ~/TODO.org 1322 | :ARCHIVE_OLPATH: Get Eshell/tramp to cache and/or save passwords 1323 | :ARCHIVE_CATEGORY: TODO 1324 | :ARCHIVE_TODO: DONE 1325 | :END: 1326 | 1327 | * DONE Get =pointhistory= to work 1328 | :PROPERTIES: 1329 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1330 | :ARCHIVE_FILE: ~/TODO.org 1331 | :ARCHIVE_CATEGORY: TODO 1332 | :ARCHIVE_TODO: DONE 1333 | :END: 1334 | - State "DONE" from "WIP" [2018-11-18 Sun 18:05] 1335 | Right now it jumps around in a way I don't understand. Maybe something to do with ring mechanics? 1336 | 1337 | * DONE Make Eshell prompt look good with a light theme 1338 | :PROPERTIES: 1339 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1340 | :ARCHIVE_FILE: ~/TODO.org 1341 | :ARCHIVE_CATEGORY: TODO 1342 | :ARCHIVE_TODO: DONE 1343 | :END: 1344 | 1345 | * DONE term-mode filter out escape sequence 1346 | :PROPERTIES: 1347 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1348 | :ARCHIVE_FILE: ~/TODO.org 1349 | :ARCHIVE_CATEGORY: TODO 1350 | :ARCHIVE_TODO: DONE 1351 | :END: 1352 | This happens when running =fish= in =shell-mode=: 1353 | =]0;fish= 1354 | Guessing this is the (remains of the?) xterm sequence to set the title? 1355 | 1356 | * DONE Fix Eshell/shell/term bouncing shell output up and down [3/3] 1357 | :PROPERTIES: 1358 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1359 | :ARCHIVE_FILE: ~/TODO.org 1360 | :ARCHIVE_CATEGORY: TODO 1361 | :ARCHIVE_TODO: DONE 1362 | :END: 1363 | Not sure if these are all the same issue. Fix term first, see if it affects the others. 1364 | Not a complete fix, but turned off xterm features. 1365 | ** DONE term - it isn't the same issue. It was the mouse scroll settings that appear to be completely unnecessary anyway. Removed them and it helped term-mode updates a lot. Did not affect shell/eshell bouncing. 1366 | - State "DONE" from "TODO" [2018-08-31 Fri 11:03] 1367 | ** DONE shell 1368 | ** DONE eshell 1369 | 1370 | * DONE Investigate Crux 1371 | :PROPERTIES: 1372 | :ARCHIVE_TIME: 2019-05-15 Wed 09:23 1373 | :ARCHIVE_FILE: ~/TODO.org 1374 | :ARCHIVE_CATEGORY: TODO 1375 | :ARCHIVE_TODO: DONE 1376 | :END: 1377 | https://github.com/bbatsov/crux/blob/master/crux.el 1378 | 1379 | * DONE Test clemera's undo config 1380 | :PROPERTIES: 1381 | :ARCHIVE_TIME: 2019-08-13 Tue 12:47 1382 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1383 | :ARCHIVE_CATEGORY: TODO 1384 | :ARCHIVE_TODO: DONE 1385 | :END: 1386 | - State "DONE" from "TODO" [2019-08-12 Mon 14:24] 1387 | 1388 | * DONE Add link type for Service Now requests (e.g. SER0242292) 1389 | :PROPERTIES: 1390 | :ARCHIVE_TIME: 2019-08-15 Thu 11:42 1391 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1392 | :ARCHIVE_CATEGORY: TODO 1393 | :ARCHIVE_TODO: DONE 1394 | :END: 1395 | - State "DONE" from "WIP" [2019-08-15 Thu 11:42] 1396 | 1397 | * DONE Add =psync_config= 1398 | :PROPERTIES: 1399 | :ARCHIVE_TIME: 2019-08-16 Fri 15:12 1400 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1401 | :ARCHIVE_CATEGORY: TODO 1402 | :ARCHIVE_TODO: DONE 1403 | :END: 1404 | - State "DONE" from "WIP" [2019-08-16 Fri 15:11] 1405 | 1406 | * DONE Rendering issues 1407 | :PROPERTIES: 1408 | :ARCHIVE_TIME: 2020-03-21 Sat 21:30 1409 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1410 | :ARCHIVE_OLPATH: Tasks 1411 | :ARCHIVE_CATEGORY: TODO 1412 | :ARCHIVE_TODO: DONE 1413 | :END: 1414 | - State "DONE" from "WIP" [2020-03-21 Sat 21:28] 1415 | - https://lists.gnu.org/archive/html/emacs-devel/2020-02/msg00079.html 1416 | - https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32932#412 1417 | 1418 | * DONE Improve =source-sh= 1419 | :PROPERTIES: 1420 | :ARCHIVE_TIME: 2020-03-21 Sat 21:30 1421 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1422 | :ARCHIVE_OLPATH: Tasks 1423 | :ARCHIVE_CATEGORY: TODO 1424 | :ARCHIVE_TODO: DONE 1425 | :END: 1426 | - State "DONE" from "WIP" [2020-03-21 Sat 21:28] 1427 | ** TODO Make it into a package? 1428 | ** TODO Make it async? 1429 | 1430 | * DONE Simplify =fiat-color.el= 1431 | :PROPERTIES: 1432 | :ARCHIVE_TIME: 2020-03-21 Sat 21:30 1433 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1434 | :ARCHIVE_OLPATH: Tasks 1435 | :ARCHIVE_CATEGORY: TODO 1436 | :ARCHIVE_TODO: DONE 1437 | :END: 1438 | - State "DONE" from "WIP" [2020-03-21 Sat 21:28] 1439 | 1440 | * DONE Figure out magit wip modes 1441 | :PROPERTIES: 1442 | :ARCHIVE_TIME: 2020-03-21 Sat 21:30 1443 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1444 | :ARCHIVE_OLPATH: Tasks 1445 | :ARCHIVE_CATEGORY: TODO 1446 | :ARCHIVE_TODO: DONE 1447 | :END: 1448 | 1449 | - State "DONE" from "WIP" [2020-03-21 Sat 21:29] 1450 | 1451 | * DONE Get Eshell/tramp to cache and/or save passwords [0/2] 1452 | :PROPERTIES: 1453 | :ARCHIVE_TIME: 2020-03-21 Sat 21:30 1454 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1455 | :ARCHIVE_OLPATH: Tasks 1456 | :ARCHIVE_CATEGORY: TODO 1457 | :ARCHIVE_TODO: DONE 1458 | :END: 1459 | - State "DONE" from "WIP" [2020-03-21 Sat 21:29] 1460 | Enable logging with =(setq auth-source-DEBUG T)= 1461 | ** TODO What about when sudo is called in other ways, such as from a script? it doesn't seem to cache the password then. 1462 | ** TODO How to ignore password prompting for just for a specific command? 1463 | See: 1464 | #+begin_src emacs-lisp 1465 | (add-hook 'comint-output-filter-functions 1466 | 'comint-watch-for-password-prompt) 1467 | #+end_src 1468 | This is already added. do we have to do something else? 1469 | 1470 | 1471 | * DONE dired-rainbow-x 1472 | :PROPERTIES: 1473 | :ARCHIVE_TIME: 2020-03-21 Sat 21:30 1474 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1475 | :ARCHIVE_OLPATH: Tasks 1476 | :ARCHIVE_CATEGORY: TODO 1477 | :ARCHIVE_TODO: DONE 1478 | :END: 1479 | - State "DONE" from "WIP" [2020-03-21 Sat 21:30] 1480 | ** TODO Fix remaining bugs 1481 | ** TODO Show to Fuco1 1482 | 1483 | 1484 | * DONE Make =mood-line= fast, particularly =shorten-file-name= 1485 | :PROPERTIES: 1486 | :ARCHIVE_TIME: 2020-08-05 Wed 12:02 1487 | :ARCHIVE_FILE: ~/.emacs.d/TODO.org 1488 | :ARCHIVE_OLPATH: Tasks 1489 | :ARCHIVE_CATEGORY: TODO 1490 | :ARCHIVE_TODO: DONE 1491 | :END: 1492 | - State "DONE" from "WAIT" [2020-08-05 Wed 12:02] 1493 | --------------------------------------------------------------------------------