├── .gitignore ├── LICENSE ├── README.md ├── company-lean.el ├── helm-lean.el ├── lean-debug.el ├── lean-dev.el ├── lean-eri.el ├── lean-flycheck.el ├── lean-hole.el ├── lean-info.el ├── lean-input.el ├── lean-leanpkg.el ├── lean-message-boxes.el ├── lean-mode.el ├── lean-right-click.el ├── lean-server.el ├── lean-settings.el ├── lean-syntax.el ├── lean-type.el └── lean-util.el /.gitignore: -------------------------------------------------------------------------------- 1 | .cask 2 | *.elc 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is the Emacs mode for the [Lean 3 theorem prover][lean]. 2 | 3 | [lean]: https://github.com/leanprover/lean 4 | 5 | Installation 6 | ============ 7 | 8 | `lean-mode` requires GNU Emacs 24.3 or newer. The recommended way to install it is via [MELPA](https://melpa.org). If you have not already configured MELPA, put the following code in your Emacs init file (typically `~/.emacs.d/init.el`): 9 | ```elisp 10 | (require 'package) ; You might already have this line 11 | (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/")) 12 | (package-initialize) ; You might already have this line 13 | ``` 14 | See also [MELPA: Getting Started](https://melpa.org/#/getting-started). 15 | 16 | With MELPA configured, you can `M-x package-install` the packages `lean-mode` and `company-lean`. The latter package gives you auto completion and is strongly recommended. There is a third package, `helm-lean`, which provides a searchable list of declarations on `C-c C-d` using the Helm interface. `helm-lean` requires Emacs 24.4 or newer. 17 | 18 | For `company-lean`, you should also bind a key to trigger completion, if you have not already done so: 19 | 20 | ```elisp 21 | ;; Trigger completion on Shift-Space 22 | (global-set-key (kbd "S-SPC") #'company-complete) 23 | ``` 24 | 25 | Updating 26 | -------- 27 | 28 | For updating the Lean MELPA packages, use `package-list-packages`. See the section "Updating Packages" on [MELPA: Getting Started](https://melpa.org/#/getting-started) for details. 29 | 30 | Trying It Out 31 | ============= 32 | 33 | If things are working correctly, you should see the word ``Lean`` in the 34 | Emacs mode line when you open a file with extension `.lean`. If you type 35 | ```lean 36 | #check id 37 | ``` 38 | the word ``#check`` will be underlined, and hovering over it will show 39 | you the type of ``id``. The mode line will show ``FlyC:0/1``, indicating 40 | that there are no errors and one piece of information displayed. 41 | 42 | Key Bindings and Commands 43 | ========================= 44 | 45 | | Key | Function | 46 | |--------------------|---------------------------------------------------------------------------------| 47 | | M-. | jump to definition in source file (`lean-find-definition`) | 48 | | M-, | jump back to position before M-. (`xref-pop-marker-stack`) | 49 | | C-c C-k | shows the keystroke needed to input the symbol under the cursor | 50 | | C-c C-x | execute lean in stand-alone mode (`lean-std-exe`) | 51 | | C-c SPC | run a command on the hole at point (`lean-hole`) | 52 | | C-c C-d | show a searchable list of definitions (`helm-lean-definitions`) | 53 | | C-c C-g | toggle showing current tactic proof goal (`lean-toggle-show-goal`) | 54 | | C-c C-n | toggle showing next error in dedicated buffer (`lean-toggle-next-error`) | 55 | | C-c C-b | toggle showing output in inline boxes (`lean-message-boxes-toggle`) | 56 | | C-c C-r | restart the lean server (`lean-server-restart`) | 57 | | C-c C-s | switch to a different Lean version via [elan](https://github.com/Kha/elan) (`lean-server-switch-version`) | 58 | | C-c ! n | flycheck: go to next error | 59 | | C-c ! p | flycheck: go to previous error | 60 | | C-c ! l | flycheck: show list of errors | 61 | 62 | In the default configuration, the Flycheck annotation `FlyC:n/n` indicates the 63 | number of errors / responses from Lean; clicking on `FlyC` opens the Flycheck menu. 64 | 65 | 66 | Message Boxes 67 | ================ 68 | To view the output of commands such as `check` and `print` in boxes in the buffer, enable the feature using C-c C-b. 69 | If you then type 70 | ```lean 71 | #check id 72 | ``` 73 | a box appears after the line showing the type of `id`. Customize `lean-message-boxes-enabled-captions` to choose categories of boxes. 74 | In particular, add `"trace output"` to the list to see proof states and other traces in the buffer. 75 | 76 | Known Issues and Possible Solutions 77 | =================================== 78 | 79 | Unicode 80 | ------- 81 | 82 | If you experience a problem rendering unicode symbols on emacs, 83 | please download the following fonts and install them on your machine: 84 | 85 | - [Quivira.ttf](http://www.quivira-font.com/files/Quivira.ttf) 86 | - [Dejavu Fonts](http://sourceforge.net/projects/dejavu/files/dejavu/2.35/dejavu-fonts-ttf-2.35.tar.bz2) 87 | - [NotoSans](https://github.com/googlei18n/noto-fonts/blob/master/hinted/NotoSans-Regular.ttc?raw=true) 88 | - [NotoSansSymbols](https://github.com/googlei18n/noto-fonts/blob/master/unhinted/NotoSansSymbols-Regular.ttf?raw=true) 89 | 90 | Then, have the following lines in your emacs setup to use `DejaVu Sans Mono` font: 91 | 92 | ```elisp 93 | (when (member "DejaVu Sans Mono" (font-family-list)) 94 | (set-face-attribute 'default nil :font "DejaVu Sans Mono-11")) 95 | ``` 96 | 97 | You may also need to install the [emacs-unicode-fonts](https://github.com/rolandwalker/unicode-fonts) package, after which you should add the following lines to your emacs setup: 98 | 99 | ```elisp 100 | (require 'unicode-fonts) 101 | (unicode-fonts-setup) 102 | ``` 103 | 104 | Contributions 105 | ============= 106 | 107 | Contributions are welcome! 108 | 109 | Building from Source 110 | -------------------- 111 | 112 | When working on `lean-mode` itself, it is much easier to just `require` the sources than repeatedly building the MELPA packages: 113 | 114 | ```elisp 115 | (add-to-list 'load-path "~/path/to/lean-mode/") 116 | (require 'lean-mode) 117 | (require 'company-lean) 118 | (require 'helm-lean) 119 | ``` 120 | 121 | Make sure you have the packages' dependencies listed on MELPA installed -- the easiest way to do this may be to just install the official Lean MELPA packages and making sure the `require` commands above are execute before `package-initialize`. 122 | -------------------------------------------------------------------------------- /company-lean.el: -------------------------------------------------------------------------------- 1 | ;;; company-lean.el --- A company backend for lean-mode -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. 4 | 5 | ;; Author: Leonardo de Moura 6 | ;; Soonho Kong 7 | ;; Gabriel Ebner 8 | ;; Sebastian Ullrich 9 | ;; Maintainer: Sebastian Ullrich 10 | ;; Created: Jan 09, 2014 11 | ;; Keywords: languages 12 | ;; Package-Requires: ((emacs "24.3") (dash "2.18.0") (s "1.10.0") (f "0.19.0") (company "0.9.3") (lean-mode "3.3.0")) 13 | ;; URL: https://github.com/leanprover/lean-mode 14 | 15 | ;; Released under Apache 2.0 license as described in the file LICENSE. 16 | 17 | ;;; Commentary: 18 | 19 | ;; Provides context-sensitive auto completion for lean-mode. 20 | 21 | ;;; Code: 22 | 23 | (require 'company) 24 | (require 'company-etags) 25 | (require 'dash) 26 | (require 'f) 27 | (require 's) 28 | (require 'cl-lib) 29 | (require 'lean-util) 30 | (require 'lean-server) 31 | 32 | (defcustom company-lean-type-foreground (face-foreground 'font-lock-keyword-face) 33 | "Color of type parameter in auto-complete candidates" 34 | :group 'lean 35 | :type 'color) 36 | 37 | ;;;###autoload 38 | (defun company-lean-hook () 39 | (set (make-local-variable 'company-backends) '(company-lean)) 40 | (setq-local company-tooltip-limit 20) ; bigger popup window 41 | (setq-local company-minimum-prefix-length 5) 42 | (setq-local company-idle-delay nil) ; decrease delay before autocompletion popup shows 43 | ;(setq-local company-echo-delay 0) ; remove annoying blinking 44 | (setq-local company-begin-commands '(self-insert-command)) ; start autocompletion only after typing 45 | (company-mode t)) 46 | 47 | (cl-defun company-lean--make-candidate (prefix &key text type (tactic_params 'empty) doc source &allow-other-keys) 48 | (cl-destructuring-bind (&key file line _column) source 49 | (let ((source (cond 50 | (file (cons file line)) 51 | (line (cons (current-buffer) (lean-pos-at-line-col line 0)))))) 52 | (propertize text 53 | 'type type 54 | 'tactic_params tactic_params 55 | 'doc doc 56 | 'source source 57 | 'prefix prefix)))) 58 | 59 | (defun company-lean--handle-singleton-candidate (prefix candidates) 60 | "Handle singleton candidate. If the candidate does not start 61 | with prefix, we add prefix itself as a candidate to prevent 62 | from auto-completion." 63 | (let ((candidate (car candidates))) 64 | (cond ((s-prefix? prefix candidate) candidates) 65 | (t `(,candidate ,prefix))))) 66 | 67 | (cl-defun company-lean--exec (&key skip-completions) 68 | "Synchronously queries completions for the current point from server and returns a plist with keys :prefix and :candidates., or nil if no completion should be triggered." 69 | (lean-server-sync) 70 | (let* ((col (lean-line-offset)) 71 | (response (lean-server-send-synchronous-command 72 | 'complete (list :file_name (buffer-file-name) 73 | :line (line-number-at-pos) 74 | :column col 75 | :skip_completions (or skip-completions :json-false)))) 76 | (candidates (plist-get response :completions)) 77 | (prefix (plist-get response :prefix))) 78 | (when candidates 79 | (setq candidates 80 | (--map (apply 'company-lean--make-candidate prefix it) 81 | candidates)) 82 | (when (= (length candidates) 1) 83 | (setq candidates 84 | (company-lean--handle-singleton-candidate prefix candidates)))) 85 | (when (plist-member response :prefix) 86 | (list :prefix prefix :candidates candidates)))) 87 | 88 | (defun company-lean--annotation (candidate) 89 | (let ((type (get-text-property 0 'type candidate)) 90 | (tactic_params (get-text-property 0 'tactic_params candidate))) 91 | (when type 92 | (let* ((annotation-str (if (not (eq tactic_params 'empty)) 93 | (format " %s" (mapconcat 'identity tactic_params " ")) 94 | (format " : %s" type))) 95 | (annotation-len (length annotation-str)) 96 | (candidate-len (length candidate)) 97 | (entry-width (+ candidate-len 98 | annotation-len)) 99 | (allowed-width (truncate (* 0.90 (window-body-width))))) 100 | (when (> entry-width allowed-width) 101 | (setq annotation-str 102 | (concat 103 | (substring-no-properties annotation-str 104 | 0 105 | (- allowed-width candidate-len 3)) 106 | "..."))) 107 | annotation-str)))) 108 | 109 | (defun company-lean--location (arg) 110 | (get-text-property 0 'source arg)) 111 | 112 | (defun company-lean--match (arg) 113 | "Return the end of matched region" 114 | (let ((prefix (get-text-property 0 'prefix arg))) 115 | (when (and prefix (eq (s-index-of prefix arg) 0)) 116 | (length prefix)))) 117 | 118 | (defun company-lean--meta (arg) 119 | (get-text-property 0 'doc arg)) 120 | 121 | (defun company-lean (command &optional arg &rest ignored) 122 | (cl-case command 123 | (prefix (plist-get (company-lean--exec :skip-completions t) :prefix)) 124 | (candidates (plist-get (company-lean--exec) :candidates)) 125 | (annotation (company-lean--annotation arg)) 126 | (location (company-lean--location arg)) 127 | (match (company-lean--match arg)) 128 | (meta (company-lean--meta arg)) 129 | (no-cache t) 130 | (require-match 'never) 131 | (sorted t))) 132 | 133 | ;; ADVICES 134 | ;; ======= 135 | 136 | (defadvice company--window-width 137 | (after company-lean--window-width activate) 138 | (when (eq major-mode 'lean-mode) 139 | (setq ad-return-value (truncate (* 0.95 (window-body-width)))))) 140 | 141 | (defun company-lean--replace-regex-return-position (regex rep string &optional start) 142 | "Find regex and replace with rep on string. 143 | 144 | Return replaced string and start and end positions of replacement." 145 | (let* ((start (or start 0)) 146 | (m-start (string-match regex string start)) 147 | (m-end (match-end 0)) 148 | pre-string post-string matched-string replaced-string result) 149 | (cond (m-start 150 | (setq pre-string (substring string 0 m-start)) 151 | (setq matched-string (substring string m-start m-end)) 152 | (setq post-string (substring string m-end)) 153 | (string-match regex matched-string) 154 | (setq replaced-string 155 | (replace-match rep nil nil matched-string)) 156 | (setq result (concat pre-string 157 | replaced-string 158 | post-string)) 159 | `(,result ,m-start ,(+ m-start (length replaced-string))) 160 | )))) 161 | 162 | (defun company-lean--replace-regex-add-properties-all (regex rep string properties) 163 | "Find all occurrences of regex in string, and replace them with 164 | rep. Then, add text-properties on the replaced region." 165 | (let ((replace-result-items (company-lean--replace-regex-return-position regex rep string)) 166 | (result string)) 167 | (while replace-result-items 168 | (pcase replace-result-items 169 | (`(,replaced-string ,m-start ,m-end) 170 | (setq result replaced-string) 171 | (add-text-properties m-start m-end properties result) 172 | (setq replace-result-items 173 | (company-lean--replace-regex-return-position regex rep result m-end))))) 174 | result)) 175 | 176 | (eval-after-load 'company 177 | '(defadvice company-fill-propertize 178 | (after company-lean-fill-propertize activate) 179 | (when (eq major-mode 'lean-mode) 180 | (let* ((selected (ad-get-arg 3)) 181 | (foreground-color company-lean-type-foreground) 182 | (background-color (if selected (face-background 'company-tooltip-selection) 183 | (face-background 'company-tooltip))) 184 | (face-attrs 185 | (cond (background-color `(:foreground ,foreground-color 186 | :background ,background-color)) 187 | (t `(:foreground ,foreground-color)))) 188 | (properties `(face ,face-attrs 189 | mouse-face company-tooltip)) 190 | (old-return ad-return-value) 191 | (old-len (length old-return)) 192 | new-return new-len) 193 | (setq new-return 194 | (company-lean--replace-regex-add-properties-all 195 | (rx "?" word-start (group (+ (not white))) word-end) 196 | "\\1" 197 | ad-return-value 198 | properties)) 199 | (setq new-len (length new-return)) 200 | (while (< (length new-return) old-len) 201 | (setq new-return 202 | (concat new-return " "))) 203 | (when background-color 204 | (add-text-properties new-len old-len properties new-return)) 205 | (setq ad-return-value new-return))))) 206 | 207 | ;;;###autoload 208 | (add-hook 'lean-mode-hook #'company-lean-hook) 209 | 210 | (provide 'company-lean) 211 | ;;; company-lean.el ends here 212 | -------------------------------------------------------------------------------- /helm-lean.el: -------------------------------------------------------------------------------- 1 | ;;; helm-lean.el --- Helm interfaces for lean-mode -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. 4 | 5 | ;; Author: Leonardo de Moura 6 | ;; Soonho Kong 7 | ;; Gabriel Ebner 8 | ;; Sebastian Ullrich 9 | ;; Maintainer: Sebastian Ullrich 10 | ;; Created: Jan 09, 2014 11 | ;; Keywords: languages 12 | ;; Package-Requires: ((emacs "24.3") (dash "2.18.0") (helm "2.8.0") (lean-mode "3.3.0")) 13 | ;; URL: https://github.com/leanprover/lean-mode 14 | 15 | ;; Released under Apache 2.0 license as described in the file LICENSE. 16 | 17 | ;;; Commentary: 18 | 19 | ;; Currently provides an interface for looking up Lean definitions by name 20 | 21 | ;;; Code: 22 | 23 | (require 'dash) 24 | (require 'helm) 25 | (require 'lean-server) 26 | 27 | (defcustom helm-lean-keybinding-helm-lean-definitions (kbd "C-c C-d") 28 | "Lean Keybinding for helm-lean-definitions" 29 | :group 'lean-keybinding :type 'key-sequence) 30 | 31 | (defun helm-lean-definitions-format-candidate (c) 32 | `(,(format "%s : %s %s" 33 | (propertize (plist-get c :text) 'face font-lock-variable-name-face) 34 | (plist-get c :type) 35 | (propertize (plist-get (plist-get c :source) :file) 'face font-lock-comment-face)) 36 | . ,c)) 37 | 38 | (defun helm-lean-definitions-candidates () 39 | (with-helm-current-buffer 40 | (let* ((response (lean-server-send-synchronous-command 'search (list :query helm-pattern))) 41 | (results (plist-get response :results)) 42 | (results (-filter (lambda (c) (plist-get c :source)) results)) 43 | (candidates (-map 'helm-lean-definitions-format-candidate results))) 44 | candidates))) 45 | 46 | ;;;###autoload 47 | (defun helm-lean-definitions () 48 | "Open a 'helm' interface for searching Lean definitions." 49 | (interactive) 50 | (require 'helm) 51 | (helm :sources (helm-build-sync-source "helm-source-lean-definitions" 52 | :requires-pattern 1 53 | :candidates 'helm-lean-definitions-candidates 54 | :volatile t 55 | :match 'identity 56 | :action '(("Go to" . (lambda (c) (with-helm-current-buffer 57 | (apply 'lean-find-definition-cont (plist-get c :source))))))) 58 | :buffer "*helm Lean definitions*")) 59 | 60 | ;;;###autoload 61 | (defun helm-lean-hook () 62 | "Set up helm-lean for current buffer" 63 | (local-set-key helm-lean-keybinding-helm-lean-definitions #'helm-lean-definitions)) 64 | 65 | ;;;###autoload 66 | (add-hook 'lean-mode-hook #'helm-lean-hook) 67 | 68 | (provide 'helm-lean) 69 | ;;; helm-lean.el ends here 70 | -------------------------------------------------------------------------------- /lean-debug.el: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. 2 | ;; Released under Apache 2.0 license as described in the file LICENSE. 3 | ;; 4 | ;; Author: Soonho Kong 5 | ;; 6 | (require 'cl-lib) 7 | 8 | (defvar lean-debug-mode nil) 9 | 10 | (defvar lean-debug-buffer-name "*lean-debug*") 11 | 12 | (defun lean-turn-on-debug-mode (&optional print-msg) 13 | (interactive) 14 | (when (or (called-interactively-p 'any) print-msg) 15 | (message "lean: turn on debug mode")) 16 | (get-buffer-create lean-debug-buffer-name) 17 | (buffer-disable-undo lean-debug-buffer-name) 18 | (display-buffer lean-debug-buffer-name 'display-buffer-reuse-window 19 | '((reusable-frames . t))) 20 | (setq lean-debug-mode t)) 21 | 22 | (defun lean-turn-off-debug-mode (&optional print-msg) 23 | (interactive) 24 | (when (eq major-mode 'lean-mode) 25 | (when (or (called-interactively-p 'any) print-msg) 26 | (message "lean: turn off debug mode")) 27 | (setq lean-debug-mode nil))) 28 | 29 | (defun lean-output-to-buffer (buffer-name format-string args) 30 | (with-current-buffer 31 | (get-buffer-create buffer-name) 32 | (save-selected-window 33 | (ignore-errors 34 | (select-window (get-buffer-window buffer-name t))) 35 | (goto-char (point-max)) 36 | (insert (apply 'format format-string args))))) 37 | 38 | (defun lean-debug (format-string &rest args) 39 | "Display a message at the bottom of the *lean-debug* buffer." 40 | (when lean-debug-mode 41 | (let ((time-str (format-time-string "%H:%M:%S.%3N" (current-time)))) 42 | (lean-output-to-buffer lean-debug-buffer-name 43 | (concat "%s -- " format-string "\n") 44 | (cons (propertize time-str 'face 'font-lock-keyword-face) 45 | args))))) 46 | 47 | (provide 'lean-debug) 48 | -------------------------------------------------------------------------------- /lean-dev.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | ;; 3 | ;; Copyright (c) 2017 Microsoft Corporation. All rights reserved. 4 | ;; Released under Apache 2.0 license as described in the file LICENSE. 5 | ;; 6 | ;; Author: Sebastian Ullrich 7 | ;; 8 | 9 | (require 'f) 10 | (require 'lean-util) 11 | 12 | (defun lean-diff-test-file () 13 | "Use interactive ./test_input.sh on file of current buffer" 14 | (interactive) 15 | (message (shell-command-to-string (format "yes | ./test_single.sh \"%s\" \"%s\" yes" 16 | (lean-get-executable "lean") 17 | (f-filename (buffer-file-name)))))) 18 | 19 | (provide 'lean-dev) 20 | -------------------------------------------------------------------------------- /lean-eri.el: -------------------------------------------------------------------------------- 1 | ;;; lean-eri.el --- Enhanced relative indentation (eri) 2 | 3 | ;;; Commentary: 4 | 5 | ;; Adapted from agda-mode (https://github.com/agda/agda/blob/master/src/data/emacs-mode/eri.el) 6 | 7 | ;;; Code: 8 | 9 | (require 'cl-lib) 10 | 11 | (defun lean-eri-current-line-length nil 12 | "Calculate length of current line." 13 | (- (line-end-position) (line-beginning-position))) 14 | 15 | (defun lean-eri-current-line-empty nil 16 | "Return non-nil if the current line is empty (not counting white space)." 17 | (equal (current-indentation) 18 | (lean-eri-current-line-length))) 19 | 20 | (defun lean-eri-maximum (xs) 21 | "Calculate maximum element in XS. 22 | Returns nil if the list is empty." 23 | (if xs (apply 'max xs))) 24 | 25 | (defun lean-eri-take (n xs) 26 | "Return the first N elements of XS." 27 | (butlast xs (- (length xs) n))) 28 | 29 | (defun lean-eri-split (x xs) 30 | "Return a pair of lists (XS1 . XS2). 31 | If XS is sorted, then XS = (append XS1 XS2), and all elements in 32 | XS1 are <= X, whereas all elements in XS2 are > X." 33 | (let* ((pos (or (cl-position-if (lambda (y) (> y x)) xs) (length xs))) 34 | (xs1 (lean-eri-take pos xs)) 35 | (xs2 (nthcdr pos xs))) 36 | (cons xs1 xs2))) 37 | 38 | (defun lean-eri-calculate-indentation-points-on-line (max) 39 | "Calculate indentation points on current line. 40 | Only points left of column number MAX are included. If MAX is 41 | nil, then all points are included. Points are returned in 42 | ascending order. 43 | 44 | Example (positions marked with ^ are returned): 45 | 46 | f x y = g 3 (Just y) 5 4 47 | ^ ^ ^ ^ ^ ^ ^ ^ | 48 | | 49 | MAX" 50 | (let ((result)) 51 | (save-excursion 52 | (save-restriction 53 | (beginning-of-line) 54 | ; To make \\` work in the regexp below: 55 | (narrow-to-region (line-beginning-position) (line-end-position)) 56 | (while 57 | (progn 58 | (let ((pos (and (search-forward-regexp 59 | "\\(?:\\s-\\|\\`\\)\\(\\S-\\)" nil t) 60 | (match-beginning 1)))) 61 | (when (not (null pos)) 62 | (let ((pos1 (- pos (line-beginning-position)))) 63 | (when (or (null max) (< pos1 max)) 64 | (add-to-list 'result pos1)))) 65 | (and pos 66 | (< (point) (line-end-position)) 67 | (or (null max) (< (current-column) max)))))) 68 | (nreverse result) ; Destructive operation. 69 | )))) 70 | 71 | (defun lean-eri-new-indentation-points () 72 | "Calculate new indentation points. 73 | Returns a singleton list containing the column number two steps 74 | in from the indentation of the first non-empty line (white space 75 | excluded) above the current line. If there is no such line, 76 | then the empty list is returned." 77 | (let ((start (line-beginning-position))) 78 | (save-excursion 79 | ; Find a non-empty line above the current one, if any. 80 | (while 81 | (progn 82 | (forward-line -1) 83 | (not (or (bobp) 84 | (not (lean-eri-current-line-empty)))))) 85 | (if (or (equal (point) start) 86 | (lean-eri-current-line-empty)) 87 | nil 88 | (list (+ 2 (current-indentation))))))) 89 | 90 | (defun lean-eri-calculate-indentation-points (reverse) 91 | "Calculate points used to indent the current line. 92 | The points are given in reverse order if REVERSE is non-nil. See 93 | `lean-eri-indent' for a description of how the indentation points are 94 | calculated; note that the current indentation is not included in 95 | the returned list." 96 | ;; First find a bunch of indentations used above the current line. 97 | (let ((points) 98 | (max) 99 | (start (line-beginning-position))) 100 | (save-excursion 101 | (while 102 | (progn 103 | (forward-line -1) 104 | ; Skip the line we started from and lines with nothing but 105 | ; white space. 106 | (unless (or (equal (point) start) 107 | (lean-eri-current-line-empty)) 108 | (setq points 109 | (append 110 | (lean-eri-calculate-indentation-points-on-line max) 111 | points)) 112 | (setq max (car points))) 113 | ;; Stop after hitting the beginning of the buffer or a 114 | ;; non-empty, non-indented line. 115 | (not (or (bobp) 116 | (and (equal (current-indentation) 0) 117 | (> (lean-eri-current-line-length) 0))))))) 118 | ;; Add new indentation points, but remove the current indentation. 119 | ;; Sort the indentations. Rearrange the points so that the next 120 | ;; point is the one after the current one. Reverse if necessary. 121 | ;; 122 | ;; Note: sort and nreverse are destructive. 123 | (let* ((ps0 (remove (current-indentation) 124 | (append (lean-eri-new-indentation-points) points))) 125 | (ps1 (lean-eri-split (current-indentation) (sort ps0 '<))) 126 | (ps2 (append (cdr ps1) (car ps1)))) 127 | (if reverse 128 | (nreverse ps2) 129 | ps2)))) 130 | 131 | (defun lean-eri-indent (&optional reverse) 132 | "Cycle between some possible indentation points. 133 | With prefix argument REVERSE, cycle in reverse order. 134 | 135 | Assume that a file contains the following lines of code, with 136 | point on the line with three dots: 137 | 138 | frob = loooooooooooooooooooooooooong identifier 139 | foo = f a b 140 | where 141 | f (Foo x) y = let bar = x 142 | baz = 3 + 5 143 | 144 | ... 145 | 146 | ^ ^ ^ ^ ^ ^ ^ ^ ^ * ^ ^ ^ ^ 147 | 148 | Then the ^'s and the * mark the indentation points that this 149 | function cycles through. The indentation points are selected as 150 | follows: 151 | 152 | * All lines before the current one, up to and including the 153 | first non-indented line (or the beginning of the buffer) are 154 | considered. 155 | 156 | foo = f a b 157 | where 158 | f (Foo x) y = let bar = x 159 | baz = 3 + 5 160 | 161 | * On these lines, erase all characters that stand to the right 162 | of some non-white space character on a lower line. 163 | 164 | foo 165 | whe 166 | f (Foo x) y = let b 167 | baz = 3 + 5 168 | 169 | * Also erase all characters not immediately preceded by white 170 | space. 171 | 172 | f 173 | w 174 | f ( x y = l b 175 | b = 3 + 5 176 | 177 | * The columns of all remaining characters are indentation 178 | points. 179 | 180 | f w f ( x y = l b = 3 + 5 181 | ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 182 | 183 | * A new indentation point is also added, two steps in from the 184 | indentation of the first non-empty line (white space 185 | excluded) above the current line (if there is such a line). 186 | 187 | f w f ( x y = l b = 3 + 5 188 | ^ ^ ^ ^ ^ ^ ^ ^ ^ * ^ ^ ^ ^" 189 | (interactive "P") 190 | (let* ((points (lean-eri-calculate-indentation-points reverse)) 191 | (remaining-points (cdr (member (current-indentation) points))) 192 | (indentation (if remaining-points 193 | (car remaining-points) 194 | (car points)))) 195 | (when indentation 196 | (save-excursion (indent-line-to indentation)) 197 | (if (< (current-column) indentation) 198 | (indent-line-to indentation))))) 199 | 200 | (defun lean-eri-indent-reverse nil 201 | "Cycle between some possible indentation points (in reverse order). 202 | See `lean-eri-indent' for a description of how the indentation points 203 | are calculated." 204 | (interactive) 205 | (lean-eri-indent t)) 206 | 207 | (provide 'lean-eri) 208 | ;;; lean-eri.el ends here 209 | -------------------------------------------------------------------------------- /lean-flycheck.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | ;; 3 | ;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. 4 | ;; Released under Apache 2.0 license as described in the file LICENSE. 5 | ;; 6 | ;; Author: Soonho Kong 7 | ;; 8 | (require 'cl-lib) 9 | (require 'flycheck) 10 | (require 'lean-settings) 11 | (require 'lean-server) 12 | (require 'lean-info) 13 | 14 | (defun lean-toggle-flycheck-mode () 15 | "Toggle flycheck-mode" 16 | (interactive) 17 | (cond 18 | (flycheck-mode (flycheck-mode -1)) 19 | (t (flycheck-mode 1)))) 20 | 21 | (cl-defun lean-flycheck-parse-task (checker buffer cur-file-name 22 | &key pos_line pos_col desc file_name &allow-other-keys) 23 | (if (equal cur-file-name file_name) 24 | (flycheck-error-new-at pos_line (1+ pos_col) 25 | 'info 26 | (format "still running: %s" desc) 27 | :filename file_name 28 | :checker checker :buffer buffer) 29 | (flycheck-error-new-at 1 1 30 | 'info 31 | (format "still running: %s" desc) 32 | :filename cur-file-name 33 | :checker checker :buffer buffer))) 34 | 35 | (defun lean-flycheck-mk-task-msgs (checker buffer sess) 36 | (if (and sess (lean-server-session-tasks sess) 37 | (plist-get (lean-server-session-tasks sess) :is_running)) 38 | (let* ((cur-fn (buffer-file-name)) 39 | (tasks (lean-server-session-tasks sess)) 40 | (cur-task (plist-get tasks :cur_task)) 41 | (tasks-for-cur-file (cl-remove-if-not (lambda (task) (equal cur-fn (plist-get task :file_name))) 42 | (plist-get tasks :tasks))) 43 | (display-tasks)) 44 | ;; do not display tasks for current file when highlighting is enabled 45 | (when (not lean-server-show-pending-tasks) 46 | (setq display-tasks tasks-for-cur-file)) 47 | ;; show current task when not in current file 48 | (when (and cur-task 49 | (not (equal cur-fn (plist-get cur-task :file_name)))) 50 | (setq display-tasks (cons cur-task display-tasks))) 51 | (mapcar (lambda (task) (apply #'lean-flycheck-parse-task checker buffer cur-fn task)) 52 | display-tasks)))) 53 | 54 | (defun lean-info-fontify-string (str) 55 | (lean-ensure-info-buffer "*lean-fontify*") 56 | (with-current-buffer "*lean-fontify*" 57 | (erase-buffer) 58 | (insert str) 59 | (font-lock-fontify-region (point-min) (point-max) nil) 60 | (buffer-string))) 61 | 62 | (cl-defun lean-flycheck-parse-error (checker buffer &key pos_line pos_col severity text file_name &allow-other-keys) 63 | (flycheck-error-new-at pos_line (1+ pos_col) 64 | (pcase severity 65 | ("error" 'error) 66 | ("warning" 'warning) 67 | ("information" 'info) 68 | (_ 'info)) 69 | (lean-info-fontify-string text) 70 | :filename file_name 71 | :checker checker :buffer buffer)) 72 | 73 | (defun lean-flycheck-start (checker callback) 74 | (let ((cur-fn (buffer-file-name)) 75 | (buffer (current-buffer))) 76 | (funcall callback 'finished 77 | (if lean-server-session 78 | (append 79 | (lean-flycheck-mk-task-msgs checker buffer lean-server-session) 80 | (mapcar (lambda (msg) (apply #'lean-flycheck-parse-error checker buffer msg)) 81 | (cl-remove-if-not (lambda (msg) (equal cur-fn (plist-get msg :file_name))) 82 | (lean-server-session-messages lean-server-session)))))))) 83 | 84 | (defun lean-flycheck-init () 85 | "Initialize lean-flycheck checker" 86 | (flycheck-define-generic-checker 'lean-checker 87 | "A Lean syntax checker." 88 | :start #'lean-flycheck-start 89 | :modes '(lean-mode)) 90 | (add-to-list 'flycheck-checkers 'lean-checker)) 91 | 92 | (defun lean-flycheck-turn-on () 93 | (flycheck-mode t)) 94 | 95 | (defconst lean-next-error-buffer-name "*Lean Next Error*") 96 | 97 | (defun lean-next-error--handler () 98 | (when (lean-info-buffer-active lean-next-error-buffer-name) 99 | (let ((deactivate-mark) ; keep transient mark 100 | (errors (or 101 | ;; prefer error of current position, if any 102 | (flycheck-overlay-errors-at (point)) 103 | ;; try errors in current line next 104 | (sort (flycheck-overlay-errors-in (line-beginning-position) (line-end-position)) 105 | #'flycheck-error-<) 106 | ;; fall back to next error position 107 | (-if-let* ((pos (flycheck-next-error-pos 1))) 108 | (flycheck-overlay-errors-at pos))))) 109 | (lean-with-info-output-to-buffer lean-next-error-buffer-name 110 | (dolist (e errors) 111 | (princ (format "%d:%d: " (flycheck-error-line e) (flycheck-error-column e))) 112 | (princ (flycheck-error-message e)) 113 | (princ "\n\n")) 114 | (when flycheck-current-errors 115 | (princ (format "(%d more messages above...)" (length flycheck-current-errors)))))))) 116 | 117 | (defun lean-toggle-next-error () 118 | (interactive) 119 | (lean-toggle-info-buffer lean-next-error-buffer-name) 120 | (lean-next-error--handler)) 121 | 122 | (provide 'lean-flycheck) 123 | -------------------------------------------------------------------------------- /lean-hole.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | ;; 3 | ;; Copyright (c) 2017 David Christiansen. 4 | ;; Released under Apache 2.0 license as described in the file LICENSE. 5 | ;; 6 | ;; Author: David Christiansen 7 | ;; 8 | ;;; Commentary: 9 | ;; This is an interface to Lean's support for holes. 10 | ;; 11 | ;; The interface consists of two components: the hole command, which 12 | ;; collects the list of completions and the message, and a handler, 13 | ;; which selects a completion. 14 | ;; 15 | ;; For now, the only handler uses completing-read, but handlers using 16 | ;; helm or company's interface would be a good addition. 17 | ;; 18 | ;;; Code: 19 | (require 'lean-server) 20 | 21 | (defun lean-hole-handler-completing-read (alternatives) 22 | "Pick a hole replacement from ALTERNATIVES with `completing-read'." 23 | (let* ((choices (cl-loop for alt in alternatives 24 | collect (cons (concat (plist-get alt :code) 25 | " — " 26 | (plist-get alt :description)) 27 | (plist-get alt :code)))) 28 | (selection (let ((this-command 'lean-hole)) 29 | (completing-read 30 | "Response: " 31 | choices 32 | nil t nil nil nil t))) 33 | (code (assoc selection choices))) 34 | (if code 35 | (cdr code) 36 | (error "Didn't select a hole completion")))) 37 | 38 | (defvar lean-hole-handler-function 'lean-hole-handler-completing-read) 39 | 40 | (defun lean-hole--line-col->pos (line col) 41 | "Compute the position corresponding to LINE and COL." 42 | (save-restriction 43 | (widen) 44 | (save-excursion 45 | (goto-char (point-min)) 46 | (forward-line (1- line)) 47 | (forward-char col) 48 | (point)))) 49 | 50 | (defun lean-hole () 51 | "Ask Lean for a list of holes, then ask the user which to use." 52 | (interactive) 53 | (with-demoted-errors "lean hole: %s" 54 | (lean-server-send-command 55 | 'hole_commands (list :file_name (buffer-file-name) 56 | :line (line-number-at-pos) 57 | :column (lean-line-offset)) 58 | (cl-function 59 | (lambda (&key start end results) 60 | (let ((start-pos (lean-hole--line-col->pos (plist-get start :line) 61 | (plist-get start :column))) 62 | (end-pos (lean-hole--line-col->pos (plist-get end :line) 63 | (plist-get end :column)))) 64 | (let ((start-marker (make-marker)) 65 | (end-marker (make-marker))) 66 | (set-marker start-marker start-pos (current-buffer)) 67 | (set-marker end-marker end-pos (current-buffer)) 68 | (let* ((choices 69 | (cl-loop for res in results 70 | collect (cons (concat (plist-get res :name) 71 | " — " 72 | (plist-get res :description)) 73 | (plist-get res :name)))) 74 | (selection (let ((this-command 'lean-hole)) 75 | (completing-read 76 | "Hole command: " 77 | choices 78 | nil t nil nil nil t))) 79 | (code (assoc selection choices))) 80 | (if code 81 | (lean-hole--command (cdr code) start-marker end-marker) 82 | (error "Didn't select a hole completion")))))))))) 83 | 84 | ;; This uses markers to ensure that if the hole moves while the 85 | ;; command is running, it is still updated. 86 | (defun lean-hole--command (command start-marker end-marker) 87 | "Execute COMMAND in the hole between START-MARKER and END-MARKER." 88 | (interactive) 89 | (with-demoted-errors "lean hole: %s" 90 | (lean-server-send-command 91 | 'hole (list :action command 92 | :file_name (buffer-file-name) 93 | :line (line-number-at-pos start-marker) 94 | :column (lean-line-offset start-marker)) 95 | (cl-function 96 | (lambda (&key message replacements) 97 | (let ((replacement-count (length (plist-get replacements :alternatives)))) 98 | (let ((selected-code 99 | (cond ((= replacement-count 0) 100 | nil) 101 | ((= replacement-count 1) 102 | (plist-get (car (plist-get replacements :alternatives)) :code)) 103 | (t 104 | (lean-hole-handler-completing-read 105 | (plist-get replacements :alternatives)))))) 106 | (when selected-code 107 | (save-excursion 108 | (goto-char start-marker) 109 | (delete-region start-marker end-marker) 110 | (insert selected-code))))) 111 | (when message 112 | (message "%s" (s-trim message))) 113 | (set-marker start-marker nil) 114 | (set-marker end-marker nil)))))) 115 | 116 | (defun lean-hole-right-click () 117 | "Ask Lean for a list of hole commands, then ask the user which to use." 118 | (interactive) 119 | (let ((buf (current-buffer))) 120 | (ignore-errors 121 | (list 122 | 'hole_commands 123 | (list :file_name (buffer-file-name) 124 | :line (line-number-at-pos) 125 | :column (lean-line-offset)) 126 | (cl-function 127 | (lambda (&key start end results) 128 | (when (and start end) 129 | (with-current-buffer buf 130 | (let ((start-pos (lean-hole--line-col->pos (plist-get start :line) 131 | (plist-get start :column))) 132 | (end-pos (lean-hole--line-col->pos (plist-get end :line) 133 | (plist-get end :column)))) 134 | (let ((start-marker (make-marker)) 135 | (end-marker (make-marker))) 136 | (set-marker start-marker start-pos (current-buffer)) 137 | (set-marker end-marker (1+ end-pos) (current-buffer)) 138 | (mapcar (lambda (res) 139 | (let ((item-name (plist-get res :name)) 140 | (item-desc (plist-get res :description))) 141 | (list :name 142 | (concat item-name " — " item-desc) 143 | :action 144 | (lambda () 145 | (lean-hole--command 146 | item-name 147 | start-marker end-marker))))) 148 | results))))))))))) 149 | 150 | (provide 'lean-hole) 151 | ;;; lean-hole.el ends here 152 | -------------------------------------------------------------------------------- /lean-info.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | ;; 3 | ;;; lean-info.el --- Emacs mode for Lean theorem prover 4 | ;; 5 | ;; Copyright (c) 2016 Gabriel Ebner. All rights reserved. 6 | ;; 7 | ;; Author: Gabriel Ebner 8 | ;; Maintainer: Gabriel Ebner 9 | ;; Created: Oct 29, 2016 10 | ;; Keywords: languages 11 | ;; Version: 0.1 12 | ;; URL: https://github.com/leanprover/lean/blob/master/src/emacs 13 | ;; 14 | ;; Released under Apache 2.0 license as described in the file LICENSE. 15 | ;; 16 | 17 | (require 'cl-lib) 18 | (require 'lean-syntax) 19 | 20 | ;; Lean Info Mode (for "*lean-info*" buffer) 21 | ;; Automode List 22 | ;;;###autoload 23 | (define-derived-mode lean-info-mode prog-mode "Lean-Info" 24 | "Major mode for Lean Info Buffer" 25 | :syntax-table lean-syntax-table 26 | :group 'lean 27 | (set (make-local-variable 'font-lock-defaults) lean-info-font-lock-defaults) 28 | (set (make-local-variable 'indent-tabs-mode) nil) 29 | (set 'compilation-mode-font-lock-keywords '()) 30 | (set-input-method "Lean") 31 | (set (make-local-variable 'lisp-indent-function) 32 | 'common-lisp-indent-function)) 33 | 34 | (cl-defmacro lean-with-info-output-to-buffer (buffer &rest body) 35 | `(let ((buf (get-buffer ,buffer))) 36 | (with-current-buffer buf 37 | (setq buffer-read-only nil) 38 | (erase-buffer) 39 | (setq standard-output buf) 40 | . ,body))) 41 | 42 | (defvar lean-info-buffer-names '() 43 | "These are the buffer names that `lean-toggle-info-buffer` will 44 | look to see if have an open window, and if so, replace that 45 | window's buffer.") 46 | 47 | (defun lean-ensure-info-buffer (buffer) 48 | (if (stringp buffer) 49 | (add-to-list 'lean-info-buffer-names buffer)) 50 | (let ((buf (get-buffer buffer))) 51 | (unless buf 52 | (setq buf (get-buffer-create buffer)) 53 | (with-current-buffer buf 54 | (buffer-disable-undo) 55 | (lean-info-mode))) 56 | buf)) 57 | 58 | (defun lean-locate-info-window () 59 | "Finds a window containing a buffer from 60 | `lean-info-buffer-names`, or nil if one does not exist" 61 | (let (window) 62 | (dolist (buffer lean-info-buffer-names window) 63 | (setq window (or window (get-buffer-window buffer)))))) 64 | 65 | (defun lean-toggle-info-buffer (buffer) 66 | "If there is an info window and it corresponds to the given 67 | buffer, delete the window. Otherwise, switch the info window to 68 | the given buffer, creating an info window if it does not already 69 | exist." 70 | (let* ((buf (lean-ensure-info-buffer buffer)) 71 | (window (or (get-buffer-window buf) (lean-locate-info-window)))) 72 | (cond 73 | ((and window (eq (window-buffer window) buf)) 74 | (delete-window window)) 75 | (window 76 | (with-selected-window window 77 | (switch-to-buffer buf))) 78 | (t 79 | (display-buffer buf))))) 80 | 81 | (defun lean-info-buffer-active (buffer) 82 | "Checks whether the given info buffer should show info for the current buffer" 83 | (and 84 | ;; info buffer visible 85 | (get-buffer-window buffer 'visible) 86 | ;; current window of current buffer is selected (i.e., in focus) 87 | (eq (current-buffer) (window-buffer)))) 88 | 89 | (defun lean-get-info-record-at-point (cont) 90 | "Get info-record at the current point" 91 | (with-demoted-errors "lean get info: %s" 92 | (lean-server-send-command 93 | 'info (list :file_name (buffer-file-name) 94 | :line (line-number-at-pos) 95 | :column (lean-line-offset)) 96 | (cl-function 97 | (lambda (&key record) 98 | (funcall cont record)))))) 99 | 100 | (defun lean-info-right-click-find-definition () 101 | "Offer to jump to definition of right-click target." 102 | (interactive) 103 | (list 'info 104 | (list :file_name (buffer-file-name) 105 | :line (line-number-at-pos) 106 | :column (lean-line-offset)) 107 | (cl-function 108 | (lambda (&key record) 109 | (let ((source-record (plist-get record :source))) 110 | (if source-record 111 | (let ((full-name (plist-get record :full-id))) 112 | (list 113 | (list :name (if full-name 114 | (concat "Find definition of " full-name) 115 | "Find definition") 116 | :action (lambda () 117 | (apply #'lean-find-definition-cont source-record))))) 118 | (list))))))) 119 | 120 | (cl-defun lean-find-definition-cont (&key file line column) 121 | (when (fboundp 'xref-push-marker-stack) (xref-push-marker-stack)) 122 | (when file 123 | (find-file file)) 124 | (goto-char (point-min)) 125 | (forward-line (1- line)) 126 | (forward-char column)) 127 | 128 | 129 | (defun lean-find-definition () 130 | "Jump to definition of thing at point" 131 | (interactive) 132 | (setq lean-show-goal--handler-mask t) ; avoid the current request to the Lean server to by 133 | ; interrupted by requests made for `lean-show-goal` 134 | (lean-get-info-record-at-point 135 | (lambda (info-record) 136 | (-if-let (source-record (plist-get info-record :source)) 137 | (apply #'lean-find-definition-cont source-record) 138 | (-if-let (id (plist-get info-record :full-id)) 139 | (message "no source location available for %s" id) 140 | (message "unknown thing at point")))))) 141 | 142 | (provide 'lean-info) 143 | -------------------------------------------------------------------------------- /lean-input.el: -------------------------------------------------------------------------------- 1 | ;;; lean-input.el --- The Lean input method (based/copied from Agda) 2 | ;;; 3 | ;;; DISCLAIMER: This file is based on agda-input.el provided with the Agda language. 4 | ;;; We did minor modifications 5 | ;; 6 | ;;; Commentary: 7 | ;; 8 | ;;;; A highly customisable input method which can inherit from other 9 | ;; Quail input methods. By default the input method is geared towards 10 | ;; the input of mathematical and other symbols in Lean programs. 11 | ;; 12 | ;; Use M-x customize-group lean-input to customise this input method. 13 | ;; Note that the functions defined under "Functions used to tweak 14 | ;; translation pairs" below can be used to tweak both the key 15 | ;; translations inherited from other input methods as well as the 16 | ;; ones added specifically for this one. 17 | ;; 18 | ;; Use lean-input-show-translations to see all the characters which 19 | ;; can be typed using this input method (except for those 20 | ;; corresponding to ASCII characters). 21 | 22 | ;;; Code: 23 | 24 | (require 'quail) 25 | 26 | (eval-when-compile 27 | (require 'cl)) 28 | ;; Quail is quite stateful, so be careful when editing this code. Note 29 | ;; that with-temp-buffer is used below whenever buffer-local state is 30 | ;; modified. 31 | 32 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 33 | ;; Utility functions 34 | 35 | (defun lean-input-concat-map (f xs) 36 | "Concat (map F XS)." 37 | (apply 'append (mapcar f xs))) 38 | 39 | (defun lean-input-to-string-list (s) 40 | "Convert a string S to a list of one-character strings, after 41 | removing all space and newline characters." 42 | (lean-input-concat-map 43 | (lambda (c) (if (member c (string-to-list " \n")) 44 | nil 45 | (list (string c)))) 46 | (string-to-list s))) 47 | 48 | (defun lean-input-character-range (from to) 49 | "A string consisting of the characters from FROM to TO." 50 | (let (seq) 51 | (dotimes (i (1+ (- to from))) 52 | (setq seq (cons (+ from i) seq))) 53 | (concat (nreverse seq)))) 54 | 55 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 56 | ;; Functions used to tweak translation pairs 57 | 58 | ;; lexical-let is used since Elisp lacks lexical scoping. 59 | 60 | (defun lean-input-compose (f g) 61 | "λx -> concatMap F (G x)" 62 | (lexical-let ((f1 f) (g1 g)) 63 | (lambda (x) (lean-input-concat-map f1 (funcall g1 x))))) 64 | 65 | (defun lean-input-or (f g) 66 | "λx -> F x ++ G x" 67 | (lexical-let ((f1 f) (g1 g)) 68 | (lambda (x) (append (funcall f1 x) (funcall g1 x))))) 69 | 70 | (defun lean-input-nonempty () 71 | "Only keep pairs with a non-empty first component." 72 | (lambda (x) (if (> (length (car x)) 0) (list x)))) 73 | 74 | (defun lean-input-prepend (prefix) 75 | "Prepend PREFIX to all key sequences." 76 | (lexical-let ((prefix1 prefix)) 77 | (lambda (x) `((,(concat prefix1 (car x)) . ,(cdr x)))))) 78 | 79 | (defun lean-input-prefix (prefix) 80 | "Only keep pairs whose key sequence starts with PREFIX." 81 | (lexical-let ((prefix1 prefix)) 82 | (lambda (x) 83 | (if (equal (substring (car x) 0 (length prefix1)) prefix1) 84 | (list x))))) 85 | 86 | (defun lean-input-suffix (suffix) 87 | "Only keep pairs whose key sequence ends with SUFFIX." 88 | (lexical-let ((suffix1 suffix)) 89 | (lambda (x) 90 | (if (equal (substring (car x) 91 | (- (length (car x)) (length suffix1))) 92 | suffix1) 93 | (list x))))) 94 | 95 | (defun lean-input-drop (ss) 96 | "Drop pairs matching one of the given key sequences. 97 | SS should be a list of strings." 98 | (lexical-let ((ss1 ss)) 99 | (lambda (x) (unless (member (car x) ss1) (list x))))) 100 | 101 | (defun lean-input-drop-beginning (n) 102 | "Drop N characters from the beginning of each key sequence." 103 | (lexical-let ((n1 n)) 104 | (lambda (x) `((,(substring (car x) n1) . ,(cdr x)))))) 105 | 106 | (defun lean-input-drop-end (n) 107 | "Drop N characters from the end of each key sequence." 108 | (lexical-let ((n1 n)) 109 | (lambda (x) 110 | `((,(substring (car x) 0 (- (length (car x)) n1)) . 111 | ,(cdr x)))))) 112 | 113 | (defun lean-input-drop-prefix (prefix) 114 | "Only keep pairs whose key sequence starts with PREFIX. 115 | This prefix is dropped." 116 | (lean-input-compose 117 | (lean-input-drop-beginning (length prefix)) 118 | (lean-input-prefix prefix))) 119 | 120 | (defun lean-input-drop-suffix (suffix) 121 | "Only keep pairs whose key sequence ends with SUFFIX. 122 | This suffix is dropped." 123 | (lexical-let ((suffix1 suffix)) 124 | (lean-input-compose 125 | (lean-input-drop-end (length suffix1)) 126 | (lean-input-suffix suffix1)))) 127 | 128 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 129 | ;; Customization 130 | 131 | ;; The :set keyword is set to 'lean-input-incorporate-changed-setting 132 | ;; so that the input method gets updated immediately when users 133 | ;; customize it. However, the setup functions cannot be run before all 134 | ;; variables have been defined. Hence the :initialize keyword is set to 135 | ;; 'custom-initialize-default to ensure that the setup is not performed 136 | ;; until lean-input-setup is called at the end of this file. 137 | 138 | (defgroup lean-input nil 139 | "The Lean input method. 140 | After tweaking these settings you may want to inspect the resulting 141 | translations using `lean-input-show-translations'." 142 | :group 'lean 143 | :group 'leim) 144 | 145 | (defcustom lean-input-tweak-all 146 | '(lean-input-compose 147 | (lean-input-prepend "\\") 148 | (lean-input-nonempty)) 149 | "An expression yielding a function which can be used to tweak 150 | all translations before they are included in the input method. 151 | The resulting function (if non-nil) is applied to every 152 | \(KEY-SEQUENCE . TRANSLATION) pair and should return a list of such 153 | pairs. (Note that the translations can be anything accepted by 154 | `quail-defrule'.) 155 | 156 | If you change this setting manually (without using the 157 | customization buffer) you need to call `lean-input-setup' in 158 | order for the change to take effect." 159 | :group 'lean-input 160 | :set 'lean-input-incorporate-changed-setting 161 | :initialize 'custom-initialize-default 162 | :type 'sexp) 163 | 164 | (defcustom lean-input-inherit 165 | `(("TeX" . (lean-input-compose 166 | (lean-input-drop '("geq" "leq" "bullet" "qed" "par")) 167 | (lean-input-or 168 | (lean-input-drop-prefix "\\") 169 | (lean-input-or 170 | (lean-input-compose 171 | (lean-input-drop '("^o")) 172 | (lean-input-prefix "^")) 173 | (lean-input-prefix "_"))))) 174 | ) 175 | "A list of Quail input methods whose translations should be 176 | inherited by the Lean input method (with the exception of 177 | translations corresponding to ASCII characters). 178 | 179 | The list consists of pairs (qp . tweak), where qp is the name of 180 | a Quail package, and tweak is an expression of the same kind as 181 | `lean-input-tweak-all' which is used to tweak the translation 182 | pairs of the input method. 183 | 184 | The inherited translation pairs are added last, after 185 | `lean-input-user-translations' and `lean-input-translations'. 186 | 187 | If you change this setting manually (without using the 188 | customization buffer) you need to call `lean-input-setup' in 189 | order for the change to take effect." 190 | :group 'lean-input 191 | :set 'lean-input-incorporate-changed-setting 192 | :initialize 'custom-initialize-default 193 | :type '(repeat (cons (string :tag "Quail package") 194 | (sexp :tag "Tweaking function")))) 195 | 196 | (defcustom lean-input-translations 197 | (let ((max-lisp-eval-depth 2800)) `( 198 | 199 | ;; Negation 200 | 201 | ("not" . ("¬")) 202 | 203 | ;; Equality and similar symbols. 204 | 205 | ("eq" . ,(lean-input-to-string-list "=∼∽≈≋∻∾∿≀≃⋍≂≅ ≌≊≡≣≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≍≎≏≬⋕")) 206 | ("eqn" . ,(lean-input-to-string-list "≠≁ ≉ ≄ ≇≆ ≢ ≭ ")) 207 | ("equiv" . ,(lean-input-to-string-list "≃⋍")) 208 | ("iso" . ,(lean-input-to-string-list "≅≌")) 209 | 210 | ("=n" . ("≠")) 211 | ("~" . ("∼")) ("~n" . ("≁")) ("homotopy" . ("∼")) 212 | ("~~" . ("≈")) ("~~n" . ("≉")) 213 | ("~~~" . ("≋")) 214 | (":~" . ("∻")) 215 | ("~-" . ("≃")) ("~-n" . ("≄")) 216 | ("-~" . ("≂")) 217 | ("~=" . ("≅")) ("~=n" . ("≇")) 218 | ("~~-" . ("≊")) 219 | ("==" . ("≡")) ("==n" . ("≢")) 220 | ("===" . ("≣")) 221 | (".=" . ("≐")) (".=." . ("≑")) 222 | (":=" . ("≔")) ("=:" . ("≕")) 223 | ("=o" . ("≗")) 224 | ("(=" . ("≘")) 225 | ("and=" . ("≙")) ("or=" . ("≚")) 226 | ("*=" . ("≛")) 227 | ("t=" . ("≜")) 228 | ("def=" . ("≝")) 229 | ("m=" . ("≞")) 230 | ("?=" . ("≟")) 231 | 232 | ("pr" . ("↣")) 233 | 234 | ("1" . ("₁")) 235 | ("2" . ("₂")) 236 | ("3" . ("₃")) 237 | ("4" . ("₄")) 238 | ("5" . ("₅")) 239 | ("6" . ("₆")) 240 | ("7" . ("₇")) 241 | ("8" . ("₈")) 242 | ("9" . ("₉")) 243 | ("0" . ("₀")) 244 | 245 | ;; Inequality and similar symbols. 246 | 247 | ("leq" . ,(lean-input-to-string-list "≤≦≲<≪⋘ ≶≺≼≾⊂⊆ ⋐⊏⊑ ⊰⊲⊴⋖⋚⋜⋞")) 248 | ("leqn" . ,(lean-input-to-string-list "≰≨≮≴⋦ ≸⊀ ⋨⊄⊈⊊ ⋢⋤ ⋪⋬ ⋠")) 249 | ("geq" . ,(lean-input-to-string-list "≥≧≳>≫⋙ ≷≻≽≿⊃⊇ ⋑⊐⊒ ⊱⊳⊵⋗⋛⋝⋟")) 250 | ("geqn" . ,(lean-input-to-string-list "≱≩≯≵⋧ ≹ ⊁ ⋩⊅⊉⊋ ⋣⋥ ⋫⋭ ⋡")) 251 | 252 | ("<=" . ("≤")) (">=" . ("≥")) 253 | ("<=n" . ("≰")) (">=n" . ("≱")) 254 | ("len" . ("≰")) ("gen" . ("≱")) 255 | ("n" . ("≯")) 256 | ("<~" . ("≲")) (">~" . ("≳")) 257 | ("<~n" . ("⋦")) (">~n" . ("⋧")) 258 | ("<~nn" . ("≴")) (">~nn" . ("≵")) 259 | ("<:" . ("⋖")) (":>" . ("⋗")) 260 | ("<=:" . ("⩿")) (":>=" . ("⪀")) 261 | ("covby" . ("⋖")) 262 | ("covers" . ("⋖")) 263 | ("wcovby" . ("⩿")) 264 | ("wcovers" . ("⩿")) 265 | 266 | ("ssub" . ("⊂")) ("ssup" . ("⊃")) 267 | ("ssubn" . ("⊄")) ("ssupn" . ("⊅")) 268 | ("sub" . ("⊆")) ("sup" . ("⊇")) 269 | ("subn" . ("⊈")) ("supn" . ("⊉")) 270 | ("ssqub" . ("⊏")) ("ssqup" . ("⊐")) 271 | ("squb" . ("⊑")) ("squp" . ("⊒")) 272 | ("squbn" . ("⋢")) ("squpn" . ("⋣")) 273 | 274 | ;; Set membership etc. 275 | 276 | ("member" . ,(lean-input-to-string-list "∈∉∊∋∌∍⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿")) 277 | ("mem" . ("∈")) 278 | 279 | ("inn" . ("∉")) 280 | ("nin" . ("∌")) 281 | 282 | ;; Types 283 | 284 | ("T1" . ("Type₁")) 285 | ("T2" . ("Type₂")) 286 | ("T+" . ("Type₊")) 287 | 288 | ;; Intersections, unions etc. 289 | 290 | ("intersection" . ,(lean-input-to-string-list "∩⋂∧⋀⋏⨇⊓⨅⋒∏ ⊼ ⨉")) 291 | ("union" . ,(lean-input-to-string-list "∪⋃∨⋁⋎⨈⊔⨆⋓∐⨿⊽⊻⊍⨃⊎⨄⊌∑⅀")) 292 | 293 | ("and" . ("∧")) ("or" . ("∨")) 294 | ("And" . ("⋀")) ("Or" . ("⋁")) 295 | ("i" . ("∩")) ("un" . ("∪")) ("u+" . ("⊎")) ("u." . ("⊍")) 296 | ("I" . ("⋂")) ("Un" . ("⋃")) ("U+" . ("⨄")) ("U." . ("⨃")) 297 | ("glb" . ("⊓")) ("lub" . ("⊔")) 298 | ("Glb" . ("⨅")) ("Lub" . ("⨆")) 299 | 300 | ;; Entailment etc. 301 | 302 | ("entails" . ,(lean-input-to-string-list "⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯")) 303 | 304 | ("|-" . ("⊢")) ("|-n" . ("⊬")) 305 | ("-|" . ("⊣")) 306 | ("|=" . ("⊨")) ("|=n" . ("⊭")) 307 | ("||-" . ("⊩")) ("||-n" . ("⊮")) 308 | ("||=" . ("⊫")) ("||=n" . ("⊯")) 309 | ("|||-" . ("⊪")) 310 | 311 | ;; Divisibility, parallelity. 312 | 313 | ("|" . ("∣")) ("|n" . ("∤")) 314 | ("||" . ("∥")) ("||n" . ("∦")) 315 | 316 | ;; Some symbols from logic and set theory. 317 | 318 | ("all" . ("∀")) 319 | ("ex" . ("∃")) 320 | ("exn" . ("∄")) 321 | ("0" . ("∅")) 322 | ("empty" . ("∅")) 323 | ("C" . ("∁")) 324 | ("powerset" . ("𝒫")) 325 | 326 | ;; Corners, ceilings and floors. 327 | 328 | ("c" . ,(lean-input-to-string-list "⌜⌝⌞⌟⌈⌉⌊⌋")) 329 | ("cu" . ,(lean-input-to-string-list "⌜⌝ ⌈⌉ ")) 330 | ("cl" . ,(lean-input-to-string-list " ⌞⌟ ⌊⌋")) 331 | 332 | ("cul" . ("⌜")) ("cuL" . ("⌈")) 333 | ("cur" . ("⌝")) ("cuR" . ("⌉")) 334 | ("cll" . ("⌞")) ("clL" . ("⌊")) 335 | ("clr" . ("⌟")) ("clR" . ("⌋")) 336 | 337 | ;; Various operators/symbols. 338 | ("tr" . ,(lean-input-to-string-list "⬝▹")) 339 | ("trans" . ,(lean-input-to-string-list "▹⬝")) 340 | ("transport" . ("▹")) 341 | ("con" . ("⬝")) 342 | ("cdot" . ("⬝")) 343 | ("dot" . ("⬝")) 344 | ("sy" . ("⁻¹")) 345 | ("inv" . ("⁻¹")) 346 | ("-1" . ("⁻¹" "₋₁")) 347 | ("^-1" . ("⁻¹")) 348 | ("-2" . ("⁻²" "₋₂")) 349 | ("-3" . ("⁻³")) 350 | ("qed" . ("∎")) 351 | ("x" . ("×")) 352 | ("o" . ("∘")) 353 | ("comp" . ("∘")) 354 | ("." . ("∙")) 355 | ("*" . ("⋆")) 356 | (".+" . ("∔")) 357 | (".-" . ("∸")) 358 | (":" . ("∶")) 359 | ("::" . ("∷")) 360 | ("::-" . ("∺")) 361 | ("-:" . ("∹")) 362 | ("+ " . ("⊹")) 363 | ("surd3" . ("∛")) 364 | ("surd4" . ("∜")) 365 | ("increment" . ("∆")) 366 | ("inf" . ("∞")) 367 | ("&" . ("⅋")) 368 | ("od" . ("ᵒᵈ")) 369 | ("aop" . ("ᵃᵒᵖ")) 370 | ("mop" . ("ᵐᵒᵖ")) 371 | ("op" . ("ᵒᵖ")) 372 | 373 | ;; Circled operators. 374 | 375 | ("o+" . ("⊕")) 376 | ("o--" . ("⊖")) 377 | ("ox" . ("⊗")) 378 | ("o/" . ("⊘")) 379 | ("o." . ("⊙")) 380 | ("oo" . ("⊚")) 381 | ("o*" . ("∘*" "⊛")) 382 | ("o=" . ("⊜")) 383 | ("o-" . ("⊝")) 384 | 385 | ("O+" . ("⨁")) 386 | ("Ox" . ("⨂")) 387 | ("O." . ("⨀")) 388 | ("O*" . ("⍟")) 389 | 390 | ;; Boxed operators. 391 | 392 | ("b+" . ("⊞")) 393 | ("b-" . ("⊟")) 394 | ("bx" . ("⊠")) 395 | ("b." . ("⊡")) 396 | 397 | ;; Various symbols. 398 | 399 | ("integral" . ,(lean-input-to-string-list "∫∬∭∮∯∰∱∲∳")) 400 | ("angle" . ,(lean-input-to-string-list "∟∡∢⊾⊿")) 401 | ("join" . ,(lean-input-to-string-list "⋈⋉⋊⋋⋌⨝⟕⟖⟗")) 402 | 403 | ;; Arrows. 404 | ("iff" . ("↔")) ("imp" . ("→")) 405 | ("l" . ,(lean-input-to-string-list "←⇐⇚⇇⇆↤⇦↞↼↽⇠⇺↜⇽⟵⟸↚⇍⇷ ↹ ↢↩↫⇋⇜⇤⟻⟽⤆↶↺⟲ ")) 406 | ("r" . ,(lean-input-to-string-list "→⇒⇛⇉⇄↦⇨↠⇀⇁⇢⇻↝⇾⟶⟹↛⇏⇸⇶ ↴ ↣↪↬⇌⇝⇥⟼⟾⤇↷↻⟳⇰⇴⟴⟿ ➵➸➙➔➛➜➝➞➟➠➡➢➣➤➧➨➩➪➫➬➭➮➯➱➲➳➺➻➼➽➾⊸")) 407 | ("u" . ,(lean-input-to-string-list "↑⇑⟰⇈⇅↥⇧↟↿↾⇡⇞ ↰↱➦ ⇪⇫⇬⇭⇮⇯ ")) 408 | ("d" . ,(lean-input-to-string-list "↓⇓⟱⇊⇵↧⇩↡⇃⇂⇣⇟ ↵↲↳➥ ↯ ")) 409 | ("ud" . ,(lean-input-to-string-list "↕⇕ ↨⇳ ")) 410 | ("lr" . ,(lean-input-to-string-list "↔⇔ ⇼↭⇿⟷⟺↮⇎⇹ ")) 411 | ("ul" . ,(lean-input-to-string-list "↖⇖ ⇱↸ ")) 412 | ("ur" . ,(lean-input-to-string-list "↗⇗ ➶➹➚ ")) 413 | ("dr" . ,(lean-input-to-string-list "↘⇘ ⇲ ➴➷➘ ")) 414 | ("dl" . ,(lean-input-to-string-list "↙⇙ ")) 415 | ("==>" . ("⟹")) ("nattrans" . ("⟹")) ("nat_trans" . ("⟹")) 416 | 417 | ("l-" . ("←")) ("<-" . ("←")) ("l=" . ("⇐")) 418 | ("r-" . ("→")) ("->" . ("→")) ("r=" . ("⇒")) ("=>" . ("⇒")) ("functor" . ("⥤")) 419 | ("u-" . ("↑")) ("u=" . ("⇑")) 420 | ("d-" . ("↓")) ("d=" . ("⇓")) 421 | ("ud-" . ("↕")) ("ud=" . ("⇕")) 422 | ("lr-" . ("↔")) ("<->" . ("↔")) ("lr=" . ("⇔")) ("<=>" . ("⇔")) 423 | ("ul-" . ("↖")) ("ul=" . ("⇖")) 424 | ("ur-" . ("↗")) ("ur=" . ("⇗")) 425 | ("dr-" . ("↘")) ("dr=" . ("⇘")) 426 | ("dl-" . ("↙")) ("dl=" . ("⇙")) 427 | 428 | ("l==" . ("⇚")) ("l-2" . ("⇇")) ("l-r-" . ("⇆")) 429 | ("r==" . ("⇛")) ("r-2" . ("⇉")) ("r-3" . ("⇶")) ("r-l-" . ("⇄")) 430 | ("u==" . ("⟰")) ("u-2" . ("⇈")) ("u-d-" . ("⇅")) 431 | ("d==" . ("⟱")) ("d-2" . ("⇊")) ("d-u-" . ("⇵")) 432 | 433 | ("l--" . ("⟵")) ("<--" . ("⟵")) ("l~" . ("↜" "⇜")) 434 | ("r--" . ("⟶")) ("-->" . ("⟶")) ("r~" . ("↝" "⇝" "⟿")) ("hom" . ("⟶")) 435 | ("lr--" . ("⟷")) ("<-->" . ("⟷")) ("lr~" . ("↭")) 436 | 437 | ("l-n" . ("↚")) ("<-n" . ("↚")) ("l=n" . ("⇍")) 438 | ("r-n" . ("↛")) ("->n" . ("↛")) ("r=n" . ("⇏")) ("=>n" . ("⇏")) 439 | ("lr-n" . ("↮")) ("<->n" . ("↮")) ("lr=n" . ("⇎")) ("<=>n" . ("⇎")) 440 | 441 | ("l-|" . ("↤")) ("ll-" . ("↞")) 442 | ("r-|" . ("↦")) ("rr-" . ("↠")) 443 | ("u-|" . ("↥")) ("uu-" . ("↟")) 444 | ("d-|" . ("↧")) ("dd-" . ("↡")) 445 | ("ud-|" . ("↨")) 446 | 447 | ("l->" . ("↢")) 448 | ("r->" . ("↣")) 449 | 450 | ("r-o" . ("⊸")) ("-o" . ("⊸")) 451 | 452 | ("dz" . ("↯")) 453 | 454 | ;; Ellipsis. 455 | 456 | ("..." . ,(lean-input-to-string-list "⋯⋮⋰⋱")) 457 | 458 | ;; Box-drawing characters. 459 | 460 | ("---" . ,(lean-input-to-string-list "─│┌┐└┘├┤┬┼┴╴╵╶╷╭╮╯╰╱╲╳")) 461 | ("--=" . ,(lean-input-to-string-list "═║╔╗╚╝╠╣╦╬╩ ╒╕╘╛╞╡╤╪╧ ╓╖╙╜╟╢╥╫╨")) 462 | ("--_" . ,(lean-input-to-string-list "━┃┏┓┗┛┣┫┳╋┻╸╹╺╻ 463 | ┍┯┑┕┷┙┝┿┥┎┰┒┖┸┚┠╂┨┞╀┦┟╁┧┢╈┪┡╇┩ 464 | ┮┭┶┵┾┽┲┱┺┹╊╉╆╅╄╃ ╿╽╼╾")) 465 | ("--." . ,(lean-input-to-string-list "╌╎┄┆┈┊ 466 | ╍╏┅┇┉┋")) 467 | 468 | ;; Triangles. 469 | 470 | ;; Big/small, black/white. 471 | 472 | ("t" . ,(lean-input-to-string-list "▸▹►▻◂◃◄◅▴▵▾▿◢◿◣◺◤◸◥◹")) 473 | ("Tr" . ,(lean-input-to-string-list "◀◁▶▷▲△▼▽◬◭◮")) 474 | 475 | ("tb" . ,(lean-input-to-string-list "◂▸▴▾◄►◢◣◤◥")) 476 | ("tw" . ,(lean-input-to-string-list "◃▹▵▿◅▻◿◺◸◹")) 477 | 478 | ("Tb" . ,(lean-input-to-string-list "◀▶▲▼")) 479 | ("Tw" . ,(lean-input-to-string-list "◁▷△▽")) 480 | 481 | ;; Squares. 482 | 483 | ("sq" . ,(lean-input-to-string-list "◾◽■□◼◻▣▢▤▥▦▧▨▩◧◨◩◪◫◰◱◲◳")) 484 | ("sqb" . ,(lean-input-to-string-list "■◼◾")) 485 | ("sqw" . ,(lean-input-to-string-list "□◻◽")) 486 | ("sq." . ("▣")) 487 | ("sqo" . ("▢")) 488 | 489 | ;; Rectangles. 490 | 491 | ("re" . ,(lean-input-to-string-list "▬▭▮▯")) 492 | ("reb" . ,(lean-input-to-string-list "▬▮")) 493 | ("rew" . ,(lean-input-to-string-list "▭▯")) 494 | 495 | ;; Parallelograms. 496 | 497 | ("pa" . ,(lean-input-to-string-list "▰▱")) 498 | ("pab" . ("▰")) 499 | ("paw" . ("▱")) 500 | 501 | ;; Diamonds. 502 | 503 | ("di" . ,(lean-input-to-string-list "◆◇◈")) 504 | ("dib" . ("◆")) 505 | ("diw" . ("◇")) 506 | ("di." . ("◈")) 507 | 508 | ;; Circles. 509 | 510 | ("ci" . ,(lean-input-to-string-list "●○◎◌◯◍◐◑◒◓◔◕◖◗◠◡◴◵◶◷⚆⚇⚈⚉")) 511 | ("cib" . ("●")) 512 | ("ciw" . ("○")) 513 | ("ci." . ("◎")) 514 | ("ci.." . ("◌")) 515 | ("ciO" . ("◯")) 516 | 517 | ;; Stars. 518 | 519 | ("st" . ,(lean-input-to-string-list "⋆✦✧✶✴✹ ★☆✪✫✯✰✵✷✸")) 520 | ("st4" . ,(lean-input-to-string-list "✦✧")) 521 | ("st6" . ("✶")) 522 | ("st8" . ("✴")) 523 | ("st12" . ("✹")) 524 | 525 | ;; Blackboard bold letters. 526 | 527 | ("bn" . ("ℕ")) 528 | ("bz" . ("ℤ")) 529 | ("bq" . ("ℚ")) 530 | ("br" . ("ℝ")) 531 | ("bc" . ("ℂ")) 532 | ("bp" . ("ℙ")) 533 | ("bb" . ("𝔹")) 534 | ("bsum" . ("⅀")) 535 | ("bbA" . ("𝔸")) 536 | ("bbB" . ("𝔹")) 537 | ("bbC" . ("ℂ")) 538 | ("bbD" . ("𝔻")) 539 | ("bbE" . ("𝔼")) 540 | ("bbF" . ("𝔽")) 541 | ("bbG" . ("𝔾")) 542 | ("bbH" . ("ℍ")) 543 | ("bbI" . ("𝕀")) 544 | ("bbJ" . ("𝕁")) 545 | ("bbK" . ("𝕂")) 546 | ("bbL" . ("𝕃")) 547 | ("bbM" . ("𝕄")) 548 | ("bbN" . ("ℕ")) 549 | ("bbO" . ("𝕆")) 550 | ("bbP" . ("ℙ")) 551 | ("bbQ" . ("ℚ")) 552 | ("bbR" . ("ℝ")) 553 | ("bbS" . ("𝕊")) 554 | ("bbT" . ("𝕋")) 555 | ("bbU" . ("𝕌")) 556 | ("bbV" . ("𝕍")) 557 | ("bbW" . ("𝕎")) 558 | ("bbX" . ("𝕏")) 559 | ("bbY" . ("𝕐")) 560 | ("bbZ" . ("ℤ")) 561 | ("bba" . ("𝕒")) 562 | ("bbb" . ("𝕓")) 563 | ("bbc" . ("𝕔")) 564 | ("bbd" . ("𝕕")) 565 | ("bbe" . ("𝕖")) 566 | ("bbf" . ("𝕗")) 567 | ("bbg" . ("𝕘")) 568 | ("bbh" . ("𝕙")) 569 | ("bbi" . ("𝕚")) 570 | ("bbj" . ("𝕛")) 571 | ("bbk" . ("𝕜")) 572 | ("bbl" . ("𝕝")) 573 | ("bbm" . ("𝕞")) 574 | ("bbn" . ("𝕟")) 575 | ("bbo" . ("𝕠")) 576 | ("bbp" . ("𝕡")) 577 | ("bbq" . ("𝕢")) 578 | ("bbr" . ("𝕣")) 579 | ("bbs" . ("𝕤")) 580 | ("bbt" . ("𝕥")) 581 | ("bbu" . ("𝕦")) 582 | ("bbv" . ("𝕧")) 583 | ("bbw" . ("𝕨")) 584 | ("bbx" . ("𝕩")) 585 | ("bby" . ("𝕪")) 586 | ("bbz" . ("𝕫")) 587 | 588 | ;; Blackboard bold numbers. 589 | 590 | ("b0" . ("𝟘")) 591 | ("b1" . ("𝟙")) 592 | ("b2" . ("𝟚")) 593 | ("b3" . ("𝟛")) 594 | ("b4" . ("𝟜")) 595 | ("b5" . ("𝟝")) 596 | ("b6" . ("𝟞")) 597 | ("b7" . ("𝟟")) 598 | ("b8" . ("𝟠")) 599 | ("b9" . ("𝟡")) 600 | 601 | ;; Parentheses. 602 | 603 | ("(" . ,(lean-input-to-string-list "([{⁅⁽₍〈⎴⟅⟦⟨⟪⦃〈《‹«「『【〔〖〚︵︷︹︻︽︿﹁﹃﹙﹛﹝([{「")) 604 | (")" . ,(lean-input-to-string-list ")]}⁆⁾₎〉⎵⟆⟧⟩⟫⦄〉》›»」』】〕〗〛︶︸︺︼︾﹀﹂﹄﹚﹜﹞)]}」")) 605 | 606 | ("[[" . ("⟦")) 607 | ("]]" . ("⟧")) 608 | ("<" . ("⟨")) 609 | (">" . ("⟩")) 610 | ("<<" . ("⟪")) 611 | (">>" . ("⟫")) 612 | ("f<" . ("‹")) 613 | ("f>" . ("›")) 614 | ("f<<" . ("«")) 615 | ("f>>" . ("»")) 616 | ("{{" . ("⦃")) 617 | ("}}" . ("⦄")) 618 | 619 | ("(b" . ("⟅")) 620 | (")b" . ("⟆")) 621 | 622 | ("lbag" . ("⟅")) 623 | ("rbag" . ("⟆")) 624 | 625 | ;; lambda 626 | 627 | ("fun" . ("λ")) 628 | ("lam" . ("λ")) 629 | 630 | ("X" . ("⨯")) 631 | 632 | ;; Primes. 633 | 634 | ("'" . ,(lean-input-to-string-list "′″‴⁗")) 635 | ("`" . ,(lean-input-to-string-list "‵‶‷")) 636 | 637 | ;; Fractions. 638 | 639 | ("frac" . ,(lean-input-to-string-list "¼½¾⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟")) 640 | 641 | ;; Bullets. 642 | 643 | ("bu" . ,(lean-input-to-string-list "•◦‣⁌⁍")) 644 | ("bub" . ("•")) 645 | ("buw" . ("◦")) 646 | ("but" . ("‣")) 647 | 648 | ;; Types 649 | ("nat" . ("ℕ")) 650 | ("Nat" . ("ℕ")) 651 | ("N" . ("ℕ")) 652 | ("int" . ("ℤ")) 653 | ("Int" . ("ℤ")) 654 | ("Z" . ("ℤ")) 655 | ("rat" . ("ℚ")) 656 | ("Rat" . ("ℚ")) 657 | ("Q" . ("ℚ")) 658 | ("real" . ("ℝ")) 659 | ("Real" . ("ℝ")) 660 | ("R" . ("ℝ")) 661 | ("Com" . ("ℂ")) 662 | ("com" . ("ℂ")) 663 | ("C" . ("ℂ")) 664 | ("A" . ("𝔸")) 665 | ("F" . ("𝔽")) 666 | ("H" . ("ℍ")) 667 | ("K" . ("𝕂")) 668 | 669 | ("a" . ("α")) 670 | ("b" . ("β")) 671 | ("g" . ("γ")) 672 | 673 | ;; Musical symbols. 674 | 675 | ("note" . ,(lean-input-to-string-list "♩♪♫♬")) 676 | ("flat" . ("♭")) 677 | ("#" . ("♯")) 678 | 679 | ;; Other punctuation and symbols. 680 | 681 | ("\\" . ("\\")) 682 | ("en" . ("–")) 683 | ("em" . ("—")) 684 | ("^i" . ("ⁱ")) 685 | ("^o" . ("ᵒ")) 686 | ("!!" . ("‼")) 687 | ("??" . ("⁇")) 688 | ("?!" . ("‽" "⁈")) 689 | ("!?" . ("⁉")) 690 | ("die" . ,(lean-input-to-string-list "⚀⚁⚂⚃⚄⚅")) 691 | ("asterisk" . ,(lean-input-to-string-list "⁎⁑⁂✢✣✤✥✱✲✳✺✻✼✽❃❉❊❋")) 692 | ("8<" . ("✂" "✄")) 693 | ("tie" . ("⁀")) 694 | ("undertie" . ("‿")) 695 | ("apl" . ,(lean-input-to-string-list "⌶⌷⌸⌹⌺⌻⌼⌽⌾⌿⍀⍁⍂⍃⍄⍅⍆⍇⍈ 696 | ⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛ 697 | ⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮ 698 | ⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⎕")) 699 | ("/" . ("⧸")) 700 | ("quot" . ("⧸")) 701 | 702 | ;; Some combining characters. 703 | ;; 704 | ;; The following combining characters also have (other) 705 | ;; translations: 706 | ;; ̀ ́ ̂ ̃ ̄ ̆ ̇ ̈ ̋ ̌ ̣ ̧ ̱ 707 | 708 | ("^--" . ,(lean-input-to-string-list"̅̿")) 709 | ("_--" . ,(lean-input-to-string-list"̲̳")) 710 | ("^~" . ,(lean-input-to-string-list"̃͌")) 711 | ("_~" . ( "̰")) 712 | ("^." . ,(lean-input-to-string-list"̇̈⃛⃜")) 713 | ("_." . ,(lean-input-to-string-list"̣̤")) 714 | ("^l" . ,(lean-input-to-string-list"⃖⃐⃔")) 715 | ("^l-" . ( "⃖")) 716 | ("^r" . ,(lean-input-to-string-list"⃗⃑⃕")) 717 | ("^r-" . ( "⃗")) 718 | ("^lr" . ( "⃡")) 719 | ("_lr" . ( "͍")) 720 | ("^^" . ,(lean-input-to-string-list"̂̑͆")) 721 | ("_^" . ,(lean-input-to-string-list"̭̯̪")) 722 | ("^v" . ,(lean-input-to-string-list"̌̆")) 723 | ("_v" . ,(lean-input-to-string-list"̬̮̺")) 724 | 725 | ;; Shorter forms of many greek letters plus ƛ. 726 | 727 | ("Ga" . ("α")) ("GA" . ("Α")) 728 | ("Gb" . ("β")) ("GB" . ("Β")) 729 | ("Gg" . ("γ")) ("GG" . ("Γ")) 730 | ("Gd" . ("δ")) ("GD" . ("Δ")) 731 | ("Ge" . ("ε")) ("GE" . ("Ε")) ("eps" . ("ε")) 732 | ("Gz" . ("ζ")) ("GZ" . ("Ζ")) 733 | ;; \eta \Eta 734 | ("Gth" . ("θ")) ("GTH" . ("Θ")) ("th" . ("θ")) 735 | ("Gi" . ("ι")) ("GI" . ("Ι")) 736 | ("Gk" . ("κ")) ("GK" . ("Κ")) 737 | ("Gl" . ("λ")) ("GL" . ("Λ")) ("Gl-" . ("ƛ")) 738 | ("Gm" . ("μ")) ("GM" . ("Μ")) 739 | ("Gn" . ("ν")) ("GN" . ("Ν")) 740 | ("Gx" . ("ξ")) ("GX" . ("Ξ")) 741 | ;; \omicron \Omicron 742 | ;; \pi \Pi 743 | ("Gr" . ("ρ")) ("GR" . ("Ρ")) 744 | ("Gs" . ("σ")) ("GS" . ("Σ")) 745 | ("Gt" . ("τ")) ("GT" . ("Τ")) 746 | ("Gu" . ("υ")) ("GU" . ("Υ")) 747 | ("Gf" . ("φ")) ("GF" . ("Φ")) 748 | ("Gc" . ("χ")) ("GC" . ("Χ")) 749 | ("Gp" . ("ψ")) ("GP" . ("Ψ")) 750 | ("Go" . ("ω")) ("GO" . ("Ω")) 751 | ;; even shorter versions for central type constructors 752 | ("S" . ("Σ")) ("P" . ("Π")) 753 | 754 | ;; Mathematical characters 755 | 756 | ("MiA" . ("𝐴")) 757 | ("MiB" . ("𝐵")) 758 | ("MiC" . ("𝐶")) 759 | ("MiD" . ("𝐷")) 760 | ("MiE" . ("𝐸")) 761 | ("MiF" . ("𝐹")) 762 | ("MiG" . ("𝐺")) 763 | ("MiH" . ("𝐻")) 764 | ("MiI" . ("𝐼")) 765 | ("MiJ" . ("𝐽")) 766 | ("MiK" . ("𝐾")) 767 | ("MiL" . ("𝐿")) 768 | ("MiM" . ("𝑀")) 769 | ("MiN" . ("𝑁")) 770 | ("MiO" . ("𝑂")) 771 | ("MiP" . ("𝑃")) 772 | ("MiQ" . ("𝑄")) 773 | ("MiR" . ("𝑅")) 774 | ("MiS" . ("𝑆")) 775 | ("MiT" . ("𝑇")) 776 | ("MiU" . ("𝑈")) 777 | ("MiV" . ("𝑉")) 778 | ("MiW" . ("𝑊")) 779 | ("MiX" . ("𝑋")) 780 | ("MiY" . ("𝑌")) 781 | ("MiZ" . ("𝑍")) 782 | ("Mia" . ("𝑎")) 783 | ("Mib" . ("𝑏")) 784 | ("Mic" . ("𝑐")) 785 | ("Mid" . ("𝑑")) 786 | ("Mie" . ("𝑒")) 787 | ("Mif" . ("𝑓")) 788 | ("Mig" . ("𝑔")) 789 | ("Mii" . ("𝑖")) 790 | ("Mij" . ("𝑗")) 791 | ("Mik" . ("𝑘")) 792 | ("Mil" . ("𝑙")) 793 | ("Mim" . ("𝑚")) 794 | ("Min" . ("𝑛")) 795 | ("Mio" . ("𝑜")) 796 | ("Mip" . ("𝑝")) 797 | ("Miq" . ("𝑞")) 798 | ("Mir" . ("𝑟")) 799 | ("Mis" . ("𝑠")) 800 | ("Mit" . ("𝑡")) 801 | ("Miu" . ("𝑢")) 802 | ("Miv" . ("𝑣")) 803 | ("Miw" . ("𝑤")) 804 | ("Mix" . ("𝑥")) 805 | ("Miy" . ("𝑦")) 806 | ("Miz" . ("𝑧")) 807 | ("MIA" . ("𝑨")) 808 | ("MIB" . ("𝑩")) 809 | ("MIC" . ("𝑪")) 810 | ("MID" . ("𝑫")) 811 | ("MIE" . ("𝑬")) 812 | ("MIF" . ("𝑭")) 813 | ("MIG" . ("𝑮")) 814 | ("MIH" . ("𝑯")) 815 | ("MII" . ("𝑰")) 816 | ("MIJ" . ("𝑱")) 817 | ("MIK" . ("𝑲")) 818 | ("MIL" . ("𝑳")) 819 | ("MIM" . ("𝑴")) 820 | ("MIN" . ("𝑵")) 821 | ("MIO" . ("𝑶")) 822 | ("MIP" . ("𝑷")) 823 | ("MIQ" . ("𝑸")) 824 | ("MIR" . ("𝑹")) 825 | ("MIS" . ("𝑺")) 826 | ("MIT" . ("𝑻")) 827 | ("MIU" . ("𝑼")) 828 | ("MIV" . ("𝑽")) 829 | ("MIW" . ("𝑾")) 830 | ("MIX" . ("𝑿")) 831 | ("MIY" . ("𝒀")) 832 | ("MIZ" . ("𝒁")) 833 | ("MIa" . ("𝒂")) 834 | ("MIb" . ("𝒃")) 835 | ("MIc" . ("𝒄")) 836 | ("MId" . ("𝒅")) 837 | ("MIe" . ("𝒆")) 838 | ("MIf" . ("𝒇")) 839 | ("MIg" . ("𝒈")) 840 | ("MIh" . ("𝒉")) 841 | ("MIi" . ("𝒊")) 842 | ("MIj" . ("𝒋")) 843 | ("MIk" . ("𝒌")) 844 | ("MIl" . ("𝒍")) 845 | ("MIm" . ("𝒎")) 846 | ("MIn" . ("𝒏")) 847 | ("MIo" . ("𝒐")) 848 | ("MIp" . ("𝒑")) 849 | ("MIq" . ("𝒒")) 850 | ("MIr" . ("𝒓")) 851 | ("MIs" . ("𝒔")) 852 | ("MIt" . ("𝒕")) 853 | ("MIu" . ("𝒖")) 854 | ("MIv" . ("𝒗")) 855 | ("MIw" . ("𝒘")) 856 | ("MIx" . ("𝒙")) 857 | ("MIy" . ("𝒚")) 858 | ("MIz" . ("𝒛")) 859 | ("McA" . ("𝒜")) 860 | ("McC" . ("𝒞")) 861 | ("McD" . ("𝒟")) 862 | ("McG" . ("𝒢")) 863 | ("McJ" . ("𝒥")) 864 | ("McK" . ("𝒦")) 865 | ("McN" . ("𝒩")) 866 | ("McO" . ("𝒪")) 867 | ("McP" . ("𝒫")) 868 | ("McQ" . ("𝒬")) 869 | ("McS" . ("𝒮")) 870 | ("McT" . ("𝒯")) 871 | ("McU" . ("𝒰")) 872 | ("McV" . ("𝒱")) 873 | ("McW" . ("𝒲")) 874 | ("McX" . ("𝒳")) 875 | ("McY" . ("𝒴")) 876 | ("McZ" . ("𝒵")) 877 | ("Mca" . ("𝒶")) 878 | ("Mcb" . ("𝒷")) 879 | ("Mcc" . ("𝒸")) 880 | ("Mcd" . ("𝒹")) 881 | ("Mcf" . ("𝒻")) 882 | ("Mch" . ("𝒽")) 883 | ("Mci" . ("𝒾")) 884 | ("Mcj" . ("𝒿")) 885 | ("Mck" . ("𝓀")) 886 | ("Mcl" . ("𝓁")) 887 | ("Mcm" . ("𝓂")) 888 | ("Mcn" . ("𝓃")) 889 | ("Mcp" . ("𝓅")) 890 | ("Mcq" . ("𝓆")) 891 | ("Mcr" . ("𝓇")) 892 | ("Mcs" . ("𝓈")) 893 | ("Mct" . ("𝓉")) 894 | ("Mcu" . ("𝓊")) 895 | ("Mcv" . ("𝓋")) 896 | ("Mcw" . ("𝓌")) 897 | ("Mcx" . ("𝓍")) 898 | ("Mcy" . ("𝓎")) 899 | ("Mcz" . ("𝓏")) 900 | ("MCA" . ("𝓐")) 901 | ("MCB" . ("𝓑")) 902 | ("MCC" . ("𝓒")) 903 | ("MCD" . ("𝓓")) 904 | ("MCE" . ("𝓔")) 905 | ("MCF" . ("𝓕")) 906 | ("MCG" . ("𝓖")) 907 | ("MCH" . ("𝓗")) 908 | ("MCI" . ("𝓘")) 909 | ("MCJ" . ("𝓙")) 910 | ("MCK" . ("𝓚")) 911 | ("MCL" . ("𝓛")) 912 | ("MCM" . ("𝓜")) 913 | ("MCN" . ("𝓝")) 914 | ("MCO" . ("𝓞")) 915 | ("MCP" . ("𝓟")) 916 | ("MCQ" . ("𝓠")) 917 | ("MCR" . ("𝓡")) 918 | ("MCS" . ("𝓢")) 919 | ("MCT" . ("𝓣")) 920 | ("MCU" . ("𝓤")) 921 | ("MCV" . ("𝓥")) 922 | ("MCW" . ("𝓦")) 923 | ("MCX" . ("𝓧")) 924 | ("MCY" . ("𝓨")) 925 | ("MCZ" . ("𝓩")) 926 | ("MCa" . ("𝓪")) 927 | ("MCb" . ("𝓫")) 928 | ("MCc" . ("𝓬")) 929 | ("MCd" . ("𝓭")) 930 | ("MCe" . ("𝓮")) 931 | ("MCf" . ("𝓯")) 932 | ("MCg" . ("𝓰")) 933 | ("MCh" . ("𝓱")) 934 | ("MCi" . ("𝓲")) 935 | ("MCj" . ("𝓳")) 936 | ("MCk" . ("𝓴")) 937 | ("MCl" . ("𝓵")) 938 | ("MCm" . ("𝓶")) 939 | ("MCn" . ("𝓷")) 940 | ("MCo" . ("𝓸")) 941 | ("MCp" . ("𝓹")) 942 | ("MCq" . ("𝓺")) 943 | ("MCr" . ("𝓻")) 944 | ("MCs" . ("𝓼")) 945 | ("MCt" . ("𝓽")) 946 | ("MCu" . ("𝓾")) 947 | ("MCv" . ("𝓿")) 948 | ("MCw" . ("𝔀")) 949 | ("MCx" . ("𝔁")) 950 | ("MCy" . ("𝔂")) 951 | ("MCz" . ("𝔃")) 952 | ("MfA" . ("𝔄")) 953 | ("MfB" . ("𝔅")) 954 | ("MfD" . ("𝔇")) 955 | ("MfE" . ("𝔈")) 956 | ("MfF" . ("𝔉")) 957 | ("MfG" . ("𝔊")) 958 | ("MfJ" . ("𝔍")) 959 | ("MfK" . ("𝔎")) 960 | ("MfL" . ("𝔏")) 961 | ("MfM" . ("𝔐")) 962 | ("MfN" . ("𝔑")) 963 | ("MfO" . ("𝔒")) 964 | ("MfP" . ("𝔓")) 965 | ("MfQ" . ("𝔔")) 966 | ("MfS" . ("𝔖")) 967 | ("MfT" . ("𝔗")) 968 | ("MfU" . ("𝔘")) 969 | ("MfV" . ("𝔙")) 970 | ("MfW" . ("𝔚")) 971 | ("MfX" . ("𝔛")) 972 | ("MfY" . ("𝔜")) 973 | ("Mfa" . ("𝔞")) 974 | ("Mfb" . ("𝔟")) 975 | ("Mfc" . ("𝔠")) 976 | ("Mfd" . ("𝔡")) 977 | ("Mfe" . ("𝔢")) 978 | ("Mff" . ("𝔣")) 979 | ("Mfg" . ("𝔤")) 980 | ("Mfh" . ("𝔥")) 981 | ("Mfi" . ("𝔦")) 982 | ("Mfj" . ("𝔧")) 983 | ("Mfk" . ("𝔨")) 984 | ("Mfl" . ("𝔩")) 985 | ("Mfm" . ("𝔪")) 986 | ("Mfn" . ("𝔫")) 987 | ("Mfo" . ("𝔬")) 988 | ("Mfp" . ("𝔭")) 989 | ("Mfq" . ("𝔮")) 990 | ("Mfr" . ("𝔯")) 991 | ("Mfs" . ("𝔰")) 992 | ("Mft" . ("𝔱")) 993 | ("Mfu" . ("𝔲")) 994 | ("Mfv" . ("𝔳")) 995 | ("Mfw" . ("𝔴")) 996 | ("Mfx" . ("𝔵")) 997 | ("Mfy" . ("𝔶")) 998 | ("Mfz" . ("𝔷")) 999 | 1000 | ;; Some ISO8859-1 characters. 1001 | 1002 | (" " . (" ")) 1003 | ("!" . ("¡")) 1004 | ("cent" . ("¢")) 1005 | ("brokenbar" . ("¦")) 1006 | ("degree" . ("°")) 1007 | ("?" . ("¿")) 1008 | ("^a_" . ("ª")) 1009 | ("^o_" . ("º")) 1010 | 1011 | ;; Circled, parenthesised etc. numbers and letters. 1012 | 1013 | ( "(0)" . ,(lean-input-to-string-list " ⓪")) 1014 | ( "(1)" . ,(lean-input-to-string-list "⑴①⒈❶➀➊")) 1015 | ( "(2)" . ,(lean-input-to-string-list "⑵②⒉❷➁➋")) 1016 | ( "(3)" . ,(lean-input-to-string-list "⑶③⒊❸➂➌")) 1017 | ( "(4)" . ,(lean-input-to-string-list "⑷④⒋❹➃➍")) 1018 | ( "(5)" . ,(lean-input-to-string-list "⑸⑤⒌❺➄➎")) 1019 | ( "(6)" . ,(lean-input-to-string-list "⑹⑥⒍❻➅➏")) 1020 | ( "(7)" . ,(lean-input-to-string-list "⑺⑦⒎❼➆➐")) 1021 | ( "(8)" . ,(lean-input-to-string-list "⑻⑧⒏❽➇➑")) 1022 | ( "(9)" . ,(lean-input-to-string-list "⑼⑨⒐❾➈➒")) 1023 | ("(10)" . ,(lean-input-to-string-list "⑽⑩⒑❿➉➓")) 1024 | ("(11)" . ,(lean-input-to-string-list "⑾⑪⒒")) 1025 | ("(12)" . ,(lean-input-to-string-list "⑿⑫⒓")) 1026 | ("(13)" . ,(lean-input-to-string-list "⒀⑬⒔")) 1027 | ("(14)" . ,(lean-input-to-string-list "⒁⑭⒕")) 1028 | ("(15)" . ,(lean-input-to-string-list "⒂⑮⒖")) 1029 | ("(16)" . ,(lean-input-to-string-list "⒃⑯⒗")) 1030 | ("(17)" . ,(lean-input-to-string-list "⒄⑰⒘")) 1031 | ("(18)" . ,(lean-input-to-string-list "⒅⑱⒙")) 1032 | ("(19)" . ,(lean-input-to-string-list "⒆⑲⒚")) 1033 | ("(20)" . ,(lean-input-to-string-list "⒇⑳⒛")) 1034 | 1035 | ("(a)" . ,(lean-input-to-string-list "⒜Ⓐⓐ")) 1036 | ("(b)" . ,(lean-input-to-string-list "⒝Ⓑⓑ")) 1037 | ("(c)" . ,(lean-input-to-string-list "⒞Ⓒⓒ")) 1038 | ("(d)" . ,(lean-input-to-string-list "⒟Ⓓⓓ")) 1039 | ("(e)" . ,(lean-input-to-string-list "⒠Ⓔⓔ")) 1040 | ("(f)" . ,(lean-input-to-string-list "⒡Ⓕⓕ")) 1041 | ("(g)" . ,(lean-input-to-string-list "⒢Ⓖⓖ")) 1042 | ("(h)" . ,(lean-input-to-string-list "⒣Ⓗⓗ")) 1043 | ("(i)" . ,(lean-input-to-string-list "⒤Ⓘⓘ")) 1044 | ("(j)" . ,(lean-input-to-string-list "⒥Ⓙⓙ")) 1045 | ("(k)" . ,(lean-input-to-string-list "⒦Ⓚⓚ")) 1046 | ("(l)" . ,(lean-input-to-string-list "⒧Ⓛⓛ")) 1047 | ("(m)" . ,(lean-input-to-string-list "⒨Ⓜⓜ")) 1048 | ("(n)" . ,(lean-input-to-string-list "⒩Ⓝⓝ")) 1049 | ("(o)" . ,(lean-input-to-string-list "⒪Ⓞⓞ")) 1050 | ("(p)" . ,(lean-input-to-string-list "⒫Ⓟⓟ")) 1051 | ("(q)" . ,(lean-input-to-string-list "⒬Ⓠⓠ")) 1052 | ("(r)" . ,(lean-input-to-string-list "⒭Ⓡⓡ")) 1053 | ("(s)" . ,(lean-input-to-string-list "⒮Ⓢⓢ")) 1054 | ("(t)" . ,(lean-input-to-string-list "⒯Ⓣⓣ")) 1055 | ("(u)" . ,(lean-input-to-string-list "⒰Ⓤⓤ")) 1056 | ("(v)" . ,(lean-input-to-string-list "⒱Ⓥⓥ")) 1057 | ("(w)" . ,(lean-input-to-string-list "⒲Ⓦⓦ")) 1058 | ("(x)" . ,(lean-input-to-string-list "⒳Ⓧⓧ")) 1059 | ("(y)" . ,(lean-input-to-string-list "⒴Ⓨⓨ")) 1060 | ("(z)" . ,(lean-input-to-string-list "⒵Ⓩⓩ")) 1061 | ("y" . ("ɏ")) 1062 | 1063 | ;; Mathlib-specific (combinations of) characters. 1064 | 1065 | ("allf" . ("∀ᶠ")) 1066 | ("all^f" . ("∀ᶠ")) 1067 | ("allm" . ("∀ₘ")) 1068 | ("all_m" . ("∀ₘ")) 1069 | ("Pi0" . ("Π₀")) 1070 | ("P0" . ("Π₀")) 1071 | ("Pi_0" . ("Π₀")) 1072 | ("P_0" . ("Π₀")) 1073 | ("to0" . ("→₀")) 1074 | ("r0" . ("→₀")) 1075 | ("to_0" . ("→₀")) 1076 | ("r_0" . ("→₀")) 1077 | ("finsupp" . ("→₀")) 1078 | ("to1" . ("→₁")) 1079 | ("r1" . ("→₁")) 1080 | ("to_1" . ("→₁")) 1081 | ("r_1" . ("→₁")) 1082 | ("l1" . ("→₁")) 1083 | ("to1s" . ("→₁ₛ")) 1084 | ("r1s" . ("→₁ₛ")) 1085 | ("to_1s" . ("→₁ₛ")) 1086 | ("r_1s" . ("→₁ₛ")) 1087 | ("l1simplefunc" . ("→₁ₛ")) 1088 | ("toa" . ("→ₐ")) 1089 | ("ra" . ("→ₐ")) 1090 | ("to_a" . ("→ₐ")) 1091 | ("r_a" . ("→ₐ")) 1092 | ("alghom" . ("→ₐ")) 1093 | ("tob" . ("→ᵇ")) 1094 | ("rb" . ("→ᵇ")) 1095 | ("to^b" . ("→ᵇ")) 1096 | ("r^b" . ("→ᵇ")) 1097 | ("boundedcontinuousfunction" . ("→ᵇ")) 1098 | ("tol" . ("→ₗ")) 1099 | ("rl" . ("→ₗ")) 1100 | ("to_l" . ("→ₗ")) 1101 | ("r_l" . ("→ₗ")) 1102 | ("linearmap" . ("→ₗ")) 1103 | ("tom" . ("→ₘ")) 1104 | ("rm" . ("→ₘ")) 1105 | ("to_m" . ("→ₘ")) 1106 | ("r_m" . ("→ₘ")) 1107 | ("aeeqfun" . ("→ₘ")) 1108 | ("rp" . ("→ₚ")) 1109 | ("to_p" . ("→ₚ")) 1110 | ("r_p" . ("→ₚ")) 1111 | ("dfinsupp" . ("→ₚ")) 1112 | ("tos" . ("→ₛ")) 1113 | ("rs" . ("→ₛ")) 1114 | ("to_s" . ("→ₛ")) 1115 | ("r_s" . ("→ₛ")) 1116 | ("simplefunc" . ("→ₛ")) 1117 | ("root" . ("√")) 1118 | ("sqrt" . ("√")) 1119 | ("boxmid" . ("◫")) 1120 | ("hcomp" . ("◫")) 1121 | ("Rge0" . ("ℝ≥0")) 1122 | ("R>=0" . ("ℝ≥0")) 1123 | ("nnreal" . ("ℝ≥0")) 1124 | ("Zsqrt" . ("ℤ√")) 1125 | ("zsqrtd" . ("ℤ√")) 1126 | ("liel" . ("⁅")) 1127 | ("[-" . ("⁅")) 1128 | ("bracketl" . ("⁅")) 1129 | ("lier" . ("⁆")) 1130 | ("-]" . ("⁆")) 1131 | ("bracketr" . ("⁆")) 1132 | ("nhds" . ("𝓝")) 1133 | ("nbhds" . ("𝓝")) 1134 | ("X" . ("×")) 1135 | ("vectorproduct" . ("⨯")) 1136 | ("crossproduct" . ("⨯")) 1137 | ("coprod" . ("⨿")) 1138 | ("sigmaobj" . ("∐")) 1139 | ("bigcoprod" . ("∐")) 1140 | ("xf" . ("×ᶠ")) 1141 | ("exf" . ("∃ᶠ")) 1142 | ("specializes" . ("⤳")) 1143 | )) 1144 | "A list of translations specific to the Lean input method. 1145 | Each element is a pair (KEY-SEQUENCE-STRING . LIST-OF-TRANSLATION-STRINGS). 1146 | All the translation strings are possible translations 1147 | of the given key sequence; if there is more than one you can choose 1148 | between them using the arrow keys. 1149 | 1150 | Note that if you customize this setting you will not 1151 | automatically benefit (or suffer) from modifications to its 1152 | default value when the library is updated. If you just want to 1153 | add some bindings it is probably a better idea to customize 1154 | `lean-input-user-translations'. 1155 | 1156 | These translation pairs are included after those in 1157 | `lean-input-user-translations', but before the ones inherited 1158 | from other input methods (see `lean-input-inherit'). 1159 | 1160 | If you change this setting manually (without using the 1161 | customization buffer) you need to call `lean-input-setup' in 1162 | order for the change to take effect." 1163 | :group 'lean-input 1164 | :set 'lean-input-incorporate-changed-setting 1165 | :initialize 'custom-initialize-default 1166 | :type '(repeat (cons (string :tag "Key sequence") 1167 | (repeat :tag "Translations" string)))) 1168 | 1169 | (defcustom lean-input-user-translations nil 1170 | "Like `lean-input-translations', but more suitable for user 1171 | customizations since by default it is empty. 1172 | 1173 | These translation pairs are included first, before those in 1174 | `lean-input-translations' and the ones inherited from other input 1175 | methods." 1176 | :group 'lean-input 1177 | :set 'lean-input-incorporate-changed-setting 1178 | :initialize 'custom-initialize-default 1179 | :type '(repeat (cons (string :tag "Key sequence") 1180 | (repeat :tag "Translations" string)))) 1181 | 1182 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1183 | ;; Inspecting and modifying translation maps 1184 | 1185 | (defun lean-input-get-translations (qp) 1186 | "Return a list containing all translations from the Quail 1187 | package QP (except for those corresponding to ASCII). 1188 | Each pair in the list has the form (KEY-SEQUENCE . TRANSLATION)." 1189 | (with-temp-buffer 1190 | (activate-input-method qp) ; To make sure that the package is loaded. 1191 | (unless (quail-package qp) 1192 | (error "%s is not a Quail package." qp)) 1193 | (let ((decode-map (list 'decode-map))) 1194 | (quail-build-decode-map (list (quail-map)) "" decode-map 0) 1195 | (cdr decode-map)))) 1196 | 1197 | (defun lean-input-show-translations (qp) 1198 | "Display all translations used by the Quail package QP (a string). 1199 | \(Except for those corresponding to ASCII)." 1200 | (interactive (list (read-input-method-name 1201 | "Quail input method (default %s): " "Lean"))) 1202 | (let ((buf (concat "*" qp " input method translations*"))) 1203 | (with-output-to-temp-buffer buf 1204 | (with-current-buffer buf 1205 | (quail-insert-decode-map 1206 | (cons 'decode-map (lean-input-get-translations qp))))))) 1207 | 1208 | (defun lean-input-add-translations (trans) 1209 | "Add the given translations TRANS to the Lean input method. 1210 | TRANS is a list of pairs (KEY-SEQUENCE . TRANSLATION). The 1211 | translations are appended to the current translations." 1212 | (with-temp-buffer 1213 | (dolist (tr (lean-input-concat-map (eval lean-input-tweak-all) trans)) 1214 | (quail-defrule (car tr) (cdr tr) "Lean" t)))) 1215 | 1216 | (defun lean-input-inherit-package (qp &optional fun) 1217 | "Let the Lean input method inherit the translations from the 1218 | Quail package QP (except for those corresponding to ASCII). 1219 | 1220 | The optional function FUN can be used to modify the translations. 1221 | It is given a pair (KEY-SEQUENCE . TRANSLATION) and should return 1222 | a list of such pairs." 1223 | (let ((trans (lean-input-get-translations qp))) 1224 | (lean-input-add-translations 1225 | (if fun (lean-input-concat-map fun trans) 1226 | trans)))) 1227 | 1228 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1229 | ;; Setting up the input method 1230 | 1231 | (defun lean-input-setup () 1232 | "Set up the Lean input method based on the customisable 1233 | variables and underlying input methods." 1234 | 1235 | ;; Create (or reset) the input method. 1236 | (with-temp-buffer 1237 | (quail-define-package "Lean" "UTF-8" "∏" t ; guidance 1238 | "Lean input method. 1239 | The purpose of this input method is to edit Lean programs, but 1240 | since it is highly customisable it can be made useful for other 1241 | tasks as well." 1242 | nil nil nil nil nil nil t ; maximum-shortest 1243 | )) 1244 | 1245 | (lean-input-add-translations 1246 | (mapcar (lambda (tr) (cons (car tr) (vconcat (cdr tr)))) 1247 | (append lean-input-user-translations 1248 | lean-input-translations))) 1249 | (dolist (def lean-input-inherit) 1250 | (lean-input-inherit-package (car def) 1251 | (eval (cdr def))))) 1252 | 1253 | (defun lean-input-incorporate-changed-setting (sym val) 1254 | "Update the Lean input method based on the customisable 1255 | variables and underlying input methods. 1256 | Suitable for use in the :set field of `defcustom'." 1257 | (set-default sym val) 1258 | (lean-input-setup)) 1259 | 1260 | ;; Set up the input method. 1261 | 1262 | (lean-input-setup) 1263 | 1264 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1265 | ;; Administrative details 1266 | 1267 | (provide 'lean-input) 1268 | ;;; lean-input.el ends here 1269 | 1270 | (require 'dash) 1271 | 1272 | (defun lean-input-export-translations () 1273 | "Export the current translation, (input, output) pairs for 1274 | input-method, in a javascript format. It can be copy-pasted to 1275 | leanprover.github.io/tutorial/js/input-method.js" 1276 | (interactive) 1277 | (with-current-buffer 1278 | (get-buffer-create "*lean-translations*") 1279 | (let ((exclude-list '("\\newline"))) 1280 | (insert "var corrections = {") 1281 | (--each 1282 | (--filter (not (member (car it) exclude-list)) 1283 | (lean-input-get-translations "Lean")) 1284 | (let* ((input (substring (car it) 1)) 1285 | (outputs (cdr it))) 1286 | (insert (format "%s:\"" (prin1-to-string input))) 1287 | (cond ((vectorp outputs) 1288 | (insert (elt outputs 0))) 1289 | (t (insert-char outputs))) 1290 | (insert (format "\",\n" input)))) 1291 | (insert "};")))) 1292 | 1293 | (defun lean-input-export-translations-to-stdout () 1294 | (lean-input-export-translations) 1295 | (with-current-buffer "*lean-translations*" 1296 | (princ (buffer-string)))) 1297 | -------------------------------------------------------------------------------- /lean-leanpkg.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | ;; 3 | ;; Copyright (c) 2017 Microsoft Corporation. All rights reserved. 4 | ;; Released under Apache 2.0 license as described in the file LICENSE. 5 | ;; 6 | ;; Author: Gabriel Ebner 7 | ;; 8 | 9 | (require 's) 10 | (require 'json) 11 | (require 'lean-util) 12 | 13 | (defun lean-leanpkg-find-dir-in (dir) 14 | (when dir 15 | (or (lean-leanpkg-find-dir-in (f-parent dir)) 16 | (when (f-exists? (f-join dir "leanpkg.toml")) dir)))) 17 | 18 | (defun lean-leanpkg-find-dir () 19 | (and (buffer-file-name) 20 | (lean-leanpkg-find-dir-in (f-dirname (buffer-file-name))))) 21 | 22 | (defun lean-leanpkg-find-dir-safe () 23 | (or (lean-leanpkg-find-dir) 24 | (error (format "cannot find leanpkg.toml for %s" (buffer-file-name))))) 25 | 26 | (defun lean-leanpkg-executable () 27 | (lean-get-executable "leanpkg")) 28 | 29 | (defvar lean-leanpkg-running nil) 30 | (defvar-local lean-leanpkg-configure-prompt-shown nil) 31 | 32 | (defun lean-leanpkg-run (cmd &optional restart-lean-server) 33 | "Call `leanpkg $cmd`" 34 | (let ((dir (file-name-as-directory (lean-leanpkg-find-dir-safe))) 35 | (orig-buf (current-buffer))) 36 | (with-current-buffer (get-buffer-create "*leanpkg*") 37 | (let ((inhibit-read-only t)) (erase-buffer)) 38 | (switch-to-buffer-other-window (current-buffer)) 39 | (redisplay) 40 | (insert (format "> leanpkg %s\n" cmd)) 41 | (setq lean-leanpkg-running t) 42 | (let* ((default-directory dir) 43 | (out-buf (current-buffer)) 44 | (proc (start-process "leanpkg" (current-buffer) 45 | (lean-leanpkg-executable) cmd))) 46 | (comint-mode) 47 | (set-process-filter proc #'comint-output-filter) 48 | (set-process-sentinel 49 | proc (lambda (_p _e) 50 | (setq lean-leanpkg-running nil) 51 | (when restart-lean-server 52 | (with-current-buffer out-buf 53 | (insert "; restarting lean server\n")) 54 | (with-current-buffer orig-buf 55 | (lean-server-restart))) 56 | (with-current-buffer out-buf 57 | (insert "; done")))))))) 58 | 59 | (defun lean-leanpkg-configure () 60 | "Call leanpkg configure" 61 | (interactive) 62 | (lean-leanpkg-run "configure" 't)) 63 | 64 | (defun lean-leanpkg-build () 65 | "Call leanpkg build" 66 | (interactive) 67 | (lean-leanpkg-run "build")) 68 | 69 | (defun lean-leanpkg-test () 70 | "Call leanpkg test" 71 | (interactive) 72 | (lean-leanpkg-run "test")) 73 | 74 | (defun lean-leanpkg-find-path-file () 75 | (let* ((json-object-type 'plist) (json-array-type 'list) (json-false nil) 76 | (path-json (shell-command-to-string 77 | (concat (shell-quote-argument (lean-get-executable lean-executable-name)) 78 | " -p"))) 79 | (path-out (json-read-from-string path-json))) 80 | (when (and (eq major-mode 'lean-mode) 81 | (plist-get path-out :is_user_leanpkg_path) 82 | (not lean-leanpkg-running) 83 | (not lean-leanpkg-configure-prompt-shown) 84 | (setq lean-leanpkg-configure-prompt-shown t) 85 | (lean-leanpkg-find-dir) 86 | (y-or-n-p (format "Found leanpkg.toml in %s, call leanpkg configure?" (lean-leanpkg-find-dir)))) 87 | (save-match-data ; running in timer so that we don't mess up the window layout 88 | (run-at-time nil nil 89 | (lambda (buf) 90 | (with-current-buffer buf 91 | (lean-leanpkg-configure))) 92 | (current-buffer)))) 93 | (setq lean-leanpkg-configure-prompt-shown t) 94 | (plist-get path-out :leanpkg_path_file))) 95 | 96 | (provide 'lean-leanpkg) 97 | -------------------------------------------------------------------------------- /lean-message-boxes.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | ;; 3 | ;; Copyright (c) 2016 David Christiansen. 4 | ;; Released under Apache 2.0 license as described in the file LICENSE. 5 | ;; 6 | ;; Author: David Christiansen 7 | ;; 8 | ;;; Code: 9 | 10 | (require 'cl-lib) 11 | (require 's) 12 | (require 'lean-server) 13 | 14 | (defface lean-message-boxes-content-face 15 | '((t :inherit font-lock-doc-face)) 16 | "Face for Lean message box contents." 17 | :group 'lean) 18 | 19 | (defcustom lean-message-boxes-enabled-captions '("check result" "eval result" "print result" "reduce result") 20 | "Which captions should result in boxes?" 21 | :group 'lean 22 | :type '(repeat (choice (const "check result") 23 | (const "eval result") 24 | (const "print result") 25 | (const "reduce result") 26 | (const "trace output")))) 27 | 28 | (defcustom lean-message-boxes-enabledp nil 29 | "Whether or not to display message boxes." 30 | :group 'lean 31 | :type 'boolean) 32 | (make-variable-buffer-local 'lean-message-boxes-enabledp) 33 | 34 | (defun lean-message-boxes--ask-for-messages () 35 | "Get the current messages out of the Lean server session." 36 | (let ((buf (current-buffer))) 37 | (if lean-server-session 38 | (cl-remove-if-not (lambda (msg) 39 | (equal (buffer-file-name buf) 40 | (plist-get msg :file_name))) 41 | (lean-server-session-messages lean-server-session)) 42 | '()))) 43 | 44 | (defun lean-message-boxes--set-enabledp (enabledp) 45 | "Enable the boxes if ENABLEDP is non-nil." 46 | (setq lean-message-boxes-enabledp enabledp) 47 | (lean-message-boxes-display (lean-message-boxes--ask-for-messages))) 48 | 49 | (defun lean-message-boxes-toggle () 50 | "Toggle the display of message boxes." 51 | (interactive) 52 | (lean-message-boxes--set-enabledp (not lean-message-boxes-enabledp))) 53 | 54 | (defun lean-message-boxes-enable () 55 | "Enable the display of message boxes." 56 | (interactive) 57 | (lean-message-boxes--set-enabledp t)) 58 | 59 | (defun lean-message-boxes-disable () 60 | "Disable the display of message boxes." 61 | (interactive) 62 | (lean-message-boxes--set-enabledp nil)) 63 | 64 | (defun lean-message-boxes--kill-overlays () 65 | "Delete all Lean message overlays in the current buffer." 66 | (remove-overlays nil nil 'category 'lean-output)) 67 | 68 | (defun lean-message-boxes--pad-to (str width) 69 | "Pad the string STR to a particular WIDTH." 70 | (concat str (make-string (max 0 (- width (length str))) ?\ ))) 71 | 72 | (defun lean-message-boxes-display (msgs) 73 | "Show the messages MSGS in the Lean buffer as boxes when `lean-message-boxes-enabledp' is non-nil." 74 | (lean-message-boxes--kill-overlays) 75 | (when lean-message-boxes-enabledp 76 | (dolist (msg msgs) 77 | (let ((end-line (plist-get msg :end_pos_line)) 78 | (end-col (plist-get msg :end_pos_col)) 79 | (caption (plist-get msg :caption)) 80 | (text (plist-get msg :text))) 81 | (when (member caption lean-message-boxes-enabled-captions) 82 | (lean-message-boxes--make-overlay 83 | end-line end-col 84 | caption text)))))) 85 | 86 | (defun lean-message-boxes--as-string (caption str) 87 | "Construct a propertized string representing CAPTION and STR." 88 | (let* ((str-copy (s-trim str))) 89 | (put-text-property 0 (length str-copy) 90 | 'face 'lean-message-boxes-content-face 91 | str-copy) 92 | (let* ((lines (s-lines str-copy)) 93 | (w (apply #'max (mapcar #'length (cons caption lines))))) 94 | (s-join "\n" 95 | (mapcar 96 | (lambda (l) (concat "│ " (lean-message-boxes--pad-to l w))) 97 | lines))))) 98 | 99 | (defun lean-message-boxes--in-comment (pos) 100 | "Use the faces set by `font-lock-mode` to deduce whether the 101 | character at the given position is contained within a comment." 102 | (let ((faces (get-text-property pos 'face)) 103 | result) 104 | (unless (listp faces) 105 | (setq faces (list faces))) 106 | (dolist (f faces result) 107 | (setq result 108 | (or result (-contains? '(font-lock-comment-face font-lock-comment-delimiter-face) f)))))) 109 | 110 | (defun lean-message-boxes--make-overlay (end-line end-col caption text) 111 | "Construct a message box overlay at LINE and COL with CAPTION and TEXT." 112 | (let* ((end-pos (save-excursion (goto-char (point-min)) 113 | (forward-line (1- end-line)) 114 | (forward-char (1- end-col)) 115 | (while (or (looking-at-p "[[:space:]\n]") (lean-message-boxes--in-comment (point))) 116 | (forward-char -1)) 117 | (end-of-line) 118 | (point))) 119 | (overlay (make-overlay end-pos end-pos nil t t)) 120 | (as-box (concat " \n" (lean-message-boxes--as-string caption text)))) 121 | (put-text-property 0 (length as-box) 'cursor t as-box) 122 | (overlay-put overlay 'after-string as-box) 123 | (overlay-put overlay 'help-echo caption) 124 | (overlay-put overlay 'category 'lean-output))) 125 | 126 | (add-hook 'lean-server-show-message-hook 'lean-message-boxes-display) 127 | (provide 'lean-message-boxes) 128 | -------------------------------------------------------------------------------- /lean-mode.el: -------------------------------------------------------------------------------- 1 | ;;; lean-mode.el --- A major mode for the Lean 3 language -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2013, 2014 Microsoft Corporation. All rights reserved. 4 | ;; Copyright (c) 2014, 2015 Soonho Kong. All rights reserved. 5 | 6 | ;; Author: Leonardo de Moura 7 | ;; Soonho Kong 8 | ;; Gabriel Ebner 9 | ;; Sebastian Ullrich 10 | ;; Maintainer: Sebastian Ullrich 11 | ;; Created: Jan 09, 2014 12 | ;; Keywords: languages 13 | ;; Package-Requires: ((emacs "24.3") (dash "2.18.0") (s "1.10.0") (f "0.19.0") (flycheck "30")) 14 | ;; URL: https://github.com/leanprover/lean-mode 15 | 16 | ;; Released under Apache 2.0 license as described in the file LICENSE. 17 | 18 | ;;; Commentary: 19 | 20 | ;; Provides a major mode for the Lean 3 programming language. 21 | 22 | ;; Provides highlighting, diagnostics, goal visualization, 23 | ;; and many other useful features for Lean users. 24 | 25 | ;; See the README.md for more advanced features and the 26 | ;; associated keybindings. 27 | 28 | ;;; Code: 29 | 30 | (require 'cl-lib) 31 | (require 'dash) 32 | (require 'pcase) 33 | (require 'flycheck) 34 | (require 'lean-eri) 35 | (require 'lean-util) 36 | (require 'lean-settings) 37 | (require 'lean-input) 38 | (require 'lean-syntax) 39 | (require 'lean-leanpkg) 40 | (require 'lean-server) 41 | (require 'lean-flycheck) 42 | (require 'lean-info) 43 | (require 'lean-hole) 44 | (require 'lean-type) 45 | (require 'lean-message-boxes) 46 | (require 'lean-right-click) 47 | (require 'lean-dev) 48 | 49 | (defun lean-compile-string (exe-name args file-name) 50 | "Concatenate EXE-NAME, ARGS, and FILE-NAME." 51 | (format "%s %s %s" exe-name args file-name)) 52 | 53 | (defun lean-create-temp-in-system-tempdir (file-name prefix) 54 | "Create a temp lean file and return its name." 55 | (make-temp-file (or prefix "flymake") nil (f-ext file-name))) 56 | 57 | (defun lean-execute (&optional arg) 58 | "Execute Lean in the current buffer." 59 | (interactive) 60 | (when (called-interactively-p 'any) 61 | (setq arg (read-string "arg: " arg))) 62 | (let ((cc compile-command) 63 | (target-file-name 64 | (or 65 | (buffer-file-name) 66 | (flymake-init-create-temp-buffer-copy 'lean-create-temp-in-system-tempdir)))) 67 | (compile (lean-compile-string 68 | (shell-quote-argument (f-full (lean-get-executable lean-executable-name))) 69 | (or arg "") 70 | (shell-quote-argument (f-full target-file-name)))) 71 | ;; restore old value 72 | (setq compile-command cc))) 73 | 74 | (defun lean-std-exe () 75 | (interactive) 76 | (lean-execute)) 77 | 78 | (defun lean-check-expansion () 79 | (interactive) 80 | (save-excursion 81 | (if (looking-at (rx symbol-start "_")) t 82 | (if (looking-at "\\_>") t 83 | (backward-char 1) 84 | (if (looking-at "\\.") t 85 | (backward-char 1) 86 | (if (looking-at "->") t nil)))))) 87 | 88 | (defun lean-tab-indent () 89 | (interactive) 90 | (cond ((looking-back (rx line-start (* white)) nil) 91 | (lean-eri-indent)) 92 | (t (indent-for-tab-command)))) 93 | 94 | (defun lean-set-keys () 95 | (local-set-key lean-keybinding-std-exe1 #'lean-std-exe) 96 | (local-set-key lean-keybinding-std-exe2 #'lean-std-exe) 97 | (local-set-key lean-keybinding-show-key #'quail-show-key) 98 | (local-set-key lean-keybinding-server-restart #'lean-server-restart) 99 | (local-set-key lean-keybinding-server-switch-version #'lean-server-switch-version) 100 | (local-set-key lean-keybinding-find-definition #'lean-find-definition) 101 | (local-set-key lean-keybinding-tab-indent #'lean-tab-indent) 102 | (local-set-key lean-keybinding-hole #'lean-hole) 103 | (local-set-key lean-keybinding-lean-toggle-show-goal #'lean-toggle-show-goal) 104 | (local-set-key lean-keybinding-lean-toggle-next-error #'lean-toggle-next-error) 105 | (local-set-key lean-keybinding-lean-message-boxes-toggle #'lean-message-boxes-toggle) 106 | (local-set-key lean-keybinding-leanpkg-configure #'lean-leanpkg-configure) 107 | (local-set-key lean-keybinding-leanpkg-build #'lean-leanpkg-build) 108 | (local-set-key lean-keybinding-leanpkg-test #'lean-leanpkg-test) 109 | ;; This only works as a mouse binding due to the event, so it is not abstracted 110 | ;; to avoid user confusion. 111 | (local-set-key (kbd "") #'lean-right-click-show-menu) 112 | ) 113 | 114 | (define-abbrev-table 'lean-abbrev-table 115 | '()) 116 | 117 | (defvar lean-mode-map (make-sparse-keymap) 118 | "Keymap used in Lean mode") 119 | 120 | (defun lean-mk-check-menu-option (text sym) 121 | `[,text (lean-set-check-mode ',sym) 122 | :style radio :selected (eq lean-server-check-mode ',sym)]) 123 | 124 | (easy-menu-define lean-mode-menu lean-mode-map 125 | "Menu for the Lean major mode" 126 | `("Lean" 127 | ["Execute lean" lean-execute t] 128 | ;; ["Create a new project" (call-interactively 'lean-project-create) (not (lean-project-inside-p))] 129 | "-----------------" 130 | ["Show type info" lean-show-type (and lean-eldoc-use eldoc-mode)] 131 | ["Toggle goal display" lean-toggle-show-goal t] 132 | ["Toggle next error display" lean-toggle-next-error t] 133 | ["Toggle message boxes" lean-message-boxes-toggle t] 134 | ["Highlight pending tasks" lean-server-toggle-show-pending-tasks 135 | :active t :style toggle :selected lean-server-show-pending-tasks] 136 | ["Find definition at point" lean-find-definition t] 137 | "-----------------" 138 | ["List of errors" flycheck-list-errors flycheck-mode] 139 | "-----------------" 140 | ["Restart lean process" lean-server-restart t] 141 | "-----------------" 142 | ,(lean-mk-check-menu-option "Check nothing" 'nothing) 143 | ,(lean-mk-check-menu-option "Check visible lines" 'visible-lines) 144 | ,(lean-mk-check-menu-option "Check visible lines and above" 'visible-lines-and-above) 145 | ,(lean-mk-check-menu-option "Check visible files" 'visible-files) 146 | ,(lean-mk-check-menu-option "Check open files" 'open-files) 147 | "-----------------" 148 | ("Configuration" 149 | ["Show type at point" 150 | lean-toggle-eldoc-mode :active t :style toggle :selected eldoc-mode]) 151 | "-----------------" 152 | ["Customize lean-mode" (customize-group 'lean) t])) 153 | 154 | (defconst lean-hooks-alist 155 | '( 156 | ;; server 157 | ;; (kill-buffer-hook . lean-server-stop) 158 | (after-change-functions . lean-server-change-hook) 159 | (focus-in-hook . lean-server-show-messages) 160 | (window-scroll-functions . lean-server-window-scroll-function-hook) 161 | ;; Handle events that may start automatic syntax checks 162 | (before-save-hook . lean-whitespace-cleanup) 163 | ;; info windows 164 | (post-command-hook . lean-show-goal--handler) 165 | (post-command-hook . lean-next-error--handler) 166 | (flycheck-after-syntax-check-hook . lean-show-goal--handler) 167 | (flycheck-after-syntax-check-hook . lean-next-error--handler) 168 | ) 169 | "Hooks which lean-mode needs to hook in. 170 | 171 | The `car' of each pair is a hook variable, the `cdr' a function 172 | to be added or removed from the hook variable if Flycheck mode is 173 | enabled and disabled respectively.") 174 | 175 | (defun lean-mode-setup () 176 | "Default lean-mode setup" 177 | ;; server 178 | (ignore-errors (lean-server-ensure-alive)) 179 | (setq mode-name '("Lean" (:eval (lean-server-status-string)))) 180 | ;; Right click menu sources 181 | (setq lean-right-click-item-functions '(lean-info-right-click-find-definition 182 | lean-hole-right-click)) 183 | ;; Flycheck 184 | (lean-flycheck-turn-on) 185 | (setq-local flycheck-disabled-checkers '()) 186 | ;; info buffers 187 | (lean-ensure-info-buffer lean-next-error-buffer-name) 188 | (lean-ensure-info-buffer lean-show-goal-buffer-name) 189 | ;; eldoc 190 | (when lean-eldoc-use 191 | (cond ((<= emacs-major-version 27) 192 | (set (make-local-variable 'eldoc-documentation-function) 193 | 'lean-eldoc-documentation-function)) 194 | (t (add-hook 'eldoc-documentation-functions 195 | #'lean-eldoc-documentation-function nil t) 196 | (setq-local eldoc-documentation-strategy 197 | 'eldoc-documentation-default))) 198 | (eldoc-mode t))) 199 | 200 | ;; Automode List 201 | ;;;###autoload 202 | (define-derived-mode lean-mode prog-mode "Lean" 203 | "Major mode for Lean 204 | \\{lean-mode-map} 205 | Invokes `lean-mode-hook'. 206 | " 207 | :syntax-table lean-syntax-table 208 | :abbrev-table lean-abbrev-table 209 | :group 'lean 210 | (set (make-local-variable 'comment-start) "--") 211 | (set (make-local-variable 'comment-start-skip) "[-/]-[ \t]*") 212 | (set (make-local-variable 'comment-end) "") 213 | (set (make-local-variable 'comment-end-skip) "[ \t]*\\(-/\\|\\s>\\)") 214 | (set (make-local-variable 'comment-padding) 1) 215 | (set (make-local-variable 'comment-use-syntax) t) 216 | (set (make-local-variable 'font-lock-defaults) lean-font-lock-defaults) 217 | (set (make-local-variable 'indent-tabs-mode) nil) 218 | (set 'compilation-mode-font-lock-keywords '()) 219 | (set-input-method "Lean") 220 | (set (make-local-variable 'lisp-indent-function) 221 | 'common-lisp-indent-function) 222 | (lean-set-keys) 223 | (if (fboundp 'electric-indent-local-mode) 224 | (electric-indent-local-mode -1)) 225 | ;; (abbrev-mode 1) 226 | (pcase-dolist (`(,hook . ,fn) lean-hooks-alist) 227 | (add-hook hook fn nil 'local)) 228 | (setq imenu-generic-expression '(("Inductive" "^ *\\(?:@\\[.*\\]\\)? *inductive +\\([^\n ]+\\)" 1) 229 | ("Function" "^ *\\(?:@\\[.*\\]\\)? *def +\\([^\n ]+\\)" 1) 230 | ("Lemma" "^ *\\(?:@\\[.*\\]\\)? *lemma +\\([^\n ]+\\)" 1) 231 | ("Theorem" "^ *\\(?:@\\[.*\\]\\)? *theorem +\\([^\n ]+\\)" 1) 232 | ("Theorem" "^ *\\(?:@\\[.*\\]\\)? *theorem +\\([^\n ]+\\)" 1) 233 | ("Namespace" "^ *\\(?:@\\[.*\\]\\)? *namespace +\\([^\n ]+\\)" 1))) 234 | (lean-mode-setup)) 235 | 236 | ;; Automatically use lean-mode for .lean files. 237 | ;;;###autoload 238 | (push '("\\.lean$" . lean-mode) auto-mode-alist) 239 | (push '("\\.hlean$" . lean-mode) auto-mode-alist) 240 | 241 | ;; Use utf-8 encoding 242 | ;;;### autoload 243 | (modify-coding-system-alist 'file "\\.lean\\'" 'utf-8) 244 | (modify-coding-system-alist 'file "\\.hlean\\'" 'utf-8) 245 | 246 | ;; Flycheck init 247 | (eval-after-load 'flycheck 248 | '(lean-flycheck-init)) 249 | 250 | (provide 'lean-mode) 251 | ;;; lean-mode.el ends here 252 | -------------------------------------------------------------------------------- /lean-right-click.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | ;; 3 | ;;; lean-right-click.el 4 | ;; 5 | ;; Copyright (c) 2017 David Christiansen. All rights reserved. 6 | ;; 7 | ;; Author: David Christiansen 8 | ;; Released under Apache 2.0 license as described in the file LICENSE. 9 | ;; 10 | 11 | ;;; Code: 12 | 13 | (defvar lean-right-click-item-functions nil 14 | "A list of functions to compute menu items from source locations. 15 | 16 | The functions take no arguments. They will be run in a context 17 | where `current-buffer' gives the buffer in which the click 18 | occurred. The function should return a three-element list, where 19 | the first is a Lean server command, the second is its parameter 20 | list, and the third is a continuation that will compute a list of 21 | menu items. 22 | 23 | Each menu item is a plist that maps the key :name to the string 24 | to show in the menu and the key :action to a zero-argument 25 | function that implements the action.") 26 | (make-variable-buffer-local 'lean-right-click-item-functions) 27 | 28 | (defvar lean-right-click--unique-val-counter 0 29 | "A global counter for unique values for lean-right-click.") 30 | 31 | (defun lean-right-click--unique-val () 32 | "Get a unique value for internal tagging." 33 | (cl-incf lean-right-click--unique-val-counter)) 34 | 35 | (defun lean-right-click--items-for-location () 36 | "Return the menu items for point in the current buffer." 37 | (let ((commands (cl-loop for fun in lean-right-click-item-functions 38 | collecting (funcall fun)))) 39 | (let ((timeout 0.15) 40 | (items '()) 41 | (start-time (time-to-seconds)) 42 | (command-count (length commands)) 43 | (commands-returned 0)) 44 | (cl-loop for (cmd args cont) in commands 45 | do (progn (lean-server-send-command 46 | cmd args 47 | (lambda (&rest result) 48 | (setq items (append items (apply cont result))) 49 | (cl-incf commands-returned)) 50 | (lambda (&rest _whatever) 51 | (cl-incf commands-returned))) 52 | ;; This is necessary to ensure that async IO happens. 53 | (sit-for 0.02))) 54 | (while (and (< (time-to-seconds) (+ timeout start-time)) 55 | (< commands-returned command-count)) 56 | (sit-for 0.02)) 57 | items))) 58 | 59 | (defun lean-right-click-show-menu (click) 60 | "Show a menu based on the location of CLICK, computed from the `lean-right-click-functions'." 61 | (interactive "e") 62 | (let* ((window (posn-window (event-end click))) 63 | (buffer (window-buffer window)) 64 | (where (posn-point (event-end click))) 65 | (menu-items (with-current-buffer buffer 66 | (save-excursion 67 | (goto-char where) 68 | (lean-right-click--items-for-location))))) 69 | (when menu-items 70 | (let* ((menu (make-sparse-keymap)) 71 | (todo (cl-loop for item in menu-items 72 | collecting (let ((sym (lean-right-click--unique-val))) 73 | (define-key-after menu `[,sym] 74 | `(menu-item ,(plist-get item :name) 75 | (lambda () (interactive))) 76 | t) 77 | (cons sym (plist-get item :action))))) 78 | (selection (x-popup-menu click menu))) 79 | (when selection 80 | (funcall (cdr (assoc (car selection) todo)))))))) 81 | 82 | 83 | (provide 'lean-right-click) 84 | ;;; lean-right-click.el ends here 85 | -------------------------------------------------------------------------------- /lean-server.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | ;; 3 | ;; Copyright (c) 2016 Microsoft Corporation. All rights reserved. 4 | ;; Released under Apache 2.0 license as described in the file LICENSE. 5 | ;; 6 | ;; Author: Gabriel Ebner 7 | ;; 8 | 9 | (require 'cl-lib) 10 | (require 'json) 11 | (require 'lean-debug) 12 | (require 'lean-leanpkg) 13 | (require 'dash) 14 | 15 | (defcustom lean-server-show-message-hook '(lean-message-boxes-display) 16 | "Hook run on messages from Lean, allowing custom display. 17 | 18 | Each hook is a function that receives a list of message objects 19 | for the current buffer. Each message object is a plist with at 20 | least the following keys: 21 | - :pos_line is the line number of the message, a number 22 | - :pos_col is the column of the start of the message, a number 23 | - :caption is a category of message, a string 24 | - :text is the text to display, a string." 25 | :group 'lean 26 | :type 'hook 27 | :options '(lean-message-boxes-display)) 28 | 29 | (cl-defstruct lean-server-session 30 | path-file ; the leanpkg.path file of this lean server 31 | process ; process object of lean --server 32 | seq-num ; sequence number 33 | callbacks ; alist of (seq_num . (success_cb . error_cb)) 34 | current-roi ; alist of (file_name (begin_line . end_line) ...) 35 | tasks ; last deserialized current_tasks message 36 | messages) ; list of messages in deserialized json 37 | 38 | (defun lean-server-session-proc-buffer (sess) 39 | (process-buffer (lean-server-session-process sess))) 40 | 41 | (defun lean-server-session-pop-callback (sess seq-num) 42 | (let ((cbp (assoc seq-num (lean-server-session-callbacks sess)))) 43 | (setf (lean-server-session-callbacks sess) 44 | (delete cbp (lean-server-session-callbacks sess))) 45 | (if cbp (cdr cbp) (cons nil nil)))) 46 | 47 | (defun lean-server-process-response (sess res) 48 | (pcase (plist-get res :response) 49 | ("additional_message" 50 | (setf (lean-server-session-messages sess) 51 | (cons (plist-get res :msg) (lean-server-session-messages sess))) 52 | (lean-server-notify-messages-changed sess)) 53 | ("all_messages" 54 | (setf (lean-server-session-messages sess) 55 | (plist-get res :msgs)) 56 | (lean-server-notify-messages-changed sess)) 57 | ("current_tasks" 58 | (let ((old-tasks (lean-server-session-tasks sess))) 59 | (setf (lean-server-session-tasks sess) res) 60 | (lean-server-notify-tasks-changed sess old-tasks))) 61 | ("error" 62 | (message "error: %s" (plist-get res :message)) 63 | ;; TODO(gabriel): maybe even add the error as a message 64 | (when (plist-get res :seq_num) 65 | (let ((cb (lean-server-session-pop-callback sess (plist-get res :seq_num)))) 66 | (when (cdr cb) (funcall (cdr cb) res))))) 67 | ("ok" 68 | (let ((cb (lean-server-session-pop-callback sess (plist-get res :seq_num)))) 69 | (when (car cb) (funcall (car cb) res)))))) 70 | 71 | (defun lean-server-process-line (sess line) 72 | (condition-case-unless-debug err 73 | (progn 74 | (lean-debug "server=> %s" line) 75 | (let* ((json-array-type 'list) 76 | (json-object-type 'plist) 77 | (json-false nil) 78 | (response (json-read-from-string line))) 79 | (lean-server-process-response sess response))) 80 | (error (message "error in lean-server command handler: %s\nServer response was:\n%s" err (buffer-string))))) 81 | 82 | (defun lean-server-process-buffer (sess) 83 | (goto-char (point-min)) 84 | (when (search-forward "\n" nil t) 85 | (let ((line (buffer-substring (point-min) (point)))) 86 | (delete-region (point-min) (point)) 87 | (lean-server-process-line sess line) 88 | (lean-server-process-buffer sess)))) 89 | 90 | (defun lean-server-filter (sess string) 91 | (when (buffer-live-p (lean-server-session-proc-buffer sess)) 92 | (with-current-buffer (lean-server-session-proc-buffer sess) 93 | (goto-char (point-max)) 94 | (insert string) 95 | (lean-server-process-buffer sess)))) 96 | 97 | (defun lean-server-handle-signal (_process event) 98 | "Handle signals for lean-server-process" 99 | (force-mode-line-update) 100 | (let ((event-string (s-trim event))) 101 | (lean-debug "lean-server-handle-signal: %s" 102 | (propertize event-string 'face '(:foreground "red"))) 103 | (if (s-contains? "abnormally" event) 104 | (message (concat "Lean server died. See lean-server stderr buffer for details; " 105 | "use lean-server-restart to restart it"))))) 106 | 107 | (defun lean-server-session-create (path-file) 108 | "Creates a new server session" 109 | (let* ((default-directory (f--traverse-upwards (f-dir? it) path-file)) 110 | (exe (lean-get-executable lean-executable-name)) 111 | (exe (if (assoc path-file lean-server-overrides) 112 | (if (f-file? (lean-get-executable "elan")) 113 | (list (lean-get-executable "elan") "run" "--install" (cdr (assoc path-file lean-server-overrides)) lean-executable-name) 114 | (progn 115 | (warn "Lean version override set but `elan` was not found; ignoring") 116 | (list exe))) 117 | (list exe))) 118 | ; Setting process-connection-type is necessary, otherwise 119 | ; emacs truncates lines with >4096 bytes: 120 | ; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=24531 121 | (process-connection-type nil) 122 | (cmd `(,@exe 123 | "--server" 124 | ,(format "-M%i" lean-memory-limit) 125 | ,(format "-T%i" lean-timeout-limit) 126 | ,@lean-extra-arguments 127 | ,(format "*%s*" path-file))) 128 | (proc (if (and (fboundp 'make-process) (fboundp 'make-pipe-process)) 129 | (make-process ;; emacs >= 25 lets us redirect stderr 130 | :name "lean-server" 131 | :buffer (format " *lean-server (%s)*" path-file) 132 | :command cmd 133 | :coding 'utf-8 134 | :noquery t 135 | :sentinel #'lean-server-handle-signal 136 | :stderr (make-pipe-process 137 | :name "lean-server stderr" 138 | :buffer (format "*lean-server stderr (%s)*" path-file) 139 | :noquery t)) 140 | (progn 141 | ; emacs 24 loves directory separators, without it 142 | ; the server gets started in the parent directory.... 143 | (setq default-directory (format "%s/" default-directory)) 144 | (apply #'start-process "lean-server" 145 | (format " *lean-server (%s)*" (buffer-name)) 146 | cmd)))) 147 | (sess (make-lean-server-session 148 | :path-file path-file 149 | :process proc 150 | :seq-num 0 151 | :current-roi 'not-yet-sent 152 | :callbacks nil 153 | :messages nil))) 154 | (set-process-filter proc (lambda (_proc string) (lean-server-filter sess string))) 155 | (set-process-coding-system proc 'utf-8 'utf-8) 156 | (set-process-query-on-exit-flag proc nil) 157 | sess)) 158 | 159 | (defun lean-server-session-send-command (sess cmd-name params &optional cb error-cb) 160 | (let* ((seq-num (lean-server-session-seq-num sess)) 161 | (req `(:seq_num ,seq-num :command ,cmd-name . ,params)) 162 | (json-array-type 'list) 163 | (json-object-type 'plist) 164 | (json-false :json-false) 165 | (json-req (json-encode req)) 166 | (cur-buf (current-buffer)) 167 | (wrapped-cb (and cb 168 | (lambda (res) 169 | (and cur-buf 170 | (with-current-buffer cur-buf 171 | (apply cb :allow-other-keys t res)))))) 172 | (wrapped-err-cb (and error-cb 173 | (lambda (res) 174 | (and cur-buf 175 | (with-current-buffer cur-buf 176 | (apply error-cb :allow-other-keys t res))))))) 177 | (setf (lean-server-session-seq-num sess) (1+ seq-num)) 178 | (if (or cb error-cb) 179 | (setf (lean-server-session-callbacks sess) 180 | (cons (cons seq-num (cons wrapped-cb wrapped-err-cb)) (lean-server-session-callbacks sess)))) 181 | (lean-debug "server<= %s" json-req) 182 | (process-send-string (lean-server-session-process sess) 183 | (concat json-req "\n")))) 184 | 185 | (defvar lean-server-sessions nil 186 | "list of all running lean-server-sessions") 187 | 188 | (defun lean-server-session-alive-p (sess) 189 | (and sess 190 | (lean-server-session-process sess) 191 | (equal 'run (process-status (lean-server-session-process sess))))) 192 | 193 | (defun lean-server-session-kill (sess) 194 | (ignore-errors (delete-process (lean-server-session-process sess))) 195 | (ignore-errors (kill-buffer (lean-server-session-proc-buffer sess))) 196 | (setf (lean-server-session-process sess) nil) 197 | (setq lean-server-sessions (delete sess lean-server-sessions))) 198 | 199 | (defun lean-server-session-get (path-file) 200 | (setq lean-server-sessions 201 | (cl-remove-if-not #'lean-server-session-alive-p 202 | lean-server-sessions)) 203 | (or (cl-find path-file lean-server-sessions 204 | :test (lambda (d s) (equal d (lean-server-session-path-file s)))) 205 | (let ((sess (lean-server-session-create path-file))) 206 | (setq lean-server-sessions (cons sess lean-server-sessions)) 207 | sess))) 208 | 209 | (defvar-local lean-server-session nil 210 | "Lean server session for the current buffer") 211 | 212 | (defvar lean-server-overrides nil 213 | "alist of (path file . toolchain name) pairs defined by `lean-server-switch-version'.") 214 | 215 | (defun lean-server-session-running-p (sess) 216 | (and sess (plist-get (lean-server-session-tasks sess) :is_running))) 217 | 218 | (defun lean-server-status-string () 219 | (if (not (lean-server-session-alive-p lean-server-session)) " ☠" 220 | (if (lean-server-session-running-p lean-server-session) " ⌛" 221 | " ✓"))) 222 | 223 | (defvar-local lean-server-flycheck-delay-timer nil) 224 | 225 | (defvar-local lean-server-task-overlays nil) 226 | 227 | (defun lean-server-task-region (task) 228 | (let ((bl (1- (plist-get task :pos_line))) 229 | (bc (plist-get task :pos_col)) 230 | (el (1- (plist-get task :end_pos_line))) 231 | (ec (plist-get task :end_pos_col))) 232 | (save-excursion 233 | (widen) 234 | (goto-char (point-min)) 235 | (forward-line bl) 236 | (if (equal (cons bl bc) (cons el ec)) 237 | (progn 238 | (let ((beg (point))) 239 | (forward-line 1) 240 | (cons beg (point)))) 241 | (ignore-errors (forward-char bc)) 242 | (let ((beg (point))) 243 | (goto-char (point-min)) 244 | (forward-line el) 245 | (ignore-errors (forward-char ec)) 246 | (cons beg (point))))))) 247 | 248 | (defface lean-server-task-face 249 | nil 250 | "Face to highlight pending Lean tasks." 251 | :group 'lean) 252 | 253 | (if (fboundp 'define-fringe-bitmap) 254 | (define-fringe-bitmap 'lean-server-fringe-bitmap 255 | (vector) 16 8)) 256 | 257 | (defface lean-server-task-fringe-face 258 | '((((class color) (background light)) 259 | :background "chocolate1") 260 | (((class color) (background dark)) 261 | :background "navajo white") 262 | (t :inverse-video t)) 263 | "Face to highlight the fringe of pending Lean tasks." 264 | :group 'lean) 265 | 266 | (defun lean-server-update-task-overlays () 267 | (dolist (ov lean-server-task-overlays) (delete-overlay ov)) 268 | (setq lean-server-task-overlays nil) 269 | (when (and lean-server-show-pending-tasks lean-server-session) 270 | (let* ((tasks (lean-server-session-tasks lean-server-session)) 271 | (cur-fn (buffer-file-name)) 272 | (roi (cdr (assq cur-fn (lean-server-session-current-roi lean-server-session))))) 273 | (dolist (task (plist-get tasks :tasks)) 274 | (if (and (equal (plist-get task :file_name) cur-fn) 275 | (--any? (<= (max (car it) (plist-get task :pos_line)) 276 | (min (cdr it) (plist-get task :end_pos_line))) 277 | roi)) 278 | (let* ((reg (lean-server-task-region task)) 279 | (ov (make-overlay (car reg) (cdr reg)))) 280 | (setq lean-server-task-overlays (cons ov lean-server-task-overlays)) 281 | (overlay-put ov 'face 'lean-server-task-face) 282 | (overlay-put ov 'line-prefix 283 | (propertize " " 'display 284 | '(left-fringe lean-server-fringe-bitmap lean-server-task-fringe-face))) 285 | (overlay-put ov 'help-echo (format "%s..." (plist-get task :desc))))))))) 286 | 287 | (defun lean-server-toggle-show-pending-tasks () 288 | "Toggles highlighting of pending tasks" 289 | (interactive) 290 | (setq lean-server-show-pending-tasks (not lean-server-show-pending-tasks)) 291 | (dolist (sess lean-server-sessions) 292 | (lean-server-notify-tasks-changed sess nil))) 293 | 294 | (defvar-local lean-server-flycheck-delay-timer nil) 295 | (defvar-local lean-server-flycheck-delayed-update nil) 296 | 297 | (defun lean-server-show-messages (&optional buf) 298 | (with-current-buffer (or buf (current-buffer)) 299 | (save-match-data 300 | (when (and (eq buf flycheck-error-list-source-buffer) 301 | (get-buffer-window buf)) 302 | (if (memq lean-server-flycheck-delay-timer timer-list) 303 | (setq lean-server-flycheck-delayed-update t) ; arm timer 304 | (flycheck-buffer) 305 | (setq lean-server-flycheck-delay-timer 306 | (run-at-time "100 milliseconds" nil 307 | (lambda (buf) 308 | (with-current-buffer buf 309 | (when lean-server-flycheck-delayed-update 310 | (setq lean-server-flycheck-delayed-update nil) 311 | (flycheck-buffer)))) 312 | (current-buffer)))))) 313 | (when lean-server-session 314 | (let ((relevant-msgs 315 | (cl-remove-if-not (lambda (msg) 316 | (equal (buffer-file-name buf) 317 | (plist-get msg :file_name))) 318 | (lean-server-session-messages lean-server-session)))) 319 | (dolist (hook lean-server-show-message-hook) 320 | (funcall hook relevant-msgs)))))) 321 | 322 | (defvar-local lean-server-show-tasks-delay-timer nil) 323 | 324 | (defun lean-server-show-tasks (&optional buf) 325 | (with-current-buffer (or buf (current-buffer)) 326 | (save-match-data 327 | (when (not (memq lean-server-show-tasks-delay-timer timer-list)) 328 | (setq lean-server-show-tasks-delay-timer 329 | (run-at-time "300 milliseconds" nil 330 | (lambda (buf) 331 | (with-current-buffer buf 332 | (lean-server-update-task-overlays))) 333 | (current-buffer))))))) 334 | 335 | (defun lean-server-notify-messages-changed (sess) 336 | (dolist (buf (buffer-list)) 337 | (with-current-buffer buf 338 | (when (and lean-server-session 339 | (eq sess lean-server-session)) 340 | (lean-server-show-messages))))) 341 | 342 | (defun lean-server-notify-tasks-changed (sess old-tasks) 343 | (force-mode-line-update) 344 | (when (and (not lean-server-show-pending-tasks) 345 | (or (plist-get old-tasks :tasks) 346 | (plist-get (lean-server-session-tasks sess) :tasks))) 347 | ; update task flycheck messages only if the task list is non-empty 348 | (lean-server-notify-messages-changed sess)) 349 | (dolist (buf (buffer-list)) 350 | (with-current-buffer buf 351 | (when (eq sess lean-server-session) 352 | (lean-server-show-tasks))))) 353 | 354 | (defun lean-server-stop () 355 | "Stops the lean server associated with the current buffer" 356 | (interactive) 357 | (when lean-server-session 358 | (lean-server-session-kill lean-server-session))) 359 | 360 | (defun lean-server-ensure-alive () 361 | "Ensures that the current buffer has a lean server" 362 | (when (not (lean-server-session-alive-p lean-server-session)) 363 | (setq lean-server-session (lean-server-session-get (lean-leanpkg-find-path-file))) 364 | (lean-server-show-tasks) 365 | (lean-server-show-messages) 366 | (lean-server-sync))) 367 | 368 | (defun lean-server-restart () 369 | "Restarts the lean server for the current buffer" 370 | (interactive) 371 | (lean-server-stop) 372 | (lean-server-ensure-alive) 373 | (flycheck-stop) 374 | (flycheck-buffer)) 375 | 376 | (defun lean-server-versions () 377 | (unless (f-file? (lean-get-executable "elan")) 378 | (error "`bin/elan` was not found in the Lean root dir \"%s\"" (lean-get-rootdir))) 379 | (with-temp-buffer 380 | (call-process (lean-get-executable "elan") nil t nil "toolchain" "list") 381 | (let ((results (split-string (buffer-string) "\n" t))) 382 | ; strip " (default)" from versions 383 | (--map (car (split-string it " ")) results)))) 384 | 385 | (defun lean-server-switch-version () 386 | "Restarts the lean server for the current buffer, using a specific version from elan prompted by `completing-read'." 387 | (interactive) 388 | (setq lean-server-overrides 389 | (cons 390 | (cons (lean-leanpkg-find-path-file) 391 | (completing-read "version: " (lean-server-versions) nil 'confirm)) 392 | lean-server-overrides)) 393 | (lean-server-restart)) 394 | 395 | (defun lean-server-send-command (cmd params &optional cb error-cb) 396 | "Sends a command to the lean server for the current buffer, with a callback to be called upon completion" 397 | (lean-server-ensure-alive) 398 | (lean-server-session-send-command lean-server-session cmd params cb error-cb)) 399 | 400 | (defvar lean-async-timeout 2 401 | "Maximum wait time for a value to be set during asynchronous call.") 402 | 403 | (defvar lean-async-wait 0.03 404 | "Pause between checks to see if the value's been set when turning an 405 | asynchronous call into synchronous.") 406 | 407 | (defun lean-server-send-synchronous-command (cmd params) 408 | "Sends a command to the lean server for the current buffer, waiting for and returning the response" 409 | ;; inspired by company--force-sync 410 | (let ((res 'trash) 411 | (ok t) 412 | (start (time-to-seconds))) 413 | (lean-server-send-command cmd params 414 | (lambda (&rest result) (setq res result)) 415 | (cl-function 416 | (lambda (&key message) 417 | (setq ok nil) 418 | (setq res message)))) 419 | (while (eq res 'trash) 420 | (if (> (- (time-to-seconds) start) lean-async-timeout) 421 | (error "Lean server timed out") 422 | (sleep-for lean-async-wait))) 423 | (if ok 424 | res 425 | (error res)))) 426 | 427 | (defun lean-server-sync (&optional buf) 428 | "Synchronizes the state of BUF (or the current buffer, if nil) with the lean server" 429 | (interactive) 430 | (with-demoted-errors "lean server sync: %s" 431 | (with-current-buffer (or buf (current-buffer)) 432 | (lean-server-sync-roi) 433 | (lean-server-send-command 434 | 'sync (list :file_name (buffer-file-name) 435 | :content (buffer-string)))))) 436 | 437 | (defvar-local lean-server-sync-timer nil) 438 | 439 | (defvar lean-server-sync-on-change t 440 | "When the value is t, sync the server when the buffer is changed.") 441 | 442 | (defun lean-server-toggle-update-on-change () 443 | "Toggle whether the server should be synced when the buffer is changed." 444 | (interactive) 445 | (setq lean-server-sync-on-change (not lean-server-sync-on-change)) 446 | (when lean-server-sync-on-change 447 | (lean-server-sync))) 448 | 449 | (defvar lean-server-change-hook-delay "50 milliseconds" 450 | "The amount of time to wait before syncing the lean server. 451 | 452 | This should be a string giving a relative time like \"90\" or \"2 hours 35 minutes\" 453 | (the acceptable forms are a number of seconds without units or 454 | some combination of values using units in timer-duration-words). 455 | ") 456 | 457 | (defun lean-server-change-hook (_begin _end _len) 458 | (when lean-server-sync-on-change 459 | (save-match-data 460 | (when lean-server-sync-timer (cancel-timer lean-server-sync-timer)) 461 | (setq lean-server-sync-timer 462 | (run-at-time lean-server-change-hook-delay nil #'lean-server-sync (current-buffer)))))) 463 | 464 | (defun lean-server-compute-roi (sess) 465 | "Compute the region of interest for the session SESS." 466 | (--mapcat (with-current-buffer it 467 | (when (eq lean-server-session sess) 468 | (list (cons (buffer-file-name) 469 | (--map (cons (line-number-at-pos (window-start it)) 470 | (line-number-at-pos (window-end it t))) 471 | (get-buffer-window-list)))))) 472 | (buffer-list))) 473 | 474 | (defun lean-server-session-send-roi (sess roi) 475 | (setf (lean-server-session-current-roi sess) roi) 476 | (lean-server-send-command 477 | 'roi (list :mode lean-server-check-mode 478 | :files (--map (list (cons :file_name (car it)) 479 | (cons :ranges (--map (list (cons :begin_line (car it)) 480 | (cons :end_line (cdr it))) 481 | (cdr it)))) 482 | roi)))) 483 | 484 | (defun lean-server-roi-subset-p (as bs) 485 | (--all? (let ((b (cdr (assq (car it) bs)))) 486 | (and b (-all? (lambda (ar) (--any? (and (<= (car it) (car ar)) 487 | (<= (cdr ar) (cdr it))) 488 | b)) 489 | (cdr it)))) 490 | as)) 491 | 492 | (defun lean-server-roi-extend (roi delta) 493 | (--map `(,(car it) . 494 | ,(--map `(,(max 1 (- (car it) delta)) . ,(+ (cdr it) delta)) (cdr it))) 495 | roi)) 496 | 497 | (defun lean-server-roi-ok (old-roi new-roi) 498 | (and (lean-server-roi-subset-p new-roi old-roi) 499 | (lean-server-roi-subset-p old-roi (lean-server-roi-extend new-roi 10)))) 500 | 501 | (defun lean-server-sync-roi (&optional force) 502 | (when lean-server-session 503 | (let ((old-roi (lean-server-session-current-roi lean-server-session)) 504 | (new-roi (lean-server-compute-roi lean-server-session))) 505 | (when (or force (eq old-roi 'not-yet-sent) (not (lean-server-roi-ok old-roi new-roi))) 506 | (lean-server-session-send-roi lean-server-session 507 | (lean-server-roi-extend new-roi 5)))))) 508 | 509 | (defun lean-server-window-scroll-function-hook (wnd _new-start-pos) 510 | (let ((buf (window-buffer wnd))) 511 | (with-demoted-errors "lean scroll hook: %s" 512 | (with-current-buffer buf 513 | (lean-server-ensure-alive) 514 | (lean-server-sync-roi))))) 515 | 516 | (defun lean-set-check-mode (mode) 517 | (setq lean-server-check-mode mode) 518 | (lean-server-sync-roi t)) 519 | 520 | (defun lean-check-nothing () 521 | "Check nothing" 522 | (interactive) 523 | (lean-set-check-mode 'nothing)) 524 | 525 | (defun lean-check-visible-lines () 526 | "Check visible lines" 527 | (interactive) 528 | (lean-set-check-mode 'visible-lines)) 529 | 530 | (defun lean-check-visible-lines-and-above () 531 | "Check visible lines and above" 532 | (interactive) 533 | (lean-set-check-mode 'visible-lines-and-above)) 534 | 535 | (defun lean-check-visible-files () 536 | "Check visible files" 537 | (interactive) 538 | (lean-set-check-mode 'visible-files)) 539 | 540 | (defun lean-check-open-files () 541 | "Check visible files" 542 | (interactive) 543 | (lean-set-check-mode 'open-files)) 544 | 545 | (provide 'lean-server) 546 | -------------------------------------------------------------------------------- /lean-settings.el: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. 2 | ;; Released under Apache 2.0 license as described in the file LICENSE. 3 | ;; 4 | ;; Author: Soonho Kong 5 | ;; 6 | 7 | (require 'cl-lib) 8 | 9 | (defgroup lean nil 10 | "Lean Theorem Prover" 11 | :prefix "lean-" 12 | :group 'languages 13 | :link '(url-link :tag "Website" "http://leanprover.github.io") 14 | :link '(url-link :tag "Github" "https://github.com/leanprover/lean")) 15 | 16 | (defgroup lean-keybinding nil 17 | "Keybindings for lean-mode." 18 | :prefix "lean-" 19 | :group 'lean) 20 | 21 | (defvar-local lean-default-executable-name 22 | (cl-case system-type 23 | ('windows-nt "lean.exe") 24 | ('cygwin "lean.exe") 25 | (t "lean")) 26 | "Default executable name of Lean") 27 | 28 | (defcustom lean-rootdir nil 29 | "Full pathname of lean root directory. It should be defined by user." 30 | :group 'lean 31 | :type 'string) 32 | 33 | (defcustom lean-executable-name lean-default-executable-name 34 | "Name of lean executable" 35 | :group 'lean 36 | :type 'string) 37 | 38 | (defcustom lean-memory-limit 4096 39 | "Memory limit for lean process in megabytes" 40 | :group 'lean 41 | :type 'number) 42 | 43 | (defcustom lean-timeout-limit 100000 44 | "Deterministic timeout limit (it is approximately the maximum number of memory allocations in thousands)" 45 | :group 'lean 46 | :type 'number) 47 | 48 | (defcustom lean-extra-arguments nil 49 | "Extra command-line arguments to the lean process" 50 | :group 'lean 51 | :type '(list string)) 52 | 53 | (defcustom lean-eldoc-use t 54 | "Use eldoc mode for lean." 55 | :group 'lean 56 | :type 'boolean) 57 | 58 | (defcustom lean-eldoc-nay-retry-time 0.3 59 | "When eldoc-function had nay, try again after this amount of time." 60 | :group 'lean 61 | :type 'number) 62 | 63 | (defcustom lean-delete-trailing-whitespace nil 64 | "Set this variable to true to automatically delete trailing 65 | whitespace when a buffer is loaded from a file or when it is 66 | written." 67 | :group 'lean 68 | :type 'boolean) 69 | 70 | (defcustom lean-show-type-add-to-kill-ring nil 71 | "If it is non-nil, add the type information to the kill-ring so 72 | that user can yank(paste) it later. By default, it's 73 | false (nil)." 74 | :group 'lean 75 | :type 'boolean) 76 | 77 | (defcustom lean-server-show-pending-tasks t 78 | "Highlights pending tasks in the current buffer." 79 | :group 'lean 80 | :type 'boolean) 81 | 82 | (defcustom lean-server-check-mode 'visible-lines-and-above 83 | "What parts of the open files the Lean server should check" 84 | :group 'lean 85 | :type 'symbol 86 | :options '(nothing visible-lines visible-lines-and-above visible-files open-files)) 87 | 88 | (defcustom lean-keybinding-std-exe1 (kbd "C-c C-x") 89 | "Lean Keybinding for std-exe #1" 90 | :group 'lean-keybinding :type 'key-sequence) 91 | (defcustom lean-keybinding-std-exe2 (kbd "C-c C-l") 92 | "Lean Keybinding for std-exe #2" 93 | :group 'lean-keybinding :type 'key-sequence) 94 | (defcustom lean-keybinding-show-key (kbd "C-c C-k") 95 | "Lean Keybinding for show-key" 96 | :group 'lean-keybinding :type 'key-sequence) 97 | (defcustom lean-keybinding-server-restart (kbd "C-c C-r") 98 | "Lean Keybinding for server-restart" 99 | :group 'lean-keybinding :type 'key-sequence) 100 | (defcustom lean-keybinding-server-switch-version (kbd "C-c C-s") 101 | "Lean Keybinding for lean-server-switch-version" 102 | :group 'lean-keybinding :type 'key-sequence) 103 | (defcustom lean-keybinding-find-definition (kbd "M-.") 104 | "Lean Keybinding for find-definition" 105 | :group 'lean-keybinding :type 'key-sequence) 106 | (defcustom lean-keybinding-tab-indent (kbd "TAB") 107 | "Lean Keybinding for tab-indent" 108 | :group 'lean-keybinding :type 'key-sequence) 109 | (defcustom lean-keybinding-auto-complete (kbd "S-SPC") 110 | "Lean Keybinding for auto completion" 111 | :group 'lean-keybinding :type 'key-sequence) 112 | (defcustom lean-keybinding-hole (kbd "C-c SPC") 113 | "Lean Keybinding for hole manipulation" 114 | :group 'lean-keybinding :type 'key-sequence) 115 | (defcustom lean-keybinding-lean-toggle-show-goal (kbd "C-c C-g") 116 | "Lean Keybinding for show-goal-at-pos" 117 | :group 'lean-keybinding :type 'key-sequence) 118 | (defcustom lean-keybinding-lean-toggle-next-error (kbd "C-c C-n") 119 | "Lean Keybinding for lean-toggle-next-error" 120 | :group 'lean-keybinding :type 'key-sequence) 121 | (defcustom lean-keybinding-lean-message-boxes-toggle (kbd "C-c C-b") 122 | "Lean Keybinding for lean-message-boxes-toggle" 123 | :group 'lean-keybinding :type 'key-sequence) 124 | (defcustom lean-keybinding-leanpkg-configure (kbd "C-c C-p C-c") 125 | "Lean Keybinding for lean-leanpkg-configure" 126 | :group 'lean-keybinding :type 'key-sequence) 127 | (defcustom lean-keybinding-leanpkg-build (kbd "C-c C-p C-b") 128 | "Lean Keybinding for lean-leanpkg-build" 129 | :group 'lean-keybinding :type 'key-sequence) 130 | (defcustom lean-keybinding-leanpkg-test (kbd "C-c C-p C-t") 131 | "Lean Keybinding for lean-leanpkg-test" 132 | :group 'lean-keybinding :type 'key-sequence) 133 | (provide 'lean-settings) 134 | -------------------------------------------------------------------------------- /lean-syntax.el: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) 2013, 2014 Microsoft Corporation. All rights reserved. 2 | ;; Released under Apache 2.0 license as described in the file LICENSE. 3 | ;; 4 | ;; Author: Leonardo de Moura 5 | ;; Soonho Kong 6 | ;; 7 | 8 | (require 'dash) 9 | (require 'rx) 10 | 11 | (defconst lean-keywords1 12 | '("import" "prelude" "protected" "private" "noncomputable" "definition" "meta" "renaming" 13 | "hiding" "exposing" "parameter" "parameters" "begin" "constant" "constants" 14 | "lemma" "variable" "variables" "theorem" "example" "abbreviation" 15 | "open" "export" "axiom" "axioms" "inductive" "coinductive" "with" "without" 16 | "structure" "universe" "universes" "hide" 17 | "precedence" "reserve" "declare_trace" "add_key_equivalence" 18 | "match" "infix" "infixl" "infixr" "notation" "postfix" "prefix" "instance" 19 | "end" "this" "using" "using_well_founded" "namespace" "section" 20 | "attribute" "local" "set_option" "extends" "include" "omit" "classes" "class" 21 | "attributes" "raw" "replacing" 22 | "calc" "have" "show" "suffices" "by" "in" "at" "do" "let" "forall" "Pi" "fun" 23 | "exists" "if" "then" "else" "assume" "from" 24 | "mutual" "def" "run_cmd") 25 | "lean keywords ending with 'word' (not symbol)") 26 | (defconst lean-keywords1-regexp 27 | (eval `(rx word-start (or ,@lean-keywords1) word-end))) 28 | (defconst lean-constants 29 | '("#" "@" "!" "$" "->" "∼" "↔" "/" "==" "=" ":=" "<->" "/\\" "\\/" "∧" "∨" 30 | "≠" "<" ">" "≤" "≥" "¬" "<=" ">=" "⁻¹" "⬝" "▸" "+" "*" "-" "/" "λ" 31 | "→" "∃" "∀" "∘" "×" "Σ" "Π" "~" "||" "&&" "≃" "≡" "≅" 32 | "ℕ" "ℤ" "ℚ" "ℝ" "ℂ" "𝔸" 33 | "⬝e" "⬝i" "⬝o" "⬝op" "⬝po" "⬝h" "⬝v" "⬝hp" "⬝vp" "⬝ph" "⬝pv" "⬝r" "◾" "◾o" 34 | "∘n" "∘f" "∘fi" "∘nf" "∘fn" "∘n1f" "∘1nf" "∘f1n" "∘fn1" 35 | "^c" "≃c" "≅c" "×c" "×f" "×n" "+c" "+f" "+n" "ℕ₋₂") 36 | "lean constants") 37 | (defconst lean-constants-regexp (regexp-opt lean-constants)) 38 | (defconst lean-numerals-regexp 39 | (eval `(rx word-start 40 | (one-or-more digit) (optional (and "." (zero-or-more digit))) 41 | word-end))) 42 | 43 | (defconst lean-warnings '("sorry" "exit") "lean warnings") 44 | (defconst lean-warnings-regexp 45 | (eval `(rx word-start (or ,@lean-warnings) word-end))) 46 | 47 | 48 | (defconst lean-syntax-table 49 | (let ((st (make-syntax-table))) 50 | ;; Matching parens 51 | (modify-syntax-entry ?\[ "(]" st) 52 | (modify-syntax-entry ?\] ")[" st) 53 | (modify-syntax-entry ?\{ "(}" st) 54 | (modify-syntax-entry ?\} "){" st) 55 | 56 | ;; comment 57 | (modify-syntax-entry ?/ ". 14nb" st) 58 | (modify-syntax-entry ?- ". 123" st) 59 | (modify-syntax-entry ?\n ">" st) 60 | (modify-syntax-entry ?« "<" st) 61 | (modify-syntax-entry ?» ">" st) 62 | 63 | ;; Word constituent 64 | (--map (modify-syntax-entry it "w" st) 65 | (list ?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m 66 | ?n ?o ?p ?q ?r ?s ?t ?u ?v ?w ?x ?y ?z 67 | ?A ?B ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M 68 | ?N ?O ?P ?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z)) 69 | (--map (modify-syntax-entry it "w" st) 70 | (list ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)) 71 | (--map (modify-syntax-entry it "w" st) 72 | (list ?α ?β ?γ ?δ ?ε ?ζ ?η ?θ ?ι ?κ ;;?λ 73 | ?μ ?ν ?ξ ?ο ?π ?ρ ?ς ?σ ?τ ?υ 74 | ?φ ?χ ?ψ ?ω)) 75 | (--map (modify-syntax-entry it "w" st) 76 | (list ?ϊ ?ϋ ?ό ?ύ ?ώ ?Ϗ ?ϐ ?ϑ ?ϒ ?ϓ ?ϔ ?ϕ ?ϖ 77 | ?ϗ ?Ϙ ?ϙ ?Ϛ ?ϛ ?Ϝ ?ϝ ?Ϟ ?ϟ ?Ϡ ?ϡ ?Ϣ ?ϣ 78 | ?Ϥ ?ϥ ?Ϧ ?ϧ ?Ϩ ?ϩ ?Ϫ ?ϫ ?Ϭ ?ϭ ?Ϯ ?ϯ ?ϰ 79 | ?ϱ ?ϲ ?ϳ ?ϴ ?ϵ ?϶ ?Ϸ ?ϸ ?Ϲ ?Ϻ ?ϻ)) 80 | (--map (modify-syntax-entry it "w" st) 81 | (list ?ἀ ?ἁ ?ἂ ?ἃ ?ἄ ?ἅ ?ἆ ?ἇ ?Ἀ ?Ἁ ?Ἂ ?Ἃ ?Ἄ 82 | ?Ἅ ?Ἆ ?Ἇ ?ἐ ?ἑ ?ἒ ?ἓ ?ἔ ?ἕ ?἖ ?἗ ?Ἐ ?Ἑ 83 | ?Ἒ ?Ἓ ?Ἔ ?Ἕ ?἞ ?἟ ?ἠ ?ἡ ?ἢ ?ἣ ?ἤ ?ἥ 84 | ?ἦ ?ἧ ?Ἠ ?Ἡ ?Ἢ ?Ἣ ?Ἤ ?Ἥ ?Ἦ ?Ἧ ?ἰ ?ἱ 85 | ?ἲ ?ἳ ?ἴ ?ἵ ?ἶ ?ἷ ?Ἰ ?Ἱ ?Ἲ ?Ἳ ?Ἴ ?Ἵ ?Ἶ ?Ἷ 86 | ?ὀ ?ὁ ?ὂ ?ὃ ?ὄ ?ὅ ?὆ ?὇ ?Ὀ ?Ὁ ?Ὂ ?Ὃ 87 | ?Ὄ ?Ὅ ?὎ ?὏ ?ὐ ?ὑ ?ὒ ?ὓ ?ὔ ?ὕ ?ὖ ?ὗ 88 | ?὘ ?Ὑ ?὚ ?Ὓ ?὜ ?Ὕ ?὞ ?Ὗ ?ὠ ?ὡ ?ὢ 89 | ?ὣ ?ὤ ?ὥ ?ὦ ?ὧ ?Ὠ ?Ὡ ?Ὢ ?Ὣ ?Ὤ ?Ὥ ?Ὦ 90 | ?Ὧ ?ὰ ?ά ?ὲ ?έ ?ὴ ?ή ?ὶ ?ί ?ὸ ?ό ?ὺ ?ύ ?ὼ 91 | ?ώ ?὾ ?὿ ?ᾀ ?ᾁ ?ᾂ ?ᾃ ?ᾄ ?ᾅ ?ᾆ ?ᾇ ?ᾈ 92 | ?ᾉ ?ᾊ ?ᾋ ?ᾌ ?ᾍ ?ᾎ ?ᾏ ?ᾐ ?ᾑ ?ᾒ ?ᾓ ?ᾔ 93 | ?ᾕ ?ᾖ ?ᾗ ?ᾘ ?ᾙ ?ᾚ ?ᾛ ?ᾜ ?ᾝ ?ᾞ ?ᾟ ?ᾠ ?ᾡ ?ᾢ 94 | ?ᾣ ?ᾤ ?ᾥ ?ᾦ ?ᾧ ?ᾨ ?ᾩ ?ᾪ ?ᾫ ?ᾬ ?ᾭ ?ᾮ ?ᾯ ?ᾰ 95 | ?ᾱ ?ᾲ ?ᾳ ?ᾴ ?᾵ ?ᾶ ?ᾷ ?Ᾰ ?Ᾱ ?Ὰ ?Ά ?ᾼ ?᾽ 96 | ?ι ?᾿ ?῀ ?῁ ?ῂ ?ῃ ?ῄ ?῅ ?ῆ ?ῇ ?Ὲ ?Έ ?Ὴ 97 | ?Ή ?ῌ ?῍ ?῎ ?῏ ?ῐ ?ῑ ?ῒ ?ΐ ?῔ ?῕ ?ῖ ?ῗ 98 | ?Ῐ ?Ῑ ?Ὶ ?Ί ?῜ ?῝ ?῞ ?῟ ?ῠ ?ῡ ?ῢ ?ΰ ?ῤ ?ῥ 99 | ?ῦ ?ῧ ?Ῠ ?Ῡ ?Ὺ ?Ύ ?Ῥ ?῭ ?΅ ?` ?῰ ?῱ ?ῲ ?ῳ 100 | ?ῴ ?῵ ?ῶ ?ῷ ?Ὸ ?Ό ?Ὼ ?Ώ ?ῼ ?´ ?῾)) 101 | (--map (modify-syntax-entry it "w" st) 102 | (list ?℀ ?℁ ?ℂ ?℃ ?℄ ?℅ ?℆ ?ℇ ?℈ ?℉ ?ℊ ?ℋ ?ℌ ?ℍ ?ℎ 103 | ?ℏ ?ℐ ?ℑ ?ℒ ?ℓ ?℔ ?ℕ ?№ ?℗ ?℘ ?ℙ ?ℚ ?ℛ ?ℜ ?ℝ 104 | ?℞ ?℟ ?℠ ?℡ ?™ ?℣ ?ℤ ?℥ ?Ω ?℧ ?ℨ ?℩ ?K ?Å ?ℬ 105 | ?ℭ ?℮ ?ℯ ?ℰ ?ℱ ?Ⅎ ?ℳ ?ℴ ?ℵ ?ℶ ?ℷ ?ℸ ?ℹ ?℺ ?℻ 106 | ?ℼ ?ℽ ?ℾ ?ℿ ?⅀ ?⅁ ?⅂ ?⅃ ?⅄ ?ⅅ ?ⅆ ?ⅇ ?ⅈ ?ⅉ ?⅊ 107 | ?⅋ ?⅌ ?⅍ ?ⅎ ?⅏)) 108 | (modify-syntax-entry ?' "w" st) 109 | (modify-syntax-entry ?_ "w" st) 110 | (modify-syntax-entry ?\. "w" st) 111 | 112 | ;; Lean operator chars 113 | (mapc #'(lambda (ch) (modify-syntax-entry ch "_" st)) 114 | "!#$%&*+<=>@^|~:") 115 | 116 | ;; Whitespace is whitespace 117 | (modify-syntax-entry ?\ " " st) 118 | (modify-syntax-entry ?\t " " st) 119 | 120 | ;; Strings 121 | (modify-syntax-entry ?\" "\"" st) 122 | (modify-syntax-entry ?\\ "/" st) 123 | 124 | st)) 125 | 126 | (defconst lean-font-lock-defaults 127 | `((;; attributes 128 | (,(rx word-start "attribute" word-end (zero-or-more whitespace) (group (one-or-more "[" (zero-or-more (not (any "]"))) "]" (zero-or-more whitespace)))) 129 | (1 'font-lock-doc-face)) 130 | (,(rx (group "@[" (zero-or-more (not (any "]"))) "]")) 131 | (1 'font-lock-doc-face)) 132 | (,(rx (group "#" (or "eval" "print" "reduce" "help" "check"))) 133 | (1 'font-lock-keyword-face)) 134 | ;; mutual definitions "names" 135 | (,(rx word-start 136 | "mutual" 137 | word-end 138 | (zero-or-more whitespace) 139 | word-start 140 | (or "inductive" "definition" "def") 141 | word-end 142 | (group (zero-or-more (not (any " \t\n\r{([,"))) (zero-or-more (zero-or-more whitespace) "," (zero-or-more whitespace) (not (any " \t\n\r{([,"))))) 143 | (1 'font-lock-function-name-face)) 144 | ;; declarations 145 | (,(rx word-start 146 | (group (or "inductive" (group "class" (zero-or-more whitespace) "inductive") "instance" "structure" "class" "theorem" "axiom" "axioms" "lemma" "definition" "def" "constant")) 147 | word-end (zero-or-more whitespace) 148 | (group (zero-or-more "{" (zero-or-more (not (any "}"))) "}" (zero-or-more whitespace))) 149 | (zero-or-more whitespace) 150 | (group (zero-or-more (not (any " \t\n\r{(["))))) 151 | (4 'font-lock-function-name-face)) 152 | ;; Constants which have a keyword as subterm 153 | (,(rx (or "∘if")) . 'font-lock-constant-face) 154 | ;; Keywords 155 | ("\\(set_option\\)[ \t]*\\([^ \t\n]*\\)" (2 'font-lock-constant-face)) 156 | (,lean-keywords1-regexp . 'font-lock-keyword-face) 157 | (,(rx word-start (group "example") ".") (1 'font-lock-keyword-face)) 158 | (,(rx (or "∎")) . 'font-lock-keyword-face) 159 | ;; Types 160 | (,(rx word-start (or "Prop" "Type" "Type*" "Sort" "Sort*") symbol-end) . 'font-lock-type-face) 161 | (,(rx word-start (group (or "Prop" "Type" "Sort")) ".") (1 'font-lock-type-face)) 162 | ;; String 163 | ("\"[^\"]*\"" . 'font-lock-string-face) 164 | ;; ;; Constants 165 | (,lean-constants-regexp . 'font-lock-constant-face) 166 | (,lean-numerals-regexp . 'font-lock-constant-face) 167 | ;; place holder 168 | (,(rx symbol-start "_" symbol-end) . 'font-lock-preprocessor-face) 169 | ;; warnings 170 | (,lean-warnings-regexp . 'font-lock-warning-face) 171 | ;; escaped identifiers 172 | (,(rx (and (group "«") (group (one-or-more (not (any "»")))) (group "»"))) 173 | (1 font-lock-comment-face t) 174 | (2 nil t) 175 | (3 font-lock-comment-face t)) 176 | ))) 177 | 178 | ;; Syntax Highlighting for Lean Info Mode 179 | (defconst lean-info-font-lock-defaults 180 | (let ((new-entries 181 | `(;; Please add more after this: 182 | (,(rx (group (+ symbol-start (+ (or word (char ?₁ ?₂ ?₃ ?₄ ?₅ ?₆ ?₇ ?₈ ?₉ ?₀))) symbol-end (* white))) ":") 183 | (1 'font-lock-variable-name-face)) 184 | (,(rx white ":" white) 185 | . 'font-lock-keyword-face) 186 | (,(rx "⊢" white) 187 | . 'font-lock-keyword-face) 188 | (,(rx "[" (group "stale") "]") 189 | (1 'font-lock-warning-face)) 190 | (,(rx line-start "No Goal" line-end) 191 | . 'font-lock-constant-face))) 192 | (inherited-entries (car lean-font-lock-defaults))) 193 | `(,(-concat new-entries inherited-entries)))) 194 | 195 | (provide 'lean-syntax) 196 | -------------------------------------------------------------------------------- /lean-type.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. 3 | ;; Released under Apache 2.0 license as described in the file LICENSE. 4 | ;; 5 | ;; Authors: Soonho Kong, Sebastian Ullrich 6 | ;; 7 | 8 | (require 'cl-lib) 9 | (require 'dash) 10 | (require 's) 11 | (require 'lean-info) 12 | (require 'lean-util) 13 | (require 'lean-server) 14 | (require 'lean-debug) 15 | (require 'flymake) 16 | 17 | (defun lean-fill-placeholder-cont (info-record) 18 | "Continuation for lean-fill-placeholder" 19 | (let ((synth (and info-record (plist-get info-record :synth)))) 20 | (when synth 21 | (let ((synth-str 22 | (replace-regexp-in-string "?M_[0-9]+" "_" synth))) 23 | (when (cl-search " " synth-str) 24 | (setq synth-str (concat "(" synth-str ")"))) 25 | (when (looking-at "_") 26 | (delete-char 1) 27 | (insert synth-str)))))) 28 | 29 | (defun lean-fill-placeholder () 30 | "Fill the placeholder with a synthesized expression by Lean." 31 | (interactive) 32 | (lean-get-info-record-at-point 'lean-fill-placeholder-cont)) 33 | 34 | (cl-defun lean-info-record-to-string (info-record) 35 | "Given typeinfo, overload, and sym-name, compose information as a string." 36 | (cl-destructuring-bind (&key type tactic_params tactic_param_idx text doc full-id &allow-other-keys) info-record 37 | (let ((name-str (or full-id text)) 38 | (type-str type) 39 | str) 40 | (when tactic_params 41 | (setq tactic_params (-map-indexed (lambda (i param) 42 | (if (eq i tactic_param_idx) 43 | (propertize param 'face 'eldoc-highlight-function-argument) 44 | param)) tactic_params)) 45 | (setq type-str (mapconcat 'identity tactic_params " "))) 46 | 47 | (when (and name-str type-str) 48 | (setq str (format (if tactic_params "%s %s" "%s : %s") 49 | (propertize name-str 'face 'font-lock-variable-name-face) 50 | type-str))) 51 | (when doc 52 | (let ((doc (if (<= emacs-major-version 27) 53 | (let ((lines (split-string doc "\n"))) 54 | (if (cdr lines) 55 | (concat (car lines) " ⋯") 56 | (car lines))) 57 | doc))) 58 | (setq str (concat str 59 | (format "\n%s" 60 | (propertize doc 'face 'font-lock-comment-face)))))) 61 | str))) 62 | 63 | (defvar-local lean-eldoc-documentation-cache nil) 64 | (defvar-local lean-add-to-kill-ring nil) 65 | 66 | (defun lean-eldoc-documentation-function-cont (info-record cb) 67 | "Continuation for lean-eldoc-documentation-function" 68 | (let ((info-string (and info-record (lean-info-record-to-string info-record)))) 69 | (when info-string 70 | (when lean-add-to-kill-ring 71 | (setq lean-add-to-kill-ring nil) 72 | (kill-new 73 | (substring-no-properties info-string)))) 74 | (setq lean-eldoc-documentation-cache (and info-string (format "%s" info-string))) 75 | (funcall cb lean-eldoc-documentation-cache))) 76 | 77 | (defun lean-eldoc-documentation-function (&optional cb) 78 | "Show information of lean expression at point if any 79 | Takes as argument an optional callback function, which defaults to `eldoc-message`" 80 | (interactive) 81 | (when (not (eq lean-server-check-mode 'nothing)) ; TODO(gabriel): revisit once info no longer reparses the file 82 | (lean-get-info-record-at-point 83 | (lambda (info-record) 84 | (lean-eldoc-documentation-function-cont info-record (or cb 'eldoc-message)))) 85 | 'non-nil-non-string)) 86 | 87 | (defun lean-show-type () 88 | "Show information of lean-expression at point if any." 89 | (interactive) 90 | (setq lean-add-to-kill-ring lean-show-type-add-to-kill-ring) 91 | (if (<= emacs-major-version 27) 92 | (lean-eldoc-documentation-function) 93 | (eldoc-print-current-symbol-info t))) 94 | 95 | (defconst lean-show-goal-buffer-name "*Lean Goal*") 96 | 97 | (setq lean-show-goal--handler-mask nil) 98 | 99 | (defun lean-show-goal--handler () 100 | (if lean-show-goal--handler-mask 101 | (setq lean-show-goal--handler-mask nil) 102 | (let ((deactivate-mark)) ; keep transient mark 103 | (when (and (not (eq lean-server-check-mode 'nothing)) 104 | ; TODO(gabriel): revisit ^^ once info no longer reparses the file 105 | (lean-info-buffer-active lean-show-goal-buffer-name)) 106 | (lean-get-info-record-at-point 107 | (lambda (info-record) 108 | (let ((state (plist-get info-record :state))) 109 | (unless (or (s-blank? state) (s-blank? (s-trim state))) 110 | (lean-with-info-output-to-buffer lean-show-goal-buffer-name (princ state)))))))))) 111 | 112 | (defun lean-toggle-show-goal () 113 | "Show goal at the current point." 114 | (interactive) 115 | (lean-toggle-info-buffer lean-show-goal-buffer-name) 116 | (lean-show-goal--handler)) 117 | 118 | (provide 'lean-type) 119 | -------------------------------------------------------------------------------- /lean-util.el: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. 2 | ;; Released under Apache 2.0 license as described in the file LICENSE. 3 | ;; 4 | ;; Author: Soonho Kong 5 | ;; 6 | 7 | (require 'cl-lib) 8 | (require 'f) 9 | (require 's) 10 | (require 'dash) 11 | 12 | (defun lean-setup-rootdir () 13 | (let ((root (executable-find lean-executable-name))) 14 | (when root 15 | (setq lean-rootdir (f-dirname (f-dirname root)))) 16 | root)) 17 | 18 | (defun lean-get-rootdir () 19 | (if lean-rootdir 20 | (let ((lean-path (f-full (f-join lean-rootdir "bin" lean-executable-name)))) 21 | (unless (f-exists? lean-path) 22 | (error "Incorrect 'lean-rootdir' value, path '%s' does not exist." lean-path)) 23 | lean-rootdir) 24 | (or 25 | (lean-setup-rootdir) 26 | (error 27 | (concat "Lean was not found in the 'exec-path' and 'lean-rootdir' is not defined. " 28 | "Please set it via M-x customize-variable RET lean-rootdir RET."))))) 29 | 30 | (defun lean-get-executable (exe-name) 31 | "Return fullpath of lean executable" 32 | (let ((lean-bin-dir-name "bin")) 33 | (f-full (f-join (lean-get-rootdir) lean-bin-dir-name exe-name)))) 34 | 35 | (defun lean-line-offset (&optional pos) 36 | "Return the byte-offset of `pos` or current position, counting from the 37 | beginning of the line" 38 | (interactive) 39 | (let* ((pos (or pos (point))) 40 | (bol-pos 41 | (save-excursion 42 | (goto-char pos) 43 | (beginning-of-line) 44 | (point)))) 45 | (- pos bol-pos))) 46 | 47 | (defun lean-pos-at-line-col (l c) 48 | "Return the point of the given line and column." 49 | ;; http://emacs.stackexchange.com/a/8083 50 | (save-excursion 51 | (goto-char (point-min)) 52 | (forward-line (- l 1)) 53 | (move-to-column c) 54 | (point))) 55 | 56 | (defun lean-whitespace-cleanup () 57 | (when lean-delete-trailing-whitespace 58 | (delete-trailing-whitespace))) 59 | 60 | (defun lean-in-comment-p () 61 | "t if a current point is inside of comment block 62 | nil otherwise" 63 | (nth 4 (syntax-ppss))) 64 | 65 | ;; The following function is a slightly modified version of 66 | ;; f--collect-entries written by Johan Andersson 67 | ;; The URL is at https://github.com/rejeep/f.el/blob/master/f.el#L416-L435 68 | (defun lean--collect-entries (path recursive) 69 | (let (result 70 | (entries 71 | (-reject 72 | (lambda (file) 73 | (or 74 | (equal (f-filename file) ".") 75 | (equal (f-filename file) ".."))) 76 | (directory-files path t)))) 77 | ;; The following line is the only modification that I made 78 | ;; It waits 0.0001 second for an event. This wait allows 79 | ;; wait-timeout function to check the timer and kill the execution 80 | ;; of this function. 81 | (sit-for 0.0001) 82 | (cond (recursive 83 | (-map 84 | (lambda (entry) 85 | (if (f-file? entry) 86 | (setq result (cons entry result)) 87 | (when (f-directory? entry) 88 | (setq result (cons entry result)) 89 | (setq result (append result (lean--collect-entries entry recursive)))))) 90 | entries)) 91 | (t (setq result entries))) 92 | result)) 93 | 94 | ;; The following function is a slightly modified version of 95 | ;; f-files function written by Johan Andersson The URL is at 96 | ;; https://github.com/rejeep/f.el/blob/master/f.el#L478-L481 97 | (defun lean-find-files (path &optional fn recursive) 98 | "Find all files in PATH." 99 | ;; It calls lean--collect-entries instead of f--collect-entries 100 | (let ((files (-select 'f-file? (lean--collect-entries path recursive)))) 101 | (if fn (-select fn files) files))) 102 | 103 | (provide 'lean-util) 104 | --------------------------------------------------------------------------------