├── .gitattributes ├── .github └── workflows │ └── test.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── clojure-essential-ref-nov.el ├── clojure-essential-ref.el └── makem.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | makem.sh linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # * test.yml --- Test Emacs packages using makem.sh on GitHub Actions 2 | 3 | # https://github.com/alphapapa/makem.sh 4 | 5 | # Based on Steve Purcell's examples at 6 | # , 7 | # . 8 | 9 | # * License: 10 | 11 | # This program is free software; you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, either version 3 of the License, or 14 | # (at your option) any later version. 15 | 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | 21 | # You should have received a copy of the GNU General Public License 22 | # along with this program. If not, see . 23 | 24 | # * Code: 25 | 26 | name: "CI" 27 | on: 28 | pull_request: 29 | push: 30 | # Comment out this section to enable testing of all branches. 31 | branches: 32 | - master 33 | 34 | jobs: 35 | build: 36 | runs-on: ubuntu-latest 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | emacs_version: 41 | - 26.3 42 | - snapshot 43 | steps: 44 | - uses: purcell/setup-emacs@master 45 | with: 46 | version: ${{ matrix.emacs_version }} 47 | 48 | - uses: actions/checkout@v2 49 | 50 | - name: Initialize sandbox 51 | run: | 52 | SANDBOX_DIR=$(mktemp -d) || exit 1 53 | echo ::set-env name=SANDBOX_DIR::$SANDBOX_DIR 54 | ./makem.sh -vv --sandbox=$SANDBOX_DIR --install-deps --install-linters 55 | 56 | # The "all" rule is not used, because it treats compilation warnings 57 | # as failures, so linting and testing are run as separate steps. 58 | 59 | - name: Lint 60 | # NOTE: Uncomment this line to treat lint failures as passing 61 | # so the job doesn't show failure. 62 | # continue-on-error: true 63 | run: ./makem.sh -vv --sandbox=$SANDBOX_DIR lint 64 | 65 | - name: Test 66 | if: always() # Run test even if linting fails. 67 | run: ./makem.sh -vv --sandbox=$SANDBOX_DIR test 68 | 69 | # Local Variables: 70 | # eval: (outline-minor-mode) 71 | # End: 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled 2 | *.elc 3 | 4 | # Packaging 5 | .cask 6 | 7 | # Backup files 8 | *~ 9 | 10 | # Undo-tree save-files 11 | *.~undo-tree 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jordan Besly 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # * makem.sh/Makefile --- Script to aid building and testing Emacs Lisp packages 2 | 3 | # This Makefile is from the makem.sh repo: . 4 | 5 | # * Arguments 6 | 7 | # For consistency, we use only var=val options, not hyphen-prefixed options. 8 | 9 | # NOTE: I don't like duplicating the arguments here and in makem.sh, 10 | # but I haven't been able to find a way to pass arguments which 11 | # conflict with Make's own arguments through Make to the script. 12 | # Using -- doesn't seem to do it. 13 | 14 | ifdef install-deps 15 | INSTALL_DEPS = "--install-deps" 16 | endif 17 | ifdef install-linters 18 | INSTALL_LINTERS = "--install-linters" 19 | endif 20 | 21 | ifdef sandbox 22 | ifeq ($(sandbox), t) 23 | SANDBOX = --sandbox 24 | else 25 | SANDBOX = --sandbox=$(sandbox) 26 | endif 27 | endif 28 | 29 | ifdef debug 30 | DEBUG = "--debug" 31 | endif 32 | 33 | # ** Verbosity 34 | 35 | # Since the "-v" in "make -v" gets intercepted by Make itself, we have 36 | # to use a variable. 37 | 38 | verbose = $(v) 39 | 40 | ifneq (,$(findstring vv,$(verbose))) 41 | VERBOSE = "-vv" 42 | else ifneq (,$(findstring v,$(verbose))) 43 | VERBOSE = "-v" 44 | endif 45 | 46 | # * Rules 47 | 48 | # TODO: Handle cases in which "test" or "tests" are called and a 49 | # directory by that name exists, which can confuse Make. 50 | 51 | %: 52 | @./makem.sh $(DEBUG) $(VERBOSE) $(SANDBOX) $(INSTALL_DEPS) $(INSTALL_LINTERS) $(@) 53 | 54 | .DEFAULT: init 55 | init: 56 | @./makem.sh $(DEBUG) $(VERBOSE) $(SANDBOX) $(INSTALL_DEPS) $(INSTALL_LINTERS) 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clojure-essential-ref 2 | 3 | Emacs packages providing commands to browse the Clojure documentation of a symbol in book [Clojure, The Essential Reference](https://livebook.manning.com/book/clojure-the-essential-reference/): 4 | 5 | - `clojure-essential-ref`: browse the default way (see _Configuration_) 6 | - `clojure-essential-ref-web`: browse online in the web version of the book (_liveBook_) 7 | - `clojure-essential-ref-nov`: browse offline in a local ebook (provided by optional sibling package `clojure-essential-ref-nov`, depends on [nov.el](https://depp.brause.cc/nov.el/)) 8 | 9 | They require a CIDER session to be launched to perform fully-qualified symbol resolution. Likewise, the source file must be known to CIDER. So, when adding a new source file during interactive development your might need to re-evaluate a `require` of this source file or even `cider-eval-buffer` it. 10 | 11 | They behave similarly to `cider-clojuredocs-web`, including the default proposal of _symbol-at-point_. They are a nice companion to the latter (alongside `cider-doc`). 12 | 13 | For more context, read the [accompanying blog post](https://www.eigenbahn.com/2020/06/04/emacs-clojure-essential-ref). 14 | 15 | 16 | ## Installation 17 | 18 | To get only the web browsing mode: 19 | 20 | ```el 21 | (use-package clojure-essential-ref) 22 | ``` 23 | 24 | [![MELPA](https://melpa.org/packages/clojure-essential-ref-badge.svg)](https://melpa.org/#/clojure-essential-ref) 25 | [![MELPA Stable](https://stable.melpa.org/packages/clojure-essential-ref-badge.svg)](https://stable.melpa.org/#/clojure-essential-ref) 26 | 27 | To also get the offline ebook browsing mode (depends on [nov.el](https://depp.brause.cc/nov.el/)): 28 | 29 | ```el 30 | (use-package clojure-essential-ref-nov 31 | :init 32 | (setq clojure-essential-ref-nov-epub-path "~/Downloads/Clojure_The_Essential_Reference_v29_MEAP.epub")) 33 | ``` 34 | 35 | [![MELPA](https://melpa.org/packages/clojure-essential-ref-nov-badge.svg)](https://melpa.org/#/clojure-essential-ref-nov) 36 | [![MELPA Stable](https://stable.melpa.org/packages/clojure-essential-ref-nov-badge.svg)](https://stable.melpa.org/#/clojure-essential-ref-nov) 37 | 38 | ## Configuration 39 | 40 | #### ebook file 41 | 42 | The offline ebook browing mode needs you to configure the path to the book (EPUB format): 43 | 44 | ```el 45 | (use-package clojure-essential-ref-nov 46 | ;; ... 47 | 48 | :init 49 | (setq clojure-essential-ref-nov-epub-path "~/Downloads/Clojure_The_Essential_Reference_v29_MEAP.epub") 50 | ``` 51 | 52 | #### Default browsing mode 53 | 54 | The default browing mode (command `clojure-essential-ref`) is the online _liveBook_. 55 | 56 | To use the offline ebook browing mode instead: 57 | 58 | ```el 59 | (use-package clojure-essential-ref-nov 60 | ;; ... 61 | 62 | :init 63 | (setq clojure-essential-ref-default-browse-fn #'clojure-essential-ref-nov-browse) 64 | ``` 65 | 66 | 67 | ## Usage 68 | 69 | Under a CIDER session, just call the command: 70 | 71 | M-x clojure-essential-ref 72 | 73 | For convenience sake, you can bind it to a keyboard shortcut: 74 | 75 | ```el 76 | (use-package clojure-essential-ref 77 | ;; ... 78 | :bind ( 79 | :map cider-mode-map 80 | ("C-h F" . clojure-essential-ref) 81 | :map cider-repl-mode-map 82 | ("C-h F" . clojure-essential-ref))) 83 | ``` 84 | 85 | 86 | ## Legibility 87 | 88 | This code uses form feeds (`^L` character) as separators. 89 | 90 | Either package [form-feed](https://github.com/wasamasa/form-feed) or [page-break-lines](https://github.com/purcell/page-break-lines) makes them appear as intended. 91 | -------------------------------------------------------------------------------- /clojure-essential-ref-nov.el: -------------------------------------------------------------------------------- 1 | ;;; clojure-essential-ref-nov.el --- Cider-doc to "Clojure, The Essential Reference" (EPUB) -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2020 Jordan Besly 4 | ;; 5 | ;; Version: 0.1.0 6 | ;; URL: https://github.com/p3r7/clojure-essential-ref 7 | ;; Package-Requires: ((emacs "24")(dash "2.16.0")(nov "0.3.1")(clojure-essential-ref "0.1.0")) 8 | ;; 9 | ;; SPDX-License-Identifier: MIT 10 | 11 | ;;; Commentary: 12 | ;; 13 | ;; Provides command `clojure-essential-ref-nov' to browse offline the 14 | ;; documentation for symbol in ebook version of book "Clojure, The 15 | ;; Essential Reference". 16 | ;; 17 | ;; Works similarly to `cider-clojuredocs-web'. 18 | ;; 19 | ;; This package is a child package of `clojure-essential-ref'. 20 | ;; 21 | ;; You can make command `clojure-essential-ref' browse to the EPUB version (instead of the online "liveBook") by putting this in your Emacs init: 22 | ;; 23 | ;; (setq clojure-essential-ref-default-browse-fn #'clojure-essential-ref-nov-browse) 24 | 25 | ;;; Code: 26 | 27 | 28 | 29 | ;; REQUIRES 30 | 31 | (require 'dash) 32 | (require 'nov) 33 | 34 | (require 'clojure-essential-ref) 35 | 36 | 37 | 38 | ;; CONFIG 39 | 40 | (defvar clojure-essential-ref-nov-epub-path "" "Path of local ebook.") 41 | 42 | 43 | 44 | ;; nov.el WRAPPERS 45 | 46 | (defun clojure-essential-ref-nov--buffer () 47 | "Get or create nov.el buffer for the book." 48 | (let* ((buff-name (file-name-nondirectory clojure-essential-ref-nov-epub-path)) 49 | (matching-p (lambda (b) 50 | (and (string= (buffer-name b) buff-name) 51 | b))) 52 | (buff (-some matching-p (buffer-list)))) 53 | (if buff 54 | buff 55 | (find-file-noselect clojure-essential-ref-nov-epub-path)))) 56 | 57 | (defun clojure-essential-ref-nov--get-index (section) 58 | "Get ebook url for SECTION from table of content." 59 | (with-current-buffer (clojure-essential-ref-nov--buffer) 60 | (nov-goto-toc) 61 | (goto-char (point-min)) 62 | (search-forward section) 63 | (get-text-property (point) 'shr-url))) 64 | 65 | 66 | (defun clojure-essential-ref-nov--browse-section (section) 67 | "Browse to SECTION of ebook." 68 | (let ((url (clojure-essential-ref-nov--get-index section)) 69 | (buff (clojure-essential-ref-nov--buffer))) 70 | (switch-to-buffer-other-window buff) 71 | (apply 'nov-visit-relative-file (nov-url-filename-and-target url)))) 72 | 73 | 74 | 75 | ;; COMMAND 76 | 77 | ;;;###autoload 78 | (defun clojure-essential-ref-nov (&optional arg) 79 | "Open Clojure documentation for symbol in local ebook. 80 | 81 | Offline ebook version of book \"Clojure, The Essential 82 | Reference\" is used as a documentation source. 83 | 84 | Prompts for the symbol to use, or uses the symbol at point, depending on 85 | the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the 86 | opposite of what that option dictates." 87 | (interactive "P") 88 | (let ((clojure-essential-ref-default-browse-fn #'clojure-essential-ref-nov-browse)) 89 | (if (called-interactively-p 'any) 90 | (call-interactively #'clojure-essential-ref nil (vector arg)) 91 | (funcall #'clojure-essential-ref arg)))) 92 | 93 | 94 | 95 | ;; BROWSE 96 | 97 | (defun clojure-essential-ref-nov-browse (symbol) 98 | "Open doc in Clojure Essential Ref for SYMBOL in local ebook." 99 | (unless clojure-essential-ref-nov-epub-path 100 | (error "Path of ebook (var `clojure-essential-ref-nov-epub-path') needs to be configured")) 101 | (unless (file-exists-p clojure-essential-ref-nov-epub-path) 102 | (error "Configured Path of ebook (\"%s\", var `clojure-essential-ref-nov-epub-path') does not exist" clojure-essential-ref-nov-epub-path)) 103 | 104 | (let ((props (clojure-essential-ref--get-props symbol))) 105 | (unless props 106 | (error "Couldn't find reference to %s in book index" symbol)) 107 | (clojure-essential-ref-nov--browse-section (plist-get props :section)))) 108 | 109 | 110 | 111 | 112 | (provide 'clojure-essential-ref-nov) 113 | 114 | ;;; clojure-essential-ref-nov.el ends here 115 | -------------------------------------------------------------------------------- /clojure-essential-ref.el: -------------------------------------------------------------------------------- 1 | ;;; clojure-essential-ref.el --- Cider-doc to "Clojure, The Essential Reference" -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2020 Jordan Besly 4 | ;; 5 | ;; Version: 0.1.0 6 | ;; URL: https://github.com/p3r7/clojure-essential-ref 7 | ;; Package-Requires: ((emacs "24")(cider "0.24.0")) 8 | ;; 9 | ;; SPDX-License-Identifier: MIT 10 | 11 | ;;; Commentary: 12 | ;; 13 | ;; Provides command `clojure-essential-ref' to browse the documentation for 14 | ;; symbol in book "Clojure, The Essential Reference". 15 | ;; 16 | ;; Works similarly to `cider-clojuredocs-web'. 17 | ;; 18 | ;; By default, browse online in a web browser ("liveBook" version) as if 19 | ;; calling explicit command `clojure-essential-ref-web'. 20 | ;; 21 | ;; Using sibling package `clojure-essential-ref-nov' one could browse 22 | ;; offline to the ebook version (command `clojure-essential-ref-nov'). 23 | 24 | ;;; Code: 25 | 26 | 27 | 28 | ;; REQUIRES 29 | 30 | (require 'nrepl-dict) 31 | (require 'cider-common) 32 | (require 'cider-connection) 33 | (require 'cider-client) 34 | 35 | 36 | 37 | ;; CONFIG 38 | 39 | (defvar clojure-essential-ref-default-browse-fn #'clojure-essential-ref-browse-web) 40 | 41 | (defvar clojure-essential-ref--base-url "https://livebook.manning.com/book/clojure-the-essential-reference" 42 | "Base URL for the \"livebook\" version of the book.") 43 | 44 | (defvar clojure-essential-ref--build-url-fn #'clojure-essential-ref--build-url-from-section-id) 45 | 46 | 47 | 48 | ;; BOOK INDEX 49 | 50 | (defvar clojure-essential-ref--index 51 | '( 52 | ;; fn creation & manipulation 53 | ("defn" . (:url "/chapter-2/v-29/10" :section "2.1.1")) 54 | ("defn-" . (:url "/chapter-2/v-29/10" :section "2.1.1")) 55 | ("fn" . (:url "/chapter-2/v-29/77" :section "2.1.2")) 56 | ("fn*" . (:url "/chapter-2/v-29/116" :section "2.1.3")) 57 | ("fnil" . (:url "/chapter-2/v-29/153" :section "2.2.1")) 58 | ("comp" . (:url "/chapter-2/v-29/179" :section "2.2.2")) 59 | ("complement" . (:url "/chapter-2/v-29/210" :section "2.2.3")) 60 | ("constantly" . (:url "/chapter-2/v-29/234" :section "2.2.4")) 61 | ("identity" . (:url "/chapter-2/v-29/257" :section "2.2.5")) 62 | ("juxt" . (:url "/chapter-2/v-29/284" :section "2.2.6")) 63 | ("memfn" . (:url "/chapter-2/v-29/321" :section "2.2.7")) 64 | ("partial" . (:url "/chapter-2/v-29/358" :section "2.2.8")) 65 | ("pred" . (:url "/chapter-2/v-29/393" :section "2.2.9")) 66 | ("every" . (:url "/chapter-2/v-29/393" :section "2.2.9")) 67 | ("fn" . (:url "/chapter-2/v-29/393" :section "2.2.9")) 68 | ("some" . (:url "/chapter-2/v-29/393" :section "2.2.9")) 69 | ("->" . (:url "/chapter-2/v-29/451" :section "2.3.1")) 70 | ("->>" . (:url "/chapter-2/v-29/504" :section "2.3.2")) 71 | ("cond->" . (:url "/chapter-2/v-29/531" :section "2.3.3")) 72 | ("cond->>" . (:url "/chapter-2/v-29/531" :section "2.3.3")) 73 | ("some->" . (:url "/chapter-2/v-29/565" :section "2.3.4")) 74 | ("some->>" . (:url "/chapter-2/v-29/567" :section "2.3.5")) 75 | ("as->" . (:url "/chapter-2/v-29/596" :section "2.3.6")) 76 | ("apply" . (:url "/chapter-2/v-29/629" :section "2.4.1")) 77 | ("memoize" . (:url "/chapter-2/v-29/668" :section "2.4.2")) 78 | ("trampoline" . (:url "/chapter-2/v-29/709" :section "2.4.3")) 79 | 80 | ;; basic constructs 81 | ("let" . (:url "/chapter-3/v-29/15" :section "3.1.1")) 82 | ("let*" . (:url "/chapter-3/v-29/15" :section "3.1.1")) 83 | ("if-let" . (:url "/chapter-3/v-29/55" :section "3.1.2")) 84 | ("when-let" . (:url "/chapter-3/v-29/55" :section "3.1.2")) 85 | ("if-some" . (:url "/chapter-3/v-29/55" :section "3.1.2")) 86 | ("when-some" . (:url "/chapter-3/v-29/55" :section "3.1.2")) 87 | ("letfn" . (:url "/chapter-3/v-29/105" :section "3.1.3")) 88 | ("letfn*" . (:url "/chapter-3/v-29/105" :section "3.1.3")) 89 | ("not" . (:url "/chapter-3/v-29/159" :section "3.2.1")) 90 | ("and" . (:url "/chapter-3/v-29/185" :section "3.2.2")) 91 | ("or" . (:url "/chapter-3/v-29/185" :section "3.2.2")) 92 | ("bit-and" . (:url "/chapter-3/v-29/228" :section "3.2.3")) 93 | ("bit-or" . (:url "/chapter-3/v-29/228" :section "3.2.3")) 94 | ("if" . (:url "/chapter-3/v-29/303" :section "3.3.1")) 95 | ("if-not" . (:url "/chapter-3/v-29/303" :section "3.3.1")) 96 | ("when" . (:url "/chapter-3/v-29/303" :section "3.3.1")) 97 | ("when-not" . (:url "/chapter-3/v-29/303" :section "3.3.1")) 98 | ("cond" . (:url "/chapter-3/v-29/346" :section "3.3.2")) 99 | ("condp" . (:url "/chapter-3/v-29/389" :section "3.3.3")) 100 | ("case" . (:url "/chapter-3/v-29/438" :section "3.3.4")) 101 | ("loop" . (:url "/chapter-3/v-29/527" :section "3.4.1")) 102 | ("recur" . (:url "/chapter-3/v-29/527" :section "3.4.1")) 103 | ("loop*" . (:url "/chapter-3/v-29/527" :section "3.4.1")) 104 | ("range" . (:url "/chapter-3/v-29/594" :section "3.4.2")) 105 | ("for" . (:url "/chapter-3/v-29/644" :section "3.4.3")) 106 | ("while" . (:url "/chapter-3/v-29/691" :section "3.4.4")) 107 | ("dotimes" . (:url "/chapter-3/v-29/728" :section "3.4.5")) 108 | ("first" . (:url "/chapter-3/v-29/785" :section "3.5.1")) 109 | ("second" . (:url "/chapter-3/v-29/785" :section "3.5.1")) 110 | ("last" . (:url "/chapter-3/v-29/785" :section "3.5.1")) 111 | ("map" . (:url "/chapter-3/v-29/849" :section "3.5.2")) 112 | ("map-indexed" . (:url "/chapter-3/v-29/849" :section "3.5.2")) 113 | ("filter" . (:url "/chapter-3/v-29/908" :section "3.5.3")) 114 | ("remove" . (:url "/chapter-3/v-29/908" :section "3.5.3")) 115 | ("reduce" . (:url "/chapter-3/v-29/941" :section "3.5.4")) 116 | ("reductions" . (:url "/chapter-3/v-29/941" :section "3.5.4")) 117 | ("reductions" . (:url "/chapter-4/v-29/17" :section "4.1")) 118 | ("&form" . (:url "/chapter-4/v-29/58" :section "4.1.2")) ; NB: implicit arg 119 | ("&env" . (:url "/chapter-4/v-29/66" :section "4.1.3")) ; NB: implicit arg 120 | ("macroexpand" . (:url "/chapter-4/v-29/103" :section "4.2")) 121 | ("macroexpand-1" . (:url "/chapter-4/v-29/103" :section "4.2")) 122 | ("clojure.walk/macroexpand-all" . (:url "/chapter-4/v-29/103" :section "4.2")) 123 | ("quote" . (:url "/chapter-4/v-29/131" :section "4.3")) 124 | ("gensym" . (:url "/chapter-4/v-29/156" :section "4.4")) 125 | ("definline" . (:url "/chapter-4/v-29/183" :section "4.5")) 126 | ("destructure" . (:url "/chapter-4/v-29/231" :section "4.6")) 127 | ("clojure.template/apply-template" . (:url "/chapter-4/v-29/272" :section "4.7")) 128 | ("clojure.template/do-template" . (:url "/chapter-4/v-29/301" :section "4.8")) 129 | 130 | ;; operations on numbers 131 | ("+" . (:url "/chapter-5/v-29/19" :section "5.1")) 132 | ("-" . (:url "/chapter-5/v-29/19" :section "5.1")) 133 | ("*" . (:url "/chapter-5/v-29/19" :section "5.1")) 134 | ("/" . (:url "/chapter-5/v-29/19" :section "5.1")) 135 | ("inc" . (:url "/chapter-5/v-29/95" :section "5.2")) 136 | ("dec" . (:url "/chapter-5/v-29/95" :section "5.2")) 137 | ("quot" . (:url "/chapter-5/v-29/133" :section "5.3")) 138 | ("rem" . (:url "/chapter-5/v-29/133" :section "5.3")) 139 | ("mod" . (:url "/chapter-5/v-29/133" :section "5.3")) 140 | ("max" . (:url "/chapter-5/v-29/168" :section "5.4")) 141 | ("min" . (:url "/chapter-5/v-29/168" :section "5.4")) 142 | ("max-key" . (:url "/chapter-5/v-29/199" :section "5.5")) 143 | ("min-key" . (:url "/chapter-5/v-29/199" :section "5.5")) 144 | ("rand" . (:url "/chapter-5/v-29/231" :section "5.6")) 145 | ("rand-int" . (:url "/chapter-5/v-29/231" :section "5.6")) 146 | ("with-precision" . (:url "/chapter-5/v-29/264" :section "5.7")) 147 | ("+'" . (:url "/chapter-5/v-29/286" :section "5.8")) 148 | ("-'" . (:url "/chapter-5/v-29/286" :section "5.8")) 149 | ("*'" . (:url "/chapter-5/v-29/286" :section "5.8")) 150 | ("inc'" . (:url "/chapter-5/v-29/286" :section "5.8")) 151 | ("dec'" . (:url "/chapter-5/v-29/286" :section "5.8")) 152 | ("unchecked-add" . (:url "/chapter-5/v-29/311" :section "5.9")) 153 | ("unchecked-add-int" . (:url "/chapter-5/v-29/346" :section "5.10")) 154 | 155 | ;; comparison & equality 156 | ("=" . (:url "/chapter-6/v-29/15" :section "6.1")) 157 | ("not=" . (:url "/chapter-6/v-29/15" :section "6.1")) 158 | ("==" . (:url "/chapter-6/v-29/81" :section "6.2")) 159 | ("<" . (:url "/chapter-6/v-29/118" :section "6.3")) 160 | (">" . (:url "/chapter-6/v-29/118" :section "6.3")) 161 | ("<=" . (:url "/chapter-6/v-29/118" :section "6.3")) 162 | (">=" . (:url "/chapter-6/v-29/118" :section "6.3")) 163 | ("compare" . (:url "/chapter-6/v-29/162" :section "6.4")) 164 | ("identical?" . (:url "/chapter-6/v-29/227" :section "6.5")) 165 | ("hash" . (:url "/chapter-6/v-29/270" :section "6.6")) 166 | ("clojure.data/diff" . (:url "/chapter-6/v-29/310" :section "6.7")) 167 | 168 | ;; reducers & transducers 169 | ("clojure.core.reducers/fold" . (:url "/chapter-7/v-29/22" :section "7.1.1")) 170 | ("clojure.core.reducers/reducer" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 171 | ("clojure.core.reducers/folder" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 172 | ("clojure.core.reducers/map" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 173 | ("clojure.core.reducers/mapcat" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 174 | ("clojure.core.reducers/filter" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 175 | ("clojure.core.reducers/remove" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 176 | ("clojure.core.reducers/flatten" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 177 | ("clojure.core.reducers/take-while" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 178 | ("clojure.core.reducers/take" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 179 | ("clojure.core.reducers/drop" . (:url "/chapter-7/v-29/70" :section "7.1.2")) 180 | ("clojure.core.reducers/monoid" . (:url "/chapter-7/v-29/115" :section "7.1.3")) 181 | ("clojure.core.reducers/foldcat" . (:url "/chapter-7/v-29/149" :section "7.1.4")) 182 | ("clojure.core.reducers/cat" . (:url "/chapter-7/v-29/149" :section "7.1.4")) 183 | ("clojure.core.reducers/append!" . (:url "/chapter-7/v-29/149" :section "7.1.4")) 184 | ("transduce" . (:url "/chapter-7/v-29/229" :section "7.2.1")) 185 | ("eduction" . (:url "/chapter-7/v-29/269" :section "7.2.2")) 186 | ("completing" . (:url "/chapter-7/v-29/314" :section "7.2.3")) 187 | ("cat" . (:url "/chapter-7/v-29/355" :section "7.2.4")) 188 | ("reduced" . (:url "/chapter-7/v-29/386" :section "7.3")) 189 | ("reduced?" . (:url "/chapter-7/v-29/386" :section "7.3")) 190 | ("ensure-reduced" . (:url "/chapter-7/v-29/386" :section "7.3")) 191 | ("unreduced" . (:url "/chapter-7/v-29/386" :section "7.3")) 192 | 193 | ;; collections 194 | ("into" . (:url "/chapter-8/v-29/8" :section "8.1.1")) 195 | ("count" . (:url "/chapter-8/v-29/47" :section "8.1.2")) 196 | ("nth" . (:url "/chapter-8/v-29/88" :section "8.1.3")) 197 | ("empty" . (:url "/chapter-8/v-29/132" :section "8.1.4")) 198 | ("every?" . (:url "/chapter-8/v-29/164" :section "8.1.5")) 199 | ("not-every?" . (:url "/chapter-8/v-29/164" :section "8.1.5")) 200 | ("some" . (:url "/chapter-8/v-29/164" :section "8.1.5")) 201 | ("not-any?" . (:url "/chapter-8/v-29/164" :section "8.1.5")) 202 | ("empty?" . (:url "/chapter-8/v-29/215" :section "8.1.6")) 203 | ("not-empty" . (:url "/chapter-8/v-29/215" :section "8.1.6")) 204 | ("conj" . (:url "/chapter-8/v-29/262" :section "8.2.1")) 205 | ("get" . (:url "/chapter-8/v-29/301" :section "8.2.2")) 206 | ("contains?" . (:url "/chapter-8/v-29/384" :section "8.2.3")) 207 | ("rand-nth" . (:url "/chapter-8/v-29/445" :section "8.3.1")) 208 | ("shuffle" . (:url "/chapter-8/v-29/480" :section "8.3.2")) 209 | ("random-sample" . (:url "/chapter-8/v-29/511" :section "8.3.3")) 210 | ("frequencies" . (:url "/chapter-8/v-29/547" :section "8.3.4")) 211 | ("sort-by" . (:url "/chapter-8/v-29/578" :section "8.3.5" sort and)) 212 | ("group-by" . (:url "/chapter-8/v-29/629" :section "8.3.6")) 213 | ("replace" . (:url "/chapter-8/v-29/659" :section "8.3.7")) 214 | ("reverse" . (:url "/chapter-8/v-29/695" :section "8.3.8")) 215 | ("walk" . (:url "/chapter-8/v-29/746" :section "8.4.1")) 216 | ("prewalk-demo" . (:url "/chapter-8/v-29/746" :section "8.4.1")) 217 | ("postwalk-demo" . (:url "/chapter-8/v-29/746" :section "8.4.1")) 218 | ("prewalk" . (:url "/chapter-8/v-29/763" :section "8.4.2")) 219 | ("postwalk" . (:url "/chapter-8/v-29/763" :section "8.4.2")) 220 | ("prewalk-replace" . (:url "/chapter-8/v-29/795" :section "8.4.3")) 221 | ("postwalk-replace" . (:url "/chapter-8/v-29/795" :section "8.4.3")) 222 | ("clojure.zip/zipper" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 223 | ("clojure.zip/seq-zip" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 224 | ("clojure.zip/xml-zip" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 225 | ("clojure.zip/vector-zip" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 226 | ("clojure.zip/make-node" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 227 | ("clojure.zip/node" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 228 | ("clojure.zip/up" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 229 | ("clojure.zip/down" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 230 | ("clojure.zip/right" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 231 | ("clojure.zip/left" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 232 | ("clojure.zip/children" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 233 | ("clojure.zip/lefts" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 234 | ("clojure.zip/rights" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 235 | ("clojure.zip/branch?" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 236 | ("clojure.zip/path" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 237 | ("clojure.zip/replace" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 238 | ("clojure.zip/edit" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 239 | ("clojure.zip/insert-left" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 240 | ("clojure.zip/insert-right" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 241 | ("clojure.zip/append-child" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 242 | ("clojure.zip/next" . (:url "/chapter-8/v-29/805" :section "8.4.4")) 243 | 244 | ;; sequences 245 | ("seq" . (:url "/chapter-9/v-29/23" :section "9.1.1")) 246 | ("sequence" . (:url "/chapter-9/v-29/23" :section "9.1.1")) 247 | ("rseq" . (:url "/chapter-9/v-29/77" :section "9.1.2")) 248 | ("subseq" . (:url "/chapter-9/v-29/112" :section "9.1.3")) 249 | ("rsubseq" . (:url "/chapter-9/v-29/112" :section "9.1.3")) 250 | ("seque" . (:url "/chapter-9/v-29/150" :section "9.1.4")) 251 | ("pmap" . (:url "/chapter-9/v-29/200" :section "9.1.5")) 252 | ("pcalls" . (:url "/chapter-9/v-29/200" :section "9.1.5")) 253 | ("pvalues" . (:url "/chapter-9/v-29/200" :section "9.1.5")) 254 | ("repeatedly" . (:url "/chapter-9/v-29/276" :section "9.2.1")) 255 | ("iterate" . (:url "/chapter-9/v-29/312" :section "9.2.2")) 256 | ("repeat" . (:url "/chapter-9/v-29/348" :section "9.2.3")) 257 | ("cycle" . (:url "/chapter-9/v-29/348" :section "9.2.3")) 258 | ("lazy-seq" . (:url "/chapter-9/v-29/395" :section "9.3.1")) 259 | ("tree-seq" . (:url "/chapter-9/v-29/449" :section "9.3.2")) 260 | ("file-seq" . (:url "/chapter-9/v-29/487" :section "9.3.3")) 261 | ("xml-seq" . (:url "/chapter-9/v-29/512" :section "9.3.4")) 262 | ("re-seq" . (:url "/chapter-9/v-29/535" :section "9.3.5")) 263 | ("line-seq" . (:url "/chapter-9/v-29/571" :section "9.3.6")) 264 | ("resultset-seq" . (:url "/chapter-9/v-29/599" :section "9.3.7")) 265 | ("iterator-seq" . (:url "/chapter-9/v-29/629" :section "9.3.8")) 266 | ("enumeration-seq" . (:url "/chapter-9/v-29/629" :section "9.3.8")) 267 | ("concat" . (:url "/chapter-9/v-29/659" :section "9.3.9")) 268 | ("lazy-cat" . (:url "/chapter-9/v-29/659" :section "9.3.9")) 269 | ("list" . (:url "/chapter-9/v-29/714" :section "9.4.1")) 270 | ("cons" . (:url "/chapter-9/v-29/765" :section "9.4.2")) 271 | ("list*" . (:url "/chapter-9/v-29/765" :section "9.4.2")) 272 | 273 | ;; sequential processing 274 | ("rest" . (:url "/chapter-10/v-29/13" :section "10.1")) 275 | ("next" . (:url "/chapter-10/v-29/13" :section "10.1")) 276 | ("fnext" . (:url "/chapter-10/v-29/13" :section "10.1")) 277 | ("nnext" . (:url "/chapter-10/v-29/13" :section "10.1")) 278 | ("ffirst" . (:url "/chapter-10/v-29/13" :section "10.1")) 279 | ("nfirst" . (:url "/chapter-10/v-29/13" :section "10.1")) 280 | ("butlast" . (:url "/chapter-10/v-29/13" :section "10.1")) 281 | ("nthnext" . (:url "/chapter-10/v-29/62" :section "10.2")) 282 | ("drop" . (:url "/chapter-10/v-29/62" :section "10.2")) 283 | ("drop-while" . (:url "/chapter-10/v-29/62" :section "10.2")) 284 | ("drop-last" . (:url "/chapter-10/v-29/62" :section "10.2")) 285 | ("take" . (:url "/chapter-10/v-29/62" :section "10.2")) 286 | ("take-while" . (:url "/chapter-10/v-29/62" :section "10.2")) 287 | ("take-last" . (:url "/chapter-10/v-29/62" :section "10.2")) 288 | ("nthrest" . (:url "/chapter-10/v-29/62" :section "10.2")) 289 | ("nthnext" . (:url "/chapter-10/v-29/62" :section "10.2")) 290 | ("keep" . (:url "/chapter-10/v-29/121" :section "10.3")) 291 | ("keep-indexed" . (:url "/chapter-10/v-29/121" :section "10.3")) 292 | ("mapcat" . (:url "/chapter-10/v-29/158" :section "10.4")) 293 | ("interpose" . (:url "/chapter-10/v-29/198" :section "10.5")) 294 | ("interleave" . (:url "/chapter-10/v-29/198" :section "10.5")) 295 | ("partition" . (:url "/chapter-10/v-29/244" :section "10.6")) 296 | ("partition-all" . (:url "/chapter-10/v-29/244" :section "10.6")) 297 | ("partition-by" . (:url "/chapter-10/v-29/244" :section "10.6")) 298 | ("flatten" . (:url "/chapter-10/v-29/297" :section "10.7")) 299 | ("distinct" . (:url "/chapter-10/v-29/327" :section "10.8")) 300 | ("dedupe" . (:url "/chapter-10/v-29/327" :section "10.8")) 301 | ("distinct?" . (:url "/chapter-10/v-29/327" :section "10.8")) 302 | ("take-nth" . (:url "/chapter-10/v-29/382" :section "10.9")) 303 | ("split-at" . (:url "/chapter-10/v-29/435" :section "10.10")) 304 | ("split-with" . (:url "/chapter-10/v-29/435" :section "10.10")) 305 | ("when-first" . (:url "/chapter-10/v-29/469" :section "10.11")) 306 | ("chunk-cons" . (:url "/chapter-10/v-29/496" :section "10.12")) 307 | ("chunk-first" . (:url "/chapter-10/v-29/496" :section "10.12")) 308 | ("chunk-rest" . (:url "/chapter-10/v-29/496" :section "10.12")) 309 | ("chunk-next" . (:url "/chapter-10/v-29/496" :section "10.12")) 310 | ("chunk-buffer" . (:url "/chapter-10/v-29/496" :section "10.12")) 311 | ("chunk-append" . (:url "/chapter-10/v-29/496" :section "10.12")) 312 | ("chunk" . (:url "/chapter-10/v-29/496" :section "10.12")) 313 | 314 | ;; maps 315 | ("hash-map" . (:url "/chapter-11/v-29/21" :section "11.1.1")) 316 | ("array-map" . (:url "/chapter-11/v-29/49" :section "11.1.2")) 317 | ("sorted-map" . (:url "/chapter-11/v-29/86" :section "11.1.3")) 318 | ("sorted-map-by" . (:url "/chapter-11/v-29/86" :section "11.1.3")) 319 | ("create-struct" . (:url "/chapter-11/v-29/142" :section "11.1.4")) 320 | ("defstruct" . (:url "/chapter-11/v-29/142" :section "11.1.4")) 321 | ("struct-map" . (:url "/chapter-11/v-29/142" :section "11.1.4")) 322 | ("struct" . (:url "/chapter-11/v-29/142" :section "11.1.4")) 323 | ("accessor" . (:url "/chapter-11/v-29/142" :section "11.1.4")) 324 | ("zipmap" . (:url "/chapter-11/v-29/195" :section "11.1.5")) 325 | ("keys" . (:url "/chapter-11/v-29/236" :section "11.2.1")) 326 | ("vals" . (:url "/chapter-11/v-29/236" :section "11.2.1")) 327 | ("find" . (:url "/chapter-11/v-29/264" :section "11.2.2")) 328 | ("key" . (:url "/chapter-11/v-29/264" :section "11.2.2")) 329 | ("val" . (:url "/chapter-11/v-29/264" :section "11.2.2")) 330 | ("select-keys" . (:url "/chapter-11/v-29/304" :section "11.2.4")) 331 | ("get-in" . (:url "/chapter-11/v-29/304" :section "11.2.4")) 332 | ("assoc" . (:url "/chapter-11/v-29/349" :section "11.3.1")) 333 | ("assoc-in" . (:url "/chapter-11/v-29/349" :section "11.3.1")) 334 | ("dissoc" . (:url "/chapter-11/v-29/349" :section "11.3.1")) 335 | ("update" . (:url "/chapter-11/v-29/407" :section "11.3.2")) 336 | ("update-in" . (:url "/chapter-11/v-29/407" :section "11.3.2")) 337 | ("merge" . (:url "/chapter-11/v-29/440" :section "11.3.3")) 338 | ("merge-with" . (:url "/chapter-11/v-29/440" :section "11.3.3")) 339 | ("reduce-kv" . (:url "/chapter-11/v-29/490" :section "11.3.4")) 340 | ("clojure.walk/keywordize-keys" . (:url "/chapter-11/v-29/536" :section "11.4.1")) 341 | ("clojure.walk/stringify-keys" . (:url "/chapter-11/v-29/536" :section "11.4.1")) 342 | ("clojure.set/rename-keys" . (:url "/chapter-11/v-29/545" :section "11.4.2")) 343 | ("clojure.set/map-invert" . (:url "/chapter-11/v-29/554" :section "11.4.3")) 344 | 345 | ;; vectors 346 | ("vector" . (:url "/chapter-12/v-29/34" :section "12.1")) 347 | ("vec" . (:url "/chapter-12/v-29/103" :section "12.2")) 348 | ("peek" . (:url "/chapter-12/v-29/153" :section "12.3")) 349 | ("pop" . (:url "/chapter-12/v-29/153" :section "12.3")) 350 | ("vector-of" . (:url "/chapter-12/v-29/189" :section "12.4")) 351 | ("mapv" . (:url "/chapter-12/v-29/238" :section "12.5")) 352 | ("filterv" . (:url "/chapter-12/v-29/286" :section "12.6")) 353 | ("subvec" . (:url "/chapter-12/v-29/318" :section "12.7")) 354 | 355 | ;; sets 356 | ("hash-set" . (:url "/chapter-13/v-29/8" :section "13.1")) 357 | ("set" . (:url "/chapter-13/v-29/47" :section "13.2")) 358 | ("sorted-set" . (:url "/chapter-13/v-29/80" :section "13.3")) 359 | ("sorted-set-by" . (:url "/chapter-13/v-29/80" :section "13.3")) 360 | ("disj" . (:url "/chapter-13/v-29/121" :section "13.4")) 361 | ("union" . (:url "/chapter-13/v-29/156" :section "13.5")) 362 | ("difference" . (:url "/chapter-13/v-29/156" :section "13.5")) 363 | ("intersection" . (:url "/chapter-13/v-29/156" :section "13.5")) 364 | ("subset?" . (:url "/chapter-13/v-29/193" :section "13.6")) 365 | ("superset?" . (:url "/chapter-13/v-29/193" :section "13.6")) 366 | ("select" . (:url "/chapter-13/v-29/198" :section "13.7")) 367 | ("index" . (:url "/chapter-13/v-29/198" :section "13.7")) 368 | ("rename" . (:url "/chapter-13/v-29/198" :section "13.7")) 369 | ("join" . (:url "/chapter-13/v-29/198" :section "13.7")) 370 | ("project" . (:url "/chapter-13/v-29/198" :section "13.7")) 371 | 372 | ;; concurency 373 | ("future" . (:url "/chapter-14/v-29/8" :section "14.1")) 374 | ("future-done?" . (:url "/chapter-14/v-29/13" :section "14.1.1")) 375 | ("future?" . (:url "/chapter-14/v-29/13" :section "14.1.1")) 376 | ("future-cancel" . (:url "/chapter-14/v-29/18" :section "14.1.2")) 377 | ("future-cancelled?" . (:url "/chapter-14/v-29/18" :section "14.1.2")) 378 | ("future-call" . (:url "/chapter-14/v-29/21" :section "14.1.3")) 379 | ("promise" . (:url "/chapter-14/v-29/35" :section "14.2")) 380 | ("deliver" . (:url "/chapter-14/v-29/35" :section "14.2")) 381 | ("delay" . (:url "/chapter-14/v-29/53" :section "14.3")) 382 | ("delay?" . (:url "/chapter-14/v-29/69" :section "14.3.1")) 383 | ("force" . (:url "/chapter-14/v-29/69" :section "14.3.1")) 384 | ("ref" . (:url "/chapter-14/v-29/77" :section "14.4")) 385 | ("alter" . (:url "/chapter-14/v-29/95" :section "14.4.1")) 386 | ("ref-set" . (:url "/chapter-14/v-29/95" :section "14.4.1")) 387 | ("commute" . (:url "/chapter-14/v-29/105" :section "14.4.2")) 388 | ("ensure" . (:url "/chapter-14/v-29/111" :section "14.4.3")) 389 | ("io!" . (:url "/chapter-14/v-29/114" :section "14.4.4")) 390 | ("atom" . (:url "/chapter-14/v-29/121" :section "14.5")) 391 | ("swap!" . (:url "/chapter-14/v-29/130" :section "14.5.1")) 392 | ("reset!" . (:url "/chapter-14/v-29/135" :section "14.5.2")) 393 | ("compare-and-set!" . (:url "/chapter-14/v-29/140" :section "14.5.3")) 394 | ("agent" . (:url "/chapter-14/v-29/146" :section "14.6")) 395 | ("deref" . (:url "/chapter-14/v-29/201" :section "14.7")) 396 | ("realized?" . (:url "/chapter-14/v-29/201" :section "14.7")) 397 | ("set-validator!" . (:url "/chapter-14/v-29/218" :section "14.8")) 398 | ("get-validator" . (:url "/chapter-14/v-29/218" :section "14.8")) 399 | ("add-watch" . (:url "/chapter-14/v-29/230" :section "14.9")) 400 | ("remove-watch" . (:url "/chapter-14/v-29/230" :section "14.9")) 401 | ("locking" . (:url "/chapter-14/v-29/237" :section "14.10")) 402 | ("monitor-enter" . (:url "/chapter-14/v-29/237" :section "14.10")) 403 | ("monitor-exit" . (:url "/chapter-14/v-29/237" :section "14.10")) 404 | 405 | ;; types / class / hierarchies / polymorphism 406 | ("symbol" . (:url "/chapter-15/v-29/17" :section "15.1")) 407 | ("keyword" . (:url "/chapter-15/v-29/17" :section "15.1")) 408 | ("name" . (:url "/chapter-15/v-29/33" :section "15.1.1")) 409 | ("find-keyword" . (:url "/chapter-15/v-29/38" :section "15.1.2")) 410 | ("type" . (:url "/chapter-15/v-29/52" :section "15.2.1")) 411 | ("instance?" . (:url "/chapter-15/v-29/52" :section "15.2.1")) 412 | ("class" . (:url "/chapter-15/v-29/52" :section "15.2.1")) 413 | ("instance?" . (:url "/chapter-15/v-29/62" :section "15.2.2")) 414 | ("gen-class" . (:url "/chapter-15/v-29/67" :section "15.3")) 415 | ("gen-interface" . (:url "/chapter-15/v-29/67" :section "15.3")) 416 | ("deftype" . (:url "/chapter-15/v-29/91" :section "15.4")) 417 | ("definterface" . (:url "/chapter-15/v-29/91" :section "15.4")) 418 | ("proxy" . (:url "/chapter-15/v-29/119" :section "15.5")) 419 | ("reify" . (:url "/chapter-15/v-29/139" :section "15.6")) 420 | ("defrecord" . (:url "/chapter-15/v-29/148" :section "15.7")) 421 | ("defprotocol" . (:url "/chapter-15/v-29/160" :section "15.8")) 422 | ("extend" . (:url "/chapter-15/v-29/179" :section "15.9")) 423 | ("extend-type" . (:url "/chapter-15/v-29/179" :section "15.9")) 424 | ("extend-protocol" . (:url "/chapter-15/v-29/179" :section "15.9")) 425 | ("derive" . (:url "/chapter-15/v-29/199" :section "15.10")) 426 | ("make-hierarchy" . (:url "/chapter-15/v-29/199" :section "15.10")) 427 | ("defmulti" . (:url "/chapter-15/v-29/223" :section "15.11")) 428 | ("defmethod" . (:url "/chapter-15/v-29/223" :section "15.11")) 429 | 430 | ;; vars & ns 431 | ("def" . (:url "/chapter-16/v-29/31" :section "16.4.1")) 432 | ("declare" . (:url "/chapter-16/v-29/41" :section "16.4.2")) 433 | ("intern" . (:url "/chapter-16/v-29/46" :section "16.4.3")) 434 | ("defonce" . (:url "/chapter-16/v-29/51" :section "16.4.4")) 435 | ("var" . (:url "/chapter-16/v-29/61" :section "16.5.1")) 436 | ("find-var" . (:url "/chapter-16/v-29/67" :section "16.5.2")) 437 | ("resolve" . (:url "/chapter-16/v-29/71" :section "16.5.3")) 438 | ("ns-resolve" . (:url "/chapter-16/v-29/71" :section "16.5.3")) 439 | ("clojure.repl/dir-fn" . (:url "/chapter-16/v-29/79" :section "16.5.4")) 440 | ("clojure.repl/dir" . (:url "/chapter-16/v-29/79" :section "16.5.4")) 441 | ("bound?" . (:url "/chapter-16/v-29/82" :section "16.5.5")) 442 | ("thread-bound?" . (:url "/chapter-16/v-29/82" :section "16.5.5")) 443 | ("alter-var-root" . (:url "/chapter-16/v-29/88" :section "16.6.1")) 444 | ("with-redefs" . (:url "/chapter-16/v-29/96" :section "16.6.2")) 445 | ("with-redefs-fn" . (:url "/chapter-16/v-29/96" :section "16.6.2")) 446 | ("bound-fn" . (:url "/chapter-16/v-29/111" :section "16.7.1")) 447 | ("with-local-vars" . (:url "/chapter-16/v-29/119" :section "16.8")) 448 | ("var-get" . (:url "/chapter-16/v-29/119" :section "16.8")) 449 | ("var-set" . (:url "/chapter-16/v-29/119" :section "16.8")) 450 | ("ns" . (:url "/chapter-16/v-29/124" :section "16.9")) 451 | ("in-ns" . (:url "/chapter-16/v-29/124" :section "16.9")) 452 | ("remove-ns" . (:url "/chapter-16/v-29/124" :section "16.9")) 453 | ("create-ns" . (:url "/chapter-16/v-29/136" :section "16.10")) 454 | ("alias" . (:url "/chapter-16/v-29/144" :section "16.10.1")) 455 | ("ns-aliases" . (:url "/chapter-16/v-29/144" :section "16.10.1")) 456 | ("ns-unalias" . (:url "/chapter-16/v-29/144" :section "16.10.1")) 457 | ("ns-unalias" . (:url "/chapter-16/v-29/153" :section "16.10.2")) 458 | ("ns-map" . (:url "/chapter-16/v-29/157" :section "16.11")) 459 | ("ns-unmap" . (:url "/chapter-16/v-29/157" :section "16.11")) 460 | ("ns-publics" . (:url "/chapter-16/v-29/164" :section "16.12")) 461 | ("ns-interns" . (:url "/chapter-16/v-29/164" :section "16.12")) 462 | ("ns-imports" . (:url "/chapter-16/v-29/164" :section "16.12")) 463 | ("refer" . (:url "/chapter-16/v-29/171" :section "16.13.1")) 464 | ("require" . (:url "/chapter-16/v-29/176" :section "16.13.2")) 465 | ("loaded-libs" . (:url "/chapter-16/v-29/179" :section "16.13.3")) 466 | ("use" . (:url "/chapter-16/v-29/189" :section "16.13.4")) 467 | ("import" . (:url "/chapter-16/v-29/193" :section "16.13.5")) 468 | ("find-ns" . (:url "/chapter-16/v-29/196" :section "16.14")) 469 | ("all-ns" . (:url "/chapter-16/v-29/196" :section "16.14")) 470 | ("the-ns" . (:url "/chapter-16/v-29/202" :section "16.15")) 471 | ("ns-name" . (:url "/chapter-16/v-29/205" :section "16.15.1")) 472 | ("namespace" . (:url "/chapter-16/v-29/208" :section "16.15.2")) 473 | ("meta" . (:url "/chapter-16/v-29/212" :section "16.16.1")) 474 | ("with-meta" . (:url "/chapter-16/v-29/229" :section "16.16.2")) 475 | ("vary-meta" . (:url "/chapter-16/v-29/232" :section "16.16.3")) 476 | ("alter-meta!" . (:url "/chapter-16/v-29/235" :section "16.16.4")) 477 | ("reset-meta!" . (:url "/chapter-16/v-29/238" :section "16.16.5")) 478 | 479 | ;; evaluation 480 | ("read" . (:url "/chapter-17/v-29/10" :section "17.1.1")) 481 | ("read-string" . (:url "/chapter-17/v-29/29" :section "17.1.2")) 482 | ("eval" . (:url "/chapter-17/v-29/35" :section "17.2")) 483 | ("load" . (:url "/chapter-17/v-29/46" :section "17.3.1")) 484 | ("load-file" . (:url "/chapter-17/v-29/52" :section "17.3.2")) 485 | ("load-string" . (:url "/chapter-17/v-29/55" :section "17.3.3")) 486 | ("load-reader" . (:url "/chapter-17/v-29/65" :section "17.3.4")) 487 | ("compile" . (:url "/chapter-17/v-29/67" :section "17.4")) 488 | ("test" . (:url "/chapter-17/v-29/80" :section "17.5")) 489 | ("assert" . (:url "/chapter-17/v-29/80" :section "17.5")) 490 | ("clojure.edn/read" . (:url "/chapter-17/v-29/89" :section "17.6")) 491 | ("clojure.edn/read-string" . (:url "/chapter-17/v-29/89" :section "17.6")) 492 | ("tagged-literal" . (:url "/chapter-17/v-29/100" :section "17.7")) 493 | ("tagged-literal?" . (:url "/chapter-17/v-29/100" :section "17.7")) 494 | ("default-data-readers" . (:url "/chapter-17/v-29/108" :section "17.8")) 495 | ("reader-conditional?" . (:url "/chapter-17/v-29/115" :section "17.9")) 496 | ("reader-conditional" . (:url "/chapter-17/v-29/115" :section "17.9")) 497 | 498 | ;; formatting & printing 499 | ("format" . (:url "/chapter-18/v-29/3" :section "18.1")) 500 | ("printf" . (:url "/chapter-18/v-29/3" :section "18.1")) 501 | ("cl-format" . (:url "/chapter-18/v-29/3" :section "18.1")) 502 | ("pr" . (:url "/chapter-18/v-29/23" :section "18.2")) 503 | ("prn" . (:url "/chapter-18/v-29/23" :section "18.2")) 504 | ("pr-str" . (:url "/chapter-18/v-29/23" :section "18.2")) 505 | ("prn-str" . (:url "/chapter-18/v-29/23" :section "18.2")) 506 | ("print" . (:url "/chapter-18/v-29/23" :section "18.2")) 507 | ("println" . (:url "/chapter-18/v-29/23" :section "18.2")) 508 | ("print-str" . (:url "/chapter-18/v-29/23" :section "18.2")) 509 | ("println-str" . (:url "/chapter-18/v-29/23" :section "18.2")) 510 | ("clojure.pprint/pprint" . (:url "/chapter-18/v-29/34" :section "18.3")) 511 | ("clojure.pprint/pp" . (:url "/chapter-18/v-29/34" :section "18.3")) 512 | ("write" . (:url "/chapter-18/v-29/34" :section "18.3")) 513 | ("print-table" . (:url "/chapter-18/v-29/34" :section "18.3")) 514 | ("print-method" . (:url "/chapter-18/v-29/52" :section "18.4")) 515 | ("print-dup" . (:url "/chapter-18/v-29/52" :section "18.4")) 516 | ("print-ctor" . (:url "/chapter-18/v-29/52" :section "18.4")) 517 | ("slurp" . (:url "/chapter-18/v-29/69" :section "18.5")) 518 | ("spit" . (:url "/chapter-18/v-29/69" :section "18.5")) 519 | 520 | ;; strings & regexps 521 | ("str" . (:url "/chapter-19/v-29/3" :section "19.1")) 522 | ("clojure.string/join" . (:url "/chapter-19/v-29/12" :section "19.2")) 523 | ("clojure.string/replace" . (:url "/chapter-19/v-29/19" :section "19.3")) 524 | ("clojure.string/replace-first" . (:url "/chapter-19/v-29/19" :section "19.3")) 525 | ("clojure.string/re-quote-replacement" . (:url "/chapter-19/v-29/19" :section "19.3")) 526 | ("clojure.string/subs" . (:url "/chapter-19/v-29/35" :section "19.4")) 527 | ("clojure.string/split" . (:url "/chapter-19/v-29/35" :section "19.4")) 528 | ("clojure.string/split-lines" . (:url "/chapter-19/v-29/35" :section "19.4")) 529 | ("clojure.string/trim" . (:url "/chapter-19/v-29/46" :section "19.5")) 530 | ("clojure.string/triml" . (:url "/chapter-19/v-29/46" :section "19.5")) 531 | ("clojure.string/trimr" . (:url "/chapter-19/v-29/46" :section "19.5")) 532 | ("clojure.string/trim-newline" . (:url "/chapter-19/v-29/46" :section "19.5")) 533 | ("clojure.string/escape" . (:url "/chapter-19/v-29/58" :section "19.6")) 534 | ("clojure.string/char-name-string" . (:url "/chapter-19/v-29/58" :section "19.6")) 535 | ("clojure.string/char-escape-string" . (:url "/chapter-19/v-29/58" :section "19.6")) 536 | ("clojure.string/lower-case" . (:url "/chapter-19/v-29/65" :section "19.7")) 537 | ("clojure.string/upper-case" . (:url "/chapter-19/v-29/65" :section "19.7")) 538 | ("clojure.string/capitalize" . (:url "/chapter-19/v-29/65" :section "19.7")) 539 | ("clojure.string/index-of" . (:url "/chapter-19/v-29/74" :section "19.8")) 540 | ("clojure.string/last-index-of" . (:url "/chapter-19/v-29/74" :section "19.8")) 541 | ("clojure.string/blank?" . (:url "/chapter-19/v-29/83" :section "19.9")) 542 | ("clojure.string/ends-with?" . (:url "/chapter-19/v-29/83" :section "19.9")) 543 | ("clojure.string/starts-with?" . (:url "/chapter-19/v-29/83" :section "19.9")) 544 | ("clojure.string/includes?" . (:url "/chapter-19/v-29/83" :section "19.9")) 545 | ("clojure.string/re-pattern" . (:url "/chapter-19/v-29/91" :section "19.10")) 546 | ("re-matcher" . (:url "/chapter-19/v-29/91" :section "19.10")) 547 | ("re-groups" . (:url "/chapter-19/v-29/91" :section "19.10")) 548 | ("re-seq" . (:url "/chapter-19/v-29/91" :section "19.10")) 549 | ("re-matches" . (:url "/chapter-19/v-29/91" :section "19.10")) 550 | ("re-find" . (:url "/chapter-19/v-29/91" :section "19.10")) 551 | 552 | ;; mutation & side-effects 553 | ("transient" . (:url "/chapter-20/v-29/3" :section "20.1")) 554 | ("persistent!" . (:url "/chapter-20/v-29/3" :section "20.1")) 555 | ("conj!" . (:url "/chapter-20/v-29/3" :section "20.1")) 556 | ("pop!" . (:url "/chapter-20/v-29/3" :section "20.1")) 557 | ("assoc!" . (:url "/chapter-20/v-29/3" :section "20.1")) 558 | ("dissoc!" . (:url "/chapter-20/v-29/3" :section "20.1")) 559 | ("disj!" . (:url "/chapter-20/v-29/3" :section "20.1")) 560 | ("persistent!" . (:url "/chapter-20/v-29/3" :section "20.1")) 561 | ("conj!" . (:url "/chapter-20/v-29/3" :section "20.1")) 562 | ("pop!" . (:url "/chapter-20/v-29/3" :section "20.1")) 563 | ("assoc!" . (:url "/chapter-20/v-29/3" :section "20.1")) 564 | ("dissoc!" . (:url "/chapter-20/v-29/3" :section "20.1")) 565 | ("disj!" . (:url "/chapter-20/v-29/3" :section "20.1" and)) 566 | ("persistent!" . (:url "/chapter-20/v-29/3" :section "20.1")) 567 | ("conj!" . (:url "/chapter-20/v-29/3" :section "20.1")) 568 | ("pop!" . (:url "/chapter-20/v-29/3" :section "20.1")) 569 | ("assoc!" . (:url "/chapter-20/v-29/3" :section "20.1")) 570 | ("dissoc!" . (:url "/chapter-20/v-29/3" :section "20.1")) 571 | ("disj!" . (:url "/chapter-20/v-29/3" :section "20.1")) 572 | ("doseq" . (:url "/chapter-20/v-29/20" :section "20.2")) 573 | ("dorun" . (:url "/chapter-20/v-29/20" :section "20.2")) 574 | ("run!" . (:url "/chapter-20/v-29/20" :section "20.2")) 575 | ("doall" . (:url "/chapter-20/v-29/20" :section "20.2")) 576 | ("do" . (:url "/chapter-20/v-29/20" :section "20.2")) 577 | ("volatile!" . (:url "/chapter-20/v-29/32" :section "20.3")) 578 | ("vreset!" . (:url "/chapter-20/v-29/32" :section "20.3")) 579 | ("vswap!" . (:url "/chapter-20/v-29/32" :section "20.3")) 580 | ("volatile?" . (:url "/chapter-20/v-29/32" :section "20.3")) 581 | ("set!" . (:url "/chapter-20/v-29/48" :section "20.4")) 582 | 583 | ;; java(script) interop 584 | ("." . (:url "/chapter-21/v-29/9" :section "21.1")) 585 | (".." . (:url "/chapter-21/v-29/9" :section "21.1")) 586 | ("doto" . (:url "/chapter-21/v-29/9" :section "21.1")) 587 | ("new" . (:url "/chapter-21/v-29/18" :section "21.2")) 588 | ("try" . (:url "/chapter-21/v-29/27" :section "21.3")) 589 | ("catch" . (:url "/chapter-21/v-29/27" :section "21.3")) 590 | ("finally" . (:url "/chapter-21/v-29/27" :section "21.3")) 591 | ("throw" . (:url "/chapter-21/v-29/27" :section "21.3")) 592 | ("ex-info" . (:url "/chapter-21/v-29/39" :section "21.4")) 593 | ("ex-data" . (:url "/chapter-21/v-29/39" :section "21.4")) 594 | ("bean" . (:url "/chapter-21/v-29/50" :section "21.5")) 595 | ("reflect" . (:url "/chapter-21/v-29/64" :section "21.6")) 596 | ("type-reflect" . (:url "/chapter-21/v-29/64" :section "21.6")) 597 | ("make-array" . (:url "/chapter-21/v-29/82" :section "21.7.1")) 598 | ("object-array" . (:url "/chapter-21/v-29/90" :section "21.7.2")) 599 | ("int-array" . (:url "/chapter-21/v-29/90" :section "21.7.2")) 600 | ("boolean-array" . (:url "/chapter-21/v-29/90" :section "21.7.2")) 601 | ("byte-array" . (:url "/chapter-21/v-29/90" :section "21.7.2")) 602 | ("short-array" . (:url "/chapter-21/v-29/90" :section "21.7.2")) 603 | ("char-array" . (:url "/chapter-21/v-29/90" :section "21.7.2")) 604 | ("long-array" . (:url "/chapter-21/v-29/90" :section "21.7.2")) 605 | ("float-array" . (:url "/chapter-21/v-29/90" :section "21.7.2")) 606 | ("double-array" . (:url "/chapter-21/v-29/90" :section "21.7.2")) 607 | ("to-array" . (:url "/chapter-21/v-29/106" :section "21.7.3")) 608 | ("into-array" . (:url "/chapter-21/v-29/106" :section "21.7.3")) 609 | ("to-array-2d" . (:url "/chapter-21/v-29/106" :section "21.7.3")) 610 | ("aget" . (:url "/chapter-21/v-29/118" :section "21.7.4")) 611 | ("aset" . (:url "/chapter-21/v-29/118" :section "21.7.4")) 612 | ("alength" . (:url "/chapter-21/v-29/118" :section "21.7.4")) 613 | ("aclone" . (:url "/chapter-21/v-29/118" :section "21.7.4")) 614 | ("amap" . (:url "/chapter-21/v-29/130" :section "21.7.5")) 615 | ("areduce" . (:url "/chapter-21/v-29/130" :section "21.7.5")) 616 | ("aset-int" . (:url "/chapter-21/v-29/154" :section "21.7.6")) 617 | ("aset-boolean" . (:url "/chapter-21/v-29/154" :section "21.7.6")) 618 | ("aset-byte" . (:url "/chapter-21/v-29/154" :section "21.7.6")) 619 | ("aset-short" . (:url "/chapter-21/v-29/154" :section "21.7.6")) 620 | ("aset-char" . (:url "/chapter-21/v-29/154" :section "21.7.6")) 621 | ("aset-long" . (:url "/chapter-21/v-29/154" :section "21.7.6")) 622 | ("aset-float" . (:url "/chapter-21/v-29/154" :section "21.7.6")) 623 | ("aset-double" . (:url "/chapter-21/v-29/154" :section "21.7.6")) 624 | ("ints" . (:url "/chapter-21/v-29/168" :section "21.7.7")) 625 | ("booleans" . (:url "/chapter-21/v-29/168" :section "21.7.7")) 626 | ("bytes" . (:url "/chapter-21/v-29/168" :section "21.7.7")) 627 | ("shorts" . (:url "/chapter-21/v-29/168" :section "21.7.7")) 628 | ("chars" . (:url "/chapter-21/v-29/168" :section "21.7.7")) 629 | ("longs" . (:url "/chapter-21/v-29/168" :section "21.7.7")) 630 | ("longs" . (:url "/chapter-21/v-29/168" :section "21.7.7")) 631 | ("floats" . (:url "/chapter-21/v-29/168" :section "21.7.7")) 632 | ("doubles" . (:url "/chapter-21/v-29/168" :section "21.7.7")) 633 | 634 | ;; toolbox 635 | ("clojure.xml/parse" . (:url "/chapter-22/v-29/12" :section "22.1")) 636 | ("clojure.inspector/inspect-tree" . (:url "/chapter-22/v-29/28" :section "22.2")) 637 | ("clojure.inspector/inspect-table" . (:url "/chapter-22/v-29/28" :section "22.2")) 638 | ("clojure.repl/doc" . (:url "/chapter-22/v-29/43" :section "22.3.1")) 639 | ("clojure.repl/find-doc" . (:url "/chapter-22/v-29/48" :section "22.3.2")) 640 | ("clojure.repl/apropos" . (:url "/chapter-22/v-29/51" :section "22.3.3")) 641 | ("clojure.repl/dir" . (:url "/chapter-22/v-29/54" :section "22.3.4")) 642 | ("clojure.repl/dir-fn" . (:url "/chapter-22/v-29/57" :section "22.3.5")) 643 | ("clojure.repl/source" . (:url "/chapter-22/v-29/60" :section "22.3.6")) 644 | ("clojure.repl/source-fn" . (:url "/chapter-22/v-29/69" :section "22.3.7")) 645 | ("clojure.repl/pst" . (:url "/chapter-22/v-29/72" :section "22.3.8")) 646 | ("clojure.repl/root-cause" . (:url "/chapter-22/v-29/83" :section "22.3.9")) 647 | ("clojure.repl/munge" . (:url "/chapter-22/v-29/86" :section "22.3.10")) 648 | ("clojure.repl/demunge" . (:url "/chapter-22/v-29/86" :section "22.3.10")) 649 | ("clojure.repl/stack-element-str" . (:url "/chapter-22/v-29/86" :section "22.3.10")) 650 | ("clojure.main/load-script" . (:url "/chapter-22/v-29/102" :section "22.4.1")) 651 | ("clojure.main/repl" . (:url "/chapter-22/v-29/107" :section "22.4.2")) 652 | ("clojure.java.browse/browse-url" . (:url "/chapter-22/v-29/116" :section "22.5")) 653 | ("clojure.java.browse/*open-url-script*" . (:url "/chapter-22/v-29/116" :section "22.5")) 654 | ("clojure.java.shell/sh" . (:url "/chapter-22/v-29/125" :section "22.6")) 655 | ("clojure.core.server/repl" . (:url "/chapter-22/v-29/140" :section "22.7")) 656 | ("clojure.core.server/stop-server" . (:url "/chapter-22/v-29/160" :section "22.7.1")) 657 | ("clojure.java.io/reader" . (:url "/chapter-22/v-29/171" :section "22.8.2")) 658 | ("clojure.java.io/with-open" . (:url "/chapter-22/v-29/175" :section "22.8.3")) 659 | ("clojure.java.io/writer" . (:url "/chapter-22/v-29/182" :section "22.8.4")) 660 | ("clojure.java.io/resource" . (:url "/chapter-22/v-29/194" :section "22.8.6")) 661 | ("clojure.java.io/as-url" . (:url "/chapter-22/v-29/197" :section "22.8.7")) 662 | ("clojure.java.io/file" . (:url "/chapter-22/v-29/204" :section "22.8.9")) 663 | ("clojure.java.io/copy" . (:url "/chapter-22/v-29/210" :section "22.8.10")) 664 | ("clojure.java.io/make-parents" . (:url "/chapter-22/v-29/216" :section "22.8.11")) 665 | ("clojure.java.io/delete-file" . (:url "/chapter-22/v-29/219" :section "22.8.12")) 666 | ("clojure.java.io/as-relative-path" . (:url "/chapter-22/v-29/222" :section "22.8.13"))) 667 | "Index of all the known symbols and their (URL) location in the book.") 668 | 669 | 670 | 671 | ;; COMMANDS 672 | 673 | ;;;###autoload 674 | (defun clojure-essential-ref (&optional arg) 675 | "Open Clojure documentation for symbol. 676 | 677 | Book \"Clojure, The Essential Reference\" is used as a documentation source. 678 | 679 | Prompts for the symbol to use, or uses the symbol at point, depending on 680 | the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the 681 | opposite of what that option dictates." 682 | (interactive "P") 683 | (cider-ensure-connected) 684 | 685 | (if (called-interactively-p 'any) 686 | (funcall (cider-prompt-for-symbol-function arg) 687 | "Doc for" 688 | clojure-essential-ref-default-browse-fn) 689 | (unless arg 690 | (error "Need to pass an argument when called non-interactively")) 691 | (funcall clojure-essential-ref-default-browse-fn arg))) 692 | 693 | ;;;###autoload 694 | (defun clojure-essential-ref-web (&optional arg) 695 | "Open Clojure documentation for symbol in the default web browser. 696 | 697 | Online version of book \"Clojure, The Essential Reference\" is 698 | used as a documentation source. 699 | 700 | Prompts for the symbol to use, or uses the symbol at point, depending on 701 | the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the 702 | opposite of what that option dictates." 703 | (interactive "P") 704 | (let ((clojure-essential-ref-default-browse-fn #'clojure-essential-ref-browse-web)) 705 | (if (called-interactively-p 'any) 706 | (call-interactively #'clojure-essential-ref nil (vector arg)) 707 | (funcall #'clojure-essential-ref arg)))) 708 | 709 | 710 | 711 | ;; LOOKUP 712 | 713 | (defun clojure-essential-ref--get-props (symbol) 714 | "Get props (section id...) for SYMBOL." 715 | (setq symbol (clojure-essential-ref--resolve-symbol symbol)) 716 | (cdr (assoc symbol clojure-essential-ref--index))) 717 | 718 | (defun clojure-essential-ref--resolve-symbol (symbol) 719 | "Gets the fully-qualified name for SYMBOL." 720 | (let* ((info (cider-var-info symbol)) 721 | (ns (nrepl-dict-get info "ns")) 722 | (name (nrepl-dict-get info "name"))) 723 | (if (and ns 724 | (not (string= ns "clojure.core"))) 725 | (concat ns "/" name) 726 | name))) 727 | 728 | 729 | 730 | ;; BROWSE 731 | 732 | (defun clojure-essential-ref-browse-web (symbol) 733 | "Open doc in Clojure Essential Ref for SYMBOL in web browser." 734 | (let ((props (clojure-essential-ref--get-props symbol))) 735 | (unless props 736 | (error "Couldn't find reference to %s in book index" symbol)) 737 | (browse-url (clojure-essential-ref--build-url props)))) 738 | 739 | (defun clojure-essential-ref--build-url (props) 740 | "Build url according to the symbol PROPS from clojure-essential-ref--index." 741 | (funcall clojure-essential-ref--build-url-fn props)) 742 | 743 | (defun clojure-essential-ref--build-url-from-raw-url-path (props) 744 | "Build url according to the url suffix in the symbol PROPS from clojure-essential-ref--index." 745 | (concat clojure-essential-ref--base-url (plist-get props :url))) 746 | 747 | (defun clojure-essential-ref--build-url-from-section-id (props) 748 | "Build url according to the section id in the symbol PROPS from clojure-essential-ref--index." 749 | (let* ((section (plist-get props :section)) 750 | (chapter (car (split-string section "\\.")))) 751 | (concat clojure-essential-ref--base-url "/chapter-" chapter "/section-" section))) 752 | 753 | 754 | 755 | 756 | (provide 'clojure-essential-ref) 757 | 758 | ;;; clojure-essential-ref.el ends here 759 | -------------------------------------------------------------------------------- /makem.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # * makem.sh --- Script to aid building and testing Emacs Lisp packages 4 | 5 | # https://github.com/alphapapa/makem.sh 6 | 7 | # * Commentary: 8 | 9 | # makem.sh is a script helps to build, lint, and test Emacs Lisp 10 | # packages. It aims to make linting and testing as simple as possible 11 | # without requiring per-package configuration. 12 | 13 | # It works similarly to a Makefile in that "rules" are called to 14 | # perform actions such as byte-compiling, linting, testing, etc. 15 | 16 | # Source and test files are discovered automatically from the 17 | # project's Git repo, and package dependencies within them are parsed 18 | # automatically. 19 | 20 | # Output is simple: by default, there is no output unless errors 21 | # occur. With increasing verbosity levels, more detail gives positive 22 | # feedback. Output is colored by default to make reading easy. 23 | 24 | # The script can run Emacs with the developer's local Emacs 25 | # configuration, or with a clean, "sandbox" configuration that can be 26 | # optionally removed afterward. This is especially helpful when 27 | # upstream dependencies may have released new versions that differ 28 | # from those installed in the developer's personal configuration. 29 | 30 | # * License: 31 | 32 | # This program is free software; you can redistribute it and/or modify 33 | # it under the terms of the GNU General Public License as published by 34 | # the Free Software Foundation, either version 3 of the License, or 35 | # (at your option) any later version. 36 | 37 | # This program is distributed in the hope that it will be useful, 38 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 39 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 40 | # GNU General Public License for more details. 41 | 42 | # You should have received a copy of the GNU General Public License 43 | # along with this program. If not, see . 44 | 45 | # * Functions 46 | 47 | function usage { 48 | cat <$file <$file <$file <"$file" <$file <&1) 258 | 259 | # Set output file. 260 | output_file=$(mktemp) || die "Unable to make output file." 261 | paths_temp+=("$output_file") 262 | 263 | # Run Emacs. 264 | debug "run_emacs: ${emacs_command[@]} $@ &>\"$output_file\"" 265 | "${emacs_command[@]}" "$@" &>"$output_file" 266 | 267 | # Check exit code and output. 268 | exit=$? 269 | [[ $exit != 0 ]] \ 270 | && debug "Emacs exited non-zero: $exit" 271 | 272 | [[ $verbose -gt 1 || $exit != 0 ]] \ 273 | && cat $output_file 274 | 275 | return $exit 276 | } 277 | 278 | # ** Compilation 279 | 280 | function batch-byte-compile { 281 | debug "batch-byte-compile: ERROR-ON-WARN:$compile_error_on_warn" 282 | 283 | [[ $compile_error_on_warn ]] && local error_on_warn=(--eval "(setq byte-compile-error-on-warn t)") 284 | 285 | run_emacs \ 286 | "${error_on_warn[@]}" \ 287 | --funcall batch-byte-compile \ 288 | "$@" 289 | } 290 | 291 | # ** Files 292 | 293 | function dirs-project { 294 | # Echo list of directories to be used in load path. 295 | files-project-feature | dirnames 296 | files-project-test | dirnames 297 | } 298 | 299 | function files-project-elisp { 300 | # Echo list of Elisp files in project. 301 | git ls-files 2>/dev/null \ 302 | | egrep "\.el$" \ 303 | | filter-files-exclude-default \ 304 | | filter-files-exclude-args 305 | } 306 | 307 | function files-project-feature { 308 | # Echo list of Elisp files that are not tests and provide a feature. 309 | files-project-elisp \ 310 | | egrep -v "$test_files_regexp" \ 311 | | filter-files-feature 312 | } 313 | 314 | function files-project-test { 315 | # Echo list of Elisp test files. 316 | files-project-elisp | egrep "$test_files_regexp" 317 | } 318 | 319 | function dirnames { 320 | # Echo directory names for files on STDIN. 321 | while read file 322 | do 323 | dirname "$file" 324 | done 325 | } 326 | 327 | function filter-files-exclude-default { 328 | # Filter out paths (STDIN) which should be excluded by default. 329 | egrep -v "(/\.cask/|-autoloads.el|.dir-locals)" 330 | } 331 | 332 | function filter-files-exclude-args { 333 | # Filter out paths (STDIN) which are excluded with --exclude. 334 | if [[ ${files_exclude[@]} ]] 335 | then 336 | ( 337 | # We use a subshell to set IFS temporarily so we can send 338 | # the list of files to grep -F. This is ugly but more 339 | # correct than replacing spaces with line breaks. Note 340 | # that, for some reason, using IFS="\n" or IFS='\n' doesn't 341 | # work, and a literal line break seems to be required. 342 | IFS=" 343 | " 344 | grep -Fv "${files_exclude[*]}" 345 | ) 346 | else 347 | cat 348 | fi 349 | } 350 | 351 | function filter-files-feature { 352 | # Read paths on STDIN and echo ones that (provide 'a-feature). 353 | while read path 354 | do 355 | egrep "^\\(provide '" "$path" &>/dev/null \ 356 | && echo "$path" 357 | done 358 | } 359 | 360 | function args-load-files { 361 | # For file in $@, echo "--load $file". 362 | for file in "$@" 363 | do 364 | printf -- '--load %q ' "$file" 365 | done 366 | } 367 | 368 | function args-load-path { 369 | # Echo load-path arguments. 370 | for path in $(dirs-project | sort -u) 371 | do 372 | printf -- '-L %q ' "$path" 373 | done 374 | } 375 | 376 | function test-files-p { 377 | # Return 0 if $files_project_test is non-empty. 378 | [[ "${files_project_test[@]}" ]] 379 | } 380 | 381 | function buttercup-tests-p { 382 | # Return 0 if Buttercup tests are found. 383 | test-files-p || die "No tests found." 384 | debug "Checking for Buttercup tests..." 385 | 386 | grep "(require 'buttercup)" "${files_project_test[@]}" &>/dev/null 387 | } 388 | 389 | function ert-tests-p { 390 | # Return 0 if ERT tests are found. 391 | test-files-p || die "No tests found." 392 | debug "Checking for ERT tests..." 393 | 394 | # We check for this rather than "(require 'ert)", because ERT may 395 | # already be loaded in Emacs and might not be loaded with 396 | # "require" in a test file. 397 | grep "(ert-deftest" "${files_project_test[@]}" &>/dev/null 398 | } 399 | 400 | function dependencies { 401 | # Echo list of package dependencies. 402 | 403 | # Search package headers. 404 | egrep -i '^;; Package-Requires: ' $(files-project-feature) $(files-project-test) \ 405 | | egrep -o '\([^([:space:]][^)]*\)' \ 406 | | egrep -o '^[^[:space:])]+' \ 407 | | sed -r 's/\(//g' \ 408 | | egrep -v '^emacs$' # Ignore Emacs version requirement. 409 | 410 | # Search Cask file. 411 | if [[ -r Cask ]] 412 | then 413 | egrep '\(depends-on "[^"]+"' Cask \ 414 | | sed -r -e 's/\(depends-on "([^"]+)".*/\1/g' 415 | fi 416 | 417 | # Search -pkg.el file. 418 | if [[ $(git ls-files ./*-pkg.el 2>/dev/null) ]] 419 | then 420 | sed -nr 's/.*\(([-[:alnum:]]+)[[:blank:]]+"[.[:digit:]]+"\).*/\1/p' $(git ls-files ./*-pkg.el 2>/dev/null) 421 | fi 422 | } 423 | 424 | # ** Sandbox 425 | 426 | function sandbox { 427 | verbose 2 "Initializing sandbox..." 428 | 429 | # *** Sandbox arguments 430 | 431 | # MAYBE: Optionally use branch-specific sandbox? 432 | 433 | # Check or make user-emacs-directory. 434 | if [[ $sandbox_dir ]] 435 | then 436 | # Directory given as argument: ensure it exists. 437 | if ! [[ -d $sandbox_dir ]] 438 | then 439 | debug "Making sandbox directory: $sandbox_dir" 440 | mkdir -p "$sandbox_dir" || die "Unable to make sandbox dir." 441 | fi 442 | 443 | # Add Emacs version-specific subdirectory, creating if necessary. 444 | sandbox_dir="$sandbox_dir/$(emacs-version)" 445 | if ! [[ -d $sandbox_dir ]] 446 | then 447 | mkdir "$sandbox_dir" || die "Unable to make sandbox subdir: $sandbox_dir" 448 | fi 449 | else 450 | # Not given: make temp directory, and delete it on exit. 451 | local sandbox_dir=$(mktemp -d) || die "Unable to make sandbox dir." 452 | paths_temp+=("$sandbox_dir") 453 | fi 454 | 455 | # Make argument to load init file if it exists. 456 | init_file="$sandbox_dir/init.el" 457 | 458 | # Set sandbox args. This is a global variable used by the run_emacs function. 459 | args_sandbox=( 460 | --title "makem.sh: $(basename $(pwd)) (sandbox: $sandbox_dir)" 461 | --eval "(setq user-emacs-directory (file-truename \"$sandbox_dir\"))" 462 | --eval "(setq user-init-file (file-truename \"$init_file\"))" 463 | ) 464 | 465 | # Add package-install arguments for dependencies. 466 | if [[ $install_deps ]] 467 | then 468 | local deps=($(dependencies)) 469 | debug "Installing dependencies: ${deps[@]}" 470 | 471 | for package in "${deps[@]}" 472 | do 473 | args_sandbox_package_install+=(--eval "(package-install '$package)") 474 | done 475 | fi 476 | 477 | # Add package-install arguments for linters. 478 | if [[ $install_linters ]] 479 | then 480 | debug "Installing linters: package-lint relint" 481 | 482 | args_sandbox_package_install+=( 483 | --eval "(package-install 'elsa)" 484 | --eval "(package-install 'package-lint)" 485 | --eval "(package-install 'relint)") 486 | fi 487 | 488 | # *** Install packages into sandbox 489 | 490 | if [[ ${args_sandbox_package_install[@]} ]] 491 | then 492 | # Initialize the sandbox (installs packages once rather than for every rule). 493 | verbose 1 "Installing packages into sandbox..." 494 | 495 | run_emacs \ 496 | --eval "(package-refresh-contents)" \ 497 | "${args_sandbox_package_install[@]}" \ 498 | && success "Packages installed." \ 499 | || die "Unable to initialize sandbox." 500 | fi 501 | 502 | verbose 2 "Sandbox initialized." 503 | } 504 | 505 | # ** Utility 506 | 507 | function cleanup { 508 | # Remove temporary paths (${paths_temp[@]}). 509 | 510 | for path in "${paths_temp[@]}" 511 | do 512 | if [[ $debug ]] 513 | then 514 | debug "Debugging enabled: not deleting temporary path: $path" 515 | elif [[ -r $path ]] 516 | then 517 | rm -rf "$path" 518 | else 519 | debug "Temporary path doesn't exist, not deleting: $path" 520 | fi 521 | done 522 | } 523 | 524 | function echo-unset-p { 525 | # Echo 0 if $1 is set, otherwise 1. IOW, this returns the exit 526 | # code of [[ $1 ]] as STDOUT. 527 | [[ $1 ]] 528 | echo $? 529 | } 530 | 531 | function ensure-package-available { 532 | # If package $1 is available, return 0. Otherwise, return 1, and 533 | # if $2 is set, give error otherwise verbose. Outputting messages 534 | # here avoids repetition in callers. 535 | local package=$1 536 | local direct_p=$2 537 | 538 | if ! run_emacs --load $package &>/dev/null 539 | then 540 | if [[ $direct_p ]] 541 | then 542 | error "$package not available." 543 | else 544 | verbose 2 "$package not available." 545 | fi 546 | return 1 547 | fi 548 | } 549 | 550 | function ensure-tests-available { 551 | # If tests of type $1 (like "ERT") are available, return 0. Otherwise, if 552 | # $2 is set, give an error and return 1; otherwise give verbose message. $1 553 | # should have a corresponding predicate command, like ert-tests-p for ERT. 554 | local test_name=$1 555 | local test_command="${test_name,,}-tests-p" # Converts name to lowercase. 556 | local direct_p=$2 557 | 558 | if ! $test_command 559 | then 560 | if [[ $direct_p ]] 561 | then 562 | error "$test_name tests not found." 563 | else 564 | verbose 2 "$test_name tests not found." 565 | fi 566 | return 1 567 | fi 568 | } 569 | 570 | function echo_color { 571 | # This allows bold, italic, etc. without needing a function for 572 | # each variation. 573 | local color_code="COLOR_$1" 574 | shift 575 | 576 | if [[ $color ]] 577 | then 578 | echo -e "${!color_code}${@}${COLOR_off}" 579 | else 580 | echo "$@" 581 | fi 582 | } 583 | function debug { 584 | if [[ $debug ]] 585 | then 586 | function debug { 587 | echo_color yellow "DEBUG ($(ts)): $@" >&2 588 | } 589 | debug "$@" 590 | else 591 | function debug { 592 | true 593 | } 594 | fi 595 | } 596 | function error { 597 | echo_color red "ERROR ($(ts)): $@" >&2 598 | ((errors++)) 599 | return 1 600 | } 601 | function die { 602 | [[ $@ ]] && error "$@" 603 | exit $errors 604 | } 605 | function log { 606 | echo "LOG ($(ts)): $@" >&2 607 | } 608 | function log_color { 609 | local color_name=$1 610 | shift 611 | echo_color $color_name "LOG ($(ts)): $@" >&2 612 | } 613 | function success { 614 | if [[ $verbose -ge 2 ]] 615 | then 616 | log_color green "$@" >&2 617 | fi 618 | } 619 | function verbose { 620 | # $1 is the verbosity level, rest are echoed when appropriate. 621 | if [[ $verbose -ge $1 ]] 622 | then 623 | [[ $1 -eq 1 ]] && local color_name=blue 624 | [[ $1 -ge 2 ]] && local color_name=cyan 625 | 626 | shift 627 | log_color $color_name "$@" >&2 628 | fi 629 | } 630 | 631 | function ts { 632 | date "+%Y-%m-%d %H:%M:%S" 633 | } 634 | 635 | function emacs-version { 636 | # Echo Emacs version number. 637 | 638 | # Don't use run_emacs function, which does more than we need. 639 | "${emacs_command[@]}" -Q --batch --eval "(princ emacs-version)" \ 640 | || die "Unable to get Emacs version." 641 | } 642 | 643 | function rule-p { 644 | # Return 0 if $1 is a rule. 645 | [[ $1 =~ ^(lint-?|tests?)$ ]] \ 646 | || [[ $1 =~ ^(batch|interactive)$ ]] \ 647 | || [[ $(type -t "$2" 2>/dev/null) =~ function ]] 648 | } 649 | 650 | # * Rules 651 | 652 | # These functions are intended to be called as rules, like a Makefile. 653 | # Some rules test $1 to determine whether the rule is being called 654 | # directly or from a meta-rule; if directly, an error is given if the 655 | # rule can't be run, otherwise it's skipped. 656 | 657 | function all { 658 | verbose 1 "Running all rules..." 659 | 660 | lint 661 | tests 662 | } 663 | 664 | function compile { 665 | [[ $compile ]] || return 0 666 | unset compile # Only compile once. 667 | 668 | verbose 1 "Compiling..." 669 | debug "Byte-compile files: ${files_project_byte_compile[@]}" 670 | 671 | batch-byte-compile "${files_project_byte_compile[@]}" \ 672 | && success "Compiling finished without errors." \ 673 | || error "Compilation failed." 674 | } 675 | 676 | function batch { 677 | # Run Emacs in batch mode with ${args_batch_interactive[@]} and 678 | # with project source and test files loaded. 679 | verbose 1 "Executing Emacs with arguments: ${args_batch_interactive[@]}" 680 | 681 | run_emacs \ 682 | $(args-load-files "${files_project_feature[@]}" "${files_project_test[@]}") \ 683 | "${args_batch_interactive[@]}" 684 | } 685 | 686 | function interactive { 687 | # Run Emacs interactively. Most useful with --sandbox and --install-deps. 688 | verbose 1 "Running Emacs interactively..." 689 | verbose 2 "Loading files:" "${files_project_feature[@]}" "${files_project_test[@]}" 690 | 691 | unset arg_batch 692 | run_emacs \ 693 | $(args-load-files "${files_project_feature[@]}" "${files_project_test[@]}") \ 694 | --eval "(load user-init-file)" \ 695 | "${args_batch_interactive[@]}" 696 | arg_batch="--batch" 697 | } 698 | 699 | function lint { 700 | verbose 1 "Linting..." 701 | 702 | lint-checkdoc 703 | lint-compile 704 | lint-declare 705 | lint-indent 706 | lint-package 707 | lint-regexps 708 | } 709 | 710 | function lint-checkdoc { 711 | verbose 1 "Linting checkdoc..." 712 | 713 | local checkdoc_file="$(elisp-checkdoc-file)" 714 | paths_temp+=("$checkdoc_file") 715 | 716 | run_emacs \ 717 | --load="$checkdoc_file" \ 718 | "${files_project_feature[@]}" \ 719 | && success "Linting checkdoc finished without errors." \ 720 | || error "Linting checkdoc failed." 721 | } 722 | 723 | function lint-compile { 724 | verbose 1 "Linting compilation..." 725 | 726 | compile_error_on_warn=true 727 | batch-byte-compile "${files_project_byte_compile[@]}" \ 728 | && success "Linting compilation finished without errors." \ 729 | || error "Linting compilation failed." 730 | unset compile_error_on_warn 731 | } 732 | 733 | function lint-declare { 734 | verbose 1 "Linting declarations..." 735 | 736 | local check_declare_file="$(elisp-check-declare-file)" 737 | paths_temp+=("$check_declare_file") 738 | 739 | run_emacs \ 740 | --load "$check_declare_file" \ 741 | -f makem-check-declare-files-and-exit \ 742 | "${files_project_feature[@]}" \ 743 | && success "Linting declarations finished without errors." \ 744 | || error "Linting declarations failed." 745 | } 746 | 747 | function lint-elsa { 748 | verbose 1 "Linting with Elsa..." 749 | 750 | # MAYBE: Install Elsa here rather than in sandbox init, to avoid installing 751 | # it when not needed. However, we should be careful to be clear about when 752 | # packages are installed, because installing them does execute code. 753 | run_emacs \ 754 | --load elsa \ 755 | -f elsa-run-files-and-exit \ 756 | "${files_project_feature[@]}" \ 757 | && success "Linting with Elsa finished without errors." \ 758 | || error "Linting with Elsa failed." 759 | } 760 | 761 | function lint-indent { 762 | verbose 1 "Linting indentation..." 763 | 764 | # We load project source files as well, because they may contain 765 | # macros with (declare (indent)) rules which must be loaded to set 766 | # indentation. 767 | 768 | run_emacs \ 769 | --load "$(elisp-lint-indent-file)" \ 770 | $(args-load-files "${files_project_feature[@]}" "${files_project_test[@]}") \ 771 | --funcall makem-lint-indent-batch-and-exit \ 772 | "${files_project_feature[@]}" "${files_project_test[@]}" \ 773 | && success "Linting indentation finished without errors." \ 774 | || error "Linting indentation failed." 775 | } 776 | 777 | function lint-package { 778 | ensure-package-available package-lint $1 || return $(echo-unset-p $1) 779 | 780 | verbose 1 "Linting package..." 781 | 782 | run_emacs \ 783 | --load package-lint \ 784 | --funcall package-lint-batch-and-exit \ 785 | "${files_project_feature[@]}" \ 786 | && success "Linting package finished without errors." \ 787 | || error "Linting package failed." 788 | } 789 | 790 | function lint-regexps { 791 | ensure-package-available relint $1 || return $(echo-unset-p $1) 792 | 793 | verbose 1 "Linting regexps..." 794 | 795 | run_emacs \ 796 | --load relint \ 797 | --funcall relint-batch \ 798 | "${files_project_source[@]}" \ 799 | && success "Linting regexps finished without errors." \ 800 | || error "Linting regexps failed." 801 | } 802 | 803 | function tests { 804 | verbose 1 "Running all tests..." 805 | 806 | test-ert 807 | test-buttercup 808 | } 809 | 810 | function test-ert-interactive { 811 | verbose 1 "Running ERT tests interactively..." 812 | 813 | unset arg_batch 814 | run_emacs \ 815 | $(args-load-files "${files_project_test[@]}") \ 816 | --eval "(ert-run-tests-interactively t)" 817 | arg_batch="--batch" 818 | } 819 | 820 | function test-buttercup { 821 | ensure-tests-available Buttercup $1 || return $(echo-unset-p $1) 822 | compile || die 823 | 824 | verbose 1 "Running Buttercup tests..." 825 | 826 | local buttercup_file="$(elisp-buttercup-file)" 827 | paths_temp+=("$buttercup_file") 828 | 829 | run_emacs \ 830 | $(args-load-files "${files_project_test[@]}") \ 831 | -f buttercup-run \ 832 | && success "Buttercup tests finished without errors." \ 833 | || error "Buttercup tests failed." 834 | } 835 | 836 | function test-ert { 837 | ensure-tests-available ERT $1 || return $(echo-unset-p $1) 838 | compile || die 839 | 840 | verbose 1 "Running ERT tests..." 841 | debug "Test files: ${files_project_test[@]}" 842 | 843 | run_emacs \ 844 | $(args-load-files "${files_project_test[@]}") \ 845 | -f ert-run-tests-batch-and-exit \ 846 | && success "ERT tests finished without errors." \ 847 | || error "ERT tests failed." 848 | } 849 | 850 | # * Defaults 851 | 852 | test_files_regexp='^((tests?|t)/)|-tests?.el$|^test-' 853 | 854 | emacs_command=("emacs") 855 | errors=0 856 | verbose=0 857 | compile=true 858 | arg_batch="--batch" 859 | 860 | # MAYBE: Disable color if not outputting to a terminal. (OTOH, the 861 | # colorized output is helpful in CI logs, and I don't know if, 862 | # e.g. GitHub Actions logging pretends to be a terminal.) 863 | color=true 864 | 865 | # TODO: Using the current directory (i.e. a package's repo root directory) in 866 | # load-path can cause weird errors in case of--you guessed it--stale .ELC files, 867 | # the zombie problem that just won't die. It's incredible how many different ways 868 | # this problem presents itself. In this latest example, an old .ELC file, for a 869 | # .EL file that had since been renamed, was present on my local system, which meant 870 | # that an example .EL file that hadn't been updated was able to "require" that .ELC 871 | # file's feature without error. But on another system (in this case, trying to 872 | # setup CI using GitHub Actions), the old .ELC was not present, so the example .EL 873 | # file was not able to load the feature, which caused a byte-compilation error. 874 | 875 | # In this case, I will prevent such example files from being compiled. But in 876 | # general, this can cause weird problems that are tedious to debug. I guess 877 | # the best way to fix it would be to actually install the repo's code as a 878 | # package into the sandbox, but doing that would require additional tooling, 879 | # pulling in something like Quelpa or package-build--and if the default recipe 880 | # weren't being used, the actual recipe would have to be fetched off MELPA or 881 | # something, which seems like getting too smart for our own good. 882 | 883 | # TODO: Emit a warning if .ELC files that don't match any .EL files are detected. 884 | 885 | # ** Colors 886 | 887 | COLOR_off='\e[0m' 888 | COLOR_black='\e[0;30m' 889 | COLOR_red='\e[0;31m' 890 | COLOR_green='\e[0;32m' 891 | COLOR_yellow='\e[0;33m' 892 | COLOR_blue='\e[0;34m' 893 | COLOR_purple='\e[0;35m' 894 | COLOR_cyan='\e[0;36m' 895 | COLOR_white='\e[0;37m' 896 | 897 | # ** Package system args 898 | 899 | args_package_archives=( 900 | --eval "(add-to-list 'package-archives '(\"gnu\" . \"https://elpa.gnu.org/packages/\") t)" 901 | --eval "(add-to-list 'package-archives '(\"melpa\" . \"https://melpa.org/packages/\") t)" 902 | ) 903 | 904 | args_org_package_archives=( 905 | --eval "(add-to-list 'package-archives '(\"org\" . \"https://orgmode.org/elpa/\") t)" 906 | ) 907 | 908 | args_package_init=( 909 | --eval "(package-initialize)" 910 | ) 911 | 912 | elisp_org_package_archive="(add-to-list 'package-archives '(\"org\" . \"https://orgmode.org/elpa/\") t)" 913 | 914 | # * Args 915 | 916 | args=$(getopt -n "$0" \ 917 | -o dhe:E:i:s::vf:CO \ 918 | -l exclude:,emacs:,install-deps,install-linters,debug,debug-load-path,help,install:,verbose,file:,no-color,no-compile,no-org-repo,sandbox:: \ 919 | -- "$@") \ 920 | || { usage; exit 1; } 921 | eval set -- "$args" 922 | 923 | while true 924 | do 925 | case "$1" in 926 | --install-deps) 927 | install_deps=true 928 | ;; 929 | --install-linters) 930 | install_linters=true 931 | ;; 932 | -d|--debug) 933 | debug=true 934 | verbose=2 935 | args_debug=(--eval "(setq init-file-debug t)" 936 | --eval "(setq debug-on-error t)") 937 | ;; 938 | --debug-load-path) 939 | debug_load_path=true 940 | ;; 941 | -h|--help) 942 | usage 943 | exit 944 | ;; 945 | -E|--emacs) 946 | shift 947 | emacs_command=($1) 948 | ;; 949 | -i|--install) 950 | shift 951 | args_sandbox_package_install+=(--eval "(package-install '$1)") 952 | ;; 953 | -s|--sandbox) 954 | sandbox=true 955 | shift 956 | sandbox_dir="$1" 957 | 958 | if ! [[ $sandbox_dir ]] 959 | then 960 | debug "No sandbox dir: installing dependencies." 961 | install_deps=true 962 | else 963 | debug "Sandbox dir: $1" 964 | fi 965 | ;; 966 | -v|--verbose) 967 | ((verbose++)) 968 | ;; 969 | -e|--exclude) 970 | shift 971 | debug "Excluding file: $1" 972 | files_exclude+=("$1") 973 | ;; 974 | -f|--file) 975 | shift 976 | args_files+=("$1") 977 | ;; 978 | -O|--no-org-repo) 979 | unset elisp_org_package_archive 980 | ;; 981 | --no-color) 982 | unset color 983 | ;; 984 | -C|--no-compile) 985 | unset compile 986 | ;; 987 | --) 988 | # Remaining args (required; do not remove) 989 | shift 990 | rest=("$@") 991 | break 992 | ;; 993 | esac 994 | 995 | shift 996 | done 997 | 998 | debug "ARGS: $args" 999 | debug "Remaining args: ${rest[@]}" 1000 | 1001 | # Set package elisp (which depends on --no-org-repo arg). 1002 | package_initialize_file="$(elisp-package-initialize-file)" 1003 | paths_temp+=("$package_initialize_file") 1004 | 1005 | # * Main 1006 | 1007 | trap cleanup EXIT INT TERM 1008 | 1009 | # Discover project files. 1010 | files_project_feature=($(files-project-feature)) 1011 | files_project_test=($(files-project-test)) 1012 | files_project_byte_compile=("${files_project_feature[@]}" "${files_project_test[@]}") 1013 | 1014 | if [[ ${args_files[@]} ]] 1015 | then 1016 | # Add specified files. 1017 | files_project_feature+=("${args_files[@]}") 1018 | files_project_byte_compile+=("${args_files[@]}") 1019 | fi 1020 | 1021 | debug "EXCLUDING FILES: ${files_exclude[@]}" 1022 | debug "FEATURE FILES: ${files_project_feature[@]}" 1023 | debug "TEST FILES: ${files_project_test[@]}" 1024 | debug "BYTE-COMPILE FILES: ${files_project_byte_compile[@]}" 1025 | 1026 | if ! [[ ${files_project_feature[@]} ]] 1027 | then 1028 | error "No files specified and not in a git repo." 1029 | exit 1 1030 | fi 1031 | 1032 | # Set load path. 1033 | args_load_paths=($(args-load-path)) 1034 | debug "LOAD PATH ARGS: ${args_load_paths[@]}" 1035 | 1036 | # If rules include linters and sandbox-dir is unspecified, install 1037 | # linters automatically. 1038 | if [[ $sandbox && ! $sandbox_dir ]] && [[ "${rest[@]}" =~ lint ]] 1039 | then 1040 | debug "Installing linters automatically." 1041 | install_linters=true 1042 | fi 1043 | 1044 | # Initialize sandbox. 1045 | [[ $sandbox ]] && sandbox 1046 | 1047 | # Run rules. 1048 | for rule in "${rest[@]}" 1049 | do 1050 | if [[ $batch || $interactive ]] 1051 | then 1052 | debug "Adding batch/interactive argument: $rule" 1053 | args_batch_interactive+=("$rule") 1054 | 1055 | elif [[ $rule = batch ]] 1056 | then 1057 | # Remaining arguments are passed to Emacs. 1058 | batch=true 1059 | elif [[ $rule = interactive ]] 1060 | then 1061 | # Remaining arguments are passed to Emacs. 1062 | interactive=true 1063 | 1064 | elif type -t "$rule" 2>/dev/null | grep function &>/dev/null 1065 | then 1066 | # Pass called-directly as $1 to indicate that the rule is 1067 | # being called directly rather than from a meta-rule. 1068 | $rule called-directly 1069 | elif [[ $rule = test ]] 1070 | then 1071 | # Allow the "tests" rule to be called as "test". Since "test" 1072 | # is a shell builtin, this workaround is required. 1073 | tests 1074 | else 1075 | error "Invalid rule: $rule" 1076 | fi 1077 | done 1078 | 1079 | # Batch/interactive rules. 1080 | [[ $batch ]] && batch 1081 | [[ $interactive ]] && interactive 1082 | 1083 | if [[ $errors -gt 0 ]] 1084 | then 1085 | log_color red "Finished with $errors errors." 1086 | else 1087 | success "Finished without errors." 1088 | fi 1089 | 1090 | exit $errors 1091 | --------------------------------------------------------------------------------