diff --git a/lean-mode/.gitignore b/lean-mode/.gitignore new file mode 100644 index 0000000000..433da842b5 --- /dev/null +++ b/lean-mode/.gitignore @@ -0,0 +1,2 @@ +.cask +*.elc diff --git a/lean-mode/LICENSE b/lean-mode/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/lean-mode/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lean-mode/README.md b/lean-mode/README.md new file mode 100644 index 0000000000..829c1382e9 --- /dev/null +++ b/lean-mode/README.md @@ -0,0 +1,120 @@ +This is the Emacs mode for the [Lean theorem prover][lean]. + +[lean]: https://github.com/leanprover/lean + +Installation +============ + +`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`): +```elisp +(require 'package) ; You might already have this line +(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/")) +(package-initialize) ; You might already have this line +``` +See also [MELPA: Getting Started](https://melpa.org/#/getting-started). + +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. + +For `company-lean`, you should also bind a key to trigger completion, if you have not already done so: + +```elisp +;; Trigger completion on Shift-Space +(global-set-key (kbd "S-SPC") #'company-complete) +``` + +Updating +-------- + +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. + +Trying It Out +============= + +If things are working correctly, you should see the word ``Lean`` in the +Emacs mode line when you open a file with extension `.lean`. If you type +```lean +#check id +``` +the word ``#check`` will be underlined, and hovering over it will show +you the type of ``id``. The mode line will show ``FlyC:0/1``, indicating +that there are no errors and one piece of information displayed. + +Key Bindings and Commands +========================= + +| Key | Function | +|--------------------|---------------------------------------------------------------------------------| +| M-. | jump to definition in source file (`lean-find-definition`) | +| M-, | jump back to position before M-. (`xref-pop-marker-stack`) | +| C-c C-k | shows the keystroke needed to input the symbol under the cursor | +| C-c C-x | execute lean in stand-alone mode (`lean-std-exe`) | +| C-c SPC | run a command on the hole at point (`lean-hole`) | +| C-c C-d | show a searchable list of definitions (`helm-lean-definitions`) | +| C-c C-g | toggle showing current tactic proof goal (`lean-toggle-show-goal`) | +| C-c C-n | toggle showing next error in dedicated buffer (`lean-toggle-next-error`) | +| C-c C-b | toggle showing output in inline boxes (`lean-message-boxes-toggle`) | +| C-c C-r | restart the lean server (`lean-server-restart`) | +| C-c C-s | switch to a different Lean version via [elan](https://github.com/Kha/elan) (`lean-server-switch-version`) | +| C-c ! n | flycheck: go to next error | +| C-c ! p | flycheck: go to previous error | +| C-c ! l | flycheck: show list of errors | + +In the default configuration, the Flycheck annotation `FlyC:n/n` indicates the +number of errors / responses from Lean; clicking on `FlyC` opens the Flycheck menu. + + +Message Boxes +================ +To view the output of commands such as `check` and `print` in boxes in the buffer, enable the feature using C-c C-b. +If you then type +```lean +#check id +``` +a box appears after the line showing the type of `id`. Customize `lean-message-boxes-enabled-captions` to choose categories of boxes. +In particular, add `"trace output"` to the list to see proof states and other traces in the buffer. + +Known Issues and Possible Solutions +=================================== + +Unicode +------- + +If you experience a problem rendering unicode symbols on emacs, +please download the following fonts and install them on your machine: + + - [Quivira.ttf](http://www.quivira-font.com/files/Quivira.ttf) + - [Dejavu Fonts](http://sourceforge.net/projects/dejavu/files/dejavu/2.35/dejavu-fonts-ttf-2.35.tar.bz2) + - [NotoSans](https://github.com/googlei18n/noto-fonts/blob/master/hinted/NotoSans-Regular.ttc?raw=true) + - [NotoSansSymbols](https://github.com/googlei18n/noto-fonts/blob/master/unhinted/NotoSansSymbols-Regular.ttf?raw=true) + +Then, have the following lines in your emacs setup to use `DejaVu Sans Mono` font: + +```elisp +(when (member "DejaVu Sans Mono" (font-family-list)) + (set-face-attribute 'default nil :font "DejaVu Sans Mono-11")) +``` + +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: + +```elisp +(require 'unicode-fonts) +(unicode-fonts-setup) +``` + +Contributions +============= + +Contributions are welcome! + +Building from Source +-------------------- + +When working on `lean-mode` itself, it is much easier to just `require` the sources than repeatedly building the MELPA packages: + +```elisp +(add-to-load-path "~/path/to/lean-mode/") +(require 'company-lean) +(require 'helm-lean) +``` + +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`. diff --git a/lean-mode/lean-debug.el b/lean-mode/lean-debug.el new file mode 100644 index 0000000000..196fed44c9 --- /dev/null +++ b/lean-mode/lean-debug.el @@ -0,0 +1,47 @@ +;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. +;; Released under Apache 2.0 license as described in the file LICENSE. +;; +;; Author: Soonho Kong +;; +(require 'cl-lib) + +(defvar lean-debug-mode nil) + +(defvar lean-debug-buffer-name "*lean-debug*") + +(defun lean-turn-on-debug-mode (&optional print-msg) + (interactive) + (when (or (called-interactively-p 'any) print-msg) + (message "lean: turn on debug mode")) + (get-buffer-create lean-debug-buffer-name) + (buffer-disable-undo lean-debug-buffer-name) + (display-buffer lean-debug-buffer-name 'display-buffer-reuse-window + '((reusable-frames . t))) + (setq lean-debug-mode t)) + +(defun lean-turn-off-debug-mode (&optional print-msg) + (interactive) + (when (eq major-mode 'lean-mode) + (when (or (called-interactively-p 'any) print-msg) + (message "lean: turn off debug mode")) + (setq lean-debug-mode nil))) + +(defun lean-output-to-buffer (buffer-name format-string args) + (with-current-buffer + (get-buffer-create buffer-name) + (save-selected-window + (ignore-errors + (select-window (get-buffer-window buffer-name t))) + (goto-char (point-max)) + (insert (apply 'format format-string args))))) + +(defun lean-debug (format-string &rest args) + "Display a message at the bottom of the *lean-debug* buffer." + (when lean-debug-mode + (let ((time-str (format-time-string "%H:%M:%S.%3N" (current-time)))) + (lean-output-to-buffer lean-debug-buffer-name + (concat "%s -- " format-string "\n") + (cons (propertize time-str 'face 'font-lock-keyword-face) + args))))) + +(provide 'lean-debug) diff --git a/lean-mode/lean-dev.el b/lean-mode/lean-dev.el new file mode 100644 index 0000000000..4d3c2b39d1 --- /dev/null +++ b/lean-mode/lean-dev.el @@ -0,0 +1,19 @@ +;; -*- lexical-binding: t -*- +;; +;; Copyright (c) 2017 Microsoft Corporation. All rights reserved. +;; Released under Apache 2.0 license as described in the file LICENSE. +;; +;; Author: Sebastian Ullrich +;; + +(require 'f) +(require 'lean-util) + +(defun lean-diff-test-file () + "Use interactive ./test_input.sh on file of current buffer" + (interactive) + (message (shell-command-to-string (format "yes | ./test_single.sh \"%s\" \"%s\" yes" + (lean-get-executable "lean") + (f-filename (buffer-file-name)))))) + +(provide 'lean-dev) diff --git a/lean-mode/lean-eri.el b/lean-mode/lean-eri.el new file mode 100644 index 0000000000..72b9d23c06 --- /dev/null +++ b/lean-mode/lean-eri.el @@ -0,0 +1,208 @@ +;;; lean-eri.el --- Enhanced relative indentation (eri) + +;;; Commentary: + +;; Adapted from agda-mode (https://github.com/agda/agda/blob/master/src/data/emacs-mode/eri.el) + +;;; Code: + +(require 'cl) + +(defun lean-eri-current-line-length nil + "Calculate length of current line." + (- (line-end-position) (line-beginning-position))) + +(defun lean-eri-current-line-empty nil + "Return non-nil if the current line is empty (not counting white space)." + (equal (current-indentation) + (lean-eri-current-line-length))) + +(defun lean-eri-maximum (xs) + "Calculate maximum element in XS. +Returns nil if the list is empty." + (if xs (apply 'max xs))) + +(defun lean-eri-take (n xs) + "Return the first N elements of XS." + (butlast xs (- (length xs) n))) + +(defun lean-eri-split (x xs) + "Return a pair of lists (XS1 . XS2). +If XS is sorted, then XS = (append XS1 XS2), and all elements in +XS1 are <= X, whereas all elements in XS2 are > X." + (let* ((pos (or (position-if (lambda (y) (> y x)) xs) (length xs))) + (xs1 (lean-eri-take pos xs)) + (xs2 (nthcdr pos xs))) + (cons xs1 xs2))) + +(defun lean-eri-calculate-indentation-points-on-line (max) + "Calculate indentation points on current line. +Only points left of column number MAX are included. If MAX is +nil, then all points are included. Points are returned in +ascending order. + +Example (positions marked with ^ are returned): + + f x y = g 3 (Just y) 5 4 + ^ ^ ^ ^ ^ ^ ^ ^ | + | + MAX" + (let ((result)) + (save-excursion + (save-restriction + (beginning-of-line) + ; To make \\` work in the regexp below: + (narrow-to-region (line-beginning-position) (line-end-position)) + (while + (progn + (let ((pos (and (search-forward-regexp + "\\(?:\\s-\\|\\`\\)\\(\\S-\\)" nil t) + (match-beginning 1)))) + (when (not (null pos)) + (let ((pos1 (- pos (line-beginning-position)))) + (when (or (null max) (< pos1 max)) + (add-to-list 'result pos1)))) + (and pos + (< (point) (line-end-position)) + (or (null max) (< (current-column) max)))))) + (nreverse result) ; Destructive operation. + )))) + +(defun lean-eri-new-indentation-points () + "Calculate new indentation points. +Returns a singleton list containing the column number two steps +in from the indentation of the first non-empty line (white space +excluded) above the current line. If there is no such line, +then the empty list is returned." + (let ((start (line-beginning-position))) + (save-excursion + ; Find a non-empty line above the current one, if any. + (while + (progn + (forward-line -1) + (not (or (bobp) + (not (lean-eri-current-line-empty)))))) + (if (or (equal (point) start) + (lean-eri-current-line-empty)) + nil + (list (+ 2 (current-indentation))))))) + +(defun lean-eri-calculate-indentation-points (reverse) + "Calculate points used to indent the current line. +The points are given in reverse order if REVERSE is non-nil. See +`lean-eri-indent' for a description of how the indentation points are +calculated; note that the current indentation is not included in +the returned list." + ;; First find a bunch of indentations used above the current line. + (let ((points) + (max) + (start (line-beginning-position))) + (save-excursion + (while + (progn + (forward-line -1) + ; Skip the line we started from and lines with nothing but + ; white space. + (unless (or (equal (point) start) + (lean-eri-current-line-empty)) + (setq points + (append + (lean-eri-calculate-indentation-points-on-line max) + points)) + (setq max (car points))) + ;; Stop after hitting the beginning of the buffer or a + ;; non-empty, non-indented line. + (not (or (bobp) + (and (equal (current-indentation) 0) + (> (lean-eri-current-line-length) 0))))))) + ;; Add new indentation points, but remove the current indentation. + ;; Sort the indentations. Rearrange the points so that the next + ;; point is the one after the current one. Reverse if necessary. + ;; + ;; Note: sort and nreverse are destructive. + (let* ((ps0 (remove (current-indentation) + (append (lean-eri-new-indentation-points) points))) + (ps1 (lean-eri-split (current-indentation) (sort ps0 '<))) + (ps2 (append (cdr ps1) (car ps1)))) + (if reverse + (nreverse ps2) + ps2)))) + +(defun lean-eri-indent (&optional reverse) + "Cycle between some possible indentation points. +With prefix argument REVERSE, cycle in reverse order. + +Assume that a file contains the following lines of code, with +point on the line with three dots: + +frob = loooooooooooooooooooooooooong identifier +foo = f a b + where + f (Foo x) y = let bar = x + baz = 3 + 5 + +... + +^ ^ ^ ^ ^ ^ ^ ^ ^ * ^ ^ ^ ^ + +Then the ^'s and the * mark the indentation points that this +function cycles through. The indentation points are selected as +follows: + + * All lines before the current one, up to and including the + first non-indented line (or the beginning of the buffer) are + considered. + + foo = f a b + where + f (Foo x) y = let bar = x + baz = 3 + 5 + + * On these lines, erase all characters that stand to the right + of some non-white space character on a lower line. + + foo + whe + f (Foo x) y = let b + baz = 3 + 5 + + * Also erase all characters not immediately preceded by white + space. + + f + w + f ( x y = l b + b = 3 + 5 + + * The columns of all remaining characters are indentation + points. + + f w f ( x y = l b = 3 + 5 + ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ + + * A new indentation point is also added, two steps in from the + indentation of the first non-empty line (white space + excluded) above the current line (if there is such a line). + + f w f ( x y = l b = 3 + 5 + ^ ^ ^ ^ ^ ^ ^ ^ ^ * ^ ^ ^ ^" + (interactive "P") + (let* ((points (lean-eri-calculate-indentation-points reverse)) + (remaining-points (cdr (member (current-indentation) points))) + (indentation (if remaining-points + (car remaining-points) + (car points)))) + (when indentation + (save-excursion (indent-line-to indentation)) + (if (< (current-column) indentation) + (indent-line-to indentation))))) + +(defun lean-eri-indent-reverse nil + "Cycle between some possible indentation points (in reverse order). +See `lean-eri-indent' for a description of how the indentation points +are calculated." + (interactive) + (lean-eri-indent t)) + +(provide 'lean-eri) +;;; lean-eri.el ends here diff --git a/lean-mode/lean-flycheck.el b/lean-mode/lean-flycheck.el new file mode 100644 index 0000000000..ab30bae320 --- /dev/null +++ b/lean-mode/lean-flycheck.el @@ -0,0 +1,122 @@ +;; -*- lexical-binding: t -*- +;; +;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. +;; Released under Apache 2.0 license as described in the file LICENSE. +;; +;; Author: Soonho Kong +;; +(require 'cl-lib) +(require 'flycheck) +(require 'lean-settings) +(require 'lean-server) +(require 'lean-info) + +(defun lean-toggle-flycheck-mode () + "Toggle flycheck-mode" + (interactive) + (cond + (flycheck-mode (flycheck-mode -1)) + (t (flycheck-mode 1)))) + +(cl-defun lean-flycheck-parse-task (checker buffer cur-file-name + &key pos_line pos_col desc file_name &allow-other-keys) + (if (equal cur-file-name file_name) + (flycheck-error-new-at pos_line (1+ pos_col) + 'info + (format "still running: %s" desc) + :filename file_name + :checker checker :buffer buffer) + (flycheck-error-new-at 1 1 + 'info + (format "still running: %s" desc) + :filename cur-file-name + :checker checker :buffer buffer))) + +(defun lean-flycheck-mk-task-msgs (checker buffer sess) + (if (and sess (lean-server-session-tasks sess) + (plist-get (lean-server-session-tasks sess) :is_running)) + (let* ((cur-fn (buffer-file-name)) + (tasks (lean-server-session-tasks sess)) + (cur-task (plist-get tasks :cur_task)) + (tasks-for-cur-file (cl-remove-if-not (lambda (task) (equal cur-fn (plist-get task :file_name))) + (plist-get tasks :tasks))) + (display-tasks)) + ;; do not display tasks for current file when highlighting is enabled + (when (not lean-server-show-pending-tasks) + (setq display-tasks tasks-for-cur-file)) + ;; show current task when not in current file + (when (and cur-task + (not (equal cur-fn (plist-get cur-task :file_name)))) + (setq display-tasks (cons cur-task display-tasks))) + (mapcar (lambda (task) (apply #'lean-flycheck-parse-task checker buffer cur-fn task)) + display-tasks)))) + +(defun lean-info-fontify-string (str) + (lean-ensure-info-buffer "*lean-fontify*") + (with-current-buffer "*lean-fontify*" + (erase-buffer) + (insert str) + (font-lock-fontify-region (point-min) (point-max) nil) + (buffer-string))) + +(cl-defun lean-flycheck-parse-error (checker buffer &key pos_line pos_col severity text file_name &allow-other-keys) + (flycheck-error-new-at pos_line (1+ pos_col) + (pcase severity + ("error" 'error) + ("warning" 'warning) + ("information" 'info) + (_ 'info)) + (lean-info-fontify-string text) + :filename file_name + :checker checker :buffer buffer)) + +(defun lean-flycheck-start (checker callback) + (let ((cur-fn (buffer-file-name)) + (buffer (current-buffer))) + (funcall callback 'finished + (if lean-server-session + (append + (lean-flycheck-mk-task-msgs checker buffer lean-server-session) + (mapcar (lambda (msg) (apply #'lean-flycheck-parse-error checker buffer msg)) + (cl-remove-if-not (lambda (msg) (equal cur-fn (plist-get msg :file_name))) + (lean-server-session-messages lean-server-session)))))))) + +(defun lean-flycheck-init () + "Initialize lean-flychek checker" + (flycheck-define-generic-checker 'lean-checker + "A Lean syntax checker." + :start #'lean-flycheck-start + :modes '(lean-mode)) + (add-to-list 'flycheck-checkers 'lean-checker)) + +(defun lean-flycheck-turn-on () + (flycheck-mode t)) + +(defconst lean-next-error-buffer-name "*Lean Next Error*") + +(defun lean-next-error--handler () + (when (lean-info-buffer-active lean-next-error-buffer-name) + (let ((deactivate-mark) ; keep transient mark + (errors (or + ;; prefer error of current position, if any + (flycheck-overlay-errors-at (point)) + ;; try errors in current line next + (sort (flycheck-overlay-errors-in (line-beginning-position) (line-end-position)) + #'flycheck-error-<) + ;; fall back to next error position + (-if-let* ((pos (flycheck-next-error-pos 1))) + (flycheck-overlay-errors-at pos))))) + (lean-with-info-output-to-buffer lean-next-error-buffer-name + (dolist (e errors) + (princ (format "%d:%d: " (flycheck-error-line e) (flycheck-error-column e))) + (princ (flycheck-error-message e)) + (princ "\n\n")) + (when flycheck-current-errors + (princ (format "(%d more messages above...)" (length flycheck-current-errors)))))))) + +(defun lean-toggle-next-error () + (interactive) + (lean-toggle-info-buffer lean-next-error-buffer-name) + (lean-next-error--handler)) + +(provide 'lean-flycheck) diff --git a/lean-mode/lean-hole.el b/lean-mode/lean-hole.el new file mode 100644 index 0000000000..864dfd241b --- /dev/null +++ b/lean-mode/lean-hole.el @@ -0,0 +1,151 @@ +;; -*- lexical-binding: t -*- +;; +;; Copyright (c) 2017 David Christiansen. +;; Released under Apache 2.0 license as described in the file LICENSE. +;; +;; Author: David Christiansen +;; +;;; Commentary: +;; This is an interface to Lean's support for holes. +;; +;; The interface consists of two components: the hole command, which +;; collects the list of completions and the message, and a handler, +;; which selects a completion. +;; +;; For now, the only handler uses completing-read, but handlers using +;; helm or company's interface would be a good addition. +;; +;;; Code: +(require 'lean-server) + +(defun lean-hole-handler-completing-read (alternatives) + "Pick a hole replacement from ALTERNATIVES with `completing-read'." + (let* ((choices (cl-loop for alt in alternatives + collect (cons (concat (plist-get alt :code) + " — " + (plist-get alt :description)) + (plist-get alt :code)))) + (selection (let ((this-command 'lean-hole)) + (completing-read + "Response: " + choices + nil t nil nil nil t))) + (code (assoc selection choices))) + (if code + (cdr code) + (error "Didn't select a hole completion")))) + +(defvar lean-hole-handler-function 'lean-hole-handler-completing-read) + +(defun lean-hole--line-col->pos (line col) + "Compute the position corresponding to LINE and COL." + (save-restriction + (widen) + (save-excursion + (goto-char (point-min)) + (forward-line (1- line)) + (forward-char col) + (point)))) + +(defun lean-hole () + "Ask Lean for a list of holes, then ask the user which to use." + (interactive) + (with-demoted-errors "lean hole: %s" + (lean-server-send-command + 'hole_commands (list :file_name (buffer-file-name) + :line (line-number-at-pos) + :column (lean-line-offset)) + (cl-function + (lambda (&key start end results) + (let ((start-pos (lean-hole--line-col->pos (plist-get start :line) + (plist-get start :column))) + (end-pos (lean-hole--line-col->pos (plist-get end :line) + (plist-get end :column)))) + (let ((start-marker (make-marker)) + (end-marker (make-marker))) + (set-marker start-marker start-pos (current-buffer)) + (set-marker end-marker end-pos (current-buffer)) + (let* ((choices + (cl-loop for res in results + collect (cons (concat (plist-get res :name) + " — " + (plist-get res :description)) + (plist-get res :name)))) + (selection (let ((this-command 'lean-hole)) + (completing-read + "Hole command: " + choices + nil t nil nil nil t))) + (code (assoc selection choices))) + (if code + (lean-hole--command (cdr code) start-marker end-marker) + (error "Didn't select a hole completion")))))))))) + +;; This uses markers to ensure that if the hole moves while the +;; command is running, it is still updated. +(defun lean-hole--command (command start-marker end-marker) + "Execute COMMAND in the hole between START-MARKER and END-MARKER." + (interactive) + (with-demoted-errors "lean hole: %s" + (lean-server-send-command + 'hole (list :action command + :file_name (buffer-file-name) + :line (line-number-at-pos start-marker) + :column (lean-line-offset start-marker)) + (cl-function + (lambda (&key message replacements) + (let ((replacement-count (length (plist-get replacements :alternatives)))) + (let ((selected-code + (cond ((= replacement-count 0) + nil) + ((= replacement-count 1) + (plist-get (car (plist-get replacements :alternatives)) :code)) + (t + (lean-hole-handler-completing-read + (plist-get replacements :alternatives)))))) + (when selected-code + (save-excursion + (goto-char start-marker) + (delete-region start-marker end-marker) + (insert selected-code))))) + (when message + (message "%s" (s-trim message))) + (set-marker start-marker nil) + (set-marker end-marker nil)))))) + +(defun lean-hole-right-click () + "Ask Lean for a list of hole commands, then ask the user which to use." + (interactive) + (let ((buf (current-buffer))) + (ignore-errors + (list + 'hole_commands + (list :file_name (buffer-file-name) + :line (line-number-at-pos) + :column (lean-line-offset)) + (cl-function + (lambda (&key start end results) + (when (and start end) + (with-current-buffer buf + (let ((start-pos (lean-hole--line-col->pos (plist-get start :line) + (plist-get start :column))) + (end-pos (lean-hole--line-col->pos (plist-get end :line) + (plist-get end :column)))) + (let ((start-marker (make-marker)) + (end-marker (make-marker))) + (set-marker start-marker start-pos (current-buffer)) + (set-marker end-marker (1+ end-pos) (current-buffer)) + (mapcar (lambda (res) + (let ((item-name (plist-get res :name)) + (item-desc (plist-get res :description))) + (list :name + (concat item-name " — " item-desc) + :action + (lambda () + (lean-hole--command + item-name + start-marker end-marker))))) + results))))))))))) + +(provide 'lean-hole) +;;; lean-hole.el ends here diff --git a/lean-mode/lean-info.el b/lean-mode/lean-info.el new file mode 100644 index 0000000000..75fbbc192c --- /dev/null +++ b/lean-mode/lean-info.el @@ -0,0 +1,114 @@ +;; -*- lexical-binding: t -*- +;; +;;; lean-info.el --- Emacs mode for Lean theorem prover +;; +;; Copyright (c) 2016 Gabriel Ebner. All rights reserved. +;; +;; Author: Gabriel Ebner +;; Maintainer: Gabriel Ebner +;; Created: Oct 29, 2016 +;; Keywords: languages +;; Version: 0.1 +;; URL: https://github.com/leanprover/lean/blob/master/src/emacs +;; +;; Released under Apache 2.0 license as described in the file LICENSE. +;; + +(require 'lean-syntax) + +;; Lean Info Mode (for "*lean-info*" buffer) +;; Automode List +;;;###autoload +(define-derived-mode lean-info-mode prog-mode "Lean-Info" + "Major mode for Lean Info Buffer" + :syntax-table lean-syntax-table + :group 'lean + (set (make-local-variable 'font-lock-defaults) lean-info-font-lock-defaults) + (set (make-local-variable 'indent-tabs-mode) nil) + (set 'compilation-mode-font-lock-keywords '()) + (set-input-method "Lean") + (set (make-local-variable 'lisp-indent-function) + 'common-lisp-indent-function)) + +(defmacro lean-with-info-output-to-buffer (buffer &rest body) + `(let ((buf (get-buffer ,buffer))) + (with-current-buffer buf + (setq buffer-read-only nil) + (erase-buffer) + (setq standard-output buf) + . ,body))) + +(defun lean-ensure-info-buffer (buffer) + (unless (get-buffer buffer) + (with-current-buffer (get-buffer-create buffer) + (buffer-disable-undo) + (lean-info-mode)))) + +(defun lean-toggle-info-buffer (buffer) + (-if-let (window (get-buffer-window buffer)) + (quit-window nil window) + (lean-ensure-info-buffer buffer) + (display-buffer buffer))) + +(defun lean-info-buffer-active (buffer) + "Checks whether the given info buffer should show info for the current buffer" + (and + ;; info buffer visible + (get-buffer-window buffer) + ;; current window of current buffer is selected (i.e., in focus) + (eq (current-buffer) (window-buffer)))) + +(defun lean-get-info-record-at-point (cont) + "Get info-record at the current point" + (with-demoted-errors "lean get info: %s" + (lean-server-send-command + 'info (list :file_name (buffer-file-name) + :line (line-number-at-pos) + :column (lean-line-offset)) + (cl-function + (lambda (&key record) + (funcall cont record)))))) + +(defun lean-info-right-click-find-definition () + "Offer to jump to definition of right-click target." + (interactive) + (list 'info + (list :file_name (buffer-file-name) + :line (line-number-at-pos) + :column (lean-line-offset)) + (cl-function + (lambda (&key record) + (let ((source-record (plist-get record :source))) + (if source-record + (let ((full-name (plist-get record :full-id))) + (list + (list :name (if full-name + (concat "Find definition of " full-name) + "Find definition") + :action (lambda () + (apply #'lean-find-definition-cont source-record))))) + (list))))))) + +(cl-defun lean-find-definition-cont (&key file line column) + (when (fboundp 'xref-push-marker-stack) (xref-push-marker-stack)) + (when file + (find-file file)) + (goto-char (point-min)) + (forward-line (1- line)) + (forward-char column)) + + +(defun lean-find-definition () + "Jump to definition of thing at point" + (interactive) + (setq lean-show-goal--handler-mask t) ; avoid the current request to the Lean server to by + ; interrupted by requests made for `lean-show-goal` + (lean-get-info-record-at-point + (lambda (info-record) + (-if-let (source-record (plist-get info-record :source)) + (apply #'lean-find-definition-cont source-record) + (-if-let (id (plist-get info-record :full-id)) + (message "no source location available for %s" id) + (message "unknown thing at point")))))) + +(provide 'lean-info) diff --git a/lean-mode/lean-input.el b/lean-mode/lean-input.el new file mode 100644 index 0000000000..dcbfdddf76 --- /dev/null +++ b/lean-mode/lean-input.el @@ -0,0 +1,1167 @@ +;;; lean-input.el --- The Lean input method (based/copied from Agda) +;;; +;;; DISCLAIMER: This file is based on agda-input.el provided with the Agda language. +;;; We did minor modifications +;; +;;; Commentary: +;; +;;;; A highly customisable input method which can inherit from other +;; Quail input methods. By default the input method is geared towards +;; the input of mathematical and other symbols in Lean programs. +;; +;; Use M-x customize-group lean-input to customise this input method. +;; Note that the functions defined under "Functions used to tweak +;; translation pairs" below can be used to tweak both the key +;; translations inherited from other input methods as well as the +;; ones added specifically for this one. +;; +;; Use lean-input-show-translations to see all the characters which +;; can be typed using this input method (except for those +;; corresponding to ASCII characters). + +;;; Code: + +(require 'quail) + +(eval-when-compile + (require 'cl)) +;; Quail is quite stateful, so be careful when editing this code. Note +;; that with-temp-buffer is used below whenever buffer-local state is +;; modified. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Utility functions + +(defun lean-input-concat-map (f xs) + "Concat (map F XS)." + (apply 'append (mapcar f xs))) + +(defun lean-input-to-string-list (s) + "Convert a string S to a list of one-character strings, after +removing all space and newline characters." + (lean-input-concat-map + (lambda (c) (if (member c (string-to-list " \n")) + nil + (list (string c)))) + (string-to-list s))) + +(defun lean-input-character-range (from to) + "A string consisting of the characters from FROM to TO." + (let (seq) + (dotimes (i (1+ (- to from))) + (setq seq (cons (+ from i) seq))) + (concat (nreverse seq)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Functions used to tweak translation pairs + +;; lexical-let is used since Elisp lacks lexical scoping. + +(defun lean-input-compose (f g) + "\x -> concatMap F (G x)" + (lexical-let ((f1 f) (g1 g)) + (lambda (x) (lean-input-concat-map f1 (funcall g1 x))))) + +(defun lean-input-or (f g) + "\x -> F x ++ G x" + (lexical-let ((f1 f) (g1 g)) + (lambda (x) (append (funcall f1 x) (funcall g1 x))))) + +(defun lean-input-nonempty () + "Only keep pairs with a non-empty first component." + (lambda (x) (if (> (length (car x)) 0) (list x)))) + +(defun lean-input-prepend (prefix) + "Prepend PREFIX to all key sequences." + (lexical-let ((prefix1 prefix)) + (lambda (x) `((,(concat prefix1 (car x)) . ,(cdr x)))))) + +(defun lean-input-prefix (prefix) + "Only keep pairs whose key sequence starts with PREFIX." + (lexical-let ((prefix1 prefix)) + (lambda (x) + (if (equal (substring (car x) 0 (length prefix1)) prefix1) + (list x))))) + +(defun lean-input-suffix (suffix) + "Only keep pairs whose key sequence ends with SUFFIX." + (lexical-let ((suffix1 suffix)) + (lambda (x) + (if (equal (substring (car x) + (- (length (car x)) (length suffix1))) + suffix1) + (list x))))) + +(defun lean-input-drop (ss) + "Drop pairs matching one of the given key sequences. +SS should be a list of strings." + (lexical-let ((ss1 ss)) + (lambda (x) (unless (member (car x) ss1) (list x))))) + +(defun lean-input-drop-beginning (n) + "Drop N characters from the beginning of each key sequence." + (lexical-let ((n1 n)) + (lambda (x) `((,(substring (car x) n1) . ,(cdr x)))))) + +(defun lean-input-drop-end (n) + "Drop N characters from the end of each key sequence." + (lexical-let ((n1 n)) + (lambda (x) + `((,(substring (car x) 0 (- (length (car x)) n1)) . + ,(cdr x)))))) + +(defun lean-input-drop-prefix (prefix) + "Only keep pairs whose key sequence starts with PREFIX. +This prefix is dropped." + (lean-input-compose + (lean-input-drop-beginning (length prefix)) + (lean-input-prefix prefix))) + +(defun lean-input-drop-suffix (suffix) + "Only keep pairs whose key sequence ends with SUFFIX. +This suffix is dropped." + (lexical-let ((suffix1 suffix)) + (lean-input-compose + (lean-input-drop-end (length suffix1)) + (lean-input-suffix suffix1)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Customization + +;; The :set keyword is set to 'lean-input-incorporate-changed-setting +;; so that the input method gets updated immediately when users +;; customize it. However, the setup functions cannot be run before all +;; variables have been defined. Hence the :initialize keyword is set to +;; 'custom-initialize-default to ensure that the setup is not performed +;; until lean-input-setup is called at the end of this file. + +(defgroup lean-input nil + "The Lean input method. +After tweaking these settings you may want to inspect the resulting +translations using `lean-input-show-translations'." + :group 'lean + :group 'leim) + +(defcustom lean-input-tweak-all + '(lean-input-compose + (lean-input-prepend "\\") + (lean-input-nonempty)) + "An expression yielding a function which can be used to tweak +all translations before they are included in the input method. +The resulting function (if non-nil) is applied to every +\(KEY-SEQUENCE . TRANSLATION) pair and should return a list of such +pairs. (Note that the translations can be anything accepted by +`quail-defrule'.) + +If you change this setting manually (without using the +customization buffer) you need to call `lean-input-setup' in +order for the change to take effect." + :group 'lean-input + :set 'lean-input-incorporate-changed-setting + :initialize 'custom-initialize-default + :type 'sexp) + +(defcustom lean-input-inherit + `(("TeX" . (lean-input-compose + (lean-input-drop '("geq" "leq" "bullet" "qed" "par")) + (lean-input-or + (lean-input-drop-prefix "\\") + (lean-input-or + (lean-input-compose + (lean-input-drop '("^o")) + (lean-input-prefix "^")) + (lean-input-prefix "_"))))) + ) + "A list of Quail input methods whose translations should be +inherited by the Lean input method (with the exception of +translations corresponding to ASCII characters). + +The list consists of pairs (qp . tweak), where qp is the name of +a Quail package, and tweak is an expression of the same kind as +`lean-input-tweak-all' which is used to tweak the translation +pairs of the input method. + +The inherited translation pairs are added last, after +`lean-input-user-translations' and `lean-input-translations'. + +If you change this setting manually (without using the +customization buffer) you need to call `lean-input-setup' in +order for the change to take effect." + :group 'lean-input + :set 'lean-input-incorporate-changed-setting + :initialize 'custom-initialize-default + :type '(repeat (cons (string :tag "Quail package") + (sexp :tag "Tweaking function")))) + +(defcustom lean-input-translations + (let ((max-lisp-eval-depth 2800)) `( + + ;; Negation + + ("not" . ("¬")) + + ;; Equality and similar symbols. + + ("eq" . ,(lean-input-to-string-list "=∼∽≈≋∻∾∿≀≃⋍≂≅ ≌≊≡≣≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≍≎≏≬⋕")) + ("eqn" . ,(lean-input-to-string-list "≠≁ ≉ ≄ ≇≆ ≢ ≭ ")) + ("equiv" . ,(lean-input-to-string-list "≃⋍")) + ("iso" . ,(lean-input-to-string-list "≅≌")) + + ("=n" . ("≠")) + ("~" . ("∼")) ("~n" . ("≁")) ("homotopy" . ("∼")) + ("~~" . ("≈")) ("~~n" . ("≉")) + ("~~~" . ("≋")) + (":~" . ("∻")) + ("~-" . ("≃")) ("~-n" . ("≄")) + ("-~" . ("≂")) + ("~=" . ("≅")) ("~=n" . ("≇")) + ("~~-" . ("≊")) + ("==" . ("≡")) ("==n" . ("≢")) + ("===" . ("≣")) + (".=" . ("≐")) (".=." . ("≑")) + (":=" . ("≔")) ("=:" . ("≕")) + ("=o" . ("≗")) + ("(=" . ("≘")) + ("and=" . ("≙")) ("or=" . ("≚")) + ("*=" . ("≛")) + ("t=" . ("≜")) + ("def=" . ("≝")) + ("m=" . ("≞")) + ("?=" . ("≟")) + + ("pr" . ("↣")) + + ("1" . ("₁")) + ("2" . ("₂")) + ("3" . ("₃")) + ("4" . ("₄")) + ("5" . ("₅")) + ("6" . ("₆")) + ("7" . ("₇")) + ("8" . ("₈")) + ("9" . ("₉")) + ("0" . ("₀")) + + ;; Inequality and similar symbols. + + ("leq" . ,(lean-input-to-string-list "≤≦≲<≪⋘ ≶≺≼≾⊂⊆ ⋐⊏⊑ ⊰⊲⊴⋖⋚⋜⋞")) + ("leqn" . ,(lean-input-to-string-list "≰≨≮≴⋦ ≸⊀ ⋨⊄⊈⊊ ⋢⋤ ⋪⋬ ⋠")) + ("geq" . ,(lean-input-to-string-list "≥≧≳>≫⋙ ≷≻≽≿⊃⊇ ⋑⊐⊒ ⊱⊳⊵⋗⋛⋝⋟")) + ("geqn" . ,(lean-input-to-string-list "≱≩≯≵⋧ ≹ ⊁ ⋩⊅⊉⊋ ⋣⋥ ⋫⋭ ⋡")) + + ("<=" . ("≤")) (">=" . ("≥")) + ("<=n" . ("≰")) (">=n" . ("≱")) + ("len" . ("≰")) ("gen" . ("≱")) + ("n" . ("≯")) + ("<~" . ("≲")) (">~" . ("≳")) + ("<~n" . ("⋦")) (">~n" . ("⋧")) + ("<~nn" . ("≴")) (">~nn" . ("≵")) + + ("ssub" . ("⊂")) ("ssup" . ("⊃")) + ("ssubn" . ("⊄")) ("ssupn" . ("⊅")) + ("sub" . ("⊆")) ("sup" . ("⊇")) + ("subn" . ("⊈")) ("supn" . ("⊉")) + ("ssqub" . ("⊏")) ("ssqup" . ("⊐")) + ("squb" . ("⊑")) ("squp" . ("⊒")) + ("squbn" . ("⋢")) ("squpn" . ("⋣")) + + ;; Set membership etc. + + ("member" . ,(lean-input-to-string-list "∈∉∊∋∌∍⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿")) + ("mem" . ("∈")) + + ("inn" . ("∉")) + ("nin" . ("∌")) + + ;; Types + + ("T1" . ("Type₁")) + ("T2" . ("Type₂")) + ("T+" . ("Type₊")) + + ;; Intersections, unions etc. + + ("intersection" . ,(lean-input-to-string-list "∩⋂∧⋀⋏⨇⊓⨅⋒∏ ⊼ ⨉")) + ("union" . ,(lean-input-to-string-list "∪⋃∨⋁⋎⨈⊔⨆⋓∐⨿⊽⊻⊍⨃⊎⨄⊌∑⅀")) + + ("and" . ("∧")) ("or" . ("∨")) + ("And" . ("⋀")) ("Or" . ("⋁")) + ("i" . ("∩")) ("un" . ("∪")) ("u+" . ("⊎")) ("u." . ("⊍")) + ("I" . ("⋂")) ("Un" . ("⋃")) ("U+" . ("⨄")) ("U." . ("⨃")) + ("glb" . ("⊓")) ("lub" . ("⊔")) + ("Glb" . ("⨅")) ("Lub" . ("⨆")) + + ;; Entailment etc. + + ("entails" . ,(lean-input-to-string-list "⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯")) + + ("|-" . ("⊢")) ("|-n" . ("⊬")) + ("-|" . ("⊣")) + ("|=" . ("⊨")) ("|=n" . ("⊭")) + ("||-" . ("⊩")) ("||-n" . ("⊮")) + ("||=" . ("⊫")) ("||=n" . ("⊯")) + ("|||-" . ("⊪")) + + ;; Divisibility, parallelity. + + ("|" . ("∣")) ("|n" . ("∤")) + ("||" . ("∥")) ("||n" . ("∦")) + + ;; Some symbols from logic and set theory. + + ("all" . ("∀")) + ("ex" . ("∃")) + ("exn" . ("∄")) + ("0" . ("∅")) + ("empty" . ("∅")) + ("C" . ("∁")) + ("powerset" . ("𝒫")) + + ;; Corners, ceilings and floors. + + ("c" . ,(lean-input-to-string-list "⌜⌝⌞⌟⌈⌉⌊⌋")) + ("cu" . ,(lean-input-to-string-list "⌜⌝ ⌈⌉ ")) + ("cl" . ,(lean-input-to-string-list " ⌞⌟ ⌊⌋")) + + ("cul" . ("⌜")) ("cuL" . ("⌈")) + ("cur" . ("⌝")) ("cuR" . ("⌉")) + ("cll" . ("⌞")) ("clL" . ("⌊")) + ("clr" . ("⌟")) ("clR" . ("⌋")) + + ;; Various operators/symbols. + ("tr" . ,(lean-input-to-string-list "⬝▹")) + ("trans" . ,(lean-input-to-string-list "▹⬝")) + ("transport" . ("▹")) + ("con" . ("⬝")) + ("cdot" . ("⬝")) + ("dot" . ("⬝")) + ("sy" . ("⁻¹")) + ("inv" . ("⁻¹")) + ("-1" . ("⁻¹" "₋₁")) + ("-1e" . ("⁻¹ᵉ")) + ("-1f" . ("⁻¹ᶠ")) + ("-1g" . ("⁻¹ᵍ")) + ("-1h" . ("⁻¹ʰ")) + ("-1i" . ("⁻¹ⁱ")) + ("-1m" . ("⁻¹ᵐ")) + ("-1o" . ("⁻¹ᵒ")) + ("-1r" . ("⁻¹ʳ")) + ("-1p" . ("⁻¹ᵖ")) + ("-1s" . ("⁻¹ˢ")) + ("-1v" . ("⁻¹ᵛ")) + ("-1E" . ("⁻¹ᴱ")) + ("-2" . ("⁻²" "₋₂")) + ("-2o" . ("⁻²ᵒ")) + ("-3" . ("⁻³")) + ("qed" . ("∎")) + ("x" . ("×")) + ("o" . ("∘")) + ("comp" . ("∘")) + ("." . ("∙")) + ("*" . ("⋆")) + (".+" . ("∔")) + (".-" . ("∸")) + (":" . ("∶")) + ("::" . ("∷")) + ("::-" . ("∺")) + ("-:" . ("∹")) + ("+ " . ("⊹")) + ("surd3" . ("∛")) + ("surd4" . ("∜")) + ("increment" . ("∆")) + ("inf" . ("∞")) + ("&" . ("⅋")) + ("op" . ("ᵒᵖ")) + ("opf" . ("ᵒᵖᶠ")) + + ;; Circled operators. + + ("o+" . ("⊕")) + ("o--" . ("⊖")) + ("ox" . ("⊗")) + ("o/" . ("⊘")) + ("o." . ("⊙")) + ("oo" . ("⊚")) + ("o*" . ("∘*" "⊛")) + ("o=" . ("⊜")) + ("o-" . ("⊝")) + + ("O+" . ("⨁")) + ("Ox" . ("⨂")) + ("O." . ("⨀")) + ("O*" . ("⍟")) + + ;; Boxed operators. + + ("b+" . ("⊞")) + ("b-" . ("⊟")) + ("bx" . ("⊠")) + ("b." . ("⊡")) + + ;; Various symbols. + + ("integral" . ,(lean-input-to-string-list "∫∬∭∮∯∰∱∲∳")) + ("angle" . ,(lean-input-to-string-list "∟∡∢⊾⊿")) + ("join" . ,(lean-input-to-string-list "⋈⋉⋊⋋⋌⨝⟕⟖⟗")) + + ;; Arrows. + ("iff" . ("↔")) ("imp" . ("→")) + ("l" . ,(lean-input-to-string-list "←⇐⇚⇇⇆↤⇦↞↼↽⇠⇺↜⇽⟵⟸↚⇍⇷ ↹ ↢↩↫⇋⇜⇤⟻⟽⤆↶↺⟲ ")) + ("r" . ,(lean-input-to-string-list "→⇒⇛⇉⇄↦⇨↠⇀⇁⇢⇻↝⇾⟶⟹↛⇏⇸⇶ ↴ ↣↪↬⇌⇝⇥⟼⟾⤇↷↻⟳⇰⇴⟴⟿ ➵➸➙➔➛➜➝➞➟➠➡➢➣➤➧➨➩➪➫➬➭➮➯➱➲➳➺➻➼➽➾⊸")) + ("u" . ,(lean-input-to-string-list "↑⇑⟰⇈⇅↥⇧↟↿↾⇡⇞ ↰↱➦ ⇪⇫⇬⇭⇮⇯ ")) + ("d" . ,(lean-input-to-string-list "↓⇓⟱⇊⇵↧⇩↡⇃⇂⇣⇟ ↵↲↳➥ ↯ ")) + ("ud" . ,(lean-input-to-string-list "↕⇕ ↨⇳ ")) + ("lr" . ,(lean-input-to-string-list "↔⇔ ⇼↭⇿⟷⟺↮⇎⇹ ")) + ("ul" . ,(lean-input-to-string-list "↖⇖ ⇱↸ ")) + ("ur" . ,(lean-input-to-string-list "↗⇗ ➶➹➚ ")) + ("dr" . ,(lean-input-to-string-list "↘⇘ ⇲ ➴➷➘ ")) + ("dl" . ,(lean-input-to-string-list "↙⇙ ")) + ("==>" . ("⟹")) ("nattrans" . ("⟹")) ("nat_trans" . ("⟹")) + + ("l-" . ("←")) ("<-" . ("←")) ("l=" . ("⇐")) + ("r-" . ("→")) ("->" . ("→")) ("r=" . ("⇒")) ("=>" . ("⇒")) ("functor" . ("⇒")) + ("u-" . ("↑")) ("u=" . ("⇑")) + ("d-" . ("↓")) ("d=" . ("⇓")) + ("ud-" . ("↕")) ("ud=" . ("⇕")) + ("lr-" . ("↔")) ("<->" . ("↔")) ("lr=" . ("⇔")) ("<=>" . ("⇔")) + ("ul-" . ("↖")) ("ul=" . ("⇖")) + ("ur-" . ("↗")) ("ur=" . ("⇗")) + ("dr-" . ("↘")) ("dr=" . ("⇘")) + ("dl-" . ("↙")) ("dl=" . ("⇙")) + + ("l==" . ("⇚")) ("l-2" . ("⇇")) ("l-r-" . ("⇆")) + ("r==" . ("⇛")) ("r-2" . ("⇉")) ("r-3" . ("⇶")) ("r-l-" . ("⇄")) + ("u==" . ("⟰")) ("u-2" . ("⇈")) ("u-d-" . ("⇅")) + ("d==" . ("⟱")) ("d-2" . ("⇊")) ("d-u-" . ("⇵")) + + ("l--" . ("⟵")) ("<--" . ("⟵")) ("l~" . ("↜" "⇜")) + ("r--" . ("⟶")) ("-->" . ("⟶")) ("r~" . ("↝" "⇝" "⟿")) ("hom" . ("⟶")) + ("lr--" . ("⟷")) ("<-->" . ("⟷")) ("lr~" . ("↭")) + + ("l-n" . ("↚")) ("<-n" . ("↚")) ("l=n" . ("⇍")) + ("r-n" . ("↛")) ("->n" . ("↛")) ("r=n" . ("⇏")) ("=>n" . ("⇏")) + ("lr-n" . ("↮")) ("<->n" . ("↮")) ("lr=n" . ("⇎")) ("<=>n" . ("⇎")) + + ("l-|" . ("↤")) ("ll-" . ("↞")) + ("r-|" . ("↦")) ("rr-" . ("↠")) + ("u-|" . ("↥")) ("uu-" . ("↟")) + ("d-|" . ("↧")) ("dd-" . ("↡")) + ("ud-|" . ("↨")) + + ("l->" . ("↢")) + ("r->" . ("↣")) + + ("r-o" . ("⊸")) ("-o" . ("⊸")) + + ("dz" . ("↯")) + + ;; Ellipsis. + + ("..." . ,(lean-input-to-string-list "⋯⋮⋰⋱")) + + ;; Box-drawing characters. + + ("---" . ,(lean-input-to-string-list "─│┌┐└┘├┤┬┼┴╴╵╶╷╭╮╯╰╱╲╳")) + ("--=" . ,(lean-input-to-string-list "═║╔╗╚╝╠╣╦╬╩ ╒╕╘╛╞╡╤╪╧ ╓╖╙╜╟╢╥╫╨")) + ("--_" . ,(lean-input-to-string-list "━┃┏┓┗┛┣┫┳╋┻╸╹╺╻ + ┍┯┑┕┷┙┝┿┥┎┰┒┖┸┚┠╂┨┞╀┦┟╁┧┢╈┪┡╇┩ + ┮┭┶┵┾┽┲┱┺┹╊╉╆╅╄╃ ╿╽╼╾")) + ("--." . ,(lean-input-to-string-list "╌╎┄┆┈┊ + ╍╏┅┇┉┋")) + + ;; Triangles. + + ;; Big/small, black/white. + + ("t" . ,(lean-input-to-string-list "▸▹►▻◂◃◄◅▴▵▾▿◢◿◣◺◤◸◥◹")) + ("Tr" . ,(lean-input-to-string-list "◀◁▶▷▲△▼▽◬◭◮")) + + ("tb" . ,(lean-input-to-string-list "◂▸▴▾◄►◢◣◤◥")) + ("tw" . ,(lean-input-to-string-list "◃▹▵▿◅▻◿◺◸◹")) + + ("Tb" . ,(lean-input-to-string-list "◀▶▲▼")) + ("Tw" . ,(lean-input-to-string-list "◁▷△▽")) + + ;; Squares. + + ("sq" . ,(lean-input-to-string-list "◾◽■□◼◻▣▢▤▥▦▧▨▩◧◨◩◪◫◰◱◲◳")) + ("sqb" . ,(lean-input-to-string-list "■◼◾")) + ("sqw" . ,(lean-input-to-string-list "□◻◽")) + ("sq." . ("▣")) + ("sqo" . ("▢")) + + ;; Rectangles. + + ("re" . ,(lean-input-to-string-list "▬▭▮▯")) + ("reb" . ,(lean-input-to-string-list "▬▮")) + ("rew" . ,(lean-input-to-string-list "▭▯")) + + ;; Parallelograms. + + ("pa" . ,(lean-input-to-string-list "▰▱")) + ("pab" . ("▰")) + ("paw" . ("▱")) + + ;; Diamonds. + + ("di" . ,(lean-input-to-string-list "◆◇◈")) + ("dib" . ("◆")) + ("diw" . ("◇")) + ("di." . ("◈")) + + ;; Circles. + + ("ci" . ,(lean-input-to-string-list "●○◎◌◯◍◐◑◒◓◔◕◖◗◠◡◴◵◶◷⚆⚇⚈⚉")) + ("cib" . ("●")) + ("ciw" . ("○")) + ("ci." . ("◎")) + ("ci.." . ("◌")) + ("ciO" . ("◯")) + + ;; Stars. + + ("st" . ,(lean-input-to-string-list "⋆✦✧✶✴✹ ★☆✪✫✯✰✵✷✸")) + ("st4" . ,(lean-input-to-string-list "✦✧")) + ("st6" . ("✶")) + ("st8" . ("✴")) + ("st12" . ("✹")) + + ;; Blackboard bold letters. + + ("bn" . ("ℕ")) + ("bz" . ("ℤ")) + ("bq" . ("ℚ")) + ("br" . ("ℝ")) + ("bc" . ("ℂ")) + ("bp" . ("ℙ")) + ("bb" . ("𝔹")) + ("bsum" . ("⅀")) + + ;; Blackboard bold numbers. + + ("b0" . ("𝟘")) + ("b1" . ("𝟙")) + ("b2" . ("𝟚")) + ("b3" . ("𝟛")) + ("b4" . ("𝟜")) + ("b5" . ("𝟝")) + ("b6" . ("𝟞")) + ("b7" . ("𝟟")) + ("b8" . ("𝟠")) + ("b9" . ("𝟡")) + + ;; Parentheses. + + ("(" . ,(lean-input-to-string-list "([{⁅⁽₍〈⎴⟅⟦⟨⟪⦃〈《‹«「『【〔〖〚︵︷︹︻︽︿﹁﹃﹙﹛﹝([{「")) + (")" . ,(lean-input-to-string-list ")]}⁆⁾₎〉⎵⟆⟧⟩⟫⦄〉》›»」』】〕〗〛︶︸︺︼︾﹀﹂﹄﹚﹜﹞)]}」")) + + ("[[" . ("⟦")) + ("]]" . ("⟧")) + ("<" . ("⟨")) + (">" . ("⟩")) + ("<<" . ("⟪")) + (">>" . ("⟫")) + ("f<" . ("‹")) + ("f>" . ("›")) + ("f<<" . ("«")) + ("f>>" . ("»")) + ("{{" . ("⦃")) + ("}}" . ("⦄")) + + ("(b" . ("⟅")) + (")b" . ("⟆")) + + ("lbag" . ("⟅")) + ("rbag" . ("⟆")) + + ;; lambda + + ("fun" . ("λ")) + ("lam" . ("λ")) + + ("X" . ("⨯")) + + ;; Primes. + + ("'" . ,(lean-input-to-string-list "′″‴⁗")) + ("`" . ,(lean-input-to-string-list "‵‶‷")) + + ;; Fractions. + + ("frac" . ,(lean-input-to-string-list "¼½¾⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟")) + + ;; Bullets. + + ("bu" . ,(lean-input-to-string-list "•◦‣⁌⁍")) + ("bub" . ("•")) + ("buw" . ("◦")) + ("but" . ("‣")) + + ;; Types + ("nat" . ("ℕ")) + ("Nat" . ("ℕ")) + ("N" . ("ℕ")) + ("N-2" . ("ℕ₋₂")) + ("N-1" . ("ℕ₋₁")) + ("int" . ("ℤ")) + ("Int" . ("ℤ")) + ("Z" . ("ℤ")) + ("rat" . ("ℚ")) + ("Rat" . ("ℚ")) + ("Q" . ("ℚ")) + ("real" . ("ℝ")) + ("Real" . ("ℝ")) + ("R" . ("ℝ")) + ("Com" . ("ℂ")) + ("com" . ("ℂ")) + ("C" . ("ℂ")) + ("A" . ("𝔸")) + ("F" . ("𝔽")) + ("H" . ("ℍ")) + ("K" . ("𝕂")) + + ("a" . ("α")) + ("b" . ("β")) + ("g" . ("γ")) + + ;; Musical symbols. + + ("note" . ,(lean-input-to-string-list "♩♪♫♬")) + ("flat" . ("♭")) + ("#" . ("♯")) + + ;; Other punctuation and symbols. + + ("\\" . ("\\")) + ("en" . ("–")) + ("em" . ("—")) + ("^i" . ("ⁱ")) + ("^o" . ("ᵒ")) + ("!!" . ("‼")) + ("??" . ("⁇")) + ("?!" . ("‽" "⁈")) + ("!?" . ("⁉")) + ("die" . ,(lean-input-to-string-list "⚀⚁⚂⚃⚄⚅")) + ("asterisk" . ,(lean-input-to-string-list "⁎⁑⁂✢✣✤✥✱✲✳✺✻✼✽❃❉❊❋")) + ("8<" . ("✂" "✄")) + ("tie" . ("⁀")) + ("undertie" . ("‿")) + ("apl" . ,(lean-input-to-string-list "⌶⌷⌸⌹⌺⌻⌼⌽⌾⌿⍀⍁⍂⍃⍄⍅⍆⍇⍈ + ⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛ + ⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮ + ⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⎕")) + + ;; Some combining characters. + ;; + ;; The following combining characters also have (other) + ;; translations: + ;; ̀ ́ ̂ ̃ ̄ ̆ ̇ ̈ ̋ ̌ ̣ ̧ ̱ + + ("^--" . ,(lean-input-to-string-list"̅̿")) + ("_--" . ,(lean-input-to-string-list"̲̳")) + ("^~" . ,(lean-input-to-string-list"̃͌")) + ("_~" . ( "̰")) + ("^." . ,(lean-input-to-string-list"̇̈⃛⃜")) + ("_." . ,(lean-input-to-string-list"̣̤")) + ("^l" . ,(lean-input-to-string-list"⃖⃐⃔")) + ("^l-" . ( "⃖")) + ("^r" . ,(lean-input-to-string-list"⃗⃑⃕")) + ("^r-" . ( "⃗")) + ("^lr" . ( "⃡")) + ("_lr" . ( "͍")) + ("^^" . ,(lean-input-to-string-list"̂̑͆")) + ("_^" . ,(lean-input-to-string-list"̭̯̪")) + ("^v" . ,(lean-input-to-string-list"̌̆")) + ("_v" . ,(lean-input-to-string-list"̬̮̺")) + + ;; Shorter forms of many greek letters plus ƛ. + + ("Ga" . ("α")) ("GA" . ("Α")) + ("Gb" . ("β")) ("GB" . ("Β")) + ("Gg" . ("γ")) ("GG" . ("Γ")) + ("Gd" . ("δ")) ("GD" . ("Δ")) + ("Ge" . ("ε")) ("GE" . ("Ε")) ("eps" . ("ε")) + ("Gz" . ("ζ")) ("GZ" . ("Ζ")) + ;; \eta \Eta + ("Gth" . ("θ")) ("GTH" . ("Θ")) ("th" . ("θ")) + ("Gi" . ("ι")) ("GI" . ("Ι")) + ("Gk" . ("κ")) ("GK" . ("Κ")) + ("Gl" . ("λ")) ("GL" . ("Λ")) ("Gl-" . ("ƛ")) + ("Gm" . ("μ")) ("GM" . ("Μ")) + ("Gn" . ("ν")) ("GN" . ("Ν")) + ("Gx" . ("ξ")) ("GX" . ("Ξ")) + ;; \omicron \Omicron + ;; \pi \Pi + ("Gr" . ("ρ")) ("GR" . ("Ρ")) + ("Gs" . ("σ")) ("GS" . ("Σ")) + ("Gt" . ("τ")) ("GT" . ("Τ")) + ("Gu" . ("υ")) ("GU" . ("Υ")) + ("Gf" . ("φ")) ("GF" . ("Φ")) + ("Gc" . ("χ")) ("GC" . ("Χ")) + ("Gp" . ("ψ")) ("GP" . ("Ψ")) + ("Go" . ("ω")) ("GO" . ("Ω")) + ;; even shorter versions for central type constructors + ("S" . ("Σ")) ("P" . ("Π")) + + ;; Mathematical characters + + ("MiA" . ("𝐴")) + ("MiB" . ("𝐵")) + ("MiC" . ("𝐶")) + ("MiD" . ("𝐷")) + ("MiE" . ("𝐸")) + ("MiF" . ("𝐹")) + ("MiG" . ("𝐺")) + ("MiH" . ("𝐻")) + ("MiI" . ("𝐼")) + ("MiJ" . ("𝐽")) + ("MiK" . ("𝐾")) + ("MiL" . ("𝐿")) + ("MiM" . ("𝑀")) + ("MiN" . ("𝑁")) + ("MiO" . ("𝑂")) + ("MiP" . ("𝑃")) + ("MiQ" . ("𝑄")) + ("MiR" . ("𝑅")) + ("MiS" . ("𝑆")) + ("MiT" . ("𝑇")) + ("MiU" . ("𝑈")) + ("MiV" . ("𝑉")) + ("MiW" . ("𝑊")) + ("MiX" . ("𝑋")) + ("MiY" . ("𝑌")) + ("MiZ" . ("𝑍")) + ("Mia" . ("𝑎")) + ("Mib" . ("𝑏")) + ("Mic" . ("𝑐")) + ("Mid" . ("𝑑")) + ("Mie" . ("𝑒")) + ("Mif" . ("𝑓")) + ("Mig" . ("𝑔")) + ("Mii" . ("𝑖")) + ("Mij" . ("𝑗")) + ("Mik" . ("𝑘")) + ("Mil" . ("𝑙")) + ("Mim" . ("𝑚")) + ("Min" . ("𝑛")) + ("Mio" . ("𝑜")) + ("Mip" . ("𝑝")) + ("Miq" . ("𝑞")) + ("Mir" . ("𝑟")) + ("Mis" . ("𝑠")) + ("Mit" . ("𝑡")) + ("Miu" . ("𝑢")) + ("Miv" . ("𝑣")) + ("Miw" . ("𝑤")) + ("Mix" . ("𝑥")) + ("Miy" . ("𝑦")) + ("Miz" . ("𝑧")) + ("MIA" . ("𝑨")) + ("MIB" . ("𝑩")) + ("MIC" . ("𝑪")) + ("MID" . ("𝑫")) + ("MIE" . ("𝑬")) + ("MIF" . ("𝑭")) + ("MIG" . ("𝑮")) + ("MIH" . ("𝑯")) + ("MII" . ("𝑰")) + ("MIJ" . ("𝑱")) + ("MIK" . ("𝑲")) + ("MIL" . ("𝑳")) + ("MIM" . ("𝑴")) + ("MIN" . ("𝑵")) + ("MIO" . ("𝑶")) + ("MIP" . ("𝑷")) + ("MIQ" . ("𝑸")) + ("MIR" . ("𝑹")) + ("MIS" . ("𝑺")) + ("MIT" . ("𝑻")) + ("MIU" . ("𝑼")) + ("MIV" . ("𝑽")) + ("MIW" . ("𝑾")) + ("MIX" . ("𝑿")) + ("MIY" . ("𝒀")) + ("MIZ" . ("𝒁")) + ("MIa" . ("𝒂")) + ("MIb" . ("𝒃")) + ("MIc" . ("𝒄")) + ("MId" . ("𝒅")) + ("MIe" . ("𝒆")) + ("MIf" . ("𝒇")) + ("MIg" . ("𝒈")) + ("MIh" . ("𝒉")) + ("MIi" . ("𝒊")) + ("MIj" . ("𝒋")) + ("MIk" . ("𝒌")) + ("MIl" . ("𝒍")) + ("MIm" . ("𝒎")) + ("MIn" . ("𝒏")) + ("MIo" . ("𝒐")) + ("MIp" . ("𝒑")) + ("MIq" . ("𝒒")) + ("MIr" . ("𝒓")) + ("MIs" . ("𝒔")) + ("MIt" . ("𝒕")) + ("MIu" . ("𝒖")) + ("MIv" . ("𝒗")) + ("MIw" . ("𝒘")) + ("MIx" . ("𝒙")) + ("MIy" . ("𝒚")) + ("MIz" . ("𝒛")) + ("McA" . ("𝒜")) + ("McC" . ("𝒞")) + ("McD" . ("𝒟")) + ("McG" . ("𝒢")) + ("McJ" . ("𝒥")) + ("McK" . ("𝒦")) + ("McN" . ("𝒩")) + ("McO" . ("𝒪")) + ("McP" . ("𝒫")) + ("McQ" . ("𝒬")) + ("McS" . ("𝒮")) + ("McT" . ("𝒯")) + ("McU" . ("𝒰")) + ("McV" . ("𝒱")) + ("McW" . ("𝒲")) + ("McX" . ("𝒳")) + ("McY" . ("𝒴")) + ("McZ" . ("𝒵")) + ("Mca" . ("𝒶")) + ("Mcb" . ("𝒷")) + ("Mcc" . ("𝒸")) + ("Mcd" . ("𝒹")) + ("Mcf" . ("𝒻")) + ("Mch" . ("𝒽")) + ("Mci" . ("𝒾")) + ("Mcj" . ("𝒿")) + ("Mck" . ("𝓀")) + ("Mcl" . ("𝓁")) + ("Mcm" . ("𝓂")) + ("Mcn" . ("𝓃")) + ("Mcp" . ("𝓅")) + ("Mcq" . ("𝓆")) + ("Mcr" . ("𝓇")) + ("Mcs" . ("𝓈")) + ("Mct" . ("𝓉")) + ("Mcu" . ("𝓊")) + ("Mcv" . ("𝓋")) + ("Mcw" . ("𝓌")) + ("Mcx" . ("𝓍")) + ("Mcy" . ("𝓎")) + ("Mcz" . ("𝓏")) + ("MCA" . ("𝓐")) + ("MCB" . ("𝓑")) + ("MCC" . ("𝓒")) + ("MCD" . ("𝓓")) + ("MCE" . ("𝓔")) + ("MCF" . ("𝓕")) + ("MCG" . ("𝓖")) + ("MCH" . ("𝓗")) + ("MCI" . ("𝓘")) + ("MCJ" . ("𝓙")) + ("MCK" . ("𝓚")) + ("MCL" . ("𝓛")) + ("MCM" . ("𝓜")) + ("MCN" . ("𝓝")) + ("MCO" . ("𝓞")) + ("MCP" . ("𝓟")) + ("MCQ" . ("𝓠")) + ("MCR" . ("𝓡")) + ("MCS" . ("𝓢")) + ("MCT" . ("𝓣")) + ("MCU" . ("𝓤")) + ("MCV" . ("𝓥")) + ("MCW" . ("𝓦")) + ("MCX" . ("𝓧")) + ("MCY" . ("𝓨")) + ("MCZ" . ("𝓩")) + ("MCa" . ("𝓪")) + ("MCb" . ("𝓫")) + ("MCc" . ("𝓬")) + ("MCd" . ("𝓭")) + ("MCe" . ("𝓮")) + ("MCf" . ("𝓯")) + ("MCg" . ("𝓰")) + ("MCh" . ("𝓱")) + ("MCi" . ("𝓲")) + ("MCj" . ("𝓳")) + ("MCk" . ("𝓴")) + ("MCl" . ("𝓵")) + ("MCm" . ("𝓶")) + ("MCn" . ("𝓷")) + ("MCo" . ("𝓸")) + ("MCp" . ("𝓹")) + ("MCq" . ("𝓺")) + ("MCr" . ("𝓻")) + ("MCs" . ("𝓼")) + ("MCt" . ("𝓽")) + ("MCu" . ("𝓾")) + ("MCv" . ("𝓿")) + ("MCw" . ("𝔀")) + ("MCx" . ("𝔁")) + ("MCy" . ("𝔂")) + ("MCz" . ("𝔃")) + ("MfA" . ("𝔄")) + ("MfB" . ("𝔅")) + ("MfD" . ("𝔇")) + ("MfE" . ("𝔈")) + ("MfF" . ("𝔉")) + ("MfG" . ("𝔊")) + ("MfJ" . ("𝔍")) + ("MfK" . ("𝔎")) + ("MfL" . ("𝔏")) + ("MfM" . ("𝔐")) + ("MfN" . ("𝔑")) + ("MfO" . ("𝔒")) + ("MfP" . ("𝔓")) + ("MfQ" . ("𝔔")) + ("MfS" . ("𝔖")) + ("MfT" . ("𝔗")) + ("MfU" . ("𝔘")) + ("MfV" . ("𝔙")) + ("MfW" . ("𝔚")) + ("MfX" . ("𝔛")) + ("MfY" . ("𝔜")) + ("Mfa" . ("𝔞")) + ("Mfb" . ("𝔟")) + ("Mfc" . ("𝔠")) + ("Mfd" . ("𝔡")) + ("Mfe" . ("𝔢")) + ("Mff" . ("𝔣")) + ("Mfg" . ("𝔤")) + ("Mfh" . ("𝔥")) + ("Mfi" . ("𝔦")) + ("Mfj" . ("𝔧")) + ("Mfk" . ("𝔨")) + ("Mfl" . ("𝔩")) + ("Mfm" . ("𝔪")) + ("Mfn" . ("𝔫")) + ("Mfo" . ("𝔬")) + ("Mfp" . ("𝔭")) + ("Mfq" . ("𝔮")) + ("Mfr" . ("𝔯")) + ("Mfs" . ("𝔰")) + ("Mft" . ("𝔱")) + ("Mfu" . ("𝔲")) + ("Mfv" . ("𝔳")) + ("Mfw" . ("𝔴")) + ("Mfx" . ("𝔵")) + ("Mfy" . ("𝔶")) + ("Mfz" . ("𝔷")) + + ;; Some ISO8859-1 characters. + + (" " . (" ")) + ("!" . ("¡")) + ("cent" . ("¢")) + ("brokenbar" . ("¦")) + ("degree" . ("°")) + ("?" . ("¿")) + ("^a_" . ("ª")) + ("^o_" . ("º")) + + ;; Circled, parenthesised etc. numbers and letters. + + ( "(0)" . ,(lean-input-to-string-list " ⓪")) + ( "(1)" . ,(lean-input-to-string-list "⑴①⒈❶➀➊")) + ( "(2)" . ,(lean-input-to-string-list "⑵②⒉❷➁➋")) + ( "(3)" . ,(lean-input-to-string-list "⑶③⒊❸➂➌")) + ( "(4)" . ,(lean-input-to-string-list "⑷④⒋❹➃➍")) + ( "(5)" . ,(lean-input-to-string-list "⑸⑤⒌❺➄➎")) + ( "(6)" . ,(lean-input-to-string-list "⑹⑥⒍❻➅➏")) + ( "(7)" . ,(lean-input-to-string-list "⑺⑦⒎❼➆➐")) + ( "(8)" . ,(lean-input-to-string-list "⑻⑧⒏❽➇➑")) + ( "(9)" . ,(lean-input-to-string-list "⑼⑨⒐❾➈➒")) + ("(10)" . ,(lean-input-to-string-list "⑽⑩⒑❿➉➓")) + ("(11)" . ,(lean-input-to-string-list "⑾⑪⒒")) + ("(12)" . ,(lean-input-to-string-list "⑿⑫⒓")) + ("(13)" . ,(lean-input-to-string-list "⒀⑬⒔")) + ("(14)" . ,(lean-input-to-string-list "⒁⑭⒕")) + ("(15)" . ,(lean-input-to-string-list "⒂⑮⒖")) + ("(16)" . ,(lean-input-to-string-list "⒃⑯⒗")) + ("(17)" . ,(lean-input-to-string-list "⒄⑰⒘")) + ("(18)" . ,(lean-input-to-string-list "⒅⑱⒙")) + ("(19)" . ,(lean-input-to-string-list "⒆⑲⒚")) + ("(20)" . ,(lean-input-to-string-list "⒇⑳⒛")) + + ("(a)" . ,(lean-input-to-string-list "⒜Ⓐⓐ")) + ("(b)" . ,(lean-input-to-string-list "⒝Ⓑⓑ")) + ("(c)" . ,(lean-input-to-string-list "⒞Ⓒⓒ")) + ("(d)" . ,(lean-input-to-string-list "⒟Ⓓⓓ")) + ("(e)" . ,(lean-input-to-string-list "⒠Ⓔⓔ")) + ("(f)" . ,(lean-input-to-string-list "⒡Ⓕⓕ")) + ("(g)" . ,(lean-input-to-string-list "⒢Ⓖⓖ")) + ("(h)" . ,(lean-input-to-string-list "⒣Ⓗⓗ")) + ("(i)" . ,(lean-input-to-string-list "⒤Ⓘⓘ")) + ("(j)" . ,(lean-input-to-string-list "⒥Ⓙⓙ")) + ("(k)" . ,(lean-input-to-string-list "⒦Ⓚⓚ")) + ("(l)" . ,(lean-input-to-string-list "⒧Ⓛⓛ")) + ("(m)" . ,(lean-input-to-string-list "⒨Ⓜⓜ")) + ("(n)" . ,(lean-input-to-string-list "⒩Ⓝⓝ")) + ("(o)" . ,(lean-input-to-string-list "⒪Ⓞⓞ")) + ("(p)" . ,(lean-input-to-string-list "⒫Ⓟⓟ")) + ("(q)" . ,(lean-input-to-string-list "⒬Ⓠⓠ")) + ("(r)" . ,(lean-input-to-string-list "⒭Ⓡⓡ")) + ("(s)" . ,(lean-input-to-string-list "⒮Ⓢⓢ")) + ("(t)" . ,(lean-input-to-string-list "⒯Ⓣⓣ")) + ("(u)" . ,(lean-input-to-string-list "⒰Ⓤⓤ")) + ("(v)" . ,(lean-input-to-string-list "⒱Ⓥⓥ")) + ("(w)" . ,(lean-input-to-string-list "⒲Ⓦⓦ")) + ("(x)" . ,(lean-input-to-string-list "⒳Ⓧⓧ")) + ("(y)" . ,(lean-input-to-string-list "⒴Ⓨⓨ")) + ("(z)" . ,(lean-input-to-string-list "⒵Ⓩⓩ")) + ("y" . ("ɏ")) + + + )) + "A list of translations specific to the Lean input method. +Each element is a pair (KEY-SEQUENCE-STRING . LIST-OF-TRANSLATION-STRINGS). +All the translation strings are possible translations +of the given key sequence; if there is more than one you can choose +between them using the arrow keys. + +Note that if you customize this setting you will not +automatically benefit (or suffer) from modifications to its +default value when the library is updated. If you just want to +add some bindings it is probably a better idea to customize +`lean-input-user-translations'. + +These translation pairs are included after those in +`lean-input-user-translations', but before the ones inherited +from other input methods (see `lean-input-inherit'). + +If you change this setting manually (without using the +customization buffer) you need to call `lean-input-setup' in +order for the change to take effect." + :group 'lean-input + :set 'lean-input-incorporate-changed-setting + :initialize 'custom-initialize-default + :type '(repeat (cons (string :tag "Key sequence") + (repeat :tag "Translations" string)))) + +(defcustom lean-input-user-translations nil + "Like `lean-input-translations', but more suitable for user +customizations since by default it is empty. + +These translation pairs are included first, before those in +`lean-input-translations' and the ones inherited from other input +methods." + :group 'lean-input + :set 'lean-input-incorporate-changed-setting + :initialize 'custom-initialize-default + :type '(repeat (cons (string :tag "Key sequence") + (repeat :tag "Translations" string)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Inspecting and modifying translation maps + +(defun lean-input-get-translations (qp) + "Return a list containing all translations from the Quail +package QP (except for those corresponding to ASCII). +Each pair in the list has the form (KEY-SEQUENCE . TRANSLATION)." + (with-temp-buffer + (activate-input-method qp) ; To make sure that the package is loaded. + (unless (quail-package qp) + (error "%s is not a Quail package." qp)) + (let ((decode-map (list 'decode-map))) + (quail-build-decode-map (list (quail-map)) "" decode-map 0) + (cdr decode-map)))) + +(defun lean-input-show-translations (qp) + "Display all translations used by the Quail package QP (a string). +\(Except for those corresponding to ASCII)." + (interactive (list (read-input-method-name + "Quail input method (default %s): " "Lean"))) + (let ((buf (concat "*" qp " input method translations*"))) + (with-output-to-temp-buffer buf + (with-current-buffer buf + (quail-insert-decode-map + (cons 'decode-map (lean-input-get-translations qp))))))) + +(defun lean-input-add-translations (trans) + "Add the given translations TRANS to the Lean input method. +TRANS is a list of pairs (KEY-SEQUENCE . TRANSLATION). The +translations are appended to the current translations." + (with-temp-buffer + (dolist (tr (lean-input-concat-map (eval lean-input-tweak-all) trans)) + (quail-defrule (car tr) (cdr tr) "Lean" t)))) + +(defun lean-input-inherit-package (qp &optional fun) + "Let the Lean input method inherit the translations from the +Quail package QP (except for those corresponding to ASCII). + +The optional function FUN can be used to modify the translations. +It is given a pair (KEY-SEQUENCE . TRANSLATION) and should return +a list of such pairs." + (let ((trans (lean-input-get-translations qp))) + (lean-input-add-translations + (if fun (lean-input-concat-map fun trans) + trans)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Setting up the input method + +(defun lean-input-setup () + "Set up the Lean input method based on the customisable +variables and underlying input methods." + + ;; Create (or reset) the input method. + (with-temp-buffer + (quail-define-package "Lean" "UTF-8" "∏" t ; guidance + "Lean input method. +The purpose of this input method is to edit Lean programs, but +since it is highly customisable it can be made useful for other +tasks as well." + nil nil nil nil nil nil t ; maximum-shortest + )) + + (lean-input-add-translations + (mapcar (lambda (tr) (cons (car tr) (vconcat (cdr tr)))) + (append lean-input-user-translations + lean-input-translations))) + (dolist (def lean-input-inherit) + (lean-input-inherit-package (car def) + (eval (cdr def))))) + +(defun lean-input-incorporate-changed-setting (sym val) + "Update the Lean input method based on the customisable +variables and underlying input methods. +Suitable for use in the :set field of `defcustom'." + (set-default sym val) + (lean-input-setup)) + +;; Set up the input method. + +(lean-input-setup) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Administrative details + +(provide 'lean-input) +;;; lean-input.el ends here + +(defun lean-input-export-translations () + "Export the current translation, (input, output) pairs for +input-method, in a javascript format. It can be copy-pasted to +leanprover.github.io/tutorial/js/input-method.js" + (interactive) + (with-current-buffer + (get-buffer-create "*lean-translations*") + (let ((exclude-list '("\\newline"))) + (insert "var corrections = {") + (--each + (--filter (not (member (car it) exclude-list)) + (lean-input-get-translations "Lean")) + (let* ((input (substring (car it) 1)) + (outputs (cdr it))) + (insert (format "%s:\"" (prin1-to-string input))) + (cond ((vectorp outputs) + (insert (elt outputs 0))) + (t (insert-char outputs))) + (insert (format "\",\n" input)))) + (insert "};")))) + +(defun lean-input-export-translations-to-stdout () + (lean-input-export-translations) + (with-current-buffer "*lean-translations*" + (princ (buffer-string)))) diff --git a/lean-mode/lean-leanpkg.el b/lean-mode/lean-leanpkg.el new file mode 100644 index 0000000000..a959ae0490 --- /dev/null +++ b/lean-mode/lean-leanpkg.el @@ -0,0 +1,96 @@ +;; -*- lexical-binding: t -*- +;; +;; Copyright (c) 2017 Microsoft Corporation. All rights reserved. +;; Released under Apache 2.0 license as described in the file LICENSE. +;; +;; Author: Gabriel Ebner +;; + +(require 's) +(require 'json) +(require 'lean-util) + +(defun lean-leanpkg-find-dir-in (dir) + (when dir + (or (lean-leanpkg-find-dir-in (f-parent dir)) + (when (f-exists? (f-join dir "leanpkg.toml")) dir)))) + +(defun lean-leanpkg-find-dir () + (and (buffer-file-name) + (lean-leanpkg-find-dir-in (f-dirname (buffer-file-name))))) + +(defun lean-leanpkg-find-dir-safe () + (or (lean-leanpkg-find-dir) + (error (format "cannot find leanpkg.toml for %s" (buffer-file-name))))) + +(defun lean-leanpkg-executable () + (lean-get-executable "leanpkg")) + +(defvar lean-leanpkg-running nil) +(defvar-local lean-leanpkg-configure-prompt-shown nil) + +(defun lean-leanpkg-run (cmd &optional restart-lean-server) + "Call `leanpkg $cmd`" + (let ((dir (file-name-as-directory (lean-leanpkg-find-dir-safe))) + (orig-buf (current-buffer))) + (with-current-buffer (get-buffer-create "*leanpkg*") + (let ((inhibit-read-only t)) (erase-buffer)) + (switch-to-buffer-other-window (current-buffer)) + (redisplay) + (insert (format "> leanpkg %s\n" cmd)) + (setq lean-leanpkg-running t) + (let* ((default-directory dir) + (out-buf (current-buffer)) + (proc (start-process "leanpkg" (current-buffer) + (lean-leanpkg-executable) cmd))) + (comint-mode) + (set-process-filter proc #'comint-output-filter) + (set-process-sentinel + proc (lambda (_p _e) + (setq lean-leanpkg-running nil) + (when restart-lean-server + (with-current-buffer out-buf + (insert "; restarting lean server\n")) + (with-current-buffer orig-buf + (lean-server-restart))) + (with-current-buffer out-buf + (insert "; done")))))))) + +(defun lean-leanpkg-configure () + "Call leanpkg configure" + (interactive) + (lean-leanpkg-run "configure" 't)) + +(defun lean-leanpkg-build () + "Call leanpkg build" + (interactive) + (lean-leanpkg-run "build")) + +(defun lean-leanpkg-test () + "Call leanpkg test" + (interactive) + (lean-leanpkg-run "test")) + +(defun lean-leanpkg-find-path-file () + (let* ((json-object-type 'plist) (json-array-type 'list) (json-false nil) + (path-json (shell-command-to-string + (concat (shell-quote-argument (lean-get-executable lean-executable-name)) + " -p"))) + (path-out (json-read-from-string path-json))) + (when (and (eq major-mode 'lean-mode) + (plist-get path-out :is_user_leanpkg_path) + (not lean-leanpkg-running) + (not lean-leanpkg-configure-prompt-shown) + (setq lean-leanpkg-configure-prompt-shown t) + (lean-leanpkg-find-dir) + (y-or-n-p (format "Found leanpkg.toml in %s, call leanpkg configure?" (lean-leanpkg-find-dir)))) + (save-match-data ; running in timer so that we don't mess up the window layout + (run-at-time nil nil + (lambda (buf) + (with-current-buffer buf + (lean-leanpkg-configure))) + (current-buffer)))) + (setq lean-leanpkg-configure-prompt-shown t) + (plist-get path-out :leanpkg_path_file))) + +(provide 'lean-leanpkg) diff --git a/lean-mode/lean-mode.el b/lean-mode/lean-mode.el new file mode 100644 index 0000000000..1d6267adb3 --- /dev/null +++ b/lean-mode/lean-mode.el @@ -0,0 +1,240 @@ +;;; lean-mode.el --- A major mode for the Lean language -*- lexical-binding: t -*- + +;; Copyright (c) 2013, 2014 Microsoft Corporation. All rights reserved. +;; Copyright (c) 2014, 2015 Soonho Kong. All rights reserved. + +;; Author: Leonardo de Moura +;; Soonho Kong +;; Gabriel Ebner +;; Sebastian Ullrich +;; Maintainer: Sebastian Ullrich +;; Created: Jan 09, 2014 +;; Keywords: languages +;; Package-Requires: ((emacs "24.3") (dash "2.12.0") (dash-functional "1.2.0") (s "1.10.0") (f "0.19.0") (flycheck "30")) +;; URL: https://github.com/leanprover/lean-mode + +;; Released under Apache 2.0 license as described in the file LICENSE. + +;;; Commentary: + +;; Provides a major mode for the Lean programming language. + +;; Provides highlighting, diagnostics, goal visualization, +;; and many other useful features for Lean users. + +;; See the README.md for more advanced features and the +;; associated keybindings. + +;;; Code: + +(require 'cl-lib) +(require 'dash) +(require 'pcase) +(require 'flycheck) +(require 'lean-eri) +(require 'lean-util) +(require 'lean-settings) +(require 'lean-input) +(require 'lean-syntax) +(require 'lean-leanpkg) +(require 'lean-server) +(require 'lean-flycheck) +(require 'lean-info) +(require 'lean-hole) +(require 'lean-type) +(require 'lean-message-boxes) +(require 'lean-right-click) +(require 'lean-dev) + +(defun lean-compile-string (exe-name args file-name) + "Concatenate EXE-NAME, ARGS, and FILE-NAME." + (format "%s %s %s" exe-name args file-name)) + +(defun lean-create-temp-in-system-tempdir (file-name prefix) + "Create a temp lean file and return its name." + (make-temp-file (or prefix "flymake") nil (f-ext file-name))) + +(defun lean-execute (&optional arg) + "Execute Lean in the current buffer." + (interactive) + (when (called-interactively-p 'any) + (setq arg (read-string "arg: " arg))) + (let ((cc compile-command) + (target-file-name + (or + (buffer-file-name) + (flymake-init-create-temp-buffer-copy 'lean-create-temp-in-system-tempdir)))) + (compile (lean-compile-string + (shell-quote-argument (f-full (lean-get-executable lean-executable-name))) + (or arg "") + (shell-quote-argument (f-full target-file-name)))) + ;; restore old value + (setq compile-command cc))) + +(defun lean-std-exe () + (interactive) + (lean-execute)) + +(defun lean-check-expansion () + (interactive) + (save-excursion + (if (looking-at (rx symbol-start "_")) t + (if (looking-at "\\_>") t + (backward-char 1) + (if (looking-at "\\.") t + (backward-char 1) + (if (looking-at "->") t nil)))))) + +(defun lean-tab-indent () + (interactive) + (cond ((looking-back (rx line-start (* white)) nil) + (lean-eri-indent)) + (t (indent-for-tab-command)))) + +(defun lean-set-keys () + (local-set-key lean-keybinding-std-exe1 #'lean-std-exe) + (local-set-key lean-keybinding-std-exe2 #'lean-std-exe) + (local-set-key lean-keybinding-show-key #'quail-show-key) + (local-set-key lean-keybinding-server-restart #'lean-server-restart) + (local-set-key lean-keybinding-server-switch-version #'lean-server-switch-version) + (local-set-key lean-keybinding-find-definition #'lean-find-definition) + (local-set-key lean-keybinding-tab-indent #'lean-tab-indent) + (local-set-key lean-keybinding-hole #'lean-hole) + (local-set-key lean-keybinding-lean-toggle-show-goal #'lean-toggle-show-goal) + (local-set-key lean-keybinding-lean-toggle-next-error #'lean-toggle-next-error) + (local-set-key lean-keybinding-lean-message-boxes-toggle #'lean-message-boxes-toggle) + (local-set-key lean-keybinding-leanpkg-configure #'lean-leanpkg-configure) + (local-set-key lean-keybinding-leanpkg-build #'lean-leanpkg-build) + (local-set-key lean-keybinding-leanpkg-test #'lean-leanpkg-test) + ;; This only works as a mouse binding due to the event, so it is not abstracted + ;; to avoid user confusion. + (local-set-key (kbd "") #'lean-right-click-show-menu) + ) + +(define-abbrev-table 'lean-abbrev-table + '()) + +(defvar lean-mode-map (make-sparse-keymap) + "Keymap used in Lean mode") + +(defun lean-mk-check-menu-option (text sym) + `[,text (lean-set-check-mode ',sym) + :style radio :selected (eq lean-server-check-mode ',sym)]) + +(easy-menu-define lean-mode-menu lean-mode-map + "Menu for the Lean major mode" + `("Lean" + ["Execute lean" lean-execute t] + ;; ["Create a new project" (call-interactively 'lean-project-create) (not (lean-project-inside-p))] + "-----------------" + ["Show type info" lean-show-type (and lean-eldoc-use eldoc-mode)] + ["Toggle goal display" lean-toggle-show-goal t] + ["Toggle next error display" lean-toggle-next-error t] + ["Toggle message boxes" lean-message-boxes-toggle t] + ["Highlight pending tasks" lean-server-toggle-show-pending-tasks + :active t :style toggle :selected lean-server-show-pending-tasks] + ["Find definition at point" lean-find-definition t] + "-----------------" + ["List of errors" flycheck-list-errors flycheck-mode] + "-----------------" + ["Restart lean process" lean-server-restart t] + "-----------------" + ,(lean-mk-check-menu-option "Check nothing" 'nothing) + ,(lean-mk-check-menu-option "Check visible lines" 'visible-lines) + ,(lean-mk-check-menu-option "Check visible lines and above" 'visible-lines-and-above) + ,(lean-mk-check-menu-option "Check visible files" 'visible-files) + ,(lean-mk-check-menu-option "Check open files" 'open-files) + "-----------------" + ("Configuration" + ["Show type at point" + lean-toggle-eldoc-mode :active t :style toggle :selected eldoc-mode]) + "-----------------" + ["Customize lean-mode" (customize-group 'lean) t])) + +(defconst lean-hooks-alist + '( + ;; server + ;; (kill-buffer-hook . lean-server-stop) + (after-change-functions . lean-server-change-hook) + (focus-in-hook . lean-server-show-messages) + (window-scroll-functions . lean-server-window-scroll-function-hook) + ;; Handle events that may start automatic syntax checks + (before-save-hook . lean-whitespace-cleanup) + ;; info windows + (post-command-hook . lean-show-goal--handler) + (post-command-hook . lean-next-error--handler) + (flycheck-after-syntax-check-hook . lean-show-goal--handler) + (flycheck-after-syntax-check-hook . lean-next-error--handler) + ) + "Hooks which lean-mode needs to hook in. + +The `car' of each pair is a hook variable, the `cdr' a function +to be added or removed from the hook variable if Flycheck mode is +enabled and disabled respectively.") + +(defun lean-mode-setup () + "Default lean-mode setup" + ;; server + (ignore-errors (lean-server-ensure-alive)) + (setq mode-name '("Lean" (:eval (lean-server-status-string)))) + ;; Right click menu sources + (setq lean-right-click-item-functions '(lean-info-right-click-find-definition + lean-hole-right-click)) + ;; Flycheck + (lean-flycheck-turn-on) + (setq-local flycheck-disabled-checkers '()) + ;; info buffers + (lean-ensure-info-buffer lean-next-error-buffer-name) + (lean-ensure-info-buffer lean-show-goal-buffer-name) + ;; eldoc + (when lean-eldoc-use + (set (make-local-variable 'eldoc-documentation-function) + 'lean-eldoc-documentation-function) + (eldoc-mode t))) + +;; Automode List +;;;###autoload +(define-derived-mode lean-mode prog-mode "Lean" + "Major mode for Lean + \\{lean-mode-map} +Invokes `lean-mode-hook'. +" + :syntax-table lean-syntax-table + :abbrev-table lean-abbrev-table + :group 'lean + (set (make-local-variable 'comment-start) "--") + (set (make-local-variable 'comment-start-skip) "[-/]-[ \t]*") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-end-skip) "[ \t]*\\(-/\\|\\s>\\)") + (set (make-local-variable 'comment-padding) 1) + (set (make-local-variable 'comment-use-syntax) t) + (set (make-local-variable 'font-lock-defaults) lean-font-lock-defaults) + (set (make-local-variable 'indent-tabs-mode) nil) + (set 'compilation-mode-font-lock-keywords '()) + (set-input-method "Lean") + (set (make-local-variable 'lisp-indent-function) + 'common-lisp-indent-function) + (lean-set-keys) + (if (fboundp 'electric-indent-local-mode) + (electric-indent-local-mode -1)) + ;; (abbrev-mode 1) + (pcase-dolist (`(,hook . ,fn) lean-hooks-alist) + (add-hook hook fn nil 'local)) + (lean-mode-setup)) + +;; Automatically use lean-mode for .lean files. +;;;###autoload +(push '("\\.lean$" . lean-mode) auto-mode-alist) +(push '("\\.hlean$" . lean-mode) auto-mode-alist) + +;; Use utf-8 encoding +;;;### autoload +(modify-coding-system-alist 'file "\\.lean\\'" 'utf-8) +(modify-coding-system-alist 'file "\\.hlean\\'" 'utf-8) + +;; Flycheck init +(eval-after-load 'flycheck + '(lean-flycheck-init)) + +(provide 'lean-mode) +;;; lean-mode.el ends here diff --git a/lean-mode/lean-right-click.el b/lean-mode/lean-right-click.el new file mode 100644 index 0000000000..7ef76192e3 --- /dev/null +++ b/lean-mode/lean-right-click.el @@ -0,0 +1,84 @@ +;; -*- lexical-binding: t -*- +;; +;;; lean-right-click.el +;; +;; Copyright (c) 2017 David Christiansen. All rights reserved. +;; +;; Author: David Christiansen +;; Released under Apache 2.0 license as described in the file LICENSE. +;; + +;;; Code: + +(defvar lean-right-click-item-functions nil + "A list of functions to compute menu items from source locations. + +The functions take no arguments. They will be run in a context +where `current-buffer' gives the buffer in which the click +occurred. The function should return a three-element list, where +the first is a Lean server command, the second is its parameter +list, and the third is a continuation that will compute a list of +menu items. + + Each menu item is a plist that maps the key :name to the string +to show in the menu and the key :action to a zero-argument +function that implements the action.") +(make-variable-buffer-local 'lean-right-click-item-functions) + +(defvar lean-right-click--unique-val-counter 0 + "A global counter for unique values for lean-right-click.") + +(defun lean-right-click--unique-val () + "Get a unique value for internal tagging." + (cl-incf lean-right-click--unique-val-counter)) + +(defun lean-right-click--items-for-location () + "Return the menu items for point in the current buffer." + (let ((commands (cl-loop for fun in lean-right-click-item-functions + collecting (funcall fun)))) + (let ((timeout 0.15) + (items '()) + (start-time (time-to-seconds)) + (command-count (length commands)) + (commands-returned 0)) + (cl-loop for (cmd args cont) in commands + do (progn (lean-server-send-command + cmd args + (lambda (&rest result) + (setq items (append items (apply cont result))) + (cl-incf commands-returned)) + (lambda (&rest _whatever) + (cl-incf commands-returned))) + ;; This is necessary to ensure that async IO happens. + (sit-for 0.02))) + (while (and (< (time-to-seconds) (+ timeout start-time)) + (< commands-returned command-count)) + (sit-for 0.02)) + items))) + +(defun lean-right-click-show-menu (click) + "Show a menu based on the location of CLICK, computed from the `lean-right-click-functions'." + (interactive "e") + (let* ((window (posn-window (event-end click))) + (buffer (window-buffer window)) + (where (posn-point (event-end click))) + (menu-items (with-current-buffer buffer + (save-excursion + (goto-char where) + (lean-right-click--items-for-location))))) + (when menu-items + (let* ((menu (make-sparse-keymap)) + (todo (cl-loop for item in menu-items + collecting (let ((sym (lean-right-click--unique-val))) + (define-key-after menu `[,sym] + `(menu-item ,(plist-get item :name) + (lambda () (interactive))) + t) + (cons sym (plist-get item :action))))) + (selection (x-popup-menu click menu))) + (when selection + (funcall (cdr (assoc (car selection) todo)))))))) + + +(provide 'lean-right-click) +;;; lean-right-click.el ends here diff --git a/lean-mode/lean-settings.el b/lean-mode/lean-settings.el new file mode 100644 index 0000000000..07d025b3c1 --- /dev/null +++ b/lean-mode/lean-settings.el @@ -0,0 +1,133 @@ +;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. +;; Released under Apache 2.0 license as described in the file LICENSE. +;; +;; Author: Soonho Kong +;; + +(require 'cl-lib) + +(defgroup lean nil + "Lean Theorem Prover" + :prefix "lean-" + :group 'languages + :link '(url-link :tag "Website" "http://leanprover.github.io") + :link '(url-link :tag "Github" "https://github.com/leanprover/lean")) + +(defgroup lean-keybinding nil + "Keybindings for lean-mode." + :prefix "lean-" + :group 'lean) + +(defvar-local lean-default-executable-name + (cl-case system-type + ('windows-nt "lean.exe") + ('cygwin "lean.exe") + (t "lean")) + "Default executable name of Lean") + +(defcustom lean-rootdir nil + "Full pathname of lean root directory. It should be defined by user." + :group 'lean + :type 'string) + +(defcustom lean-executable-name lean-default-executable-name + "Name of lean executable" + :group 'lean + :type 'string) + +(defcustom lean-memory-limit 1024 + "Memory limit for lean process in megabytes" + :group 'lean + :type 'number) + +(defcustom lean-timeout-limit 100000 + "Deterministic timeout limit (it is approximately the maximum number of memory allocations in thousands)" + :group 'lean + :type 'number) + +(defcustom lean-extra-arguments nil + "Extra command-line arguments to the lean process" + :group 'lean + :type '(list string)) + +(defcustom lean-eldoc-use t + "Use eldoc mode for lean." + :group 'lean + :type 'boolean) + +(defcustom lean-eldoc-nay-retry-time 0.3 + "When eldoc-function had nay, try again after this amount of time." + :group 'lean + :type 'number) + +(defcustom lean-delete-trailing-whitespace nil + "Set this variable to true to automatically delete trailing +whitespace when a buffer is loaded from a file or when it is +written." + :group 'lean + :type 'boolean) + +(defcustom lean-show-type-add-to-kill-ring nil + "If it is non-nil, add the type information to the kill-ring so +that user can yank(paste) it later. By default, it's +false (nil)." + :group 'lean + :type 'boolean) + +(defcustom lean-server-show-pending-tasks t + "Highlights pending tasks in the current buffer." + :group 'lean + :type 'boolean) + +(defcustom lean-server-check-mode 'visible-lines-and-above + "What parts of the open files the Lean server should check" + :group 'lean + :type 'symbol + :options '(nothing visible-lines visible-lines-and-above visible-files open-files)) + +(defcustom lean-keybinding-std-exe1 (kbd "C-c C-x") + "Lean Keybinding for std-exe #1" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-std-exe2 (kbd "C-c C-l") + "Lean Keybinding for std-exe #2" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-show-key (kbd "C-c C-k") + "Lean Keybinding for show-key" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-server-restart (kbd "C-c C-r") + "Lean Keybinding for server-restart" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-server-switch-version (kbd "C-c C-s") + "Lean Keybinding for lean-server-switch-version" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-find-definition (kbd "M-.") + "Lean Keybinding for find-definition" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-tab-indent (kbd "TAB") + "Lean Keybinding for tab-indent" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-auto-complete (kbd "S-SPC") + "Lean Keybinding for auto completion" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-hole (kbd "C-c SPC") + "Lean Keybinding for hole manipulation" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-lean-toggle-show-goal (kbd "C-c C-g") + "Lean Keybinding for show-goal-at-pos" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-lean-toggle-next-error (kbd "C-c C-n") + "Lean Keybinding for lean-toggle-next-error" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-lean-message-boxes-toggle (kbd "C-c C-b") + "Lean Keybinding for lean-message-boxes-toggle" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-leanpkg-configure (kbd "C-c C-p C-c") + "Lean Keybinding for lean-leanpkg-configure" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-leanpkg-build (kbd "C-c C-p C-b") + "Lean Keybinding for lean-leanpkg-build" + :group 'lean-keybinding :type 'key-sequence) +(defcustom lean-keybinding-leanpkg-test (kbd "C-c C-p C-t") + "Lean Keybinding for lean-leanpkg-test" + :group 'lean-keybinding :type 'key-sequence) +(provide 'lean-settings) diff --git a/lean-mode/lean-syntax.el b/lean-mode/lean-syntax.el new file mode 100644 index 0000000000..a04c21fd22 --- /dev/null +++ b/lean-mode/lean-syntax.el @@ -0,0 +1,194 @@ +;; Copyright (c) 2013, 2014 Microsoft Corporation. All rights reserved. +;; Released under Apache 2.0 license as described in the file LICENSE. +;; +;; Author: Leonardo de Moura +;; Soonho Kong +;; + +(require 'rx) + +(defconst lean-keywords1 + '("import" "prelude" "protected" "private" "noncomputable" "definition" "meta" "renaming" + "hiding" "exposing" "parameter" "parameters" "begin" "constant" "constants" + "lemma" "variable" "variables" "theorem" "example" "abbreviation" + "open" "export" "axiom" "axioms" "inductive" "coinductive" "with" "without" + "structure" "universe" "universes" "hide" + "precedence" "reserve" "declare_trace" "add_key_equivalence" + "match" "infix" "infixl" "infixr" "notation" "postfix" "prefix" "instance" + "end" "this" "using" "using_well_founded" "namespace" "section" + "attribute" "local" "set_option" "extends" "include" "omit" "classes" "class" + "attributes" "raw" "replacing" + "calc" "have" "show" "suffices" "by" "in" "at" "do" "let" "forall" "Pi" "fun" + "exists" "if" "then" "else" "assume" "from" + "mutual" "def" "run_cmd") + "lean keywords ending with 'word' (not symbol)") +(defconst lean-keywords1-regexp + (eval `(rx word-start (or ,@lean-keywords1) word-end))) +(defconst lean-constants + '("#" "@" "!" "$" "->" "∼" "↔" "/" "==" "=" ":=" "<->" "/\\" "\\/" "∧" "∨" + "≠" "<" ">" "≤" "≥" "¬" "<=" ">=" "⁻¹" "⬝" "▸" "+" "*" "-" "/" "λ" + "→" "∃" "∀" "∘" "×" "Σ" "Π" "~" "||" "&&" "≃" "≡" "≅" + "ℕ" "ℤ" "ℚ" "ℝ" "ℂ" "𝔸" + "⬝e" "⬝i" "⬝o" "⬝op" "⬝po" "⬝h" "⬝v" "⬝hp" "⬝vp" "⬝ph" "⬝pv" "⬝r" "◾" "◾o" + "∘n" "∘f" "∘fi" "∘nf" "∘fn" "∘n1f" "∘1nf" "∘f1n" "∘fn1" + "^c" "≃c" "≅c" "×c" "×f" "×n" "+c" "+f" "+n" "ℕ₋₂") + "lean constants") +(defconst lean-constants-regexp (regexp-opt lean-constants)) +(defconst lean-numerals-regexp + (eval `(rx word-start + (one-or-more digit) (optional (and "." (zero-or-more digit))) + word-end))) + +(defconst lean-warnings '("sorry" "exit") "lean warnings") +(defconst lean-warnings-regexp + (eval `(rx word-start (or ,@lean-warnings) word-end))) + + +(defconst lean-syntax-table + (let ((st (make-syntax-table))) + ;; Matching parens + (modify-syntax-entry ?\[ "(]" st) + (modify-syntax-entry ?\] ")[" st) + (modify-syntax-entry ?\{ "(}" st) + (modify-syntax-entry ?\} "){" st) + + ;; comment + (modify-syntax-entry ?/ ". 14nb" st) + (modify-syntax-entry ?- ". 123" st) + (modify-syntax-entry ?\n ">" st) + (modify-syntax-entry ?« "<" st) + (modify-syntax-entry ?» ">" st) + + ;; Word constituent + (--map (modify-syntax-entry it "w" st) + (list ?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m + ?n ?o ?p ?q ?r ?s ?t ?u ?v ?w ?x ?y ?z + ?A ?B ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M + ?N ?O ?P ?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z)) + (--map (modify-syntax-entry it "w" st) + (list ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)) + (--map (modify-syntax-entry it "w" st) + (list ?α ?β ?γ ?δ ?ε ?ζ ?η ?θ ?ι ?κ ;;?λ + ?μ ?ν ?ξ ?ο ?π ?ρ ?ς ?σ ?τ ?υ + ?φ ?χ ?ψ ?ω)) + (--map (modify-syntax-entry it "w" st) + (list ?ϊ ?ϋ ?ό ?ύ ?ώ ?Ϗ ?ϐ ?ϑ ?ϒ ?ϓ ?ϔ ?ϕ ?ϖ + ?ϗ ?Ϙ ?ϙ ?Ϛ ?ϛ ?Ϝ ?ϝ ?Ϟ ?ϟ ?Ϡ ?ϡ ?Ϣ ?ϣ + ?Ϥ ?ϥ ?Ϧ ?ϧ ?Ϩ ?ϩ ?Ϫ ?ϫ ?Ϭ ?ϭ ?Ϯ ?ϯ ?ϰ + ?ϱ ?ϲ ?ϳ ?ϴ ?ϵ ?϶ ?Ϸ ?ϸ ?Ϲ ?Ϻ ?ϻ)) + (--map (modify-syntax-entry it "w" st) + (list ?ἀ ?ἁ ?ἂ ?ἃ ?ἄ ?ἅ ?ἆ ?ἇ ?Ἀ ?Ἁ ?Ἂ ?Ἃ ?Ἄ + ?Ἅ ?Ἆ ?Ἇ ?ἐ ?ἑ ?ἒ ?ἓ ?ἔ ?ἕ ?἖ ?἗ ?Ἐ ?Ἑ + ?Ἒ ?Ἓ ?Ἔ ?Ἕ ?἞ ?἟ ?ἠ ?ἡ ?ἢ ?ἣ ?ἤ ?ἥ + ?ἦ ?ἧ ?Ἠ ?Ἡ ?Ἢ ?Ἣ ?Ἤ ?Ἥ ?Ἦ ?Ἧ ?ἰ ?ἱ + ?ἲ ?ἳ ?ἴ ?ἵ ?ἶ ?ἷ ?Ἰ ?Ἱ ?Ἲ ?Ἳ ?Ἴ ?Ἵ ?Ἶ ?Ἷ + ?ὀ ?ὁ ?ὂ ?ὃ ?ὄ ?ὅ ?὆ ?὇ ?Ὀ ?Ὁ ?Ὂ ?Ὃ + ?Ὄ ?Ὅ ?὎ ?὏ ?ὐ ?ὑ ?ὒ ?ὓ ?ὔ ?ὕ ?ὖ ?ὗ + ?὘ ?Ὑ ?὚ ?Ὓ ?὜ ?Ὕ ?὞ ?Ὗ ?ὠ ?ὡ ?ὢ + ?ὣ ?ὤ ?ὥ ?ὦ ?ὧ ?Ὠ ?Ὡ ?Ὢ ?Ὣ ?Ὤ ?Ὥ ?Ὦ + ?Ὧ ?ὰ ?ά ?ὲ ?έ ?ὴ ?ή ?ὶ ?ί ?ὸ ?ό ?ὺ ?ύ ?ὼ + ?ώ ?὾ ?὿ ?ᾀ ?ᾁ ?ᾂ ?ᾃ ?ᾄ ?ᾅ ?ᾆ ?ᾇ ?ᾈ + ?ᾉ ?ᾊ ?ᾋ ?ᾌ ?ᾍ ?ᾎ ?ᾏ ?ᾐ ?ᾑ ?ᾒ ?ᾓ ?ᾔ + ?ᾕ ?ᾖ ?ᾗ ?ᾘ ?ᾙ ?ᾚ ?ᾛ ?ᾜ ?ᾝ ?ᾞ ?ᾟ ?ᾠ ?ᾡ ?ᾢ + ?ᾣ ?ᾤ ?ᾥ ?ᾦ ?ᾧ ?ᾨ ?ᾩ ?ᾪ ?ᾫ ?ᾬ ?ᾭ ?ᾮ ?ᾯ ?ᾰ + ?ᾱ ?ᾲ ?ᾳ ?ᾴ ?᾵ ?ᾶ ?ᾷ ?Ᾰ ?Ᾱ ?Ὰ ?Ά ?ᾼ ?᾽ + ?ι ?᾿ ?῀ ?῁ ?ῂ ?ῃ ?ῄ ?῅ ?ῆ ?ῇ ?Ὲ ?Έ ?Ὴ + ?Ή ?ῌ ?῍ ?῎ ?῏ ?ῐ ?ῑ ?ῒ ?ΐ ?῔ ?῕ ?ῖ ?ῗ + ?Ῐ ?Ῑ ?Ὶ ?Ί ?῜ ?῝ ?῞ ?῟ ?ῠ ?ῡ ?ῢ ?ΰ ?ῤ ?ῥ + ?ῦ ?ῧ ?Ῠ ?Ῡ ?Ὺ ?Ύ ?Ῥ ?῭ ?΅ ?` ?῰ ?῱ ?ῲ ?ῳ + ?ῴ ?῵ ?ῶ ?ῷ ?Ὸ ?Ό ?Ὼ ?Ώ ?ῼ ?´ ?῾)) + (--map (modify-syntax-entry it "w" st) + (list ?℀ ?℁ ?ℂ ?℃ ?℄ ?℅ ?℆ ?ℇ ?℈ ?℉ ?ℊ ?ℋ ?ℌ ?ℍ ?ℎ + ?ℏ ?ℐ ?ℑ ?ℒ ?ℓ ?℔ ?ℕ ?№ ?℗ ?℘ ?ℙ ?ℚ ?ℛ ?ℜ ?ℝ + ?℞ ?℟ ?℠ ?℡ ?™ ?℣ ?ℤ ?℥ ?Ω ?℧ ?ℨ ?℩ ?K ?Å ?ℬ + ?ℭ ?℮ ?ℯ ?ℰ ?ℱ ?Ⅎ ?ℳ ?ℴ ?ℵ ?ℶ ?ℷ ?ℸ ?ℹ ?℺ ?℻ + ?ℼ ?ℽ ?ℾ ?ℿ ?⅀ ?⅁ ?⅂ ?⅃ ?⅄ ?ⅅ ?ⅆ ?ⅇ ?ⅈ ?ⅉ ?⅊ + ?⅋ ?⅌ ?⅍ ?ⅎ ?⅏)) + (modify-syntax-entry ?' "w" st) + (modify-syntax-entry ?_ "w" st) + (modify-syntax-entry ?\. "w" st) + + ;; Lean operator chars + (mapc #'(lambda (ch) (modify-syntax-entry ch "_" st)) + "!#$%&*+<=>@^|~:") + + ;; Whitespace is whitespace + (modify-syntax-entry ?\ " " st) + (modify-syntax-entry ?\t " " st) + + ;; Strings + (modify-syntax-entry ?\" "\"" st) + (modify-syntax-entry ?\\ "/" st) + + st)) + +(defconst lean-font-lock-defaults + `((;; attributes + (,(rx word-start "attribute" word-end (zero-or-more whitespace) (group (one-or-more "[" (zero-or-more (not (any "]"))) "]" (zero-or-more whitespace)))) + (1 'font-lock-doc-face)) + (,(rx (group "@[" (zero-or-more (not (any "]"))) "]")) + (1 'font-lock-doc-face)) + (,(rx (group "#" (or "eval" "print" "reduce" "help" "check"))) + (1 'font-lock-keyword-face)) + ;; mutual definitions "names" + (,(rx word-start + "mutual" + word-end + (zero-or-more whitespace) + word-start + (or "inductive" "definition" "def") + word-end + (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{([,"))))) + (1 'font-lock-function-name-face)) + ;; declarations + (,(rx word-start + (group (or "inductive" (group "class" (zero-or-more whitespace) "inductive") "instance" "structure" "class" "theorem" "axiom" "axioms" "lemma" "definition" "def" "constant")) + word-end (zero-or-more whitespace) + (group (zero-or-more "{" (zero-or-more (not (any "}"))) "}" (zero-or-more whitespace))) + (zero-or-more whitespace) + (group (zero-or-more (not (any " \t\n\r{(["))))) + (4 'font-lock-function-name-face)) + ;; Constants which have a keyword as subterm + (,(rx (or "∘if")) . 'font-lock-constant-face) + ;; Keywords + ("\\(set_option\\)[ \t]*\\([^ \t\n]*\\)" (2 'font-lock-constant-face)) + (,lean-keywords1-regexp . 'font-lock-keyword-face) + (,(rx word-start (group "example") ".") (1 'font-lock-keyword-face)) + (,(rx (or "∎")) . 'font-lock-keyword-face) + ;; Types + (,(rx word-start (or "Prop" "Type" "Type*" "Sort" "Sort*") symbol-end) . 'font-lock-type-face) + (,(rx word-start (group (or "Prop" "Type" "Sort")) ".") (1 'font-lock-type-face)) + ;; String + ("\"[^\"]*\"" . 'font-lock-string-face) + ;; ;; Constants + (,lean-constants-regexp . 'font-lock-constant-face) + (,lean-numerals-regexp . 'font-lock-constant-face) + ;; place holder + (,(rx symbol-start "_" symbol-end) . 'font-lock-preprocessor-face) + ;; warnings + (,lean-warnings-regexp . 'font-lock-warning-face) + ;; escaped identifiers + (,(rx (and (group "«") (group (one-or-more (not (any "»")))) (group "»"))) + (1 font-lock-comment-face t) + (2 nil t) + (3 font-lock-comment-face t)) + ))) + +;; Syntax Highlighting for Lean Info Mode +(defconst lean-info-font-lock-defaults + (let ((new-entries + `(;; Please add more after this: + (,(rx (group (+ symbol-start (+ (or word (char ?₁ ?₂ ?₃ ?₄ ?₅ ?₆ ?₇ ?₈ ?₉ ?₀))) symbol-end (* white))) ":") + (1 'font-lock-variable-name-face)) + (,(rx white ":" white) + . 'font-lock-keyword-face) + (,(rx "⊢" white) + . 'font-lock-keyword-face) + (,(rx "[" (group "stale") "]") + (1 'font-lock-warning-face)) + (,(rx line-start "No Goal" line-end) + . 'font-lock-constant-face))) + (inherited-entries (car lean-font-lock-defaults))) + `(,(-concat new-entries inherited-entries)))) + +(provide 'lean-syntax) diff --git a/lean-mode/lean-type.el b/lean-mode/lean-type.el new file mode 100644 index 0000000000..92688f965b --- /dev/null +++ b/lean-mode/lean-type.el @@ -0,0 +1,110 @@ +;; -*- lexical-binding: t; -*- +;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. +;; Released under Apache 2.0 license as described in the file LICENSE. +;; +;; Authors: Soonho Kong, Sebastian Ullrich +;; + +(require 'cl-lib) +(require 'dash) +(require 'dash-functional) +(require 's) +(require 'lean-util) +(require 'lean-server) +(require 'lean-debug) +(require 'flymake) + +(defun lean-fill-placeholder-cont (info-record) + "Continuation for lean-fill-placeholder" + (let ((synth (and info-record (plist-get info-record :synth)))) + (when synth + (let ((synth-str + (replace-regexp-in-string "?M_[0-9]+" "_" synth))) + (when (cl-search " " synth-str) + (setq synth-str (concat "(" synth-str ")"))) + (when (looking-at "_") + (delete-char 1) + (insert synth-str)))))) + +(defun lean-fill-placeholder () + "Fill the placeholder with a synthesized expression by Lean." + (interactive) + (lean-get-info-record-at-point 'lean-fill-placeholder-cont)) + +(cl-defun lean-info-record-to-string (info-record) + "Given typeinfo, overload, and sym-name, compose information as a string." + (destructuring-bind (&key type tactic_params tactic_param_idx text doc full-id &allow-other-keys) info-record + (let ((name-str (or full-id text)) + (type-str type) + str) + (when tactic_params + (setq tactic_params (-map-indexed (lambda (i param) + (if (eq i tactic_param_idx) + (propertize param 'face 'eldoc-highlight-function-argument) + param)) tactic_params)) + (setq type-str (mapconcat 'identity tactic_params " "))) + + (when (and name-str type-str) + (setq str (format (if tactic_params "%s %s" "%s : %s") + (propertize name-str 'face 'font-lock-variable-name-face) + type-str))) + (when doc + (let* ((lines (split-string doc "\n")) + (doc (if (cdr lines) + (concat (car lines) " ⋯") + (car lines)))) + (setq str (concat str + (format "\n%s" + (propertize doc 'face 'font-lock-comment-face)))))) + str))) + +(defvar-local lean-eldoc-documentation-cache nil) + +(defun lean-eldoc-documentation-function-cont (info-record &optional add-to-kill-ring) + "Continuation for lean-eldoc-documentation-function" + (let ((info-string (and info-record (lean-info-record-to-string info-record)))) + (when info-string + (when add-to-kill-ring + (kill-new + (substring-no-properties info-string)))) + (setq lean-eldoc-documentation-cache (and info-string (format "%s" info-string))) + (eldoc-message lean-eldoc-documentation-cache))) + +(defun lean-eldoc-documentation-function (&optional add-to-kill-ring) + "Show information of lean expression at point if any" + (interactive) + (when (not (eq lean-server-check-mode 'nothing)) ; TODO(gabriel): revisit once info no longer reparses the file + (lean-get-info-record-at-point + (lambda (info-record) + (lean-eldoc-documentation-function-cont info-record add-to-kill-ring))) + lean-eldoc-documentation-cache)) + +(defun lean-show-type () + "Show information of lean-expression at point if any." + (interactive) + (lean-eldoc-documentation-function lean-show-type-add-to-kill-ring)) + +(defconst lean-show-goal-buffer-name "*Lean Goal*") + +(setq lean-show-goal--handler-mask nil) + +(defun lean-show-goal--handler () + (if lean-show-goal--handler-mask + (setq lean-show-goal--handler-mask nil) + (let ((deactivate-mark)) ; keep transient mark + (when (and (not (eq lean-server-check-mode 'nothing)) + ; TODO(gabriel): revisit ^^ once info no longer reparses the file + (lean-info-buffer-active lean-show-goal-buffer-name)) + (lean-get-info-record-at-point + (lambda (info-record) + (let ((state (plist-get info-record :state))) + (unless (or (s-blank? state) (s-blank? (s-trim state))) + (lean-with-info-output-to-buffer lean-show-goal-buffer-name (princ state)))))))))) + +(defun lean-toggle-show-goal () + "Show goal at the current point." + (interactive) + (lean-toggle-info-buffer lean-show-goal-buffer-name) + (lean-show-goal--handler)) + +(provide 'lean-type) diff --git a/lean-mode/lean-util.el b/lean-mode/lean-util.el new file mode 100644 index 0000000000..a6b79ca8aa --- /dev/null +++ b/lean-mode/lean-util.el @@ -0,0 +1,104 @@ +;; Copyright (c) 2014 Microsoft Corporation. All rights reserved. +;; Released under Apache 2.0 license as described in the file LICENSE. +;; +;; Author: Soonho Kong +;; + +(require 'cl-lib) +(require 'f) +(require 's) +(require 'dash) +(require 'dash-functional) + +(defun lean-setup-rootdir () + (let ((root (executable-find lean-executable-name))) + (when root + (setq lean-rootdir (f-dirname (f-dirname root)))) + root)) + +(defun lean-get-rootdir () + (if lean-rootdir + (let ((lean-path (f-full (f-join lean-rootdir "bin" lean-executable-name)))) + (unless (f-exists? lean-path) + (error "Incorrect 'lean-rootdir' value, path '%s' does not exist." lean-path)) + lean-rootdir) + (or + (lean-setup-rootdir) + (error + (concat "Lean was not found in the 'exec-path' and 'lean-rootdir' is not defined. " + "Please set it via M-x customize-variable RET lean-rootdir RET."))))) + +(defun lean-get-executable (exe-name) + "Return fullpath of lean executable" + (let ((lean-bin-dir-name "bin")) + (f-full (f-join (lean-get-rootdir) lean-bin-dir-name exe-name)))) + +(defun lean-line-offset (&optional pos) + "Return the byte-offset of `pos` or current position, counting from the + beginning of the line" + (interactive) + (let* ((pos (or pos (point))) + (bol-pos + (save-excursion + (goto-char pos) + (beginning-of-line) + (point)))) + (- pos bol-pos))) + +(defun lean-pos-at-line-col (l c) + "Return the point of the given line and column." + ;; http://emacs.stackexchange.com/a/8083 + (save-excursion + (goto-char (point-min)) + (forward-line (- l 1)) + (move-to-column c) + (point))) + +(defun lean-whitespace-cleanup () + (when lean-delete-trailing-whitespace + (delete-trailing-whitespace))) + +(defun lean-in-comment-p () + "t if a current point is inside of comment block + nil otherwise" + (nth 4 (syntax-ppss))) + +;; The following function is a slightly modified version of +;; f--collect-entries written by Johan Andersson +;; The URL is at https://github.com/rejeep/f.el/blob/master/f.el#L416-L435 +(defun lean--collect-entries (path recursive) + (let (result + (entries + (-reject + (lambda (file) + (or + (equal (f-filename file) ".") + (equal (f-filename file) ".."))) + (directory-files path t)))) + ;; The following line is the only modification that I made + ;; It waits 0.0001 second for an event. This wait allows + ;; wait-timeout function to check the timer and kill the execution + ;; of this function. + (sit-for 0.0001) + (cond (recursive + (-map + (lambda (entry) + (if (f-file? entry) + (setq result (cons entry result)) + (when (f-directory? entry) + (setq result (cons entry result)) + (setq result (append result (lean--collect-entries entry recursive)))))) + entries)) + (t (setq result entries))) + result)) + +;; The following function is a slightly modified version of +;; f-files function written by Johan Andersson The URL is at +;; https://github.com/rejeep/f.el/blob/master/f.el#L478-L481 +(defun lean-find-files (path &optional fn recursive) + "Find all files in PATH." + ;; It calls lean--collect-entries instead of f--collect-entries + (let ((files (-select 'f-file? (lean--collect-entries path recursive)))) + (if fn (-select fn files) files))) + +(provide 'lean-util)