From bdf9a71ab7baa2b1de9abcfd5df1a9107a55d141 Mon Sep 17 00:00:00 2001 From: Jack Jamison Date: Sun, 5 Jul 2026 01:19:30 -0400 Subject: add a bunch of emacs packages HELP --- .config/emacs/lisp/evil/.dir-locals.el | 3 + .config/emacs/lisp/evil/.gitattributes | 1 + .config/emacs/lisp/evil/.github/ISSUE_TEMPLATE.md | 29 + .config/emacs/lisp/evil/.github/workflows/test.yml | 58 + .config/emacs/lisp/evil/.gitignore | 27 + .config/emacs/lisp/evil/.readthedocs.yaml | 17 + .config/emacs/lisp/evil/CHANGES.org | 56 + .config/emacs/lisp/evil/CONTRIBUTING.md | 56 + .config/emacs/lisp/evil/COPYING | 674 ++ .config/emacs/lisp/evil/Eask | 57 + .config/emacs/lisp/evil/Makefile | 80 + .config/emacs/lisp/evil/README.md | 83 + .config/emacs/lisp/evil/doc/Makefile | 20 + .config/emacs/lisp/evil/doc/front.png | Bin 0 -> 3258 bytes .config/emacs/lisp/evil/doc/logo.png | Bin 0 -> 2147 bytes .config/emacs/lisp/evil/doc/logo.svg | 110 + .config/emacs/lisp/evil/doc/make.bat | 35 + .config/emacs/lisp/evil/doc/requirements.txt | 2 + .config/emacs/lisp/evil/doc/source/_ext/elisp.py | 289 + .config/emacs/lisp/evil/doc/source/conf.py | 85 + .config/emacs/lisp/evil/doc/source/extension.rst | 146 + .config/emacs/lisp/evil/doc/source/faq.rst | 118 + .config/emacs/lisp/evil/doc/source/hooks.rst | 16 + .config/emacs/lisp/evil/doc/source/index.rst | 21 + .config/emacs/lisp/evil/doc/source/indices.rst | 6 + .config/emacs/lisp/evil/doc/source/internals.rst | 36 + .config/emacs/lisp/evil/doc/source/keymaps.rst | 145 + .config/emacs/lisp/evil/doc/source/license.rst | 447 + .config/emacs/lisp/evil/doc/source/overview.rst | 117 + .config/emacs/lisp/evil/doc/source/settings.rst | 199 + .config/emacs/lisp/evil/evil-command-window.el | 191 + .config/emacs/lisp/evil/evil-commands.el | 5288 ++++++++++ .config/emacs/lisp/evil/evil-common.el | 3843 +++++++ .config/emacs/lisp/evil/evil-core.el | 1322 +++ .config/emacs/lisp/evil/evil-development.el | 50 + .config/emacs/lisp/evil/evil-digraphs.el | 1735 ++++ .config/emacs/lisp/evil/evil-ex.el | 940 ++ .config/emacs/lisp/evil/evil-integration.el | 468 + .config/emacs/lisp/evil/evil-jumps.el | 358 + .config/emacs/lisp/evil/evil-keybindings.el | 124 + .config/emacs/lisp/evil/evil-macros.el | 772 ++ .config/emacs/lisp/evil/evil-maps.el | 686 ++ .config/emacs/lisp/evil/evil-repeat.el | 641 ++ .config/emacs/lisp/evil/evil-search.el | 1249 +++ .config/emacs/lisp/evil/evil-states.el | 930 ++ .config/emacs/lisp/evil/evil-test-helpers.el | 416 + .config/emacs/lisp/evil/evil-tests.el | 10130 +++++++++++++++++++ .config/emacs/lisp/evil/evil-types.el | 465 + .config/emacs/lisp/evil/evil-vars.el | 2121 ++++ .config/emacs/lisp/evil/evil.el | 158 + .../lisp/evil/scripts/evil-extract-docstrings | 41 + .../emacs/lisp/evil/scripts/evil-release-stable | 52 + .config/emacs/lisp/evil/scripts/evilupdate | 177 + 53 files changed, 35090 insertions(+) create mode 100644 .config/emacs/lisp/evil/.dir-locals.el create mode 100644 .config/emacs/lisp/evil/.gitattributes create mode 100644 .config/emacs/lisp/evil/.github/ISSUE_TEMPLATE.md create mode 100644 .config/emacs/lisp/evil/.github/workflows/test.yml create mode 100644 .config/emacs/lisp/evil/.gitignore create mode 100644 .config/emacs/lisp/evil/.readthedocs.yaml create mode 100644 .config/emacs/lisp/evil/CHANGES.org create mode 100644 .config/emacs/lisp/evil/CONTRIBUTING.md create mode 100644 .config/emacs/lisp/evil/COPYING create mode 100644 .config/emacs/lisp/evil/Eask create mode 100644 .config/emacs/lisp/evil/Makefile create mode 100644 .config/emacs/lisp/evil/README.md create mode 100644 .config/emacs/lisp/evil/doc/Makefile create mode 100644 .config/emacs/lisp/evil/doc/front.png create mode 100644 .config/emacs/lisp/evil/doc/logo.png create mode 100644 .config/emacs/lisp/evil/doc/logo.svg create mode 100644 .config/emacs/lisp/evil/doc/make.bat create mode 100644 .config/emacs/lisp/evil/doc/requirements.txt create mode 100644 .config/emacs/lisp/evil/doc/source/_ext/elisp.py create mode 100644 .config/emacs/lisp/evil/doc/source/conf.py create mode 100644 .config/emacs/lisp/evil/doc/source/extension.rst create mode 100644 .config/emacs/lisp/evil/doc/source/faq.rst create mode 100644 .config/emacs/lisp/evil/doc/source/hooks.rst create mode 100644 .config/emacs/lisp/evil/doc/source/index.rst create mode 100644 .config/emacs/lisp/evil/doc/source/indices.rst create mode 100644 .config/emacs/lisp/evil/doc/source/internals.rst create mode 100644 .config/emacs/lisp/evil/doc/source/keymaps.rst create mode 100644 .config/emacs/lisp/evil/doc/source/license.rst create mode 100644 .config/emacs/lisp/evil/doc/source/overview.rst create mode 100644 .config/emacs/lisp/evil/doc/source/settings.rst create mode 100644 .config/emacs/lisp/evil/evil-command-window.el create mode 100644 .config/emacs/lisp/evil/evil-commands.el create mode 100644 .config/emacs/lisp/evil/evil-common.el create mode 100644 .config/emacs/lisp/evil/evil-core.el create mode 100644 .config/emacs/lisp/evil/evil-development.el create mode 100644 .config/emacs/lisp/evil/evil-digraphs.el create mode 100644 .config/emacs/lisp/evil/evil-ex.el create mode 100644 .config/emacs/lisp/evil/evil-integration.el create mode 100644 .config/emacs/lisp/evil/evil-jumps.el create mode 100644 .config/emacs/lisp/evil/evil-keybindings.el create mode 100644 .config/emacs/lisp/evil/evil-macros.el create mode 100644 .config/emacs/lisp/evil/evil-maps.el create mode 100644 .config/emacs/lisp/evil/evil-repeat.el create mode 100644 .config/emacs/lisp/evil/evil-search.el create mode 100644 .config/emacs/lisp/evil/evil-states.el create mode 100644 .config/emacs/lisp/evil/evil-test-helpers.el create mode 100644 .config/emacs/lisp/evil/evil-tests.el create mode 100644 .config/emacs/lisp/evil/evil-types.el create mode 100644 .config/emacs/lisp/evil/evil-vars.el create mode 100644 .config/emacs/lisp/evil/evil.el create mode 100755 .config/emacs/lisp/evil/scripts/evil-extract-docstrings create mode 100755 .config/emacs/lisp/evil/scripts/evil-release-stable create mode 100755 .config/emacs/lisp/evil/scripts/evilupdate (limited to '.config/emacs/lisp/evil') diff --git a/.config/emacs/lisp/evil/.dir-locals.el b/.config/emacs/lisp/evil/.dir-locals.el new file mode 100644 index 0000000..d79ba71 --- /dev/null +++ b/.config/emacs/lisp/evil/.dir-locals.el @@ -0,0 +1,3 @@ +;; Don't use tabs for el files +((emacs-lisp-mode . + ((indent-tabs-mode . nil)))) diff --git a/.config/emacs/lisp/evil/.gitattributes b/.config/emacs/lisp/evil/.gitattributes new file mode 100644 index 0000000..b362cfa --- /dev/null +++ b/.config/emacs/lisp/evil/.gitattributes @@ -0,0 +1 @@ +*.el diff=lisp diff --git a/.config/emacs/lisp/evil/.github/ISSUE_TEMPLATE.md b/.config/emacs/lisp/evil/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..53cbe14 --- /dev/null +++ b/.config/emacs/lisp/evil/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,29 @@ + +# Issue type + + +- Bug report +- Enhancement request +- Question +- Other + +# Environment + +Emacs version: +Operating System: +Evil version: +Evil installation type: +Graphical/Terminal: +Tested in a `make emacs` session (see [CONTRIBUTING.md](https://github.com/emacs-evil/evil/blob/master/CONTRIBUTING.md#bug-reports)): + +# Reproduction steps +- Start Emacs + + +# Expected behavior + + +# Actual behavior + + +# Further notes diff --git a/.config/emacs/lisp/evil/.github/workflows/test.yml b/.config/emacs/lisp/evil/.github/workflows/test.yml new file mode 100644 index 0000000..d73cf73 --- /dev/null +++ b/.config/emacs/lisp/evil/.github/workflows/test.yml @@ -0,0 +1,58 @@ +name: CI + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + emacs-version: + - 26.3 + - 27.2 + - 28.2 + - 29.4 + experimental: [false] + include: + - os: ubuntu-latest + emacs-version: snapshot + experimental: true + - os: macos-latest + emacs-version: snapshot + experimental: true + exclude: + - os: macos-latest + emacs-version: 26.3 + - os: macos-latest + emacs-version: 27.2 + + steps: + - uses: jcs090218/setup-emacs@master + with: + version: ${{ matrix.emacs-version }} + + - uses: actions/checkout@v4 + + - name: Print emacs version + run: | + emacs --version + + - uses: emacs-eask/setup-eask@master + with: + version: 'snapshot' + + - name: Run tests + run: | + make test + + - name: Print results + if: ${{ always() }} + run: | + cat test-results.txt diff --git a/.config/emacs/lisp/evil/.gitignore b/.config/emacs/lisp/evil/.gitignore new file mode 100644 index 0000000..c5b19d7 --- /dev/null +++ b/.config/emacs/lisp/evil/.gitignore @@ -0,0 +1,27 @@ +*.elc +*.cp +*.ky +*.pg +*.tp +.depend +/evil-autoloads.el +/evil-pkg.el +test-results.txt +typescript + +/doc/dir +/doc/*.aux +/doc/*.fn +/doc/*.fns +/doc/*.info +/doc/*.log +/doc/*.toc +/doc/*.vr +/doc/*.vrs +/doc/docstringdb.json +/doc/build/*/ +!/doc/build/texinfo/evil.texi +*.pyc + +/.eask +/dist diff --git a/.config/emacs/lisp/evil/.readthedocs.yaml b/.config/emacs/lisp/evil/.readthedocs.yaml new file mode 100644 index 0000000..f4b346b --- /dev/null +++ b/.config/emacs/lisp/evil/.readthedocs.yaml @@ -0,0 +1,17 @@ +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.12" + apt_packages: [emacs-nox] + jobs: + pre_build: + - scripts/evil-extract-docstrings + +sphinx: + configuration: doc/source/conf.py + +python: + install: + - requirements: doc/requirements.txt diff --git a/.config/emacs/lisp/evil/CHANGES.org b/.config/emacs/lisp/evil/CHANGES.org new file mode 100644 index 0000000..102c6ab --- /dev/null +++ b/.config/emacs/lisp/evil/CHANGES.org @@ -0,0 +1,56 @@ +* Changes and New Features in Evil + +** News in 1.0.7 + + * Fix #319. + +** News in 1.0.6 + + * Fix bug induced in emacs trunk due to changed behaviour of + `overriding-terminal-local-map` (fixes #309). + +** News in 1.0.5 + + * Fix bug in `show-paren-mode` due to variable renaming in latest + emacs. + + * Fix bug in isearch module due to variable renaming in latest + emacs. + +** News in 1.0.4 + + * Undo one restriction when argument is read in operator state. + +** News in 1.0.3 + + * Fix `evil-delete-backward-word` at first non-blank in a line. + +** News in 1.0.2 + + * Fix #290. + +** News in 1.0.1 + +*** Improvements + + * An ELPA package built by =make elpa= contains COPYING file. + + * Bind =[tab]= like =\t= in =evil-ex-completion-map=, so that it + cannot be overwritten by a =[tab]= binding in the parent keymap + =minibuffer-local-completion-map=. + + * Improve worst case performance of internal functions + =evil-in-string-p=, =evil-string-beginning= and + =evil-string-end=. This functions are used, e.g., in certain text + objects to detect whether point is within a string. + + * Update authors list. + +*** Fixes + + * The interactive code == is fixed and used correctly in + =evil-ex-set-initial-state=. + + * =evil-ex-global= always generates a single undo-step. + + * Resolved issues: #249, #250, #253 and #257. diff --git a/.config/emacs/lisp/evil/CONTRIBUTING.md b/.config/emacs/lisp/evil/CONTRIBUTING.md new file mode 100644 index 0000000..913a5e2 --- /dev/null +++ b/.config/emacs/lisp/evil/CONTRIBUTING.md @@ -0,0 +1,56 @@ +# Bug reports + +Bug reports and feature requests should be made on the +[issue tracker](https://github.com/emacs-evil/evil/issues) here on github. + +**Before** sending a bug report, please take note of the following comments. + +1. Please provide a full description of the configuration of your + system. This includes + + - Emacs version, + - Evil version (e.g., the hash in the git repository), + - Whether you use Emacs/Evil in X mode or in terminal mode, + - Whether you use Emacs/Evil in terminal mode with some terminal multiplexer + like **tmux** or **screen** (and see below). + +2. Test the bug in a clean Emacs environment without any additional + packages loaded (besides Evil itself). You can easily get such an + environment by executing either `make emacs` (for X) or `make + terminal` (for terminal mode) in Evil's source directory. If your + bug is related to some other package, try to load only this package + from the clean environment after using `M-x package-initialize` + first. + +3. If you've just updated your Evil from the repository, **do not forget to + recompile** Evil by executing `make` in Evil's source directory. + +4. If possible, try to give a minimal example how to reproduce the error + starting from a fresh Emacs. The minimal example could be some buffer content + and a sequence of key-strokes that show up the error. + +5. If the bug you want to report is related to the behavior of some commands or + motions, please note the following. One goal of Evil is to get a behavior as + close to **Vim** as possible unless there is a good reason not to do so. The + reason is that many users come from Vim or use both, Vim and Evil, and we + want to keep the number of annoying differences between both as small as + possible, especially in common commands. In case you get an unexpected + behavior, please compare the behavior with plain Vim (i.e., without any + customization), if there's a difference please file the bug with a + description of this difference (and possibly a reference to Vim's + documentation). If you realize that both, Vim and Evil, behave the same but + you want a different behavior, you may make a **feature request** for some + customization option (but the default behavior will probably not be changed + in favor for Vim compatibility). Evil already contains several customization + options and sometimes the desired changes can be achieved as easily as + redefining some key-bindings. + +# Pull Requests + +1. Please follow the + [Emacs Lisp Style Guide](https://github.com/bbatsov/emacs-lisp-style-guide/) +2. When in doubt, adhere to the style conventions in the source code, + but don't be afraid of suggesting code enhancements in case you + encounter suboptimal code. +3. If your pull requests adds a feature or fixes a regression, add + tests covering it to `evil-tests.el`. diff --git a/.config/emacs/lisp/evil/COPYING b/.config/emacs/lisp/evil/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/.config/emacs/lisp/evil/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/.config/emacs/lisp/evil/Eask b/.config/emacs/lisp/evil/Eask new file mode 100644 index 0000000..77bf0c7 --- /dev/null +++ b/.config/emacs/lisp/evil/Eask @@ -0,0 +1,57 @@ +;; -*- mode: eask; lexical-binding: t -*- + +(package "evil" + "1.15.0" + "Extensible vi layer") + +(website-url "https://github.com/emacs-evil/evil") +(keywords "emulations") + +(package-file "evil.el") +(files "evil-*.el" '(:exclude "*-tests.el" "*-pkg.el")) + +(script "test" "echo \"Error: no test specified\" && exit 1") + +(source 'gnu) +(source 'melpa) + +(depends-on "emacs" "24.1") +(depends-on "cl-lib") +(depends-on "goto-chg") +(depends-on "nadvice") + +(setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 + +(eask-defcommand test + "Basic tests." + (require 'evil-tests) + (evil-tests-initialize nil nil)) + +(eask-defcommand terminal + "Load Evil in a terminal Emacs and run all tests." + (require 'evil-tests) + (evil-tests-initialize nil nil t)) + +(eask-defcommand profiler + "Run all tests with profiler." + (require 'evil-tests) + (evil-mode 1) + (evil-tests-initialize nil t)) + +(eask-defcommand indent + "Re-indent all Evil code." + (setq vc-handled-backends nil) + (dolist (file (eask-package-el-files)) (load file)) + (dolist (buffer (reverse (buffer-list))) + (when (buffer-file-name buffer) + (set-buffer buffer) + (message "Indenting %s" (current-buffer)) + (setq-default indent-tabs-mode nil) + (untabify (point-min) (point-max)) + (indent-region (point-min) (point-max)) + (delete-trailing-whitespace) + (untabify (point-min) (point-max)) + (goto-char (point-min)) + (while (re-search-forward "\\n\\\\{3,\\\\}" nil t) + (replace-match "\\n\\n")) + (when (buffer-modified-p) (save-buffer 0))))) diff --git a/.config/emacs/lisp/evil/Makefile b/.config/emacs/lisp/evil/Makefile new file mode 100644 index 0000000..6dd8f25 --- /dev/null +++ b/.config/emacs/lisp/evil/Makefile @@ -0,0 +1,80 @@ +EMACS ?= emacs +SED ?= sed +FILES = $(filter-out evil-test-helpers.el evil-tests.el evil-pkg.el,$(wildcard evil*.el)) +VERSION := $(shell $(SED) -n '/^;\+ \+Version: *\(.*\)/{s//\1/p;q}' evil.el) +PROFILER = +TAG = +EASK ?= eask + +ELCFILES = $(FILES:.el=.elc) + +.PHONY: all compile docstrings doc clean tests test emacs term terminal profiler indent elpa version + +# Byte-compile Evil. +all: compile + +compile: + $(EASK) compile + +# Documentation. +docstrings: + @$(EMACS) --script scripts/evil-extract-docstrings + +doc: docstrings + @$(MAKE) -C doc texinfo + +info: doc + @$(MAKE) -C doc info + +# Delete byte-compiled files etc. +clean: + $(EASK) clean all + +# Run tests. +# The TAG variable may specify a test tag or a test name: +# make test TAG=repeat +# This will only run tests pertaining to the repeat system. +test: + $(EASK) run command test + +# Byte-compile Evil and run all tests. +tests: compile + $(EASK) run command test + $(EASK) clean elc + rm -f .depend + +# Load Evil in a fresh instance of Emacs and run all tests. +emacs: + $(EMACS) -Q -L . -l evil-tests.el \ + --eval "(evil-mode 1)" \ + --eval "(evil-tests-initialize '(${TAG}) '(${PROFILER}) t)" + +# Load Evil in a terminal Emacs and run all tests. +term terminal: + $(EASK) run command terminal + +# Run all tests with profiler. +profiler: + $(EASK) run command profiler + +# Re-indent all Evil code. +# Loads Evil into memory in order to indent macros properly. +# Also removes trailing whitespace, tabs and extraneous blank lines. +indent: clean + $(EASK) run command indent + +evil-pkg.el: evil.el + $(EMACS) --batch -l package $^ \ + --eval "(package-generate-description-file (package-buffer-info) \"$@\")" + +# Create an ELPA package. +elpa: evil-pkg.el + $(EASK) package + +# Change the version using make VERSION=x.y.z +version: + @$(EMACS) --script scripts/evilupdate "${VERSION}" + +# Change the version using make VERSION=x.y.z, but do not post to the newsgroup +nversion: + @$(EMACS) --script scripts/evilupdate nonews "${VERSION}" diff --git a/.config/emacs/lisp/evil/README.md b/.config/emacs/lisp/evil/README.md new file mode 100644 index 0000000..b753ef5 --- /dev/null +++ b/.config/emacs/lisp/evil/README.md @@ -0,0 +1,83 @@ +![An extensible vi layer for Emacs](https://raw.githubusercontent.com/emacs-evil/evil/master/doc/logo.png) + +[![Build status](https://github.com/emacs-evil/evil/actions/workflows/test.yml/badge.svg)](https://github.com/emacs-evil/evil/actions/workflows/test.yml) +[![MELPA](https://melpa.org/packages/evil-badge.svg)](https://melpa.org/#/evil) +[![MELPA Stable](https://stable.melpa.org/packages/evil-badge.svg)](https://stable.melpa.org/#/evil) +[![NonGNU ELPA](http://elpa.nongnu.org/nongnu/evil.svg)](http://elpa.nongnu.org/nongnu/evil.html) +[![Documentation Status](https://readthedocs.org/projects/evil/badge/?version=latest)](https://evil.readthedocs.io/en/latest/?badge=latest) +[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) + +Evil is an **e**xtensible **vi** **l**ayer +for [Emacs](http://www.gnu.org/software/emacs/). It emulates the main features +of [Vim](http://www.vim.org/), and provides facilities for writing custom +extensions. Also see our page on [EmacsWiki](http://emacswiki.org/emacs/Evil). + +# Installation + +See the +[official documentation](https://evil.readthedocs.io/en/latest/overview.html#installation-via-package-el) +for installation instructions. We recommend using *package.el*. + +As a quickstart, you can add the following code to your Emacs init +file. + +```elisp +;; Set up package.el to work with MELPA +(require 'package) +(add-to-list 'package-archives + '("melpa" . "https://melpa.org/packages/")) +(package-initialize) +(package-refresh-contents) + +;; Download Evil +(unless (package-installed-p 'evil) + (package-install 'evil)) + +;; Enable Evil +(require 'evil) +(evil-mode 1) +``` + +## Dependencies + +* Evil requires Emacs 24.1 or later. + +* Evil requires any of the following for `C-r`: + * `undo-redo` from Emacs 28 + * The [undo-tree] package (available via GNU ELPA) + * The [undo-fu] package (available via MELPA and NonGNU ELPA) + +* For the motions `g;` `g,` and for the last-change-register `.`, Evil + requires the [goto-chg.el](https://github.com/emacs-evil/goto-chg) + package (available via MELPA and NonGNU ELPA), which provides the + functions `goto-last-change` and `goto-last-change-reverse`. + +* For Emacs 24.1 and 24.2 Evil also requires + [cl-lib](https://elpa.gnu.org/packages/cl-lib.html). + +# Documentation + +The latest version of the documentation is readable online +[here](https://evil.readthedocs.io/en/latest/index.html). It is also +available as +[PDF](https://evil.readthedocs.io/_/downloads/en/stable/pdf/) and +as [EPUB](https://evil.readthedocs.io/_/downloads/en/stable/epub/). + +# Mailing list + +Evil is discussed at the +[gmane.emacs.vim-emulation](http://lists.ourproject.org/cgi-bin/mailman/listinfo/implementations-list) +mailing list. + +# IRC + +Visit us on `irc.libera.chat #evil-mode`. + +# Contribution + +See +[CONTRIBUTING.md](https://github.com/emacs-evil/evil/blob/master/CONTRIBUTING.md) +for guidelines for issues and pull requests. + +[undo-tree]: https://gitlab.com/tsc25/undo-tree +[undo-fu]: https://codeberg.org/ideasman42/emacs-undo-fu diff --git a/.config/emacs/lisp/evil/doc/Makefile b/.config/emacs/lisp/evil/doc/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/.config/emacs/lisp/evil/doc/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/.config/emacs/lisp/evil/doc/front.png b/.config/emacs/lisp/evil/doc/front.png new file mode 100644 index 0000000..f79a67e Binary files /dev/null and b/.config/emacs/lisp/evil/doc/front.png differ diff --git a/.config/emacs/lisp/evil/doc/logo.png b/.config/emacs/lisp/evil/doc/logo.png new file mode 100644 index 0000000..842a49d Binary files /dev/null and b/.config/emacs/lisp/evil/doc/logo.png differ diff --git a/.config/emacs/lisp/evil/doc/logo.svg b/.config/emacs/lisp/evil/doc/logo.svg new file mode 100644 index 0000000..ff833c2 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/logo.svg @@ -0,0 +1,110 @@ + + + + + + + + + + image/svg+xml + + + + + + + + EVIL + + + + + + diff --git a/.config/emacs/lisp/evil/doc/make.bat b/.config/emacs/lisp/evil/doc/make.bat new file mode 100644 index 0000000..6247f7e --- /dev/null +++ b/.config/emacs/lisp/evil/doc/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/.config/emacs/lisp/evil/doc/requirements.txt b/.config/emacs/lisp/evil/doc/requirements.txt new file mode 100644 index 0000000..4e09d9c --- /dev/null +++ b/.config/emacs/lisp/evil/doc/requirements.txt @@ -0,0 +1,2 @@ +Sphinx==7.3.7 +sphinx-rtd-theme==2.0.0 diff --git a/.config/emacs/lisp/evil/doc/source/_ext/elisp.py b/.config/emacs/lisp/evil/doc/source/_ext/elisp.py new file mode 100644 index 0000000..ab82105 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/_ext/elisp.py @@ -0,0 +1,289 @@ +import re +from os import path +import json + +from sphinx import addnodes +from sphinx.domains import Domain, ObjType, Index +from sphinx.domains.std import StandardDomain +from sphinx.directives import ObjectDescription +from sphinx.roles import XRefRole +from sphinx.util.nodes import make_refnode + + +with open(path.join(path.dirname(__file__), '..', '..', 'docstringdb.json')) as f: + DATA = json.load(f) + + +re_evilcode = re.compile(r"‘(evil-[^’]*)’") +re_code = re.compile(r"‘([^:][^ ’]*)’") +re_kwd = re.compile(r"‘(:[^’]*)’") +re_item = re.compile(r"([^\n])\n- ") +re_sexp = re.compile(r"\([A-Z \-\.‘’\[\]]+\)|[A-Z\-]+") +re_capitals = re.compile(r"[A-Z\-]+") +re_nonspace = re.compile(r"[^ ]") +re_signature = re.compile(r'\(fn (.*)\)') + +emphasis = [ + 'thing-at-point', +] + +def emacs_is_local(var): + return DATA[var]['local'] + +def emacs_default_value(var): + default = DATA[var]['default'] + tp = DATA[var]['default-type'] + if tp == 'string': + rep = repr(default)[1:-1] + return f'"{rep}"' + return str(default) + +def process_docstring(docstring, capitals=None): + # Remove explicit signature + docstring = re_signature.sub('', docstring) + + # Add code blocks to indented sections + def blockified_lines(lines): + in_block = False + for line in lines: + try: + indented = next(re_nonspace.finditer(line)).start(0) >= 4 + except StopIteration: + indented = None + if indented is True and not in_block: + yield '.. code-block:: elisp' + yield '' + in_block = True + elif indented is False: + in_block = False + yield line + docstring = '\n'.join(blockified_lines(docstring.split('\n'))) + + # Substitute `evil-alpha' with :elisp:ref:`evil-alpha` + docstring = re_evilcode.sub(r':elisp:ref:`\1`', docstring) + + # Substitute `alpha' with ``alpha`` + docstring = re_code.sub(r'``\1``', docstring) + + # Substitute `:alpha' with ``alpha`` + docstring = re_kwd.sub(r'``\1``', docstring) + + # Add empty line between list items + docstring = re_item.sub(r'\1\n\n- ', docstring) + + if capitals is None: + capitals = [] + else: + capitals = list(capitals) + + # Find things that look like sexps + def substitute_sexp(match): + s = match.group(0) + if re_capitals.match(s): + if s in capitals: + return f'*{s}*' + return s + else: + capitals.extend(re_capitals.findall(s)) + return f'``{s}``' + docstring = re_sexp.sub(substitute_sexp, docstring) + + # Italicize some words + for s in emphasis: + docstring = docstring.replace(s, f'*{s}*') + + return docstring + +def emacs_variable_docstring(var): + docstring = DATA[var]['var-docstring'] + return process_docstring(docstring) + +def emacs_function_docstring(var): + docstring = DATA[var]['fn-docstring'] + return process_docstring(docstring, capitals=emacs_argnames(var)) + +def emacs_argnames(var): + arglist = emacs_arglist(var) + return re_capitals.findall(arglist) + +def emacs_arglist(var): + docstring = DATA[var]['fn-docstring'] + match = re_signature.search(docstring) + if match: + return match.group(1) + + arglist = [arg.upper() for arg in DATA[var]['arglist']] + state = None + ret = '' + for arg in arglist: + if arg in ('&REST', '&OPTIONAL'): + if state == '&OPTIONAL': + ret += ']' + state = arg + ret += ' [' + continue + ret += ('' if state in ('&REST', '&OPTIONAL') else ' ') + arg + if state == '&OPTIONAL': + state += '-CONT' + if state is not None and state.startswith('&OPTIONAL'): + ret += ']' + if state == '&REST': + ret += '...]' + return ret + + +class AbstractElisp(ObjectDescription): + + def add_target_and_index(self, name, sig, signode): + anchor = f'elispobj-{sig}' + signode['ids'].append(anchor) + + objs = self.env.domaindata['elisp']['objects'] + objs[sig] = { + 'docname': self.env.docname, + 'anchor': f'elispobj-{sig}', + 'type': self.object_type, + } + + +class AbstractVariable(AbstractElisp): + object_type = 'variable' + + def handle_signature(self, sig, signode): + signode += addnodes.desc_annotation(sig, sig) + return sig + + def run(self): + extra = [] + + default = self.default_value() + if default: + extra.append(f'Default: ``{default}``') + if self.is_buffer_local(): + extra.append('buffer-local') + + self.content.data.extend(['', ', '.join(extra)]) + retval = super().run() + return retval + + +class Variable(AbstractVariable): + required_arguments = 1 + optional_arguments = 2 + + def default_value(self): + try: + return self.arguments[1] + except IndexError: + return None + + def is_buffer_local(self): + return 'bufloc' in self.arguments[1:] + + +class AutoVariable(AbstractVariable): + required_arguments = 1 + + def is_buffer_local(self): + return emacs_is_local(self.arguments[0]) + + def default_value(self): + return emacs_default_value(self.arguments[0]) + + def run(self): + docstring = emacs_variable_docstring(self.arguments[0]) + self.content.data.extend(docstring.split('\n')) + return super().run() + + +class AutoFunction(AbstractElisp): + required_arguments = 1 + + @property + def object_type(self): + return 'macro' if DATA[self.arguments[0]]['macrop'] else 'function' + + def handle_signature(self, sig, signode): + args = emacs_arglist(sig) + signode += addnodes.desc_annotation(sig, f'({sig} {args})') + return sig + + def run(self): + docstring = emacs_function_docstring(self.arguments[0]) + self.content.data.extend(docstring.split('\n')) + return super().run() + + +class ElispIndex(Index): + name = 'index' + localname = 'Emacs lisp functions and variables' + shortname = 'Elisp' + + def generate(self, docnames=None): + index = {} + for name, item in self.domain.data['objects'].items(): + if name.startswith('evil-'): + letter = name[5].upper() + else: + letter = name[0].upper() + index.setdefault(letter, []).append(( + name, + 0, + item['docname'], + item['anchor'], + item['type'], + '', + '', + )) + + index = {k: sorted(v, key=lambda k: k[0].lower()) for k, v in index.items()} + index = list(index.items()) + index = sorted(index, key=lambda k: k[0]) + return index, True + + +class Elisp(Domain): + name = 'elisp' + label = 'Emacs lisp' + + object_types = { + 'variable': ObjType('variable', 'variable', 'obj'), + 'autovariable': ObjType('autovariable', 'autovariable', 'obj'), + 'autofunction': ObjType('autofunction', 'autofunction', 'obj'), + } + + directives = { + 'variable': Variable, + 'autovariable': AutoVariable, + 'autofunction': AutoFunction, + } + + roles = { + 'ref': XRefRole(), + } + + initial_data = { + 'objects': {}, + } + + indices = { + ElispIndex, + } + + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): + obj = self.data['objects'].get(target, None) + if obj is None: + return None + return make_refnode(builder, fromdocname, obj['docname'], obj['anchor'], contnode, obj['anchor']) + + +def setup(app): + app.add_domain(Elisp) + StandardDomain.initial_data['labels']['elispindex'] = ('elisp-index', '', 'Emacs lisp functions and variables') + StandardDomain.initial_data['anonlabels']['elispindex'] = ('elisp-index', '') + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/.config/emacs/lisp/evil/doc/source/conf.py b/.config/emacs/lisp/evil/doc/source/conf.py new file mode 100644 index 0000000..5b1b6f5 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/conf.py @@ -0,0 +1,85 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# + +import os, sys +sys.path.append(os.path.abspath("./_ext")) + + +# -- Project information ----------------------------------------------------- + +project = 'Evil' +copyright = '2011-2019, Eivind Fonn, Frank Fischer, Vegard Øye' +author = 'Eivind Fonn, Frank Fischer, Vegard Øye' + +# The full version, including alpha/beta/rc tags +release = '1.15.0' + +master_doc = 'index' + + +latex_elements = { + 'fontpkg': r'\usepackage{palatino} \usepackage{inconsolata}', + 'maketitle': r""" + \newcommand\sphinxbackoftitlepage{{ + Copyright {copyright}. + + \begin{{quote}} + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.3 or any later version published by the Free Software + Foundation; with no Invariant Sections, no Front-Cover Texts, + and no Back-Cover Texts. A copy of the license is included in + the section entitled ``GNU Free Documentation License''. + \end{{quote}} + + The Evil team thanks everyone at gmane.emacs.vim-emulation for + their feedback and contributions. + }} + \sphinxmaketitle + """.format(copyright=copyright), +} + + +texinfo_documents = [ + (master_doc, 'evil', 'Evil documentation', author, 'evil', + 'Extensible vi layer for Emacs', 'Emacs'), +] + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'elisp', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' diff --git a/.config/emacs/lisp/evil/doc/source/extension.rst b/.config/emacs/lisp/evil/doc/source/extension.rst new file mode 100644 index 0000000..444502f --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/extension.rst @@ -0,0 +1,146 @@ +Extension +========= + +The main functionality of Evil is implemented in terms of reusable +macros. Package writers can use these to define new commands. + + +Motions +------- + +A *motion* is a command which moves the cursor, such as :kbd:`w` or +:kbd:`e`. Motions are defined with the macro +:elisp:ref:`evil-define-motion`. Motions not defined in this way +should be declared with :elisp:ref:`evil-declare-motion`. + +.. elisp:autofunction:: evil-declare-motion + +.. elisp:autofunction:: evil-define-motion + +For example, this is a motion that moves the cursor forward by a +number of characters: + +.. code-block:: elisp + + (evil-define-motion foo-forward (count) + "Move to the right by COUNT characters." + :type inclusive + (forward-char (or count 1))) + +The *type* of a motion determines how it works when used together with +an operator. Inclusive motions include the endpoint in the range +being operated on, while exclusive motions do not. Line motions +extend the whole range to linewise positions, effectively behaving as +if the endpoint were really at the end of the line. Blockwise ranges +behave as a "rectangle" on screen rather than a contiguous range of +characters. + + +Operators +--------- + +An operator is a command that acts on the text moved over by a motion, +such as :kbd:`c` (change), :kbd:`d` (delete) or :kbd:`y` (yank or +copy, not to be confused with "yank" in Emacs terminology which means +*paste*). + +.. elisp:autofunction:: evil-define-operator + +For example, this is an operator that performs ROT13 encryption on the +text under consideration: + +.. code-block:: elisp + + (evil-define-operator evil-rot13 (beg end) + "ROT13 encrypt text." + (rot13-region beg end)) + +Binding this to :kbd:`g?` (where it is by default) will cause a key +sequence such as :kbd:`g?w` to encrypt from the current cursor to the +end of the word. + + +Text objects +------------ + +Text objects are like motions in that they define a range over which +an operator may act. Unlike motions, text objects can set both a +beginning and an endpoint. In visual state, text objects alter both +ends of the selection. + +Text objects are not directly usable in normal state. Instead, they +are bound in the two keymaps ``evil-inner-text-ojects-map`` and +``evil-outer-text-objects-map``, which are available in visual and +operator-pending state under the keys :kbd:`i` and :kbd:`a` +respectively. + +.. elisp:autofunction:: evil-define-text-object + +For example, this is a text object which selects the next three +characters after the current location: + +.. code-block:: elisp + + (evil-define-text-object foo (count) + "Select three characters." + (list (point) (+ 3 (point)))) + +For convenience, Evil provides several functions returning a list of +positions which can be used for defining text objects. All of them +follow the convention that a positive *count* selects text after the +current location, while negative *count* selects text before it. + +.. note:: + + The *thingatpt* library is used quite extensively in Evil to define + text objects, and this dependency leaks through in the following + functions. A *thing* in this context is any symbol for which there + is a function called ``forward-THING`` [#thing]_ which moves past a + number of *things*. + +.. elisp:autofunction:: evil-select-inner-object + +.. elisp:autofunction:: evil-select-an-object + +.. elisp:autofunction:: evil-select-paren + + +Range types +----------- + +A *type* is a transformation acting on a pair of buffer positions. +Evil defines the types ``inclusive``, ``line``, ``block`` and +``exclusive``, which are used for motion ranges and visual selection. +New types may be defined with the macro *evil-define-type*. + +.. elisp:autofunction:: evil-define-type + + +States +------ + +States are defined with the macro :elisp:ref:`evil-define-state`, +which takes care to define the necessary hooks, keymaps and variables, +as well as a toggle function ``evil-NAME-state`` and a predicate +function ``evil-NAME-state-p`` for checking whether the state is +active. + +.. elisp:autofunction:: evil-define-state + +For example: + +.. code-block:: elisp + + (evil-define-state test + "Test state." + :tag " " + (message (if (evil-test-state-p) + "Enabling test state." + "Disabling test state."))) + + +.. rubric:: Footnotes + +.. [#thing] There are many more ways that a *thing* can be defined, + but the definition of ``forward-THING`` is perhaps the most + straightforward way to go about it. diff --git a/.config/emacs/lisp/evil/doc/source/faq.rst b/.config/emacs/lisp/evil/doc/source/faq.rst new file mode 100644 index 0000000..d389df0 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/faq.rst @@ -0,0 +1,118 @@ +Frequently Asked Questions +========================== + +Problems with the escape key in the terminal +-------------------------------------------- + +A common problem when using Evil in terminal mode is a certain delay +after pressing the escape key. Even more, when pressing the escape key +followed quickly by another key the command is recognized as +:kbd:`M-` instead of two separate keys: :kbd:`ESC` followed by +:kbd:``. In fact, it is perfectly valid to simulate +:kbd:`M-` by pressing :kbd:`ESC ` quickly (but see below). + +The reason for this is that in terminal mode a key sequence involving +the meta key (or alt key) always generates a so called "escape +sequence", i.e. a sequence of two events sent to Emacs, the first +being :kbd:`ESC` and the second the key pressed simultaneously. The +problem is that pressing the escape key itself also generates the +:kbd:`ESC` event. Thus, if Emacs (and therefore Evil) receives an +:kbd:`ESC` event there is no way to tell whether the escape key has +been pressed (and no further event will arrive) or a :kbd:`M-` +combination has been pressed (and the :kbd:`` event will arrive +soon). In order to distinguish both situations Evil does the +following. After receiving an :kbd:`ESC` event Evil waits for a short +time period (specified by the variable :elisp:ref:`evil-esc-delay` +which defaults to 0.01 seconds) for another event. If no other event +arrives Evil assumes that the plain escape key has been pressed, +otherwise it assumes a :kbd:`M-` combination has been pressed and +combines the :kbd:`ESC` event with the second one. Because a +:kbd:`M-` sequence usually generates both events in very quick +succession, 0.01 seconds are usually enough and the delay is hardly +noticeable by the user. + +If you use a terminal multiplexer like *tmux* or *screen* the +situation may be worse. These multiplexers have exactly the same +problem recognizing :kbd:`M-` sequences and often introduce their +own delay for the :kbd:`ESC` key. There is no way for Evil to +influence this delay. In order to reduce it you must reconfigure your +terminal multiplexer. + +Note that this problem should not arise when using Evil in graphical +mode. The reason is that in this case the escape key itself generates +a different command, namely ``escape`` (a symbol) and hence Evil can +distinguish whether the escape key or a :kbd:`M-` combination has +been pressed. But this also implies that pressing :kbd:`ESC` followed +by cannot be used to simulate :kbd:`M-` in graphical mode! + + +Underscore is not a word character +---------------------------------- + +An underscore ``_`` is a word character in Vim. This means that word +motions like :kbd:`w` skip over underlines in a sequence of letters as +if it was a letter itself. In contrast, in Evil the underscore is +often a non-word character like operators, e.g. ``+``. + +The reason is that Evil uses Emacs' definition of a word and this +definition does often not include the underscore. In Emacs word +characters are determined by the syntax-class of the buffer. The +syntax-class usually depends on the major-mode of this buffer. This +has the advantage that the definition of a "word" may be adapted to +the particular type of document being edited. Evil uses Emacs' +definition and does not simply use Vim's definition in order to be +consistent with other Emacs functions. For example, word characters +are exactly those characters that are matched by the regular +expression character class ``[:word:]``. + +If you would be satisfied by having the :kbd:`*` and :kbd:`#` searches +use symbols instead of words, this can be achieved by setting the +:elisp:ref:`evil-symbol-word-search` variable to ``t``. + +If you want the underscore to be recognised as word character for other +motions, you can modify its entry in the syntax-table: + +.. code-block:: elisp + + (modify-syntax-entry ?_ "w") + +This gives the underscore the 'word' syntax class. You can use a +mode-hook to modify the syntax-table in all buffers of some mode, +e.g.: + +.. code-block:: elisp + + (add-hook 'c-mode-common-hook + (lambda () (modify-syntax-entry ?_ "w"))) + +This gives the underscore the word syntax-class in all C-like buffers. + +Similarly to Emacs' definition of a word, the definition of a "symbol" is also +dependent on the syntax-class of the buffer, which often includes the +underscore. The default text objects keymap associates kbd::`o` with the symbol +object, making kbd::`cio` a good alternative to Vim's kbd::`ciw`, for example. +The following will swap between the word and symbol objects in the keymap: + +.. code-block:: elisp + + (define-key evil-outer-text-objects-map "w" 'evil-a-symbol) + (define-key evil-inner-text-objects-map "w" 'evil-inner-symbol) + (define-key evil-outer-text-objects-map "o" 'evil-a-word) + (define-key evil-inner-text-objects-map "o" 'evil-inner-word) + +This will not change the motion keys, however. One way to make word motions +operate as symbol motions is to alias the ``evil-word`` *thing* [#thingatpt]_ to +the ``evil-symbol`` thing: + +.. code-block:: elisp + + (defalias 'forward-evil-word 'forward-evil-symbol) + + +.. rubric:: Footnotes + +.. [#thingatpt] Many of Evil's text objects and motions are defined in + terms of the *thingatpt* library, which in this case are defined + entirely in terms of ``forward-THING`` functions. Thus aliasing + one to another should make all motions and text objects implemented + in terms of that *thing* behave the same. diff --git a/.config/emacs/lisp/evil/doc/source/hooks.rst b/.config/emacs/lisp/evil/doc/source/hooks.rst new file mode 100644 index 0000000..28ade72 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/hooks.rst @@ -0,0 +1,16 @@ +Hooks +===== + +A *hook* is a list of functions that are executed when certain events +happen. Hooks are modified with the Emacs function ``add-hook``. +Evil provides entry and exit hooks for all its states. For example, +when switching from normal state to insert state, all functions in +``evil-normal-state-exit-hook`` and ``evil-insert-state-entry-hook`` +are executed. + +It is guaranteed that the exit hook will be executed before the entry +hook on all state switches. + +During the hook execution, the variables ``evil-next-state`` and +``evil-previous-state`` contain information about the states being +switched to and from, respectively. diff --git a/.config/emacs/lisp/evil/doc/source/index.rst b/.config/emacs/lisp/evil/doc/source/index.rst new file mode 100644 index 0000000..95da7b7 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/index.rst @@ -0,0 +1,21 @@ +.. Evil documentation master file, created by + sphinx-quickstart on Thu Dec 12 10:34:49 2019. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Evil +==== + +.. toctree:: + :maxdepth: 2 + :caption: Contents + + overview + settings + keymaps + hooks + extension + faq + internals + license + indices diff --git a/.config/emacs/lisp/evil/doc/source/indices.rst b/.config/emacs/lisp/evil/doc/source/indices.rst new file mode 100644 index 0000000..46f687e --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/indices.rst @@ -0,0 +1,6 @@ +.. only:: html + + Index + ===== + + - :ref:`elispindex` diff --git a/.config/emacs/lisp/evil/doc/source/internals.rst b/.config/emacs/lisp/evil/doc/source/internals.rst new file mode 100644 index 0000000..9984f7c --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/internals.rst @@ -0,0 +1,36 @@ +Internals +========= + +Command properties +------------------ + +Evil defines *command properties* to store information about commands +[#command]_, such as whether they should be repeated. A command +property is a ``:keyword`` with an associated value, e.g. +``:repeat nil``. + +.. elisp:autofunction:: evil-add-command-properties + +.. elisp:autofunction:: evil-set-command-properties + +.. elisp:autofunction:: evil-get-command-properties + +.. elisp:autofunction:: evil-get-command-property + +.. elisp:autofunction:: evil-define-command + + +For setting repeat properties, use the following functions: + +.. elisp:autofunction:: evil-declare-repeat + +.. elisp:autofunction:: evil-declare-not-repeat + +.. elisp:autofunction:: evil-declare-change-repeat + + +.. rubric:: Footnotes + +.. [#command] In this context, a *command* may mean any Evil motion, + text object, operator or indeed other Emacs commands, which have + not been defined through the Evil machinery. diff --git a/.config/emacs/lisp/evil/doc/source/keymaps.rst b/.config/emacs/lisp/evil/doc/source/keymaps.rst new file mode 100644 index 0000000..36f8141 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/keymaps.rst @@ -0,0 +1,145 @@ +.. _chapter-keymaps: + +Keymaps +======= + +Evil's key bindings are stored in a number of different keymaps. Each +state has a *global keymap*, where the default bindings for that state +are stored. They are named ``evil-normal-state-map``, +``evil-insert-state-map``, and so on. The bindings in these maps are +visible in all buffers currently in the corresponding state. + +These keymaps function like ordinary Emacs keymaps and may be modified +using the Emacs function ``define-key``: + +.. code-block:: elisp + + (define-key evil-normal-state-map (kbd "w") 'some-function) + +This binds the key :kbd:`w` to the command ``some-function`` in normal +state. The use of ``kbd`` is optional for simple key sequences, like +this one, but recommended in general. + +Most of Evil's bindings are defined in the file ``evil-maps.el``. + +To facilitate shared keybindings between states, some states may +activate keybindings from other states as well. For example, motion +state bindings are visible in normal and visual state, and normal +state bindings are also visible in visual state. + +Each state also has a *buffer-local keymap* which is specific to the +current buffer, and which takes precedence over the global keymap. +These maps are most suitably modified by a mode hook. They are named +``evil-normal-state-local-map``, ``evil-insert-state-local-map``, and +so on. + +.. code-block:: elisp + + (add-hook 'some-mode-hook + (lambda () + (define-key evil-normal-state-local-map + (kbd "w") 'some-function))) + +For convenience, the functions :elisp:ref:`evil-global-set-key` and +:elisp:ref:`evil-local-set-key` are available for setting global and +local state keys. + +.. elisp:autofunction:: evil-global-set-key + +.. elisp:autofunction:: evil-local-set-key + +The above examples could therefore have been written as follows: + +.. code-block:: elisp + + (evil-global-set-key 'normal (kbd "w") 'some-function) + + (add-hook 'some-mode-hook + (lambda () + (evil-local-set-key 'normal (kbd "w") 'some-function))) + + +evil-define-key +--------------- + +Evil provides the macro :elisp:ref:`evil-define-key` for adding state +bindings to ordinary keymaps. It is quite powerful, and is the +preferred method for fine-tuning bindings to activate in specific +circumstances. + +.. elisp:autofunction:: evil-define-key + +There follows a brief overview of the main functions of this macro. + +- Define a binding in a given state + + .. code-block:: elisp + + (evil-define-key 'state 'global (kbd "key") 'target) + +- Define a binding in a given state in the current buffer + + .. code-block:: elisp + + (evil-define-key 'state 'local (kbd "key") 'target) + +- Define a binding in a given state under the *foo-mode* major mode. + + .. code-block:: elisp + + (evil-define-key 'state foo-mode-map (kbd "key") 'target) + + Note that ``foo-mode-map`` is unquoted, and that this form is safe + before ``foo-mode-map`` is loaded. + +- Define a binding in a given state under the *bar-mode* minor mode. + + .. code-block:: elisp + + (evil-define-key 'state 'bar-mode (kbd "key") 'target) + + Note that ``bar-mode`` is quoted, and that this form is safe before + ``bar-mode`` is loaded. + + +The macro :elisp:ref:`evil-define-key` can be used to augment existing +modes with state bindings, as well as creating packages with custom +bindings. For example, the following will create a minor mode +``foo-mode`` with normal state bindings for the keys :kbd:`w` and +:kbd:`e`: + +.. code-block:: elisp + + (define-minor-mode foo-mode + "Foo mode." + :keymap (make-sparse-keymap)) + + (evil-define-key 'normal 'foo-mode "w" 'bar) + (evil-define-key 'normal 'foo-mode "e" 'baz) + +This minor mode can then be enabled in any buffers where the custom +bindings are desired: + +.. code-block:: elisp + + (add-hook 'text-mode-hook 'foo-mode) ; enable alongside text-mode + + +Leader keys +----------- + +Evil supports a simple implementation of Vim's *leader* keys. To bind +a function to a leader key you can use the expression ```` in +a key mapping, e.g. + +.. code-block:: elisp + + (evil-define-key 'normal 'global (kbd "fs") 'save-buffer) + +Likewise, you can use the expression ```` to mimic Vim's +local leader, which is designed for mode-specific key bindings. + +You can use the function :elisp:ref:`evil-set-leader` to designate +which key acts as the leader and the local leader. + +.. elisp:autofunction:: evil-set-leader diff --git a/.config/emacs/lisp/evil/doc/source/license.rst b/.config/emacs/lisp/evil/doc/source/license.rst new file mode 100644 index 0000000..39d6701 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/license.rst @@ -0,0 +1,447 @@ +The GNU Free Documentation License +================================== + +Version 1.3, 3 November 2008 + + Copyright (c) 2000, 2001, 2002, 2007, 2008 Free Software + Foundation, Inc. http://fsf.org/ + + Everyone is permitted to copy and distribute verbatim copies of + this license document, but changing it is not allowed. + + +0. PREAMBLE + + The purpose of this License is to make a manual, textbook, or other + functional and useful document *free* in the sense of freedom: to + assure everyone the effective freedom to copy and redistribute it, + with or without modifying it, either commercially or + noncommercially. Secondarily, this License preserves for the + author and publisher a way to get credit for their work, while not + being considered responsible for modifications made by others. + + This License is a kind of "copyleft", which means that derivative + works of the document must themselves be free in the same sense. + It complements the GNU General Public License, which is a copyleft + license designed for free software. + + We have designed this License in order to use it for manuals for + free software, because free software needs free documentation: a + free program should come with manuals providing the same freedoms + that the software does. But this License is not limited to + software manuals; it can be used for any textual work, regardless + of subject matter or whether it is published as a printed book. We + recommend this License principally for works whose purpose is + instruction or reference. + +1. APPLICABILITY AND DEFINITIONS + + This License applies to any manual or other work, in any medium, + that contains a notice placed by the copyright holder saying it can + be distributed under the terms of this License. Such a notice + grants a world-wide, royalty-free license, unlimited in duration, + to use that work under the conditions stated herein. The + "Document", below, refers to any such manual or work. Any member + of the public is a licensee, and is addressed as "you". You accept + the license if you copy, modify or distribute the work in a way + requiring permission under copyright law. + + A "Modified Version" of the Document means any work containing the + Document or a portion of it, either copied verbatim, or with + modifications and/or translated into another language. + + A "Secondary Section" is a named appendix or a front-matter section + of the Document that deals exclusively with the relationship of the + publishers or authors of the Document to the Document's overall + subject (or to related matters) and contains nothing that could + fall directly within that overall subject. (Thus, if the Document + is in part a textbook of mathematics, a Secondary Section may not + explain any mathematics.) The relationship could be a matter of + historical connection with the subject or with related matters, or + of legal, commercial, philosophical, ethical or political position + regarding them. + + The "Invariant Sections" are certain Secondary Sections whose + titles are designated, as being those of Invariant Sections, in the + notice that says that the Document is released under this License. + If a section does not fit the above definition of Secondary then it + is not allowed to be designated as Invariant. The Document may + contain zero Invariant Sections. If the Document does not identify + any Invariant Sections then there are none. + + The "Cover Texts" are certain short passages of text that are + listed, as Front-Cover Texts or Back-Cover Texts, in the notice + that says that the Document is released under this License. A + Front-Cover Text may be at most 5 words, and a Back-Cover Text may + be at most 25 words. + + A "Transparent" copy of the Document means a machine-readable copy, + represented in a format whose specification is available to the + general public, that is suitable for revising the document + straightforwardly with generic text editors or (for images composed + of pixels) generic paint programs or (for drawings) some widely + available drawing editor, and that is suitable for input to text + formatters or for automatic translation to a variety of formats + suitable for input to text formatters. A copy made in an otherwise + Transparent file format whose markup, or absence of markup, has + been arranged to thwart or discourage subsequent modification by + readers is not Transparent. An image format is not Transparent if + used for any substantial amount of text. A copy that is not + "Transparent" is called "Opaque". + + Examples of suitable formats for Transparent copies include plain + ASCII without markup, Texinfo input format, LaTeX input format, + SGML or XML using a publicly available DTD, and standard-conforming + simple HTML, PostScript or PDF designed for human modification. + Examples of transparent image formats include PNG, XCF and JPG. + Opaque formats include proprietary formats that can be read and + edited only by proprietary word processors, SGML or XML for which + the DTD and/or processing tools are not generally available, and + the machine-generated HTML, PostScript or PDF produced by some word + processors for output purposes only. + + The "Title Page" means, for a printed book, the title page itself, + plus such following pages as are needed to hold, legibly, the + material this License requires to appear in the title page. For + works in formats which do not have any title page as such, "Title + Page" means the text near the most prominent appearance of the + work's title, preceding the beginning of the body of the text. + + The "publisher" means any person or entity that distributes copies + of the Document to the public. + + A section "Entitled XYZ" means a named subunit of the Document + whose title either is precisely XYZ or contains XYZ in parentheses + following text that translates XYZ in another language. (Here XYZ + stands for a specific section name mentioned below, such as + "Acknowledgements", "Dedications", "Endorsements", or "History".) + To "Preserve the Title" of such a section when you modify the + Document means that it remains a section "Entitled XYZ" according + to this definition. + + The Document may include Warranty Disclaimers next to the notice + which states that this License applies to the Document. These + Warranty Disclaimers are considered to be included by reference in + this License, but only as regards disclaiming warranties: any other + implication that these Warranty Disclaimers may have is void and + has no effect on the meaning of this License. + +2. VERBATIM COPYING + + You may copy and distribute the Document in any medium, either + commercially or noncommercially, provided that this License, the + copyright notices, and the license notice saying this License + applies to the Document are reproduced in all copies, and that you + add no other conditions whatsoever to those of this License. You + may not use technical measures to obstruct or control the reading + or further copying of the copies you make or distribute. However, + you may accept compensation in exchange for copies. If you + distribute a large enough number of copies you must also follow the + conditions in section 3. + + You may also lend copies, under the same conditions stated above, + and you may publicly display copies. + +3. COPYING IN QUANTITY + + If you publish printed copies (or copies in media that commonly + have printed covers) of the Document, numbering more than 100, and + the Document's license notice requires Cover Texts, you must + enclose the copies in covers that carry, clearly and legibly, all + these Cover Texts: Front-Cover Texts on the front cover, and + Back-Cover Texts on the back cover. Both covers must also clearly + and legibly identify you as the publisher of these copies. The + front cover must present the full title with all words of the title + equally prominent and visible. You may add other material on the + covers in addition. Copying with changes limited to the covers, as + long as they preserve the title of the Document and satisfy these + conditions, can be treated as verbatim copying in other respects. + + If the required texts for either cover are too voluminous to fit + legibly, you should put the first ones listed (as many as fit + reasonably) on the actual cover, and continue the rest onto + adjacent pages. + + If you publish or distribute Opaque copies of the Document + numbering more than 100, you must either include a machine-readable + Transparent copy along with each Opaque copy, or state in or with + each Opaque copy a computer-network location from which the general + network-using public has access to download using public-standard + network protocols a complete Transparent copy of the Document, free + of added material. If you use the latter option, you must take + reasonably prudent steps, when you begin distribution of Opaque + copies in quantity, to ensure that this Transparent copy will + remain thus accessible at the stated location until at least one + year after the last time you distribute an Opaque copy (directly or + through your agents or retailers) of that edition to the public. + + It is requested, but not required, that you contact the authors of + the Document well before redistributing any large number of copies, + to give them a chance to provide you with an updated version of the + Document. + +4. MODIFICATIONS + + You may copy and distribute a Modified Version of the Document + under the conditions of sections 2 and 3 above, provided that you + release the Modified Version under precisely this License, with the + Modified Version filling the role of the Document, thus licensing + distribution and modification of the Modified Version to whoever + possesses a copy of it. In addition, you must do these things in + the Modified Version: + + A. Use in the Title Page (and on the covers, if any) a title + distinct from that of the Document, and from those of previous + versions (which should, if there were any, be listed in the + History section of the Document). You may use the same title as + a previous version if the original publisher of that version + gives permission. + + B. List on the Title Page, as authors, one or more persons or + entities responsible for authorship of the modifications in the + Modified Version, together with at least five of the principal + authors of the Document (all of its principal authors, if it has + fewer than five), unless they release you from this requirement. + + C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + D. Preserve all the copyright notices of the Document. + + E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + + F. Include, immediately after the copyright notices, a license + notice giving the public permission to use the Modified Version + under the terms of this License, in the form shown in the + Addendum below. + + G. Preserve in that license notice the full lists of Invariant + Sections and required Cover Texts given in the Document's + license notice. + + H. Include an unaltered copy of this License. + + I. Preserve the section Entitled "History", Preserve its Title, and + add to it an item stating at least the title, year, new authors, + and publisher of the Modified Version as given on the Title + Page. If there is no section Entitled "History" in the + Document, create one stating the title, year, authors, and + publisher of the Document as given on its Title Page, then add + an item describing the Modified Version as stated in the + previous sentence. + + J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and + likewise the network locations given in the Document for + previous versions it was based on. These may be placed in the + "History" section. You may omit a network location for a work + that was published at least four years before the Document + itself, or if the original publisher of the version it refers to + gives permission. + + K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section + all the substance and tone of each of the contributor + acknowledgements and/or dedications given therein. + + L. Preserve all the Invariant Sections of the Document, unaltered + in their text and in their titles. Section numbers or the + equivalent are not considered part of the section titles. + + M. Delete any section Entitled "Endorsements". Such a section may + not be included in the Modified Version. + + N. Do not retitle any existing section to be Entitled + "Endorsements" or to conflict in title with any Invariant + Section. + + O. Preserve any Warranty Disclaimers. + + If the Modified Version includes new front-matter sections or + appendices that qualify as Secondary Sections and contain no + material copied from the Document, you may at your option designate + some or all of these sections as invariant. To do this, add their + titles to the list of Invariant Sections in the Modified Version's + license notice. These titles must be distinct from any other + section titles. + + You may add a section Entitled "Endorsements", provided it contains + nothing but endorsements of your Modified Version by various + parties---for example, statements of peer review or that the text + has been approved by an organization as the authoritative + definition of a standard. + + You may add a passage of up to five words as a Front-Cover Text, + and a passage of up to 25 words as a Back-Cover Text, to the end of + the list of Cover Texts in the Modified Version. Only one passage + of Front-Cover Text and one of Back-Cover Text may be added by (or + through arrangements made by) any one entity. If the Document + already includes a cover text for the same cover, previously added + by you or by arrangement made by the same entity you are acting on + behalf of, you may not add another; but you may replace the old + one, on explicit permission from the previous publisher that added + the old one. + + The author(s) and publisher(s) of the Document do not by this + License give permission to use their names for publicity for or to + assert or imply endorsement of any Modified Version. + +5. COMBINING DOCUMENTS + + You may combine the Document with other documents released under + this License, under the terms defined in section 4 above for + modified versions, provided that you include in the combination all + of the Invariant Sections of all of the original documents, + unmodified, and list them all as Invariant Sections of your + combined work in its license notice, and that you preserve all + their Warranty Disclaimers. + + The combined work need only contain one copy of this License, and + multiple identical Invariant Sections may be replaced with a single + copy. If there are multiple Invariant Sections with the same name + but different contents, make the title of each such section unique + by adding at the end of it, in parentheses, the name of the + original author or publisher of that section if known, or else a + unique number. Make the same adjustment to the section titles in + the list of Invariant Sections in the license notice of the + combined work. + + In the combination, you must combine any sections Entitled + "History" in the various original documents, forming one section + Entitled "History"; likewise combine any sections Entitled + "Acknowledgements", and any sections Entitled "Dedications". You + must delete all sections Entitled "Endorsements." + +6. COLLECTIONS OF DOCUMENTS + + You may make a collection consisting of the Document and other + documents released under this License, and replace the individual + copies of this License in the various documents with a single copy + that is included in the collection, provided that you follow the + rules of this License for verbatim copying of each of the documents + in all other respects. + + You may extract a single document from such a collection, and + distribute it individually under this License, provided you insert + a copy of this License into the extracted document, and follow this + License in all other respects regarding verbatim copying of that + document. + +7. AGGREGATION WITH INDEPENDENT WORKS + + A compilation of the Document or its derivatives with other + separate and independent documents or works, in or on a volume of a + storage or distribution medium, is called an "aggregate" if the + copyright resulting from the compilation is not used to limit the + legal rights of the compilation's users beyond what the individual + works permit. When the Document is included in an aggregate, this + License does not apply to the other works in the aggregate which + are not themselves derivative works of the Document. + + If the Cover Text requirement of section 3 is applicable to these + copies of the Document, then if the Document is less than one half + of the entire aggregate, the Document's Cover Texts may be placed + on covers that bracket the Document within the aggregate, or the + electronic equivalent of covers if the Document is in electronic + form. Otherwise they must appear on printed covers that bracket + the whole aggregate. + +8. TRANSLATION + + Translation is considered a kind of modification, so you may + distribute translations of the Document under the terms of + section 4. Replacing Invariant Sections with translations requires + special permission from their copyright holders, but you may + include translations of some or all Invariant Sections in addition + to the original versions of these Invariant Sections. You may + include a translation of this License, and all the license notices + in the Document, and any Warranty Disclaimers, provided that you + also include the original English version of this License and the + original versions of those notices and disclaimers. In case of a + disagreement between the translation and the original version of + this License or a notice or disclaimer, the original version will + prevail. + + If a section in the Document is Entitled "Acknowledgements", + "Dedications", or "History", the requirement (section 4) to + Preserve its Title (section 1) will typically require changing the + actual title. + +9. TERMINATION + + You may not copy, modify, sublicense, or distribute the Document + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, or distribute it is void, + and will automatically terminate your rights under this License. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, receipt of a copy of some or all of the + same material does not give you any rights to use it. + +10. FUTURE REVISIONS OF THIS LICENSE + + The Free Software Foundation may publish new, revised versions of + the GNU Free Documentation License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. See + http://www.gnu.org/copyleft. + + Each version of the License is given a distinguishing version + number. If the Document specifies that a particular numbered + version of this License "or any later version" applies to it, you + have the option of following the terms and conditions either of + that specified version or of any later version that has been + published (not as a draft) by the Free Software Foundation. If the + Document does not specify a version number of this License, you may + choose any version ever published (not as a draft) by the Free + Software Foundation. If the Document specifies that a proxy can + decide which future versions of this License can be used, that + proxy's public statement of acceptance of a version permanently + authorizes you to choose that version for the Document. + +11. RELICENSING + + "Massive Multiauthor Collaboration Site" (or "MMC Site") means any + World Wide Web server that publishes copyrightable works and also + provides prominent facilities for anybody to edit those works. A + public wiki that anybody can edit is an example of such a server. + A "Massive Multiauthor Collaboration" (or "MMC") contained in the + site means any set of copyrightable works thus published on the MMC + site. + + "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 + license published by Creative Commons Corporation, a not-for-profit + corporation with a principal place of business in San Francisco, + California, as well as future copyleft versions of that license + published by that same organization. + + "Incorporate" means to publish or republish a Document, in whole or + in part, as part of another Document. + + An MMC is "eligible for relicensing" if it is licensed under this + License, and if all works that were first published under this + License somewhere other than this MMC, and subsequently + incorporated in whole or in part into the MMC, (1) had no cover + texts or invariant sections, and (2) were thus incorporated prior + to November 1, 2008. + + The operator of an MMC Site may republish an MMC contained in the + site under CC-BY-SA on the same site at any time before August 1, + 2009, provided the MMC is eligible for relicensing. diff --git a/.config/emacs/lisp/evil/doc/source/overview.rst b/.config/emacs/lisp/evil/doc/source/overview.rst new file mode 100644 index 0000000..96af183 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/overview.rst @@ -0,0 +1,117 @@ +Overview +======== + +Evil is an extensible vi layer for Emacs. It emulates the main +features of Vim, [#vim]_ turning Emacs into a modal editor. Like Emacs in +general, Evil is extensible in Emacs Lisp. + + +Installation via package.el +--------------------------- + +Evil is available as a package from MELPA stable, MELPA unstable and +NonGNU ELPA. This is the recommended way of installing Evil. + +To set up `package.el` to work with one of the MELPA repositories, you +can follow the instructions on `melpa.org +`_. + +Alternatively you can use NonGNU ELPA. It is part of the default +package archives as of Emacs 28. For older Emacs versions you'll need +to add it yourself: + +.. code-block:: elisp + + (add-to-list 'package-archives + (cons "nongnu" (format "http%s://elpa.nongnu.org/nongnu/" + (if (gnutls-available-p) "s" "")))) + +Once that is done, you can execute the following commands:: + + M-x package-refresh-contents + M-x package-install RET evil RET + +Finally, add the following lines to your Emacs init file: + +.. code-block:: elisp + + (require 'evil) + (evil-mode 1) + + +Manual installation +------------------- + +First, install `goto-chg` and `cl-lib`. If you have an Emacs version +of 24.3 or newer, you should already have `cl-lib`. + +Evil lives in a git repository. To download Evil, do:: + + git clone --depth 1 https://github.com/emacs-evil/evil.git + +Then add the following lines to your Emacs init file: + +.. code-block:: elisp + + (add-to-list 'load-path "path/to/evil") + (require 'evil) + (evil-mode 1) + +Ensure that your replace ``path/to/evil`` with the actual path to +where you cloned Evil. + + +Modes and states +---------------- + +The next time Emacs is started, it will come up in *normal state*, +denoted by ```` in the mode line. This is where the main vi +bindings are defined. Note that you can always disable normal state +with :kbd:`C-z`, which switches to an "Emacs state" (denoted by +````) in which vi keys are completely disabled. Press :kbd:`C-z` +again to switch back to normal state. + +state + Evil uses the term *state* for what is called a "mode" in regular vi + usage, because *modes* are understood in Emacs terms to mean + something else. + +Evil defines a number of states by default: + +normal state (````) + This is the default "resting state" of Evil, in which the main body + of vi bindings are defined. + +insert state (````) + This is the state for insertion of text, where non-modified keys + will insert the corresponding character in the buffer. + +visual state (````) + A state for selecting text regions. Motions are available for + modifying the selected region, and operators are available for + acting on it. + +replace state (````) + A special state mostly similar to insert state, except it replaces + text instead of inserting. + +operator-pending state (````) + A special state entered after launching an operator, but before + specifying the corresponding motion or text object. + +motion state (````) + A special state useful for buffers that are read-only, where motions + are available but editing operations are not. + +Emacs state (````) + A state that as closely as possible mimics default Emacs behaviour, + by eliminating all vi bindings, except for :kbd:`C-z`, to re-enter + normal state. + + +.. rubric:: Footnotes + +.. [#vim] Vim is the most popular version of *vi*, a modal text editor + with many implementations. Vim also adds some functions of its + own, like visual selection and text objects. For more information + see `the official Vim website `_. diff --git a/.config/emacs/lisp/evil/doc/source/settings.rst b/.config/emacs/lisp/evil/doc/source/settings.rst new file mode 100644 index 0000000..8ca78e6 --- /dev/null +++ b/.config/emacs/lisp/evil/doc/source/settings.rst @@ -0,0 +1,199 @@ +Settings +======== + +Evil's behaviour can be adjusted by setting some variables. The list +of all available variables and their current values can be inspected +by doing:: + + M-x customize-group RET evil RET + +To change the value of a variable, you can use this interface, or add +a ``setq`` form to your Emacs init file, preferably before Evil is +loaded. [#order]_ + +.. code-block:: elisp + + (setq evil-shift-width 0) + ;; Load Evil + (require 'evil) + +What follows is a non-exhaustive list of the most relevant +customization options. + + +The initial state +----------------- + +The initial state of a buffer is determined by its major mode. Evil +maintains an association between major modes and their corresponding +states, which is most easily modified using the function +:elisp:ref:`evil-set-initial-state`. + +.. elisp:autofunction:: evil-set-initial-state + +If no state can be found, Evil uses the default initial state. + +.. elisp:autovariable:: evil-default-state + +Alternatively, it is possible to select the initial state based on the +buffer *name* rather than its major mode. This is checked first, so +it takes precedence over the other methods for setting the state. + +.. elisp:autovariable:: evil-buffer-regexps + + +Keybindings and other behaviour +------------------------------- + +Evil comes with a rich system for modifying its key bindings +:ref:`chapter-keymaps`. For the most common tweaks, the following +variables are available. + +.. elisp:autovariable:: evil-toggle-key + +.. elisp:autovariable:: evil-want-C-i-jump + +.. elisp:autovariable:: evil-want-C-u-delete + +.. elisp:autovariable:: evil-want-C-u-scroll + +.. elisp:autovariable:: evil-want-C-d-scroll + +.. elisp:autovariable:: evil-want-C-w-delete + +.. elisp:autovariable:: evil-want-C-w-in-emacs-state + +.. elisp:autovariable:: evil-want-Y-yank-to-eol + +.. elisp:autovariable:: evil-disable-insert-state-bindings + + +Search +------ + +.. elisp:autovariable:: evil-search-module + +.. elisp:autovariable:: evil-regexp-search + +.. elisp:autovariable:: evil-search-wrap + +.. elisp:autovariable:: evil-flash-delay + +.. elisp:autovariable:: evil-ex-hl-update-delay + +.. elisp:autovariable:: evil-ex-search-incremental + + +Indentation +----------- + +.. elisp:autovariable:: evil-auto-indent + +.. elisp:autovariable:: evil-shift-width + +.. elisp:autovariable:: evil-shift-round + +.. elisp:autovariable:: evil-indent-convert-tabs + + +Cursor movement +--------------- + +In standard Emacs terms, the cursor is generally understood to be +located between two characters. In Vim, and therefore also Evil, this +is the case in insert state, but in other states the cursor is +understood to be *on* a character, and that this character is not a +newline. + +Forcing this behaviour in Emacs is the source of some potentially +surprising results (especially for traditional Emacs users---users +used to Vim may find the default behavior to their satisfaction). Many +of them can be tweaked using the following variables. + +.. elisp:autovariable:: evil-repeat-move-cursor + +.. elisp:autovariable:: evil-move-cursor-back + +.. elisp:autovariable:: evil-move-beyond-eol + +.. elisp:autovariable:: evil-v$-excludes-newline + +.. elisp:autovariable:: evil-cross-lines + +.. elisp:autovariable:: evil-respect-visual-line-mode + +.. elisp:autovariable:: evil-track-eol + +.. elisp:autovariable:: evil-start-of-line + + +Cursor display +-------------- + +A state may change the appearance of the cursor. Use the variable +:elisp:ref:`evil-default-cursor` to set the default cursor, and the +variables ``evil-normal-state-cursor``, ``evil-insert-state-cursor`` +etc. to set the cursors for specific states. The acceptable values +for all of them are the same. + +.. elisp:autovariable:: evil-default-cursor + + +Window management +----------------- + +.. elisp:autovariable:: evil-auto-balance-windows + +.. elisp:autovariable:: evil-split-window-below + +.. elisp:autovariable:: evil-vsplit-window-right + + +Parenthesis highlighting +------------------------ + +These settings concern the integration between Evil and +``show-paren-mode``. They take no effect if this mode is not enabled. + +.. elisp:autovariable:: evil-show-paren-range + +.. elisp:autovariable:: evil-highlight-closing-paren-at-point-states + + +Miscellaneous +------------- + +.. elisp:autovariable:: evil-want-fine-undo + +.. elisp:autovariable:: evil-undo-system + +.. elisp:autovariable:: evil-backspace-join-lines + +.. elisp:autovariable:: evil-kbd-macro-suppress-motion-error + +.. elisp:autovariable:: evil-mode-line-format + +.. elisp:autovariable:: evil-mouse-word + +.. elisp:autovariable:: evil-transient-mouse-selection + +.. elisp:autovariable:: evil-bigword + +.. elisp:autovariable:: evil-esc-delay + +.. elisp:autovariable:: evil-intercept-esc + +.. elisp:autovariable:: evil-kill-on-visual-paste + +.. elisp:autovariable:: evil-echo-state + +.. elisp:autovariable:: evil-complete-all-buffers + +.. elisp:autovariable:: evil-want-empty-ex-last-command + + +.. rubric:: Footnotes + +.. [#order] Strictly speaking, the order only matters if the variable + affects the way Evil is loaded. This is the case with some + variables. diff --git a/.config/emacs/lisp/evil/evil-command-window.el b/.config/emacs/lisp/evil/evil-command-window.el new file mode 100644 index 0000000..ef22be0 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-command-window.el @@ -0,0 +1,191 @@ +;;; evil-command-window.el --- Evil command-line window -*- lexical-binding: t -*- +;; Author: Emanuel Evans +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; This provides an implementation of the Vim command-line window for +;; editing and repeating past Ex commands and searches. + +;;; Code: + +(require 'evil-vars) +(require 'evil-common) +(require 'evil-ex) +(require 'evil-search) + +(defvar evil-search-module) + +(defvar evil-command-window-current-buffer nil + "The buffer from which the command-line window was called.") + +(defvar evil-command-window-execute-fn nil + "The command to execute when exiting the command-line window.") + +(defvar evil--command-window-prompt nil + "The key for the command that opened the command-line window (:, /, or ?).") + +(define-derived-mode evil-command-window-mode fundamental-mode "Evil-cmd" + "Major mode for the Evil command-line window." + (add-hook 'after-change-functions #'evil--command-window-draw-prefix nil t) + (auto-fill-mode 0)) + +(defun evil-command-window (history prompt execute-fn) + "Open a command-line window for HISTORY with PROMPT and EXECUTE-FN. +HISTORY should be a list of commands. PROMPT should be the +command-line prompt (one of \":\", \"/\" or \"?\"). EXECUTE-FN should +be a unary function to execute on the result that the user selects. + +If called interactively, edit this minibuffer argument." + (interactive + (list (cons (minibuffer-contents) (minibuffer-history-value)) + (or (minibuffer-prompt) (user-error "Minibuffer is inactive")) + #'evil--command-window-minibuffer-execute)) + (when (derived-mode-p 'evil-command-window-mode) + (user-error "Command-line window is already open")) + (when (evil-ex-p) (evil-ex-teardown)) + (let ((previous-buffer (current-buffer)) + (buffer (get-buffer-create "*Command Line*"))) + (with-current-buffer buffer + (erase-buffer) + (evil-command-window-mode) + (setq-local evil-command-window-current-buffer previous-buffer) + (setq-local evil-command-window-execute-fn execute-fn) + (setq-local evil--command-window-prompt prompt) + (evil--command-window-insert-commands history)) + + (let* ((action + `((display-buffer-reuse-window display-buffer-at-bottom) + ,@(unless (zerop evil-command-window-height) + `((window-height body-lines . ,evil-command-window-height) + (preserve-size nil . t))) + (dedicated . t))) + (window (display-buffer buffer action)) + (delete-window-fun + (lambda (window) + (set-window-parameter window 'delete-window nil) + (delete-window window) + (switch-to-minibuffer)))) + (when (minibufferp) + (set-window-parameter window 'delete-window delete-window-fun)) + (select-window window))) + (goto-char (point-max)) + (unless (bobp) (backward-char) (evil-adjust-cursor))) + +(defun evil--command-window-draw-prefix (beg end _old-len) + "Display `evil--command-window-prompt' as a prefix of the changed lines." + (let ((prefix (propertize evil--command-window-prompt + 'font-lock-face 'minibuffer-prompt))) + (put-text-property beg end 'line-prefix prefix))) + +(defun evil--command-window-insert-commands (history) + "Insert the commands in HISTORY." + (let ((inhibit-modification-hooks t)) + (dolist (cmd (reverse history)) (insert cmd "\n")) + (evil--command-window-draw-prefix (point-min) (point-max) nil))) + +(defun evil-command-window-execute () + "Execute the command on the current line in the appropriate buffer. +The local variable `evil-command-window-execute-fn' determines which +function to execute." + (interactive) + (let ((result (buffer-substring-no-properties + (line-beginning-position) (line-end-position))) + (original-buffer evil-command-window-current-buffer) + (execute-fn evil-command-window-execute-fn)) + (let ((ignore-window-parameters t)) + (ignore-errors (kill-buffer-and-window))) + (unless (buffer-live-p original-buffer) + (user-error "Originating buffer is no longer active")) + (let ((window (get-buffer-window original-buffer))) + (when window (select-window window))) + (with-current-buffer original-buffer (funcall execute-fn result)))) + +(defun evil--command-window-minibuffer-execute (result) + "Terminate this minibuffer argument with RESULT." + (delete-minibuffer-contents) + (insert result) + (exit-minibuffer)) + +(defun evil-command-window-ex (&optional current-command) + "Open a command-line window for editing and executing Ex commands. +If CURRENT-COMMAND is present, it will be inserted under the cursor as +the current command to be edited." + (interactive) + (evil-command-window (cons (or current-command "") evil-ex-history) + ":" + #'evil-command-window-ex-execute)) + +(define-obsolete-function-alias + 'evil-ex-command-window #'evil-command-window "1.15.0" + "Start command window with Ex history and current minibuffer content.") + +(define-obsolete-function-alias + 'evil-ex-search-command-window #'evil-command-window "1.15.0" + "Start command window with search history and current minibuffer content.") + +(defun evil-command-window-ex-execute (result) + "Execute RESULT as an Ex command." + (unless (string-match-p "\\`[ \t\n\r]*\\'" result) + (unless (equal result (car evil-ex-history)) + (push result evil-ex-history)) + (evil-ex-execute result))) + +(defun evil--command-window-search (forward) + "Open a command-line window for searches." + (evil-command-window + (cons "" (cond ((eq evil-search-module 'evil-search) + evil-ex-search-history) + (forward evil-search-forward-history) + (t evil-search-backward-history))) + (evil-search-prompt forward) + (lambda (result) (evil-command-window-search-execute result forward)))) + +(defun evil-command-window-search-forward () + "Open a command-line window for forward searches." + (interactive) + (evil--command-window-search t)) + +(defun evil-command-window-search-backward () + "Open a command-line window for backward searches." + (interactive) + (evil--command-window-search nil)) + +(defun evil-command-window-search-execute (result forward) + "Search for RESULT using FORWARD to determine direction." + (unless (string= result "") + (if (eq evil-search-module 'evil-search) + (progn + (setq evil-ex-search-pattern (evil-ex-make-search-pattern result) + evil-ex-search-direction (if forward 'forward 'backward)) + (unless (equal result (car evil-ex-search-history)) + (push result evil-ex-search-history)) + (evil-ex-search)) + (evil-push-search-history result forward) + (evil-search result forward evil-regexp-search)))) + +(provide 'evil-command-window) + +;;; evil-command-window.el ends here diff --git a/.config/emacs/lisp/evil/evil-commands.el b/.config/emacs/lisp/evil/evil-commands.el new file mode 100644 index 0000000..5dbda83 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-commands.el @@ -0,0 +1,5288 @@ +;;; evil-commands.el --- Evil commands and operators -*- lexical-binding: t -*- +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Code: + +(require 'evil-common) +(require 'evil-search) +(require 'evil-states) +(require 'evil-ex) +(require 'evil-types) +(require 'evil-command-window) +(require 'evil-jumps) +(require 'evil-vars) +(require 'cl-lib) +(require 'reveal) + +(declare-function imenu--in-alist "imenu") + +;;; Motions + +;; Movement commands, or motions, are defined with the macro +;; `evil-define-motion'. A motion is a command with an optional +;; argument COUNT (interactively accessed by the code ""). +;; It may specify the :type command property (e.g., :type line), +;; which determines how it is handled by an operator command. +;; Furthermore, the command must have the command properties +;; :keep-visual t and :repeat motion; these are automatically +;; set by the `evil-define-motion' macro. + +(evil-define-motion evil-forward-char (count &optional crosslines noerror) + "Move cursor to the right by COUNT characters. +Movement is restricted to the current line unless CROSSLINES is non-nil. +If NOERROR is non-nil, don't signal an error upon reaching the end +of the line or the buffer; just return nil." + :type exclusive + (interactive "" (list evil-cross-lines + (evil-kbd-macro-suppress-motion-error))) + (cond + ((not crosslines) + ;; For efficiency, narrow the buffer to the projected + ;; movement before determining the current line + (evil-with-restriction nil (min (+ (point) (or count 1) 1) (point-max)) + (condition-case err + (evil-narrow-to-line (forward-char count)) + (error + ;; Restore the previous command (this one never happened). + ;; This preserves the current column if the previous command + ;; was `evil-next-line' or `evil-previous-line'. + (setq this-command last-command) + (unless noerror (signal (car err) (cdr err))))))) + (noerror (ignore-errors (evil-forward-char count crosslines))) + (t (evil-motion-loop (nil (or count 1)) + (forward-char) + ;; don't put the cursor on a newline + (or evil-move-beyond-eol + (evil-visual-state-p) (evil-operator-state-p) + (not (eolp)) (bolp) + (forward-char)))))) + +(evil-define-motion evil-backward-char (count &optional crosslines noerror) + "Move cursor to the left by COUNT characters. +Movement is restricted to the current line unless CROSSLINES is non-nil. +If NOERROR is non-nil, don't signal an error upon reaching the beginning +of the line or the buffer; just return nil." + :type exclusive + (interactive "" (list evil-cross-lines + (evil-kbd-macro-suppress-motion-error))) + (cond + ((not crosslines) + ;; Restrict movement to the current line + (evil-with-restriction + (max (- (point) (or count 1)) (point-min)) + (min (1+ (point)) (point-max)) + (condition-case err + (evil-narrow-to-line (backward-char count)) + (error + ;; Restore the previous command (this one never happened). + ;; This preserves the current column if the previous command + ;; was `evil-next-line' or `evil-previous-line'. + (setq this-command last-command) + (unless noerror (signal (car err) (cdr err))))))) + (noerror (ignore-errors (evil-backward-char count crosslines))) + (t (evil-motion-loop (nil (or count 1)) + (backward-char) + ;; don't put the cursor on a newline + (unless (or (evil-visual-state-p) (evil-operator-state-p)) + (evil-adjust-cursor)))))) + +(evil-define-motion evil-next-line (count) + "Move the cursor COUNT lines down." + :type line + (let (line-move-visual) + (evil-line-move (or count 1)))) + +(evil-define-motion evil-previous-line (count) + "Move the cursor COUNT lines up." + :type line + (let (line-move-visual) + (evil-line-move (- (or count 1))))) + +(evil-define-motion evil-next-visual-line (count) + "Move the cursor COUNT screen lines down." + :type exclusive + (let ((line-move-visual t)) + (evil-line-move (or count 1)))) + +(evil-define-motion evil-previous-visual-line (count) + "Move the cursor COUNT screen lines up." + :type exclusive + (let ((line-move-visual t)) + (evil-line-move (- (or count 1))))) + +;; Used for repeated commands like "dd" +(evil-define-motion evil-line (count) + "Move COUNT - 1 lines down." + :type line + (let (line-move-visual) + (evil-line-move (1- (or count 1)) t))) + +(evil-define-motion evil-line-or-visual-line (count) + "Move COUNT - 1 lines down." + :type screen-line + (let ((line-move-visual (and evil-respect-visual-line-mode + visual-line-mode))) + (evil-line-move (1- (or count 1)) t))) + +(evil-define-motion evil-beginning-of-line () + "Move the cursor to the beginning of the current line." + :type exclusive + (move-beginning-of-line nil)) + +(evil-define-motion evil-end-of-line (count) + "Move the cursor to the end of the current line. +If COUNT is given, move COUNT - 1 lines downward first." + :type inclusive + (move-end-of-line count) + (when evil-track-eol + (setq temporary-goal-column most-positive-fixnum + this-command 'next-line)) + (let ((evil-move-beyond-eol + (if (evil-visual-state-p) (not evil-v$-excludes-newline) + evil-move-beyond-eol))) + (evil-adjust-cursor)) + ;; Prevent "c$" and "d$" from deleting blank lines + (when (eolp) (setq evil-this-type 'exclusive))) + +(evil-define-motion evil-beginning-of-visual-line () + "Move the cursor to the first character of the current screen line." + :type exclusive + (beginning-of-visual-line)) + +(evil-define-motion evil-end-of-visual-line (count) + "Move the cursor to the last character of the current screen line. +If COUNT is given, move COUNT - 1 screen lines downward first." + :type inclusive + (end-of-visual-line count) + (evil-move-cursor-back t)) + +(evil-define-motion evil-end-of-line-or-visual-line (count) + "Move the cursor to the last character of the current screen +line if `visual-line-mode' is active and +`evil-respect-visual-line-mode' is non-nil. If COUNT is given, +move COUNT - 1 screen lines downward first." + :type inclusive + (if (and evil-respect-visual-line-mode + visual-line-mode) + (evil-end-of-visual-line count) + (evil-end-of-line count))) + +(evil-define-motion evil-middle-of-visual-line () + "Move the cursor to the middle of the current visual line." + :type exclusive + (beginning-of-visual-line) + (evil-with-restriction + nil + (save-excursion (end-of-visual-line) (point)) + (move-to-column + (+ (current-column) -1 (/ (window-body-width) 2))))) + +(evil-define-motion evil-percentage-of-line (count) + "Move the cursor to COUNT % of the width of the current line. +If no COUNT is given, default to 50%." + :type exclusive + (let ((line-length (- (line-end-position) (line-beginning-position) + (if evil-move-beyond-eol -1 0)))) + (move-to-column (truncate (* line-length (or count 50)) 100)))) + +(evil-define-motion evil-first-non-blank () + "Move the cursor to the first non-blank character of the current line." + :type exclusive + (evil-narrow-to-line (back-to-indentation))) + +(evil-define-motion evil-last-non-blank (count) + "Move the cursor to the last non-blank character of the current line. +If COUNT is given, move COUNT - 1 lines downward first." + :type inclusive + (evil-move-end-of-line count) + (skip-chars-backward " \t") + (unless (bolp) (backward-char))) + +(evil-define-motion evil-first-non-blank-of-visual-line () + "Move the cursor to the first non blank character +of the current screen line." + :type exclusive + (evil-beginning-of-visual-line) + (skip-chars-forward " \t\r")) + +(evil-define-motion evil-next-line-first-non-blank (count) + "Move the cursor COUNT lines down on the first non-blank character." + :type line + (let ((this-command this-command)) + (evil-next-line (or count 1))) + (evil-first-non-blank)) + +(evil-define-motion evil-next-line-1-first-non-blank (count) + "Move the cursor COUNT-1 lines down on the first non-blank character." + :type line + (let ((this-command this-command)) + (evil-next-line (1- (or count 1)))) + (evil-first-non-blank)) + +(evil-define-motion evil-previous-line-first-non-blank (count) + "Move the cursor COUNT lines up on the first non-blank character." + :type line + (let ((this-command this-command)) + (evil-previous-line (or count 1))) + (evil-first-non-blank)) + +(evil-define-motion evil-goto-line (count) + "Go to line COUNT. By default the last line." + :jump t + :type line + (evil-ensure-column + (if (null count) + (progn + (goto-char (point-max)) + (when (and (eq (current-buffer) (window-buffer)) + (> (point) (window-end nil t))) + ;; If the end of the buffer is not already on the screen, scroll to + ;; position it near the bottom. + (overlay-recenter (point)) + (recenter (and (< 100 scroll-conservatively) -1)))) + (goto-char (point-min)) + (forward-line (1- count))))) + +(evil-define-motion evil-goto-first-line (count) + "Go to line COUNT. By default the first line." + :jump t + :type line + (evil-goto-line (or count 1))) + +(evil-define-motion evil-forward-word-begin (count &optional bigword) + "Move the cursor to the beginning of the COUNT-th next word. +If BIGWORD is non-nil, move by WORDS. + +If this command is called in operator-pending state it behaves +differently. If point reaches the beginning of a word on a new +line point is moved back to the end of the previous line. + +If called after a change operator, i.e. cw or cW, +`evil-want-change-word-to-end' is non-nil and point is on a word, +then both behave like ce or cE. + +If point is at the end of the buffer and cannot be moved then +`end-of-buffer' is signaled." + :type exclusive + (let ((thing (if bigword 'evil-WORD 'evil-word)) + (orig (point)) + (count (or count 1))) + (evil-signal-at-bob-or-eob count) + (cond + ;; default motion, beginning of next word + ((not (evil-operator-state-p)) + (evil-forward-beginning thing count)) + ;; the evil-change operator, maybe behave like ce or cE + ((and evil-want-change-word-to-end + (memq evil-this-operator evil-change-commands) + (< orig (or (cdr-safe (bounds-of-thing-at-point thing)) orig))) + ;; forward-thing moves point to the correct position because + ;; this is an exclusive motion + (forward-thing thing count)) + ;; operator state + (t + (prog1 (evil-forward-beginning thing count) + ;; if we reached the beginning of a word on a new line in + ;; Operator-Pending state, go back to the end of the previous + ;; line + (when (and (> (line-beginning-position) orig) + (looking-back "^[[:space:]]*" (line-beginning-position))) + ;; move cursor back as long as the line contains only + ;; whitespaces and is non-empty + (evil-move-end-of-line 0) + ;; skip non-empty lines containing only spaces + (while (and (looking-back "^[[:space:]]+$" (line-beginning-position)) + (not (<= (line-beginning-position) orig))) + (evil-move-end-of-line 0)) + ;; but if the previous line is empty, delete this line + (when (bolp) (forward-char)))))))) + +(evil-define-motion evil-forward-word-end (count &optional bigword) + "Move the cursor to the end of the COUNT-th next word. +If BIGWORD is non-nil, move by WORDS." + :type inclusive + (let ((thing (if bigword 'evil-WORD 'evil-word)) + (count (or count 1))) + (evil-signal-at-bob-or-eob count) + ;; Evil special behaviour: e or E on a one-character word in + ;; operator state does not move point + (unless (and (evil-operator-state-p) + (= 1 count) + (let ((bnd (bounds-of-thing-at-point thing))) + (and bnd + (= (car bnd) (point)) + (= (cdr bnd) (1+ (point))))) + (looking-at "[[:word:]]")) + (evil-forward-end thing count)))) + +(evil-define-motion evil-backward-word-begin (count &optional bigword) + "Move the cursor to the beginning of the COUNT-th previous word. +If BIGWORD is non-nil, move by WORDS." + :type exclusive + (let ((thing (if bigword 'evil-WORD 'evil-word))) + (evil-signal-at-bob-or-eob (- (or count 1))) + (evil-backward-beginning thing count))) + +(evil-define-motion evil-backward-word-end (count &optional bigword) + "Move the cursor to the end of the COUNT-th previous word. +If BIGWORD is non-nil, move by WORDS." + :type inclusive + (let ((thing (if bigword 'evil-WORD 'evil-word))) + (evil-signal-at-bob-or-eob (- (or count 1))) + (evil-backward-end thing count))) + +(evil-define-motion evil-forward-WORD-begin (count) + "Move the cursor to the beginning of the COUNT-th next WORD." + :type exclusive + (evil-forward-word-begin count t)) + +(evil-define-motion evil-forward-WORD-end (count) + "Move the cursor to the end of the COUNT-th next WORD." + :type inclusive + (evil-forward-word-end count t)) + +(evil-define-motion evil-backward-WORD-begin (count) + "Move the cursor to the beginning of the COUNT-th previous WORD." + :type exclusive + (evil-backward-word-begin count t)) + +(evil-define-motion evil-backward-WORD-end (count) + "Move the cursor to the end of the COUNT-th previous WORD." + :type inclusive + (evil-backward-word-end count t)) + +;; section movement +(evil-define-motion evil-forward-section-begin (count) + "Move the cursor to the beginning of the COUNT-th next section." + :jump t + :type exclusive + (evil-signal-at-bob-or-eob count) + (evil-forward-beginning 'evil-defun count)) + +(evil-define-motion evil-forward-section-end (count) + "Move the cursor to the end of the COUNT-th next section." + :jump t + :type inclusive + (evil-signal-at-bob-or-eob count) + (evil-forward-end 'evil-defun count) + (unless (eobp) (forward-line))) + +(evil-define-motion evil-backward-section-begin (count) + "Move the cursor to the beginning of the COUNT-th previous section." + :jump t + :type exclusive + (evil-signal-at-bob-or-eob (- (or count 1))) + (evil-backward-beginning 'evil-defun count)) + +(evil-define-motion evil-backward-section-end (count) + "Move the cursor to the end of the COUNT-th previous section." + :jump t + :type inclusive + (evil-signal-at-bob-or-eob (- (or count 1))) + (end-of-line -1) + (evil-backward-end 'evil-defun count) + (unless (eobp) (forward-line))) + +(evil-define-motion evil-forward-sentence-begin (count) + "Move to the next COUNT-th beginning of a sentence or end of a paragraph." + :jump t + :type exclusive + (evil-signal-at-bob-or-eob count) + (evil-forward-nearest count + #'(lambda (_cnt) + (evil-forward-beginning 'evil-sentence)) + #'evil-forward-paragraph)) + +(evil-define-motion evil-backward-sentence-begin (count) + "Move to the previous COUNT-th beginning of a sentence or paragraph." + :jump t + :type exclusive + (evil-signal-at-bob-or-eob (- (or count 1))) + (evil-forward-nearest (- (or count 1)) + #'(lambda (_cnt) + (evil-backward-beginning 'evil-sentence)) + #'(lambda (_cnt) + (evil-backward-paragraph)))) + +(evil-define-motion evil-forward-paragraph (count) + "Move to the end of the COUNT-th next paragraph." + :jump t + :type exclusive + (evil-signal-at-bob-or-eob count) + (evil-forward-end 'evil-paragraph count) + (unless (eobp) (forward-line))) + +(evil-define-motion evil-backward-paragraph (count) + "Move to the beginning of the COUNT-th previous paragraph." + :jump t + :type exclusive + (evil-signal-at-bob-or-eob (- (or count 1))) + (unless (eobp) (forward-line)) + (evil-backward-beginning 'evil-paragraph count) + (unless (bobp) (forward-line -1))) + +(defvar hif-ifx-else-endif-regexp) +(evil-define-motion evil-jump-item (count) + "Find the next item in this line after or under the cursor +and jump to the corresponding one." + :jump t + :type inclusive + (cond + ;; COUNT% jumps to a line COUNT percentage down the file + (count + (evil-ensure-column + (goto-char + (let ((size (- (point-max) (point-min)))) + (+ (point-min) + (if (> size 80000) + (* count (/ size 100)) + (/ (* count size) 100)))))) + (setq evil-this-type 'line)) + ((and (evil-looking-at-start-comment t) + (let ((pnt (point))) + (forward-comment 1) + (or (not (bolp)) + (prog1 nil (goto-char pnt))))) + (backward-char)) + ((and (not (eolp)) (evil-looking-at-end-comment t)) + (forward-comment -1)) + ((and + (memq major-mode '(c-mode c++-mode c-ts-mode c++-ts-mode)) + (require 'hideif nil t) + (with-no-warnings + (let* ((hif-else-regexp (concat hif-cpp-prefix "\\(?:else\\|elif[ \t]+\\)")) + (hif-ifx-else-endif-regexp + (concat hif-ifx-regexp "\\|" hif-else-regexp "\\|" hif-endif-regexp))) + (cond + ((save-excursion (beginning-of-line) (or (hif-looking-at-ifX) (hif-looking-at-else))) + (hif-find-next-relevant) + (while (hif-looking-at-ifX) + (hif-ifdef-to-endif) + (hif-find-next-relevant)) + t) + ((save-excursion (beginning-of-line) (hif-looking-at-endif)) + (hif-endif-to-ifdef) + t)))))) + (t + (let* ((open (point-max)) + (close (point-max)) + (open-pair (ignore-errors + (save-excursion + ;; consider the character right before eol given that + ;; point may be placed there, e.g. in visual state + (when (and (eolp) (not (bolp))) + (backward-char)) + (setq open (1- (scan-lists (point) 1 -1))) + (when (< open (line-end-position)) + (goto-char open) + (forward-list) + (1- (point)))))) + (close-pair (ignore-errors + (save-excursion + ;; consider the character right before eol given that + ;; point may be placed there, e.g. in visual state + (when (and (eolp) (not (bolp))) + (backward-char)) + (setq close (1- (scan-lists (point) 1 1))) + (when (< close (line-end-position)) + (goto-char (1+ close)) + (backward-list) + (point)))))) + (cond + ((not (or open-pair close-pair)) + ;; nothing found, check if we are inside a string + (let ((pnt (point)) + (bnd (bounds-of-thing-at-point 'evil-string))) + (if (not (and bnd (< (point) (cdr bnd)))) + ;; no, then we really failed + (user-error "No matching item found on the current line") + ;; yes, go to the end of the string and try again + (let ((endstr (cdr bnd))) + (when (or (save-excursion + (goto-char endstr) + (let ((b (bounds-of-thing-at-point 'evil-string))) + (and b (< (point) (cdr b))))) ; not at end of string + (condition-case nil + (progn + (goto-char endstr) + (evil-jump-item) + nil) + (error t))) + ;; failed again, go back to original point + (goto-char pnt) + (user-error "No matching item found on the current line")))))) + ((< open close) (goto-char open-pair)) + (t (goto-char close-pair))))))) + +(declare-function flyspell-overlay-p "flyspell") +(evil-define-motion evil-next-flyspell-error (count) + "Go to the COUNT'th spelling mistake after point." + :jump t + (unless (bound-and-true-p flyspell-mode) (signal 'search-failed nil)) + (let ((fwd (> (or count 1) 0)) (start (point)) (pos (point)) ov) + (dotimes (_ (abs (or count 1))) + (let ((limit (if fwd (point-max) (point-min))) wrappedp) + (when fwd (setq pos (save-excursion (goto-char pos) + (skip-syntax-forward "w") (point)))) + (while (progn (if (if fwd (>= pos limit) (<= pos limit)) + (if (or wrappedp (not evil-search-wrap)) + (signal 'search-failed nil) + (setq wrappedp t + limit start + pos (if fwd (point-min) + (previous-overlay-change (point-max))))) + (setq pos (if fwd (next-overlay-change pos) + (previous-overlay-change pos)))) + (not (setq ov (seq-find #'flyspell-overlay-p + (overlays-at pos)))))) + (when wrappedp (let (message-log-max) (message "Search wrapped"))))) + (goto-char (overlay-start ov)))) + +(evil-define-motion evil-prev-flyspell-error (count) + "Go to the COUNT'th spelling mistake preceding point." + :jump t + (evil-next-flyspell-error (- (or count 1)))) + +(evil-define-motion evil-previous-open-paren (count) + "Go to COUNT previous unmatched \"(\"." + :jump t + :type exclusive + (evil-up-paren ?\( ?\) (- (or count 1)))) + +(evil-define-motion evil-next-close-paren (count) + "Go to COUNT next unmatched \")\"." + :jump t + :type exclusive + (forward-char) + (evil-up-paren ?\( ?\) (or count 1)) + (backward-char)) + +(evil-define-motion evil-previous-open-brace (count) + "Go to COUNT previous unmatched \"{\"." + :jump t + :type exclusive + (evil-up-paren ?{ ?} (- (or count 1)))) + +(evil-define-motion evil-next-close-brace (count) + "Go to COUNT next unmatched \"}\"." + :jump t + :type exclusive + (forward-char) + (evil-up-paren ?{ ?} (or count 1)) + (backward-char)) + +(defun evil--lowercase-markers () + "Get all lowercase markers." + (cl-remove-if-not (lambda (x) (and (markerp (cdr x)) + (<= ?a (car x) ?z))) + evil-markers-alist)) + +(defun evil--next-mark (forwardp) + "Move to next lowercase mark. +Move forward if FORWARDP is non-nil, and backward otherwise. Loop back +to the beginning of buffer if the end is reached." + (let* ((pos (if forwardp (1+ (point)) (point))) + (centered-markers + (cl-sort + (or (evil--lowercase-markers) (user-error "No marks in this buffer")) + (if forwardp #'< #'>) + :key (lambda (x) (+ (if (< (cdr x) pos) 0 most-negative-fixnum) + (cdr x)))))) + (goto-char (cdar centered-markers)))) + +(evil-define-motion evil-next-mark (count) + "Go to COUNT next lowercase mark." + :keep-visual t + :repeat nil + :type exclusive + :jump t + (dotimes (_ (or count 1)) + (evil--next-mark t))) + +(evil-define-motion evil-next-mark-line (count) + "Go to COUNT line of next lowercase mark after current line." + :keep-visual t + :repeat nil + :type exclusive + :jump t + (unless (evil--lowercase-markers) (user-error "No marks in this buffer")) + (dotimes (_ (or count 1)) + (move-end-of-line nil) + (evil--next-mark t)) + (evil-first-non-blank)) + +(evil-define-motion evil-previous-mark (count) + "Go to COUNT previous lowercase mark." + :keep-visual t + :repeat nil + :type exclusive + :jump t + (dotimes (_ (or count 1)) + (evil--next-mark nil))) + +(evil-define-motion evil-previous-mark-line (count) + "Go to COUNT line of previous lowercase mark before current line." + :keep-visual t + :repeat nil + :type exclusive + :jump t + (unless (evil--lowercase-markers) (user-error "No marks in this buffer")) + (dotimes (_ (or count 1)) + (move-beginning-of-line nil) + (evil--next-mark nil)) + (evil-first-non-blank)) + +(evil-define-command evil-set-col-0-mark (_beg end mark) + "Set MARK at column 0 of line of END. +Default is cursor line." + (interactive "") + (if (< 1 (length mark)) + (user-error "Trailing characters") + (save-excursion + (goto-char (if (eobp) end (1- end))) + (evil-beginning-of-line) + (evil-set-marker (string-to-char mark))))) + +(evil-define-motion evil-find-char (count char) + "Move to the next COUNT'th occurrence of CHAR. +Movement is restricted to the current line unless `evil-cross-lines' +is non-nil." + :type inclusive + (interactive "") + (setq count (or count 1)) + (let ((fwd (> count 0)) + (visual (and evil-respect-visual-line-mode + visual-line-mode)) + case-fold-search) + (setq evil-last-find (list #'evil-find-char char fwd)) + (when fwd (evil-forward-char 1 evil-cross-lines)) + (unless (prog1 + (search-forward + (char-to-string char) + (cond (evil-cross-lines nil) + ((and fwd visual) + (save-excursion + (end-of-visual-line) + (point))) + (fwd (line-end-position)) + (visual + (save-excursion + (beginning-of-visual-line) + (point))) + (t (line-beginning-position))) + t count) + (when fwd (backward-char))) + (user-error "Can't find `%c'" char)))) + +(evil-define-motion evil-find-char-backward (count char) + "Move to the previous COUNT'th occurrence of CHAR." + :type exclusive + (interactive "") + (evil-find-char (- (or count 1)) char)) + +(evil-define-motion evil-find-char-to (count char) + "Move before the next COUNT'th occurrence of CHAR." + :type inclusive + (interactive "") + (unwind-protect + (evil-find-char count char) + (setcar evil-last-find #'evil-find-char-to)) + (if (> (or count 1) 0) + (backward-char) + (forward-char))) + +(evil-define-motion evil-find-char-to-backward (count char) + "Move before the previous COUNT'th occurrence of CHAR." + :type exclusive + (interactive "") + (evil-find-char-to (- (or count 1)) char)) + +(evil-define-motion evil-repeat-find-char (count) + "Repeat the last find COUNT times." + :type inclusive + (setq count (or count 1)) + (cl-destructuring-bind (cmd char fwd) + (or evil-last-find (user-error "No previous search")) + ;; ensure count is non-negative + (when (< count 0) + (setq count (- count) + fwd (not fwd))) + ;; skip next character when repeating t or T + (and (eq cmd #'evil-find-char-to) + evil-repeat-find-to-skip-next + (= count 1) + (eql (if fwd (char-after (1+ (point))) (char-before)) char) + (setq count (1+ count))) + (let (evil-last-find) + (funcall cmd (if fwd count (- count)) char) + (unless (nth 2 evil-last-find) + (setq evil-this-type 'exclusive))))) + +(evil-define-motion evil-repeat-find-char-reverse (count) + "Repeat the last find COUNT times in the opposite direction." + :type inclusive + (evil-repeat-find-char (- (or count 1)))) + +;; ceci n'est pas une pipe +(evil-define-motion evil-goto-column (count) + "Go to column COUNT on the current line. +Columns are counted from zero." + :type exclusive + (move-to-column (or count 0))) + +(evil-define-command evil-goto-mark (char &optional noerror) + "Go to the marker specified by CHAR." + :keep-visual t + :repeat nil + :type exclusive + :jump t + (interactive (list (read-char))) + (let ((marker (evil-get-marker char))) + (cond + ((markerp marker) + (switch-to-buffer (marker-buffer marker)) + (goto-char marker)) + ((numberp marker) (goto-char marker)) + ((consp marker) + (when (or (find-buffer-visiting (car marker)) + (and (y-or-n-p (format "Visit file %s again? " + (car marker))) + (find-file (car marker)))) + (goto-char (cdr marker)))) + ((not noerror) + (user-error "Marker `%c' is not set%s" char + (if (evil-global-marker-p char) "" + " in this buffer")))))) + +(evil-define-command evil-goto-mark-line (char &optional noerror) + "Go to the line of the marker specified by CHAR." + :keep-visual t + :repeat nil + :type line + :jump t + (interactive (list (read-char))) + (evil-goto-mark char noerror) + (evil-first-non-blank)) + +(declare-function goto-last-change "ext:goto-chg") +(declare-function goto-last-change-reverse "ext:goto-chg") + +(evil-define-motion evil-goto-last-change (count) + "Like `goto-last-change' but takes a COUNT rather than a span." + (setq this-command 'goto-last-change) + (dotimes (_ (or count 1)) + (goto-last-change nil))) + +(evil-define-motion evil-goto-last-change-reverse (count) + "Like `goto-last-change-reverse' but takes a COUNT rather than a span." + (setq this-command 'goto-last-change-reverse) + (dotimes (_ (or count 1)) + (goto-last-change-reverse nil))) + +(evil-define-motion evil-jump-backward (count) + "Go to older position in jump list. +To go the other way, press \ +\\\\[evil-jump-forward]." + (evil--jump-backward count)) + +(evil-define-motion evil-jump-forward (count) + "Go to newer position in jump list. +To go the other way, press \ +\\\\[evil-jump-backward]." + (evil--jump-forward count)) + +(evil-define-motion evil-jump-backward-swap (_count) + "Go to the previous position in jump list. +The current position is placed in the jump list." + (let ((pnt (point))) + (evil--jump-backward 1) + (evil-set-jump pnt))) + +(defvar xref-prompt-for-identifier) +(evil-define-motion evil-jump-to-tag (arg) + "Jump to tag under point. +If called with a prefix argument, provide a prompt +for specifying the tag." + :jump t + (interactive "P") + (cond + ((fboundp 'xref-find-definitions) + (let ((xref-prompt-for-identifier arg)) + (call-interactively #'xref-find-definitions))) + ((fboundp 'find-tag) + (if arg (call-interactively #'find-tag) + (let ((tag (funcall (or find-tag-default-function + (get major-mode 'find-tag-default-function) + #'find-tag-default)))) + (find-tag (or tag (user-error "No tag found around point")))))))) + +(evil-define-motion evil-lookup () + "Look up the keyword at point. +Calls `evil-lookup-func'." + (funcall evil-lookup-func)) + +(defun evil-ret-gen (count indent?) + (let ((widget (or (get-char-property (point) 'field) + (get-char-property (point) 'button) + (get-char-property (point) 'widget-doc)))) + (cond + ((and widget + (fboundp 'widget-type) + (fboundp 'widget-button-press) + (or (and (symbolp widget) + (get widget 'widget-type)) + (and (consp widget) + (get (widget-type widget) 'widget-type)))) + (when (evil-operator-state-p) + (setq evil-inhibit-operator t)) + (when (fboundp 'widget-button-press) + (widget-button-press (point)))) + ((and (fboundp 'button-at) + (fboundp 'push-button) + (button-at (point))) + (when (evil-operator-state-p) + (setq evil-inhibit-operator t)) + (push-button)) + ((or (evil-emacs-state-p) + (and (evil-insert-state-p) + (not buffer-read-only))) + (if (not indent?) + (newline count) + (delete-horizontal-space t) + (newline count) + (indent-according-to-mode))) + (t (evil-next-line-first-non-blank count))))) + +(evil-define-motion evil-ret (count) + "Move the cursor COUNT lines down. +If point is on a widget or a button, click on it. +In Insert state, insert a newline." + :type line + (evil-ret-gen count nil)) + +(evil-define-motion evil-ret-and-indent (count) + "Move the cursor COUNT lines down. +If point is on a widget or a button, click on it. +In Insert state, insert a newline and indent." + :type line + (evil-ret-gen count t)) + +(evil-define-motion evil-window-top (count) + "Move the cursor to line COUNT from the top of the window." + :jump t + :type line + (evil-ensure-column + (move-to-window-line + (max (or count 0) + (if (or (zerop scroll-margin) + (= (point-min) (window-start)) + (< (count-screen-lines (window-start) (point)) + scroll-margin)) + 0 + scroll-margin))))) + +(evil-define-motion evil-window-middle () + "Move the cursor to the middle line in the window." + :jump t + :type line + (evil-ensure-column (move-to-window-line nil))) + +(evil-define-motion evil-window-bottom (count) + "Move the cursor to line COUNT from the bottom of the window." + :jump t + :type line + (evil-ensure-column + (move-to-window-line + (- (max (or count 1) + (if (or (zerop scroll-margin) + (< (count-screen-lines (point) (window-end)) + scroll-margin)) + 1 + (1+ scroll-margin))))))) + +;; scrolling +(evil-define-command evil-scroll-line-up (count) + "Scroll the window COUNT lines upwards." + :repeat nil + :keep-visual t + (interactive "p") + (let (scroll-preserve-screen-position) (scroll-down count))) + +(evil-define-command evil-scroll-line-down (count) + "Scroll the window COUNT lines downwards." + :repeat nil + :keep-visual t + (interactive "p") + (let (scroll-preserve-screen-position) (scroll-up count))) + +(evil-define-command evil-scroll-count-reset () + "Set `evil-scroll-count' to 0. +`evil-scroll-up' and `evil-scroll-down' will then scroll by half of +the screen (the default)." + :repeat nil + :keep-visual t + (interactive) + (setq evil-scroll-count 0)) + +(defun evil--get-scroll-count (count) + "Given a user-supplied COUNT, return scroll count." + (cl-flet ((posint (x) (and (natnump x) (< 0 x) x))) + (or (posint count) + (posint evil-scroll-count) + (/ (window-body-height) 2)))) + +;; With `scroll-preserve-screen-position' `scroll-up'/`scroll-down' +;; target the same cursor pixel Y-coordinate while `last-command' has +;; the `scroll-command' property. However the target needs updating +;; when e.g. scrolling maximally to the first or last line and then +;; switching scroll direction. Do this by resetting `last-command' +;; when `real-this-command' (`evil-ensure-column' modifies +;; `this-command') changed from last value: + +(evil-define-command evil-scroll-up (count) + "Scroll the window and the cursor COUNT lines upwards. +If COUNT is not specified the function scrolls up `evil-scroll-count' +lines, which is the last used count. +If the scroll count is zero the command scrolls half the screen." + :repeat nil + :keep-visual t + (interactive "") + (when (= (line-beginning-position) (point-min)) + (signal 'beginning-of-buffer nil)) + (setq count (evil--get-scroll-count count)) + (evil-ensure-column + (let ((opoint (point))) + (condition-case nil + (let ((scroll-preserve-screen-position 'always) + (last-command (when (eq real-last-command real-this-command) + real-last-command))) + (scroll-down count)) + (:success + ;; Redo if `scroll-down' only did partial scroll up to BOB + (when (<= (window-start) (point-min)) + (goto-char opoint) + (vertical-motion (- count)))) + (beginning-of-buffer (vertical-motion (- count))))))) +(put 'evil-scroll-up 'scroll-command t) + +(evil-define-command evil-scroll-down (count) + "Scroll the window and the cursor COUNT lines downwards. +If COUNT is not specified the function scrolls down +`evil-scroll-count' lines, which is the last used count. +If the scroll count is zero the command scrolls half the screen." + :repeat nil + :keep-visual t + (interactive "") + (when (= (line-end-position) (point-max)) + (signal 'end-of-buffer nil)) + (setq count (evil--get-scroll-count count)) + (evil-ensure-column + ;; BUG #660: First check whether the eob is visible. + ;; In that case we do not scroll but merely move point. + (if (<= (point-max) (window-end)) + (vertical-motion count) + (condition-case nil + (let ((scroll-preserve-screen-position 'always) + (last-command (when (eq real-last-command real-this-command) + real-last-command))) + (scroll-up count)) + (:success + ;; If EOB became visible: Scroll it to the bottom + (save-excursion + (goto-char (window-start)) + (vertical-motion (max 0 (- (window-height) 1 scroll-margin))) + (when (<= (point-max) (point)) (recenter -1)))) + (end-of-buffer (goto-char (point-max)) (recenter -1)))))) +(put 'evil-scroll-down 'scroll-command t) + +(evil-define-command evil-scroll-page-up (count) + "Scroll the window COUNT pages upwards." + :repeat nil + :keep-visual t + (interactive "p") + (evil-ensure-column + (dotimes (i count) + (condition-case err + (scroll-down nil) + (beginning-of-buffer + (if (and (bobp) (zerop i)) + (signal (car err) (cdr err)) + (goto-char (point-min)))))))) + +(evil-define-command evil-scroll-page-down (count) + "Scroll the window COUNT pages downwards." + :repeat nil + :keep-visual t + (interactive "p") + (evil-ensure-column + (dotimes (i count) + (condition-case err + (scroll-up nil) + (end-of-buffer + (if (and (eobp) (zerop i)) + (signal (car err) (cdr err)) + (goto-char (point-max)))))))) + +(evil-define-command evil-scroll-line-to-top (count) + "Scroll line number COUNT (or the cursor line) to the top of the window." + :repeat nil + :keep-visual t + (interactive "") + (when count + (evil-save-column + (goto-char (point-min)) + (forward-line (1- count)))) + (recenter 0)) + +(evil-define-command evil-scroll-line-to-top-first-non-blank (count) + "Scroll line number COUNT (or the cursor line) to the top of the window. +Then move the cursor to the first non-blank character of that line." + :repeat nil + :keep-visual t + (interactive "") + (evil-scroll-line-to-top count) + (evil-first-non-blank)) + +(evil-define-command evil-scroll-line-to-center (count) + "Scroll line number COUNT (or the cursor line) to the center of the window." + :repeat nil + :keep-visual t + (interactive "") + (when count + (evil-save-column + (goto-char (point-min)) + (forward-line (1- count)))) + (recenter)) + +(evil-define-command evil-scroll-line-to-center-first-non-blank (count) + "Scroll line number COUNT (or the cursor line) to the center of the window. +Then move the cursor to the first non-blank character of that line." + :repeat nil + :keep-visual t + (interactive "") + (evil-scroll-line-to-center count) + (evil-first-non-blank)) + +(evil-define-command evil-scroll-line-to-bottom (count) + "Scroll line number COUNT (or the cursor line) to the bottom of the window." + :repeat nil + :keep-visual t + (interactive "") + (when count + (evil-save-column + (goto-char (point-min)) + (forward-line (1- count)))) + (recenter -1)) + +(evil-define-command evil-scroll-line-to-bottom-first-non-blank (count) + "Scroll line number COUNT (or the cursor line) to the bottom of the window. +Then move the cursor to the first non-blank character of that line." + :repeat nil + :keep-visual t + (interactive "") + (evil-scroll-line-to-bottom count) + (evil-first-non-blank)) + +(evil-define-command evil-scroll-bottom-line-to-top (count) + "Scroll the line right below the window, +or line COUNT to the top of the window." + :repeat nil + :keep-visual t + (interactive "") + (unless count + (goto-char (window-end)) + (evil-first-non-blank)) + (evil-scroll-line-to-top count)) + +(evil-define-command evil-scroll-top-line-to-bottom (count) + "Scroll the line right above the window, +or line COUNT to the bottom of the window." + :repeat nil + :keep-visual t + (interactive "") + (unless count + (goto-char (window-start)) + (evil-first-non-blank)) + (evil-scroll-line-to-bottom count)) + +(evil-define-command evil-scroll-left (count) + "Scroll the window COUNT half-screenwidths to the left." + :repeat nil + :keep-visual t + (interactive "p") + (evil-with-hproject-point-on-window + (scroll-right (* count (/ (window-width) 2))))) + +(evil-define-command evil-scroll-right (count) + "Scroll the window COUNT half-screenwidths to the right." + :repeat nil + :keep-visual t + (interactive "p") + (evil-with-hproject-point-on-window + (scroll-left (* count (/ (window-width) 2))))) + +(evil-define-command evil-scroll-column-left (count) + "Scroll the window COUNT columns to the left." + :repeat nil + :keep-visual t + (interactive "p") + (evil-with-hproject-point-on-window + (scroll-right count))) + +(evil-define-command evil-scroll-column-right (count) + "Scroll the window COUNT columns to the right." + :repeat nil + :keep-visual t + (interactive "p") + (evil-with-hproject-point-on-window + (scroll-left count))) + +(evil-define-command evil-scroll-end-column () + "Scroll the window to position the cursor at the end (right side) of the screen. +Warn if `hscroll-margin' > 0, as cursor will be `hscroll-margin' chars from +the right edge." + :repeat nil + :keep-visual t + (interactive) + (let* ((window-hpos (- (current-column) (window-hscroll))) + (dist-from-right-edge (- (window-width) window-hpos))) + (when (< 0 hscroll-margin) + (evil-echo "%s: hscroll-margin = %d" this-command hscroll-margin)) + (evil-scroll-column-left (- dist-from-right-edge (abs (* 2 hscroll-margin)))))) + +(evil-define-command evil-scroll-start-column () + "Scroll the window to position the cursor at the start (left side) of the screen. +Warn if `hscroll-margin' > 0, as cursor will be `hscroll-margin' chars from +the left edge." + :repeat nil + :keep-visual t + (interactive) + (let ((initial-column (current-column)) + (initial-hscroll (window-hscroll))) + (when (< 0 hscroll-margin) + (evil-echo "%s: hscroll-margin = %d" this-command hscroll-margin)) + (evil-scroll-column-right (- initial-column initial-hscroll hscroll-margin 1)))) + +;;; Text objects + +;; Text objects are defined with `evil-define-text-object'. In Visual +;; state, they modify the current selection; in Operator-Pending +;; state, they return a pair of buffer positions. Outer text objects +;; are bound in the keymap `evil-outer-text-objects-map', and inner +;; text objects are bound in `evil-inner-text-objects-map'. +;; +;; Common text objects like words, WORDS, paragraphs and sentences are +;; defined via a corresponding move-function. This function must have +;; the following properties: +;; +;; 1. Take exactly one argument, the count. +;; 2. When the count is positive, move point forward to the first +;; character after the end of the next count-th object. +;; 3. When the count is negative, move point backward to the first +;; character of the count-th previous object. +;; 4. If point is placed on the first character of an object, the +;; backward motion does NOT count that object. +;; 5. If point is placed on the last character of an object, the +;; forward motion DOES count that object. +;; 6. The return value is "count left", i.e., in forward direction +;; count is decreased by one for each successful move and in +;; backward direction count is increased by one for each +;; successful move, returning the final value of count. +;; Therefore, if the complete move is successful, the return +;; value is 0. +;; +;; A useful macro in this regard is `evil-motion-loop', which quits +;; when point does not move further and returns the count difference. +;; It also provides a "unit value" of 1 or -1 for use in each +;; iteration. For example, a hypothetical "foo-bar" move could be +;; written as such: +;; +;; (defun foo-bar (count) +;; (evil-motion-loop (var count) +;; (forward-foo var) ; `var' is 1 or -1 depending on COUNT +;; (forward-bar var))) +;; +;; If "forward-foo" and "-bar" didn't accept negative arguments, +;; we could choose their backward equivalents by inspecting `var': +;; +;; (defun foo-bar (count) +;; (evil-motion-loop (var count) +;; (cond +;; ((< var 0) +;; (backward-foo 1) +;; (backward-bar 1)) +;; (t +;; (forward-foo 1) +;; (forward-bar 1))))) +;; +;; After a forward motion, point has to be placed on the first +;; character after some object, unless no motion was possible at all. +;; Similarly, after a backward motion, point has to be placed on the +;; first character of some object. This implies that point should +;; NEVER be moved to eob or bob, unless an object ends or begins at +;; eob or bob. (Usually, Emacs motions always move as far as possible. +;; But we want to use the motion-function to identify certain objects +;; in the buffer, and thus exact movement to object boundaries is +;; required.) + +(evil-define-text-object evil-a-word (count &optional beg end _type) + "Select a word." + (evil-select-a-restricted-object 'evil-word beg end type count)) + +(evil-define-text-object evil-inner-word (count &optional beg end _type) + "Select inner word." + (evil-select-inner-restricted-object 'evil-word beg end type count)) + +(evil-define-text-object evil-a-WORD (count &optional beg end _type) + "Select a WORD." + (evil-select-a-restricted-object 'evil-WORD beg end type count)) + +(evil-define-text-object evil-inner-WORD (count &optional beg end _type) + "Select inner WORD." + (evil-select-inner-restricted-object 'evil-WORD beg end type count)) + +(evil-define-text-object evil-a-symbol (count &optional beg end _type) + "Select a symbol." + (evil-select-an-object 'evil-symbol beg end type count)) + +(evil-define-text-object evil-inner-symbol (count &optional beg end _type) + "Select inner symbol." + (evil-select-inner-object 'evil-symbol beg end type count)) + +(evil-define-text-object evil-a-sentence (count &optional beg end _type) + "Select a sentence." + (evil-select-an-object 'evil-sentence beg end type count)) + +(evil-define-text-object evil-inner-sentence (count &optional beg end _type) + "Select inner sentence." + (evil-select-inner-object 'evil-sentence beg end type count)) + +(evil-define-text-object evil-a-paragraph (count &optional beg end _type) + "Select a paragraph." + :type line + (evil-select-an-object 'evil-paragraph beg end type count t)) + +(evil-define-text-object evil-inner-paragraph (count &optional beg end _type) + "Select inner paragraph." + :type line + (evil-select-inner-object 'evil-paragraph beg end type count t)) + +(evil-define-text-object evil-a-paren (count &optional beg end _type) + "Select a parenthesis." + :extend-selection nil + (evil-select-paren ?\( ?\) beg end type count t)) + +(evil-define-text-object evil-inner-paren (count &optional beg end _type) + "Select inner parenthesis." + :extend-selection nil + (evil-select-paren ?\( ?\) beg end type count)) + +(evil-define-text-object evil-a-bracket (count &optional beg end _type) + "Select a square bracket." + :extend-selection nil + (evil-select-paren ?\[ ?\] beg end type count t)) + +(evil-define-text-object evil-inner-bracket (count &optional beg end _type) + "Select inner square bracket." + :extend-selection nil + (evil-select-paren ?\[ ?\] beg end type count)) + +(evil-define-text-object evil-a-curly (count &optional beg end _type) + "Select a curly bracket (\"brace\")." + :extend-selection nil + (evil-select-paren ?{ ?} beg end type count t)) + +(evil-define-text-object evil-inner-curly (count &optional beg end _type) + "Select inner curly bracket (\"brace\")." + :extend-selection nil + (evil-select-paren ?{ ?} beg end type count)) + +(evil-define-text-object evil-an-angle (count &optional beg end _type) + "Select an angle bracket." + :extend-selection nil + (evil-select-paren ?< ?> beg end type count t)) + +(evil-define-text-object evil-inner-angle (count &optional beg end _type) + "Select inner angle bracket." + :extend-selection nil + (evil-select-paren ?< ?> beg end type count)) + +(evil-define-text-object evil-a-single-quote (count &optional beg end _type) + "Select a single-quoted expression." + :extend-selection t + (evil-select-quote ?' beg end type count t)) + +(evil-define-text-object evil-inner-single-quote (count &optional beg end _type) + "Select inner single-quoted expression." + :extend-selection nil + (evil-select-quote ?' beg end type count)) + +(evil-define-text-object evil-a-double-quote (count &optional beg end _type) + "Select a double-quoted expression." + :extend-selection t + (evil-select-quote ?\" beg end type count t)) + +(evil-define-text-object evil-inner-double-quote (count &optional beg end _type) + "Select inner double-quoted expression." + :extend-selection nil + (evil-select-quote ?\" beg end type count)) + +(evil-define-text-object evil-a-back-quote (count &optional beg end _type) + "Select a back-quoted expression." + :extend-selection t + (evil-select-quote ?\` beg end type count t)) + +(evil-define-text-object evil-inner-back-quote (count &optional beg end _type) + "Select inner back-quoted expression." + :extend-selection nil + (evil-select-quote ?\` beg end type count)) + +(evil-define-text-object evil-a-tag (count &optional beg end _type) + "Select a tag block." + :extend-selection nil + (evil-select-xml-tag beg end type count t)) + +(evil-define-text-object evil-inner-tag (count &optional beg end _type) + "Select inner tag block." + :extend-selection nil + (evil-select-xml-tag beg end type count)) + +(defun evil-match (direction count) + "Find COUNTth next match in DIRECTION." + (defvar evil-search-module) + (unless (eq evil-search-module 'evil-search) + (user-error "Match text objects only work with Evil search module")) + (let ((pnt (point)) + (count (abs count)) ; Undo effect of evil-visual-direction + (evil-ex-search-direction 'backward) + (visual-state (evil-visual-state-p)) + on-start-match in-match on-end-match) + (save-excursion + (unless (eobp) (forward-char)) ; If on start of a match, stay there + (evil-ex-search 1) + (setq on-start-match (= evil-ex-search-match-beg pnt) + in-match (<= evil-ex-search-match-beg pnt (1- evil-ex-search-match-end)) + on-end-match (= (1- evil-ex-search-match-end) pnt) + evil-ex-search-direction direction) + (cond + ((and visual-state on-start-match (eq 'backward direction)) + (evil-ex-search count)) + ((and visual-state on-end-match (eq 'forward direction)) + (evil-ex-search count)) + ((or in-match (eq 'backward direction)) + (evil-ex-search (1- count))) + (t (evil-ex-search count))) + (setq pnt (point))) + (goto-char pnt) + (cond + ((evil-normal-state-p) + (evil-visual-select evil-ex-search-match-beg + evil-ex-search-match-end + 'inclusive + (pcase direction ('forward +1) ('backward -1)) + t) + (list evil-ex-search-match-beg evil-ex-search-match-end)) + ((and visual-state (eq 'forward direction)) + (goto-char (1- evil-ex-search-match-end))) + ((and visual-state (eq 'backward direction)) + (goto-char evil-ex-search-match-beg)) + ;; e.g. operator pending... + (t (list evil-ex-search-match-beg evil-ex-search-match-end))))) + +(evil-define-text-object evil-next-match (count &optional _beg _end _type) + "Select next match." + :extend-selection t + (evil-match 'forward count)) + +(evil-define-text-object evil-previous-match (count &optional _beg _end _type) + "Select previous match." + :extend-selection t + (evil-match 'backward count)) + +;;; Operator commands + +(evil-define-operator evil-yank (beg end type register yank-handler) + "Save the characters in motion into the kill-ring." + :move-point nil + :repeat nil + (interactive "") + (let ((evil-was-yanked-without-register + (and evil-was-yanked-without-register (not register)))) + (cond + ((and (fboundp 'cua--global-mark-active) + (fboundp 'cua-copy-region-to-global-mark) + (cua--global-mark-active)) + (cua-copy-region-to-global-mark beg end)) + ((eq type 'block) + (evil-yank-rectangle beg end register yank-handler)) + ((memq type '(line screen-line)) + (evil-yank-lines beg end register yank-handler)) + (t + (evil-yank-characters beg end register yank-handler) + (goto-char beg))))) + +(defun evil-expand-line-for-line-based-operators (beg end type) + "Expand to line when in visual mode possibly changing BEG, END and TYPE. +Avoids double expansion for line-based commands like \"V\" or \"D\"." + (when (evil-visual-state-p) + (unless (memq type '(line block screen-line)) + ;; Subtract 1 from end to avoid expanding to the next line + ;; when \n is part of the visually selected region. + ;; If removed (evil-expand beg end 'line) + ;; will expand to the end of the next line instead of the current + ;; line which will cause line expanding commands like 'Y' to + ;; misbehave when used in visual state. + (when (eq ?\n (char-before end)) + (cl-decf end)) + (let ((range (evil-expand beg end (if (and evil-respect-visual-line-mode + visual-line-mode) + 'screen-line + 'line)))) + (setq beg (evil-range-beginning range) + end (evil-range-end range) + type (evil-type range)))) + (evil-exit-visual-state)) + (list beg end type)) + +(evil-define-operator evil-yank-line (beg end type register) + "Save whole lines into the kill-ring." + :motion evil-line-or-visual-line + :move-point nil + (interactive "") + (cl-destructuring-bind + (beg end type) (evil-expand-line-for-line-based-operators beg end type) + (evil-yank beg end type register))) + +(evil-define-operator evil-delete (beg end type register yank-handler) + "Delete text from BEG to END with TYPE. +Save in REGISTER or in the kill-ring with YANK-HANDLER." + (interactive "") + (when (and (memq type '(inclusive exclusive)) + (not (evil-visual-state-p)) + (eq 'evil-delete evil-this-operator) + (save-excursion (goto-char beg) (bolp)) + (save-excursion (goto-char end) (eolp)) + (<= 1 (evil-count-lines beg end))) + ;; Imitate Vi strangeness: if motion meets above criteria, + ;; delete linewise. Not for change operator or visual state. + (let ((new-range (evil-line-expand beg end))) + (setq beg (car new-range) + end (cadr new-range) + type 'line))) + (unless register + (let ((text (filter-buffer-substring beg end))) + (unless (string-match-p "\n" text) + ;; set the small delete register + (evil-set-register ?- text)))) + (let ((evil-was-yanked-without-register nil)) + (evil-yank beg end type register yank-handler)) + (cond + ((eq type 'block) + (evil-apply-on-block #'delete-region beg end nil)) + ((and (eq type 'line) + (= end (point-max)) + (or (= beg end) + (/= (char-before end) ?\n)) + (/= beg (point-min)) + (= (char-before beg) ?\n)) + (delete-region (1- beg) end)) + (t (delete-region beg end))) + (when (and (eq type 'line) + (called-interactively-p 'any)) + (evil-first-non-blank) + (when (and (not evil-start-of-line) + evil-operator-start-col + ;; Special exceptions to ever saving column: + (not (memq evil-this-motion '(evil-forward-word-begin + evil-forward-WORD-begin)))) + (move-to-column (if (and (eq most-positive-fixnum temporary-goal-column) + (memq last-command '(next-line previous-line))) + temporary-goal-column + evil-operator-start-col))))) + +(evil-define-operator evil-delete-line (beg end type register yank-handler) + "Delete to end of line." + :motion evil-end-of-line-or-visual-line + (interactive "") + (cl-destructuring-bind + (beg end type) (evil-expand-line-for-line-based-operators beg end type) + (if (eq type 'block) + ;; Equivalent to $d, i.e., we use the block-to-eol selection and + ;; call `evil-delete'. In this case we fake the call to + ;; `evil-end-of-line' by setting `temporary-goal-column' and + ;; `last-command' appropriately as `evil-end-of-line' would do. + (let ((temporary-goal-column most-positive-fixnum) + (last-command 'next-line)) + (evil-delete beg end 'block register yank-handler)) + (evil-delete beg end type register yank-handler) + (when (eq 'line type) + (evil-first-non-blank) + (when (and (not evil-start-of-line) evil-operator-start-col) + (move-to-column (if (and (eq most-positive-fixnum temporary-goal-column) + (memq last-command '(next-line previous-line))) + temporary-goal-column + evil-operator-start-col))))))) + +(evil-define-operator evil-delete-whole-line + (beg end type register yank-handler) + "Delete whole line." + :motion evil-line-or-visual-line + (interactive "") + (evil-delete beg end type register yank-handler)) + +(evil-define-operator evil-delete-char (beg end type register) + "Delete next character." + :motion evil-forward-char + (interactive "") + (evil-delete beg end type register)) + +(evil-define-operator evil-delete-backward-char (beg end type register) + "Delete previous character." + :motion evil-backward-char + (interactive "") + (evil-delete beg end type register)) + +(evil-define-command evil-delete-backward-char-and-join (_count) + "Delete previous character and join lines. +If point is at the beginning of a line then the current line will +be joined with the previous line if and only if +`evil-backspace-join-lines'." + (interactive "p") + (if (or evil-backspace-join-lines (not (bolp))) + (call-interactively #'delete-backward-char) + (signal 'beginning-of-line nil))) + +(evil-define-command evil-delete-backward-word () + "Delete previous word." + (let ((beg (save-excursion (evil-backward-word-begin) (point))) + (end (point))) + (cond + ((evil-replace-state-p) (while (< beg (point)) + (evil-replace-backspace))) + ((or (not (bolp)) (bobp)) (delete-region (max beg (line-beginning-position)) + end)) + (evil-backspace-join-lines (delete-char -1)) + (t (signal 'beginning-of-line nil))))) + +(evil-define-command evil-delete-back-to-indentation () + "Delete back to the first non-whitespace character. +If point is before the first non-whitespace character of a +current line then delete from the point to the beginning of the +current line. If point is on the beginning of the line, behave +according to `evil-backspace-join-lines'." + (let ((beg (if (<= (current-column) (current-indentation)) + (line-beginning-position) + (save-excursion (evil-first-non-blank) (point))))) + (cond + ((and (bolp) (evil-replace-state-p)) (evil-replace-backspace)) + ((bolp) (evil-delete-backward-char-and-join 1)) + ((evil-replace-state-p) + (while (< beg (point)) (evil-replace-backspace))) + (t (delete-region beg (point)))))) + +(defun evil-ex-delete-or-yank (should-delete beg end type register count yank-handler) + "Execute evil-delete or evil-yank on the given region. +If SHOULD-DELETE is t, evil-delete will be executed, otherwise +evil-yank. +The region specified by BEG and END will be adjusted if COUNT is +given." + (when count + ;; with COUNT, the command should go the end of the region and delete/yank + ;; COUNT lines from there + (setq beg (save-excursion (goto-char end) + (line-beginning-position 0)) + end (save-excursion (goto-char end) + (line-beginning-position count)) + type 'line)) + (funcall (if should-delete #'evil-delete #'evil-yank) + beg end type register yank-handler)) + +(evil-define-operator evil-ex-delete (beg end type register count yank-handler) + "The Ex delete command. +\[BEG,END]delete [REGISTER] [COUNT]" + (interactive "") + (evil-ex-delete-or-yank t beg end type register count yank-handler)) + +(evil-define-operator evil-ex-yank (beg end type register count yank-handler) + "The Ex yank command. +\[BEG,END]yank [REGISTER] [COUNT]" + :restore-point t + (interactive "") + (evil-ex-delete-or-yank nil beg end type register count yank-handler)) + +(evil-define-command evil-ex-put (_beg end ex-arg &optional force) + (interactive "") + (let* ((arg-chars (remove ?\s (string-to-list ex-arg))) + (reg (or (car arg-chars) ?\")) + (text (cond + ((and (< 1 (length arg-chars)) + (/= ?= reg)) + (user-error "Trailing characters")) + ((eq ?= reg) + (evil--eval-expr (if (= 1 (length arg-chars)) + evil-last-=-register-input + (setq evil-last-=-register-input (substring ex-arg 1))))) + (t (evil-get-register reg))))) + (unless text (user-error "Nothing in register %c" reg)) + (evil-remove-yank-excluded-properties text) + (goto-char (if (= (point-max) end) end (1- end))) + (if force (evil-insert-newline-above) (evil-insert-newline-below)) + (evil-set-marker ?\[ (point)) + ;; `insert' rather than `insert-for-yank' as we want to ignore yank-handlers... + (insert (if (and (< 0 (length text)) + (eq ?\n (aref text (1- (length text))))) + (substring text 0 (1- (length text))) + text)) + (evil-set-marker ?\] (1- (point))) + (back-to-indentation) + (evil-normal-state))) + +(evil-define-operator evil-change + (beg end type register yank-handler delete-func) + "Change text from BEG to END with TYPE. +Save in REGISTER or the kill-ring with YANK-HANDLER. +DELETE-FUNC is a function for deleting text, default `evil-delete'. +If TYPE is `line', insertion starts on an empty line. +If TYPE is `block', the inserted text in inserted at each line +of the block." + (interactive "") + (let ((delete-func (or delete-func #'evil-delete)) + (nlines (1+ (evil-count-lines beg end))) + opoint leftmost-point) + (save-excursion + (goto-char beg) + (setq opoint (line-beginning-position)) + (setq leftmost-point + (let ((inhibit-field-text-motion t)) (line-beginning-position)))) + (unless (eq evil-want-fine-undo t) + (evil-start-undo-step)) + (funcall delete-func beg end type register yank-handler) + (cond + ((eq type 'line) + (setq this-command 'evil-change-whole-line) ; for evil-maybe-remove-spaces + (cond + ((/= opoint leftmost-point) (evil-insert 1)) ; deletion didn't delete line + ((= opoint (point)) (evil-open-above 1)) + (t (evil-open-below 1)))) + ((eq type 'block) + (evil-insert 1 nlines)) + (t (evil-insert 1))) + (setq evil-this-register nil))) + +(evil-define-operator evil-change-line (beg end type register yank-handler) + "Change to end of line, or change whole line if characterwise visual mode." + :motion evil-end-of-line-or-visual-line + (interactive "") + (if (and (evil-visual-state-p) (eq type 'inclusive)) + (cl-destructuring-bind + (beg end _type) (evil-expand-line-for-line-based-operators beg end type) + (evil-change-whole-line beg end register yank-handler)) + (evil-change beg end type register yank-handler #'evil-delete-line))) + +(evil-define-operator evil-change-whole-line + (beg end register yank-handler) + "Change whole line." + :motion evil-line-or-visual-line + :type line + (interactive "") + (evil-change beg end 'line register yank-handler)) + +(evil-define-command evil-copy (beg end address) + "Copy lines in BEG END below line given by ADDRESS." + :motion evil-line-or-visual-line + (interactive "") + (goto-char (point-min)) + (forward-line address) + (let* ((txt (buffer-substring-no-properties beg end)) + (len (length txt))) + ;; ensure text consists of complete lines + (when (or (zerop len) (/= (aref txt (1- len)) ?\n)) + (setq txt (concat txt "\n"))) + (when (and (eobp) (not (bolp))) (newline)) ; incomplete last line + (insert txt) + (forward-line -1))) + +(evil-define-command evil-move (beg end address) + "Move lines in BEG .. END below the line given by ADDRESS." + :motion evil-line-or-visual-line + (interactive "") + (unless (= (1+ address) (line-number-at-pos beg)) + (goto-char (point-min)) + (forward-line address) + (let* ((m (set-marker (make-marker) (point))) + (txt (buffer-substring-no-properties beg end)) + (len (length txt)) + (last-line-blank (progn (goto-char (point-max)) (bolp)))) + (delete-region beg end) + (unless last-line-blank ; as vim, preserve lack of blank last line + (progn (goto-char (point-max)) (when (bolp) (delete-char -1)))) + (goto-char m) + (set-marker m nil) + ;; ensure text consists of complete lines + (when (or (zerop len) (/= (aref txt (1- len)) ?\n)) + (setq txt (concat txt "\n"))) + (when (and (eobp) (not (bolp))) (newline)) ; incomplete last line + (when (evil-visual-state-p) + (move-marker evil-visual-mark (point))) + (insert txt) + (forward-line -1) + (when (evil-visual-state-p) + (move-marker evil-visual-point (point)))))) + +(defun evil--check-undo-system () + (when (and (eq evil-undo-system 'undo-tree) + (not (bound-and-true-p undo-tree-mode))) + (user-error "Enable `global-undo-tree-mode' to use undo-tree commands. +Add (add-hook 'evil-local-mode-hook 'turn-on-undo-tree-mode) to your init file for undo in non-file buffers."))) + +(evil-define-command evil-undo (count) + "Undo COUNT changes in buffer using `evil-undo-function'." + :repeat abort + :jump t + (interactive "*p") + (evil--check-undo-system) + (let ((state-before evil-state)) + (unless (memq state-before '(normal visual)) (evil-normal-state)) + (funcall evil-undo-function count) + (unless (memq state-before '(normal visual)) (evil-change-state state-before)))) + +(evil-define-command evil-redo (count) + "Undo COUNT changes in buffer using `evil-redo-function'." + :repeat abort + (interactive "*p") + (evil--check-undo-system) + (funcall evil-redo-function count)) + +(evil-define-operator evil-substitute (beg end type register) + "Change a character." + :motion evil-forward-char + (interactive "") + (evil-change beg end type register)) + +(evil-define-operator evil-upcase (beg end type) + "Convert text to upper case." + (if (eq type 'block) + (evil-apply-on-block #'evil-upcase beg end nil) + (upcase-region beg end))) + +(evil-define-operator evil-downcase (beg end type) + "Convert text to lower case." + (if (eq type 'block) + (evil-apply-on-block #'evil-downcase beg end nil) + (downcase-region beg end))) + +(evil-define-operator evil-invert-case (beg end type) + "Invert case of text." + (if (eq type 'block) + (evil-apply-on-block #'evil-invert-case beg end nil) + (save-excursion + (goto-char beg) + (while (< beg end) + (let ((char (following-char))) + (delete-char 1) + (insert-char + (if (eq (upcase char) char) (downcase char) (upcase char)))) + (setq beg (1+ beg)))))) + +(evil-define-operator evil-invert-char (beg end type) + "Invert case of character." + :motion evil-forward-char + (if (eq type 'block) + (evil-apply-on-block #'evil-invert-case beg end nil) + (evil-invert-case beg end) + (when evil-this-motion + (goto-char end) + (when (and evil-cross-lines + (not evil-move-beyond-eol) + (not (evil-visual-state-p)) + (not (evil-operator-state-p)) + (eolp) (not (eobp)) (not (bolp))) + (forward-char))))) + +(evil-define-operator evil-rot13 (beg end type) + "ROT13 encrypt text." + (if (eq type 'block) + (evil-apply-on-block #'rot13-region beg end nil) + (rot13-region beg end))) + +(evil-define-operator evil-join (beg end) + "Join the selected lines." + :motion evil-line + (let ((count (count-lines beg end)) + last-line-blank) + (when (> count 1) + (setq count (1- count))) + (goto-char beg) + (dotimes (i count) + (when (= (1+ i) count) ; i.e. we're just before the last join + (evil-move-beginning-of-line) + (setq last-line-blank (looking-at "[ \t]*$"))) + (join-line 1)) + (and last-line-blank (indent-according-to-mode)))) + +(evil-define-operator evil-join-whitespace (beg end) + "Join the selected lines without changing whitespace. +\\Like \\[evil-join], \ +but doesn't insert or remove any spaces." + :motion evil-line + (let ((count (count-lines beg end))) + (when (> count 1) + (setq count (1- count))) + (goto-char beg) + (dotimes (_ count) + (evil-move-end-of-line 1) + (unless (eobp) + (delete-char 1))))) + +(evil-define-operator evil-ex-join (beg end &optional count bang) + "Join the selected lines with optional COUNT and BANG." + (interactive "") + (let ((join-fn (if bang 'evil-join-whitespace 'evil-join))) + (if (not count) + ;; without count - just join the given region + (funcall join-fn beg end) + (unless (string-match-p "^[1-9][0-9]*$" count) + (user-error "Invalid count")) + ;; emulate Vim's :join when count is given - start from the + ;; end of the region and join COUNT lines from there + (save-excursion + (goto-char end) + (let ((beg-adjusted (line-beginning-position 0)) + (end-adjusted (line-beginning-position (string-to-number count)))) + (funcall join-fn beg-adjusted end-adjusted)))))) + +(defun evil--ex-string-for-print (beg end linump borderline) + "Return a string to be printed by :print etc. +Starts at line of BEG and end at line of END. +Include line number at the start of each line if LINUMP is non-nil. +Surround line denoted by BORDERLINE with dashes if non-nil." + (let ((result-string "") + (continue t) + (line-move-visual nil)) + (save-excursion + (goto-char beg) + (while continue + (let* ((line-num (line-number-at-pos)) + (border (and (natnump borderline) (= borderline line-num))) + (raw-line (thing-at-point 'line)) + (line (if (string= "\n" (substring raw-line -1)) + raw-line (concat raw-line "\n"))) + (border-length (1- (min (length line) (frame-width))))) + (when border + (setq result-string + (concat result-string (make-string border-length ?-) "\n"))) + (when linump + (setq result-string + (concat result-string + (propertize (number-to-string (line-number-at-pos)) + 'face 'line-number-current-line) + " "))) + (unless (eobp) + (setq result-string (concat result-string line))) + (when border + (setq result-string + (concat result-string (make-string border-length ?-) "\n"))) + (when (or (= (point) (progn (evil-line-move 1 t) (point))) + (> (line-end-position) end)) + (setq continue nil))))) + (string-trim-right result-string "\n"))) + +(defun evil--ex-print-to-minibuffer (string) + "Print STRING to the minibuffer for better persistence." + (let ((keymap (make-keymap)) + minibuffer-local-map) + (set-char-table-range (nth 1 keymap) t 'abort-recursive-edit) + (setq minibuffer-local-map keymap) + (read-from-minibuffer "" (propertize string 'read-only t)))) + +(defun evil--ex-print (beg end count &optional linump borderline) + "Print lines in range to the minibuffer. +Starting at BEG and ending at END + COUNT lines. +Include line number at the start of each line if LINUMP is non-nil. +Surround line denoted by BORDERLINE with dashes if non-nil." + (let* ((count (cond ((stringp count) (string-to-number count)) + ((natnump count) count) + (t 1))) + (end (save-excursion (goto-char (if (= (point-max) end) end (1- end))) + (line-end-position count))) + (substring (evil--ex-string-for-print beg end linump borderline))) + (cond ((> 1 count) (user-error "Positive count required")) + (evil--ex-global-active-p + (setq evil--ex-print-accumulator + (if (string= "" evil--ex-print-accumulator) + (concat evil--ex-print-accumulator substring) + (concat evil--ex-print-accumulator "\n" substring)))) + (t (evil--ex-print-to-minibuffer substring) + (when (string-match-p "\n" substring) + (goto-char end) + (evil-beginning-of-line)))))) + +(defun evil--global-print+clear () + "Print accumulated print output from :global print, and clear." + (let ((s evil--ex-print-accumulator)) + (unless (string= s "") + (setq evil--ex-print-accumulator "") + (evil--ex-print-to-minibuffer s)))) +(add-hook 'evil-after-global-hook #'evil--global-print+clear) + +(evil-define-command evil-ex-print (beg end &optional count) + (interactive "") + (evil--ex-print beg end count)) + +(evil-define-command evil-ex-numbered-print (beg end &optional count) + (interactive "") + (evil--ex-print beg end count t)) + +(evil-define-command evil-ex-z (_beg end &optional zmarks _bang) + "Display several lines of text surrounding the line specified by range. +BEG and END represent the range, ZMARKS represents the args in string form. +With a count supplied in the args, display that number of lines. Without a +count, display `evil-scroll-count' number of lines, or half the window height. +This table explains what each mark argument does. + +mark | first line | last line | new cursor line +-----+-------------------------+----------------------------+---------------- ++ | current line | 1 scr (or 1 count) forward | last line +- | 1 scr (or 1 count) back | current line | last line +^ | 2 scr (or 2 count) back | 1 scr (or 1 count) back | last line +. | ½ scr (or ½ count) back | ½ scr (or ½ count) forward | last line += | ½ scr (or ½ count) back | ½ scr (or ½ count) forward | current line + +Specifying no mark at all is the same as `+'. +If the mark is `=' a line of dashes is printed around the current line. +If a `#' is included before the mark args, the lines are numbered." + ;; TODO implement bang argument. + (interactive "") + (goto-char (setq end (1- end))) + (save-match-data + (string-match "\\(#?\\)\\([^0-9]*\\)\\([0-9]*\\)" (or zmarks "")) + (cl-destructuring-bind (_ _ hs he ms me cs ce) (match-data) + (let* ((linump (/= hs he)) + (mark-string (if (= ms me) "+" (substring zmarks ms me))) + (count-string (if (= cs ce) "" (substring zmarks cs ce))) + (count (evil--get-scroll-count (string-to-number count-string))) + (max-mini-window-height 0.5)) + (cond + ((< 1 (- me ms)) + (evil-beginning-of-line) + (user-error "Too many mark args (got %d, expected 1)" (- me ms))) + ((string= "+" mark-string) + (ignore-errors (beginning-of-line count)) + (evil--ex-print end end count linump)) + ((string= "-" mark-string) + (evil-beginning-of-line) + (evil--ex-print (line-beginning-position (- 2 count)) end 1 linump)) + ((string= "^" mark-string) + (let ((end* (progn (move-end-of-line (- 1 count)) (point))) + (beg* (line-beginning-position (- 2 count)))) + (evil-beginning-of-line) + (evil--ex-print beg* end* 1 linump))) + ((string= "." mark-string) + (let ((beg* (line-beginning-position (- 1 (floor count 2)))) + (end* (progn (move-end-of-line (ceiling count 2)) (point)))) + (evil-beginning-of-line) + (evil--ex-print beg* end* 1 linump))) + ((string= "=" mark-string) + (let ((beg* (line-beginning-position (- 1 (floor count 2)))) + (end* (line-end-position (ceiling count 2)))) + (evil-beginning-of-line) + (evil--ex-print beg* end* 1 linump (line-number-at-pos end)))) + (t (evil-beginning-of-line) + (user-error "Invalid mark arg: %s" mark-string))))))) + +(evil-define-operator evil-fill (beg end) + "Fill text." + :move-point nil + :type line + (save-excursion + (ignore-errors (fill-region beg end)))) + +(evil-define-operator evil-fill-and-move (beg end) + "Fill text and move point to the end of the filled region." + :move-point nil + :type line + (let ((marker (make-marker))) + (move-marker marker (1- end)) + (ignore-errors + (fill-region beg end) + (goto-char marker) + (evil-first-non-blank)))) + +(evil-define-operator evil-indent (beg end) + "Indent text." + :move-point nil + :type line + (evil-ensure-column + (setq this-command real-this-command) ; Reset change made by evil-ensure-column + (save-restriction + (narrow-to-region beg end) + (if (save-excursion (goto-char beg) (= end (line-beginning-position 2))) + ;; since some Emacs modes can only indent one line at a time, + ;; implement "==" as a call to `indent-according-to-mode' + (indent-according-to-mode) + (goto-char beg) + (indent-region beg end)) + ;; Tabify or untabify leading whitespace characters + (when evil-indent-convert-tabs + ;; Whether tab or space should be used is determined by indent-tabs-mode + (let ((convert-fun (if indent-tabs-mode #'tabify #'untabify))) + (save-excursion + (goto-char (point-min)) + (while (not (eolp)) + (funcall convert-fun (point) (progn (skip-chars-forward " \t") (point))) + (forward-line)))))))) + +(evil-define-operator evil-indent-line (beg end) + "Indent the line." + :motion evil-line + (evil-indent beg end)) + +(evil-define-operator evil-shift-left (beg end &optional count preserve-empty) + "Shift text from BEG to END to the left. +The text is shifted to the nearest multiple of `evil-shift-width' +\(the rounding can be disabled by setting `evil-shift-round'). +If PRESERVE-EMPTY is non-nil, lines that contain only spaces are +indented, too, otherwise they are ignored. Location of point +is preserved relative to text when called from insert state. +Otherwise, it is determined by `evil-start-of-line' and/or `evil-track-eol'. +See also `evil-shift-right'." + :type line + (interactive "") + (evil-shift-right beg end (- (or count 1)) preserve-empty)) + +(evil-define-operator evil-shift-right (beg end &optional count preserve-empty) + "Shift text from BEG to END to the right. +The text is shifted to the nearest multiple of `evil-shift-width' +\(the rounding can be disabled by setting `evil-shift-round'). +If PRESERVE-EMPTY is non-nil, lines that contain only spaces are +indented, too, otherwise they are ignored. Location of point +is preserved relative to text when called from insert or replace states. +Otherwise, it is determined by `evil-start-of-line' and/or `evil-track-eol'. +See also `evil-shift-left'." + :type line + :move-point nil ; point is moved according to `evil-start-of-line' and state + (interactive "") + (setq count (or count 1)) + (let ((beg (set-marker (make-marker) beg)) + (end (set-marker (make-marker) end)) + (col-for-insert (current-column)) + first-shift) ; shift of first line + (save-excursion + (goto-char beg) + (while (< (point) end) + (let* ((indent (current-indentation)) + (new-indent + (max 0 + (if (not evil-shift-round) + (+ indent (* count evil-shift-width)) + (* (+ (/ indent evil-shift-width) + count + (cond + ((> count 0) 0) + ((zerop (mod indent evil-shift-width)) 0) + (t 1))) + evil-shift-width))))) + (unless first-shift + (setq first-shift (- new-indent indent))) + (when (or preserve-empty + (save-excursion + (skip-chars-forward " \t") + (not (eolp)))) + (indent-to new-indent 0)) + (delete-region (point) (progn (skip-chars-forward " \t") (point))) + (forward-line 1)))) + ;; in case we're in an empty buffer first-shift is still unchanged + (unless first-shift + (if (< count 0) + (setq first-shift 0) + (setq first-shift (* count evil-shift-width)) + (indent-to first-shift))) + ;; When called from insert state (C-t or C-d) the cursor should shift with the line, + ;; otherwise (normal state) its position is determined by `evil-start-of-line'. + (cond + ((or (evil-insert-state-p) (evil-replace-state-p)) + (move-to-column (max 0 (+ col-for-insert first-shift)))) + (evil-start-of-line (evil-first-non-blank)) + ((evil--stick-to-eol-p) (move-end-of-line 1)) + (t (move-to-column (or goal-column evil-operator-start-col col-for-insert)))))) + +(defun evil-delete-indentation () + "Delete all indentation on current line." + (interactive) + (save-excursion + (move-beginning-of-line nil) + (delete-region (point) (progn (skip-chars-forward " \t") (point))))) + +(evil-define-command evil-shift-right-line (count) + "Shift the current line COUNT times to the right. +The text is shifted to the nearest multiple of +`evil-shift-width'. Like `evil-shift-right' but always works on +the current line." + (interactive "") + (evil-shift-right (line-beginning-position) (line-beginning-position 2) count t)) + +(evil-define-command evil-shift-left-line (count) + "Shift the current line COUNT times to the left. +The text is shifted to the nearest multiple of +`evil-shift-width'. Like `evil-shift-left' but always works on +the current line." + (interactive "") + (if (and (eq 'self-insert-command last-command) + (eq ?0 (char-before))) + (progn (delete-char -1) + (evil-delete-indentation)) + (evil-shift-left (line-beginning-position) (line-beginning-position 2) count t))) + +(evil-define-operator evil-align-left (beg end _type &optional width) + "Left-align lines in the region at WIDTH columns. +The default for width is the value of `fill-column'." + :motion evil-line + :type line + (interactive "") + (evil-justify-lines beg end 'left (if width + (string-to-number width) + 0))) + +(evil-define-operator evil-align-right (beg end _type &optional width) + "Right-align lines in the region at WIDTH columns. +The default for width is the value of `fill-column'." + :motion evil-line + :type line + (interactive "") + (evil-justify-lines beg end 'right (if width + (string-to-number width) + fill-column))) + +(evil-define-operator evil-align-center (beg end _type &optional width) + "Center lines in the region between WIDTH columns. +The default for width is the value of `fill-column'." + :motion evil-line + :type line + (interactive "") + (evil-justify-lines beg end 'center (if width + (string-to-number width) + fill-column))) + +(evil-define-operator evil-replace (beg end type char) + "Replace text from BEG to END with CHAR." + :motion evil-forward-char + (interactive "" + (unwind-protect + (let ((evil-force-cursor 'replace)) + (evil-refresh-cursor) + (list (evil-read-key))) + (evil-refresh-cursor))) + (when char + (if (eq type 'block) + (save-excursion + (evil-apply-on-rectangle + #'(lambda (begcol endcol char) + (let ((maxcol (evil-column (line-end-position)))) + (when (< begcol maxcol) + (setq endcol (min endcol maxcol)) + (let ((beg (evil-move-to-column begcol nil t)) + (end (evil-move-to-column endcol nil t))) + (delete-region beg end) + (insert (make-string (- endcol begcol) char)))))) + beg end char)) + (goto-char beg) + (cond + ((eq char ?\n) + (delete-region beg end) + (newline) + (when evil-auto-indent + (indent-according-to-mode))) + (t + (while (< (point) end) + (if (eq (char-after) ?\n) + (forward-char) + (delete-char 1) + (insert-char char 1))) + (goto-char (max beg (1- end)))))))) + +(evil-define-command evil-paste-before + (count &optional register yank-handler) + "Paste the latest yanked text before the cursor position. +The return value is the yanked text." + :suppress-operator t + (interactive "*P") + (setq count (prefix-numeric-value count)) + (if (evil-visual-state-p) + ;; This is the only difference with evil-paste-after in visual-state + (let ((evil-kill-on-visual-paste (not evil-kill-on-visual-paste))) + (evil-visual-paste count register)) + (evil-with-undo + (let* ((text (copy-sequence + (if register + (evil-get-register register) + (current-kill 0)))) + (yank-handler (or yank-handler + (when (stringp text) + (car-safe (get-text-property + 0 'yank-handler text))))) + (opoint (point))) + (when evil-paste-clear-minibuffer-first + (delete-minibuffer-contents) + (setq evil-paste-clear-minibuffer-first nil)) + (when text + (if (functionp yank-handler) + (let ((evil-paste-count count) + ;; for non-interactive use + (this-command #'evil-paste-before)) + (push-mark opoint t) + (insert-for-yank text)) + ;; no yank-handler, default + (when (vectorp text) + (setq text (evil-vector-to-string text))) + (set-text-properties 0 (length text) nil text) + (push-mark opoint t) + (dotimes (_ (or count 1)) + (insert-for-yank text)) + (setq evil-last-paste + (list #'evil-paste-before + count + opoint + opoint ; beg + (point))) ; end + (evil-set-marker ?\[ opoint) + (evil-set-marker ?\] (1- (point))) + (when (and evil-move-cursor-back + (> (length text) 0)) + (backward-char)))) + (when evil--cursor-after + (if (eq 'evil-yank-line-handler yank-handler) + (ignore-errors (evil-next-line-first-non-blank)) + (evil-forward-char 1 nil t)) + (setq evil--cursor-after nil)) + ;; no paste-pop after pasting from a register + (when register + (setq evil-last-paste nil)) + (and (> (length text) 0) text))))) + +(evil-define-command evil-paste-after + (count &optional register yank-handler) + "Paste the latest yanked text behind point. +The return value is the yanked text." + :suppress-operator t + (interactive "*P") + (setq count (prefix-numeric-value count)) + (if (evil-visual-state-p) + (evil-visual-paste count register) + (evil-with-undo + (let* ((text (copy-sequence + (if register + (evil-get-register register) + (current-kill 0)))) + (yank-handler (or yank-handler + (when (stringp text) + (car-safe (get-text-property + 0 'yank-handler text))))) + (opoint (point))) + (when text + (if (functionp yank-handler) + (let ((evil-paste-count count) + ;; for non-interactive use + (this-command #'evil-paste-after)) + (insert-for-yank text)) + ;; no yank-handler, default + (when (vectorp text) + (setq text (evil-vector-to-string text))) + (set-text-properties 0 (length text) nil text) + (unless (eolp) (forward-char)) + (push-mark (point) t) + ;; TODO: Perhaps it is better to collect a list of all + ;; (point . mark) pairs to undo the yanking for COUNT > 1. + ;; The reason is that this yanking could very well use + ;; `yank-handler'. + (let ((beg (point))) + (dotimes (_ (or count 1)) + (insert-for-yank text)) + (setq evil-last-paste + (list #'evil-paste-after + count + opoint + beg ; beg + (point))) ; end + (evil-set-marker ?\[ beg) + (evil-set-marker ?\] (1- (point))) + (when (evil-normal-state-p) + (evil-move-cursor-back))))) + (when evil--cursor-after + (if (eq 'evil-yank-line-handler yank-handler) + (ignore-errors (evil-next-line-first-non-blank 1)) + (evil-forward-char 1 nil t)) + (setq evil--cursor-after nil)) + (when register + (setq evil-last-paste nil)) + (and (> (length text) 0) text))))) + +(evil-define-command evil-paste-before-cursor-after + (count &optional register yank-handler) + "The same as `evil-paste-before' but +leave the cursor just after the new text." + :suppress-operator t + (interactive "*P") + (setq count (prefix-numeric-value count) + evil--cursor-after t) + (evil-paste-before count register yank-handler)) + +(evil-define-command evil-paste-after-cursor-after + (count &optional register yank-handler) + "The same as `evil-paste-after' but +leave the cursor just after the new text." + :suppress-operator t + (interactive "*P") + (setq count (prefix-numeric-value count) + evil--cursor-after t) + (evil-paste-after count register yank-handler)) + +(defun evil-insert-for-yank-at-col (startcol _endcol string count) + "Insert STRING at STARTCOL." + (move-to-column startcol) + (dotimes (_ (or count 1)) + (insert-for-yank string)) + (evil-set-marker ?\] (1- (point)))) + +(evil-define-command evil-visual-paste (count &optional register) + "Paste over Visual selection." + :suppress-operator t + (interactive "*P") + (setq count (prefix-numeric-value count)) + ;; evil-visual-paste is typically called from evil-paste-before or + ;; evil-paste-after, but we have to mark that the paste was from + ;; visual state + (setq this-command 'evil-visual-paste) + (let* ((text (if register + (evil-get-register register) + (current-kill 0))) + (yank-handler (car-safe (get-text-property + 0 'yank-handler text))) + (dir (evil-visual-direction)) + beg end type) + (evil-with-undo + (let ((kill-ring-yank-pointer (when kill-ring (list (current-kill 0))))) + (when (evil-visual-state-p) + (setq beg evil-visual-beginning + end evil-visual-end + type (evil-visual-type)) + ;; When pasting charwise text into linewise selection, keep trailing NL + (when (and text end + (eq 'line type) + (not (eq ?\n (aref text (1- (length text)))))) + (cl-decf end)) + (evil-visual-rotate 'upper-left) + (evil-delete beg end type (unless evil-kill-on-visual-paste ?_)) + (when (and (eq yank-handler #'evil-yank-line-handler) + (not (memq type '(line block))) + (/= end (point-max))) + (insert "\n")) + (evil-normal-state) + (when kill-ring (current-kill 1))) + ;; Effectively memoize `evil-get-register' because it can be + ;; side-effecting (e.g. for the "=" register)... + (cl-letf (((symbol-function #'evil-get-register) + (lambda (&rest _) text))) + (cond + ;; When replacing the last buffer line and it does not end + ;; in a newline, use `evil-paste-after' because + ;; `evil-delete' will have moved point to the line above. + ((cond ((eq type 'line) (= end (point-max))) + ((eq type 'block) (eq yank-handler #'evil-yank-line-handler))) + (goto-char end) + (evil-paste-after count register)) + ((and (eq type 'block) + (not (eq yank-handler #'evil-yank-block-handler)) + (not (string-match-p "\n" text))) + (evil-apply-on-block #'evil-insert-for-yank-at-col beg end t text count)) + (t (evil-paste-before count register))))) + (when evil-kill-on-visual-paste + (current-kill -1)) + ;; Ensure that gv can restore visually pasted area... + (set-marker evil-visual-point (evil-get-marker (if (< dir 0) ?\[ ?\]) t)) + (set-marker evil-visual-mark (evil-get-marker (if (< dir 0) ?\] ?\[) t)) + ;; mark the last paste as visual-paste + (setq evil-last-paste + (list (nth 0 evil-last-paste) + (nth 1 evil-last-paste) + (nth 2 evil-last-paste) + (nth 3 evil-last-paste) + (nth 4 evil-last-paste) + t))))) + +(defun evil-paste-from-register (register) + "Paste from REGISTER." + (interactive + (let ((ov (make-overlay (point) (+ (point) (if (evil-replace-state-p) 1 0))))) + (unwind-protect + (progn + (overlay-put ov 'invisible t) + (overlay-put ov 'after-string + #("\"" 0 1 (face minibuffer-prompt cursor 1))) + (list (or evil-this-register (read-char)))) + (delete-overlay ov)))) + (let ((opoint (point)) + evil-move-cursor-back) + (evil-paste-before nil register t) + (when (evil-replace-state-p) + (let* ((reg-length (- (point) opoint)) + (chars-to-delete (min (- (line-end-position) (point)) reg-length))) + ;; TODO: handle multi-line paste backspacing + (evil-update-replace-alist (point) reg-length chars-to-delete chars-to-delete) + (delete-char chars-to-delete))))) + +(defun evil-paste-last-insertion () + "Paste last insertion." + (interactive) + (evil-paste-from-register ?.)) + +(defun evil-paste-last-insertion-and-stop-insert () + "Paste last insertion and change to normal state." + (interactive) + (evil-paste-last-insertion) + (evil-normal-state)) + +(evil-define-command evil-use-register (register) + "Use REGISTER for the next command." + :keep-visual t + :repeat ignore + :suppress-operator t + (interactive "") + (setq evil-this-register register)) + +(defvar evil-macro-buffer nil + "The buffer that has been active on macro recording.") + +(defvar evil-execute-normal-keys nil + "The keys used to invoke the current `evil-execute-in-normal-state'. +Can be used to detect if we are currently in that quasi-state. +With current bindings, it will be \\\\[evil-execute-in-normal-state]") + +(defun evil-end-and-return-macro () + "Like `kmacro-end-macro' but also return the macro. +Remove \\\\[evil-execute-in-normal-state] from the end." + ;; `end-kbd-macro' rather than `kmacro-end-macro' to allow clearing registers + (end-kbd-macro nil #'kmacro-loop-setup-function) + (let ((end-keys-seq (append evil-execute-normal-keys nil)) + (last-kbd-macro-seq (append last-kbd-macro nil))) + (unless last-kbd-macro-seq + (setq last-kbd-macro nil)) + (if (and end-keys-seq last-kbd-macro-seq) + (apply #'vector (butlast last-kbd-macro-seq (length end-keys-seq))) + last-kbd-macro))) + +(evil-define-command evil-record-macro (register) + "Record a keyboard macro into REGISTER. +If REGISTER is :, /, or ?, the corresponding command line window +will be opened instead." + :keep-visual t + :suppress-operator t + (interactive + (list (unless (and evil-this-macro defining-kbd-macro) + (or evil-this-register (evil-read-key))))) + (let (last-macro) + (cond + ((eq register ?\C-g) + (keyboard-quit)) + ((and evil-this-macro defining-kbd-macro) + (setq evil-macro-buffer nil + last-macro (ignore-errors (evil-end-and-return-macro))) + (when last-macro + (evil-set-register evil-this-macro last-macro)) + (setq evil-this-macro nil)) + ((eq register ?:) + (evil-command-window-ex)) + ((eq register ?/) + (evil-command-window-search-forward)) + ((eq register ??) + (evil-command-window-search-backward)) + ((or (<= ?0 register ?9) + (<= ?a register ?z) + (<= ?A register ?Z)) + (when defining-kbd-macro (end-kbd-macro)) + (setq evil-this-macro register + evil-last-recorded-register register) + (evil-set-register evil-this-macro nil) + (kmacro-start-macro nil) + (setq evil-macro-buffer (current-buffer))) + (t (error "Invalid register `%s'" register))))) + +(defun evil-repeat-execute-macro (flag) + "Called to record a macro execution. +FLAG is either `pre' or `post' if the function is called before resp. +after executing the command." + (pcase flag + ('pre (evil-repeat-record (this-command-keys))) + ('post (evil-repeat-record (string evil-last-register))))) + +(evil-define-command evil-execute-macro (count macro) + "Execute keyboard macro MACRO, COUNT times. +When called with a non-numerical prefix \ +\(such as \\[universal-argument]), +COUNT is infinite. MACRO is read from a register +when called interactively." + :keep-visual t + :suppress-operator t + :repeat evil-repeat-execute-macro + (interactive + (let (count macro register) + (setq count (cond ((null current-prefix-arg) 1) + ((numberp current-prefix-arg) current-prefix-arg) + (t 0)) + register (or evil-this-register (read-char))) + (cond + ((or (eq register ?:) + (and (eq register ?@) (eq evil-last-register ?:))) + (setq macro #'evil-ex-repeat + evil-last-register ?:)) + ((eq register ?@) + (unless evil-last-register + (user-error "No previously executed keyboard macro.")) + (setq macro (evil-get-register evil-last-register t))) + (t + (setq macro (evil-get-register register t) + evil-last-register register))) + (list count macro))) + (cond + ((functionp macro) + (evil-repeat-abort) + (if (zerop count) + (while t (funcall macro)) + (dotimes (_ (or count 1)) (funcall macro)))) + ((or (and (not (stringp macro)) + (not (vectorp macro))) + (member macro '("" []))) + ;; allow references to currently empty registers + ;; when defining macro + (unless evil-this-macro (user-error "No previous macro"))) + (t + (condition-case err + (evil-with-single-undo + (let (pre-command-hook post-command-hook) ; For performance + (combine-after-change-calls + (execute-kbd-macro macro count) + (setq this-command 'evil-execute-macro)))) ; For repeatability + ;; enter Normal state if the macro fails + (error + (evil-normal-state) + (signal (car err) (cdr err))))))) + +(evil-define-command evil-execute-last-recorded-macro (count) + "Execute last recorded keyboard macro COUNT times. +When called with a non-numerical prefix \ +\(such as \\[universal-argument]), +COUNT is infinite." + :keep-visual t + :suppress-operator t + (interactive + (list (cond + ((and current-prefix-arg (numberp current-prefix-arg)) current-prefix-arg) + (current-prefix-arg 0) + (t 1)))) + (if evil-last-recorded-register + (evil-execute-macro count (evil-get-register evil-last-recorded-register t)) + (user-error "No previous macro")) + (setq evil-last-register evil-last-recorded-register)) + +;;; Visual commands + +(evil-define-motion evil-visual-restore () + "Restore previous selection." + (cond + ;; Called from visual state + ((and (evil-visual-state-p) + evil-prev-visual-mark evil-prev-visual-point evil-prev-visual-selection) + (let ((tmp-visual-mark (marker-position evil-visual-mark)) + (tmp-visual-point (marker-position evil-visual-point)) + (tmp-visual-selection evil-visual-selection)) + (evil-visual-make-selection evil-prev-visual-mark + evil-prev-visual-point + evil-prev-visual-selection + t) + (move-marker evil-prev-visual-mark tmp-visual-mark) + (move-marker evil-prev-visual-point tmp-visual-point) + (setq evil-prev-visual-selection tmp-visual-selection))) + ;; Called from other state + ((and evil-visual-selection evil-visual-mark evil-visual-point) + (evil-visual-make-selection evil-visual-mark + evil-visual-point + (evil-visual-type) + t)))) + +(evil-define-motion evil-visual-exchange-corners () + "Rearrange corners in Visual Block mode. + + M---+ +---M + | | <=> | | + +---P P---+ + +For example, if mark is in the upper left corner and point +in the lower right, this function puts mark in the upper right +corner and point in the lower left." + (cond + ((eq evil-visual-selection 'block) + (let* ((point (point)) + (mark (or (mark t) point)) + (point-col (evil-column point)) + (mark-col (evil-column mark)) + (mark (save-excursion + (goto-char mark) + (evil-move-to-column point-col) + (point))) + (point (save-excursion + (goto-char point) + (evil-move-to-column mark-col) + (point)))) + (evil-visual-refresh mark point))) + (t + (evil-exchange-point-and-mark) + (evil-visual-refresh)))) + +(evil-define-command evil-visual-rotate (corner &optional beg end type) + "Move point to CORNER of the Visual selection. +Corner may be one of `upper-left', `upper-right', `lower-left' and +`lower-right': + + upper-left +---+ upper-right + | | + lower-left +---+ lower-right + +When called interactively, the selection is rotated clockwise." + :keep-visual t + (interactive + (let ((corners '(#1=upper-left upper-right lower-right lower-left #1#))) + (list (cadr (memq (evil-visual-block-corner) corners))))) + (let* ((beg (or beg (point))) + (end (or end (mark t) beg)) + (type (or type evil-this-type)) + range) + (cond + ((memq type '(rectangle block)) + (setq range (evil-block-rotate beg end :corner corner) + beg (pop range) + end (pop range)) + (unless (eq corner (evil-visual-block-corner corner beg end)) + (evil-swap beg end)) + (goto-char beg) + (when (evil-visual-state-p) + (evil-move-mark end) + (evil-visual-refresh nil nil nil :corner corner))) + ((memq corner '(upper-right lower-right)) + (goto-char (max beg end)) + (when (evil-visual-state-p) + (evil-move-mark (min beg end)))) + (t + (goto-char (min beg end)) + (when (evil-visual-state-p) + (evil-move-mark (max beg end))))))) + +;;; Insertion commands + +(defun evil-insert (count &optional vcount skip-empty-lines) + "Switch to Insert state just before point. +The insertion will be repeated COUNT times on the next VCOUNT lines, +starting at the same column. +If SKIP-EMPTY-LINES is non-nil, the insertion will not be performed +on lines on which the insertion point would be after the end of the +lines. This is the default behaviour for Visual-state insertion." + (interactive + (if (not (evil-visual-state-p)) + (list (prefix-numeric-value current-prefix-arg)) + (evil-visual-rotate 'upper-left) + (list (prefix-numeric-value current-prefix-arg) + (when (memq (evil-visual-type) '(line block)) + (1+ (evil-count-lines evil-visual-point evil-visual-mark))) + t))) + (and evil-want-fine-undo vcount (evil-start-undo-step)) + (if (and (evil-visual-state-p) (eq (evil-visual-type) 'line)) + (evil-insert-line count vcount) + (setq evil-insert-count count + evil-insert-lines nil + evil-insert-vcount (and vcount + (> vcount 1) + (list (line-number-at-pos) + (current-column) + vcount)) + evil-insert-skip-empty-lines skip-empty-lines) + (evil-insert-state 1))) + +(defun evil-append (count &optional vcount skip-empty-lines) + "Switch to Insert state just after point. +The insertion will be repeated COUNT times and repeated once for +the next VCOUNT - 1 lines starting at the same column. If +SKIP-EMPTY-LINES is non-nil, the insertion will not be performed +on lines on which the insertion point would be after the end of +the lines." + (interactive + (list (prefix-numeric-value current-prefix-arg) + (and (evil-visual-state-p) + (memq (evil-visual-type) '(line block)) + (1+ (evil-count-lines evil-visual-point evil-visual-mark))))) + (if (and (called-interactively-p 'any) + (evil-visual-state-p)) + (cond + ((or (eq (evil-visual-type) 'line) + (and (eq (evil-visual-type) 'block) + (memq last-command '(next-line previous-line)) + (eq temporary-goal-column most-positive-fixnum))) + (evil-visual-rotate 'upper-left) + (evil-append-line count vcount)) + ((eq (evil-visual-type) 'block) + (let ((column (max (evil-column evil-visual-beginning) + (evil-column evil-visual-end)))) + (evil-visual-rotate 'upper-left) + (move-to-column column t) + (evil-insert count vcount skip-empty-lines))) + (t + (evil-visual-rotate 'lower-right) + (backward-char) + (evil-append count))) + (unless (eolp) (forward-char)) + (evil-insert count vcount skip-empty-lines) + (add-hook 'post-command-hook #'evil-maybe-remove-spaces))) + +(evil-define-command evil-insert-resume (count) + "Switch to Insert state at previous insertion point. +The insertion will be repeated COUNT times. If called from visual +state, only place point at the previous insertion position but do not +switch to insert state." + :keep-visual t + :type exclusive + :jump t + (interactive "p") + (evil-goto-mark ?^ t) + (unless (evil-visual-state-p) + (evil-insert count))) + +(defun evil-quoted-insert (count) + "Like `quoted-insert' but delete COUNT chars forward in replace state. +Adds a \"^\" overlay as an input prompt." + (interactive "p") + (let* ((opoint (point)) + chars-to-delete + (ov (if (not (evil-replace-state-p)) + (make-overlay opoint opoint) + (setq chars-to-delete (min (- (line-end-position) opoint) count)) + (evil-update-replace-alist opoint count chars-to-delete) + (make-overlay opoint (+ chars-to-delete opoint))))) + (unwind-protect + (progn + (overlay-put ov 'invisible t) + (overlay-put ov 'after-string #("^" 0 1 (face escape-glyph cursor 1))) + (let (overwrite-mode) ; Force `read-quoted-char' + (quoted-insert count)) + (when chars-to-delete (delete-char chars-to-delete))) + (delete-overlay ov)))) + +(evil-define-command evil-open-above (count) + "Insert a new line above point and switch to Insert state. +The insertion will be repeated COUNT times." + :suppress-operator t + (interactive "p") + (unless (eq evil-want-fine-undo t) + (evil-start-undo-step)) + (evil-insert-newline-above) + (setq evil-insert-count count + evil-insert-lines t + evil-insert-vcount nil) + (unwind-protect + (when evil-auto-indent + (indent-according-to-mode)) + (evil-insert-state 1))) + +(evil-define-command evil-open-below (count) + "Insert a new line below point and switch to Insert state. +The insertion will be repeated COUNT times." + :suppress-operator t + (interactive "p") + (unless (eq evil-want-fine-undo t) + (evil-start-undo-step)) + (push (point) buffer-undo-list) + (evil-insert-newline-below) + (setq evil-insert-count count + evil-insert-lines t + evil-insert-vcount nil) + (unwind-protect + (when evil-auto-indent + (indent-according-to-mode)) + (evil-insert-state 1))) + +(defun evil--insert-line (count vcount non-blank-p) + "Switch to insert state at the beginning of the current line. +If NON-BLANK-P is non-nil, point is placed at the first non-blank character +on the current line. If NON-BLANK-P is nil, point is placed at column 0, +or the beginning of visual line. The insertion will be repeated COUNT times. +If VCOUNT is non nil it should be number > 0. The insertion will be repeated +in the next VCOUNT - 1 lines below the current one." + (push (point) buffer-undo-list) + (let ((move-fn (if non-blank-p #'evil-first-non-blank #'evil-beginning-of-line))) + (if (and visual-line-mode + evil-respect-visual-line-mode) + (goto-char + (max (save-excursion + (funcall move-fn) + (point)) + (save-excursion + (beginning-of-visual-line) + (point)))) + (funcall move-fn)) + (setq evil-insert-count count + evil-insert-lines nil + evil-insert-vcount + (and vcount + (> vcount 1) + (list (line-number-at-pos) + move-fn + vcount)))) + (evil-insert-state 1)) + +(defun evil-insert-line (count &optional vcount) + "Switch to insert state at beginning of current line. +Point is placed at the first non-blank character on the current +line. The insertion will be repeated COUNT times. If VCOUNT is +non nil it should be number > 0. The insertion will be repeated +in the next VCOUNT - 1 lines below the current one." + (interactive "p") + (evil--insert-line count vcount t)) + +(evil-define-command evil-insert-0-line (count &optional vcount) + "Switch to insert state at beginning of current line. +Point is placed at column 0, or the beginning of the visual line. +The insertion will be repeated COUNT times. If VCOUNT is +non nil it should be number > 0. The insertion will be repeated +in the next VCOUNT - 1 lines below the current one." + :keep-visual t + :type exclusive + (interactive "p") + (if (evil-visual-state-p) + (evil-beginning-of-line) + (evil--insert-line count vcount nil))) + +(defun evil-append-line (count &optional vcount) + "Switch to Insert state at the end of the current line. +The insertion will be repeated COUNT times. If VCOUNT is non nil +it should be number > 0. The insertion will be repeated in the +next VCOUNT - 1 lines below the current one." + (interactive "p") + (if (and visual-line-mode + evil-respect-visual-line-mode) + (end-of-visual-line) + (evil-move-end-of-line)) + (setq evil-insert-count count + evil-insert-lines nil + evil-insert-vcount + (and vcount + (> vcount 1) + (list (line-number-at-pos) + #'end-of-line + vcount))) + (evil-insert-state 1)) + +(evil-define-command evil-insert-digraph (count) + "Insert COUNT digraphs." + :repeat change + (interactive "p") + (let ((opoint (point)) + chars-to-delete insert-prompt) + (if (evil-replace-state-p) + (progn + (setq chars-to-delete (min (- (line-end-position) opoint) count) + insert-prompt (make-overlay opoint (+ chars-to-delete opoint))) + (evil-update-replace-alist opoint count chars-to-delete)) + (setq insert-prompt (make-overlay opoint opoint))) + (insert-char (evil-read-digraph-char-with-overlay insert-prompt) count) + (when chars-to-delete (delete-char chars-to-delete)))) + +(evil-define-command evil-ex-show-digraphs () + "Show a list of all available digraphs." + :repeat nil + (eval-and-compile (require 'evil-digraphs)) + (let ((columns 3)) + (evil-with-view-list + :name "evil-digraphs" + :mode-name "Evil Digraphs" + :format + (cl-loop repeat columns vconcat [("Digraph" 8 nil) ("Sequence" 16 nil)]) + :entries + (cl-loop + with xs = (append evil-digraphs-table-user evil-digraphs-table) + while xs collect + (cl-loop + repeat columns vconcat + (cl-destructuring-bind (chars . digraph) (or (pop xs) '(() . ?\ )) + (list (char-to-string digraph) (concat chars))) + into row finally return (list nil row)))))) + +(defun evil--self-insert-string (string) + "Insert STRING as if typed interactively." + (let ((chars (append string nil))) + (dolist (char chars) + (let ((last-command-event char)) + (self-insert-command 1))))) + +(defun evil-copy-from-above (arg) + "Copy characters from preceding non-blank line. +The copied text is inserted before point. +ARG is the number of lines to move backward. +See also \\\\[evil-copy-from-below]." + (interactive + (cond + ;; if a prefix argument was given, repeat it for subsequent calls + ((and (null current-prefix-arg) + (eq last-command #'evil-copy-from-above)) + (setq current-prefix-arg last-prefix-arg) + (list (prefix-numeric-value current-prefix-arg))) + (t + (list (prefix-numeric-value current-prefix-arg))))) + (evil--self-insert-string (evil-copy-chars-from-line arg -1))) + +(defun evil-copy-from-below (arg) + "Copy characters from following non-blank line. +The copied text is inserted before point. +ARG is the number of lines to move forward. +See also \\\\[evil-copy-from-above]." + (interactive + (cond + ((and (null current-prefix-arg) + (eq last-command #'evil-copy-from-below)) + (setq current-prefix-arg last-prefix-arg) + (list (prefix-numeric-value current-prefix-arg))) + (t + (list (prefix-numeric-value current-prefix-arg))))) + (evil--self-insert-string (evil-copy-chars-from-line arg 1))) + +;; adapted from `copy-from-above-command' in misc.el +(defun evil-copy-chars-from-line (n num &optional col) + "Return N characters from line NUM, starting at column COL. +NUM is relative to the current line and can be negative. +COL defaults to the current column." + (interactive "p") + (let ((col (or col (current-column))) prefix) + (save-excursion + (forward-line num) + (when (looking-at "[[:space:]]*$") + (if (< num 0) + (skip-chars-backward " \t\n") + (skip-chars-forward " \t\n"))) + (evil-move-beginning-of-line) + (move-to-column col) + ;; if the column winds up in middle of a tab, + ;; return the appropriate number of spaces + (when (< col (current-column)) + (if (eq (preceding-char) ?\t) + (let ((len (min n (- (current-column) col)))) + (setq prefix (make-string len ?\s) + n (- n len))) + ;; if in middle of a control char, return the whole char + (backward-char 1))) + (concat prefix + (buffer-substring (point) + (min (line-end-position) + (+ n (point)))))))) + +(defun evil-enter-replace-state (count) + "Switch to Replace state at point. +The insertion will be repeated COUNT times." + (interactive "p") + (setq evil-insert-count count) + (setq evil-insert-count count + evil-insert-lines nil + evil-insert-vcount nil) + (evil-replace-state 1)) + +;; completion +(evil-define-command evil-complete-next (&optional arg) + "Complete to the nearest following word. +Search backward if a match isn't found. +Calls `evil-complete-next-func'." + :repeat change + (interactive "P") + (if (minibufferp) + (funcall evil-complete-next-minibuffer-func) + (funcall evil-complete-next-func arg))) + +(evil-define-command evil-complete-previous (&optional arg) + "Complete to the nearest preceding word. +Search forward if a match isn't found. +Calls `evil-complete-previous-func'." + :repeat change + (interactive "P") + (if (minibufferp) + (funcall evil-complete-previous-minibuffer-func) + (funcall evil-complete-previous-func arg))) + +(evil-define-command evil-complete-next-line (&optional arg) + "Complete a whole line. +Calls `evil-complete-next-line-func'." + :repeat change + (interactive "P") + (if (minibufferp) + (funcall evil-complete-next-minibuffer-func) + (funcall evil-complete-next-line-func arg))) + +(evil-define-command evil-complete-previous-line (&optional arg) + "Complete a whole line. +Calls `evil-complete-previous-line-func'." + :repeat change + (interactive "P") + (if (minibufferp) + (funcall evil-complete-previous-minibuffer-func) + (funcall evil-complete-previous-line-func arg))) + +;;; Search + +(defun evil-repeat-search (flag) + "Called to record a search command. +FLAG is either `pre' or `post' if the function is called before resp. +after executing the command." + (cond + ((and (evil-operator-state-p) (eq flag 'pre)) + (evil-repeat-record (this-command-keys)) + (evil-clear-command-keys)) + ((and (evil-operator-state-p) (eq flag 'post)) + ;; The value of (this-command-keys) at this point should be the + ;; key-sequence that called the last command that finished the + ;; search, usually RET. Therefore this key-sequence will be + ;; recorded in the post-command of the operator. Alternatively we + ;; could do it here. + (evil-repeat-record (if evil-regexp-search + (car-safe regexp-search-ring) + (car-safe search-ring)))) + (t (evil-repeat-motion flag)))) + +(evil-define-motion evil-search-forward () + (format "Search forward for user-entered text. +Searches for regular expression if `evil-regexp-search' is t.%s" + (if (and (fboundp 'isearch-forward) + (documentation 'isearch-forward)) + (format "\n\nBelow is the documentation string \ +for `isearch-forward',\nwhich lists available keys:\n\n%s" + (documentation 'isearch-forward)) "")) + :jump t + :type exclusive + :repeat evil-repeat-search + (evil-search-incrementally t evil-regexp-search)) + +(evil-define-motion evil-search-backward () + (format "Search backward for user-entered text. +Searches for regular expression if `evil-regexp-search' is t.%s" + (if (and (fboundp 'isearch-forward) + (documentation 'isearch-forward)) + (format "\n\nBelow is the documentation string \ +for `isearch-forward',\nwhich lists available keys:\n\n%s" + (documentation 'isearch-forward)) "")) + :jump t + :type exclusive + :repeat evil-repeat-search + (evil-search-incrementally nil evil-regexp-search)) + +(evil-define-motion evil-search-next (count) + "Repeat the last search." + :jump t + :type exclusive + (let ((orig (point)) + (search-string (if evil-regexp-search + (car-safe regexp-search-ring) + (car-safe search-ring)))) + (goto-char + ;; Wrap in `save-excursion' so that multiple searches have no visual effect. + (save-excursion + (evil-search search-string isearch-forward evil-regexp-search) + (when (and (> (point) orig) + (save-excursion + (evil-adjust-cursor) + (= (point) orig))) + ;; Point won't move after first attempt and `evil-adjust-cursor' takes + ;; effect, so start again. + (evil-search search-string isearch-forward evil-regexp-search)) + (point))) + (when (and count (> count 1)) + (dotimes (_ (1- count)) + (evil-search search-string isearch-forward evil-regexp-search))))) + +(evil-define-motion evil-search-previous (count) + "Repeat the last search in the opposite direction." + :jump t + :type exclusive + (dotimes (_ (or count 1)) + (evil-search (if evil-regexp-search + (car-safe regexp-search-ring) + (car-safe search-ring)) + (not isearch-forward) evil-regexp-search))) + +(evil-define-motion evil-search-word-backward (count &optional symbol) + "Search backward for symbol under point." + :jump t + :type exclusive + (interactive (list (prefix-numeric-value current-prefix-arg) + evil-symbol-word-search)) + (dotimes (_ (or count 1)) + (evil-search-word nil nil symbol))) + +(evil-define-motion evil-search-word-forward (count &optional symbol) + "Search forward for symbol under point." + :jump t + :type exclusive + (interactive (list (prefix-numeric-value current-prefix-arg) + evil-symbol-word-search)) + (dotimes (_ (or count 1)) + (evil-search-word t nil symbol))) + +(evil-define-motion evil-search-unbounded-word-backward (count &optional symbol) + "Search backward for symbol under point. +The search is unbounded, i.e., the pattern is not wrapped in +\\<...\\>." + :jump t + :type exclusive + (interactive (list (prefix-numeric-value current-prefix-arg) + evil-symbol-word-search)) + (dotimes (_ (or count 1)) + (evil-search-word nil t symbol))) + +(evil-define-motion evil-search-unbounded-word-forward (count &optional symbol) + "Search forward for symbol under point. +The search is unbounded, i.e., the pattern is not wrapped in +\\<...\\>." + :jump t + :type exclusive + (interactive (list (prefix-numeric-value current-prefix-arg) + evil-symbol-word-search)) + (dotimes (_ (or count 1)) + (evil-search-word t t symbol))) + +(defun evil-goto-definition-imenu (string _position) + "Find definition for STRING with imenu." + (require 'imenu nil t) + (let (ientry ipos) + (when (fboundp 'imenu--make-index-alist) + (ignore-errors (setq ientry (imenu--make-index-alist))) + (setq ientry (imenu--in-alist string ientry)) + (setq ipos (cdr ientry)) + (when (and (markerp ipos) + (eq (marker-buffer ipos) (current-buffer))) + (setq ipos (marker-position ipos)) + (when (numberp ipos) + (evil-search (format "\\_<%s\\_>" (regexp-quote string)) t t ipos) + t))))) + +(defun evil-goto-definition-semantic (_string position) + "Find definition for POSITION with semantic." + (and (fboundp 'semantic-ia-fast-jump) + (ignore-errors (semantic-ia-fast-jump position)))) + +(declare-function xref-backend-identifier-at-point "xref") + +(defun evil-goto-definition-xref (_string position) + "Find definition at POSITION with xref." + (when (fboundp 'xref-find-definitions) + (let ((identifier (save-excursion + (goto-char position) + (xref-backend-identifier-at-point (xref-find-backend))))) + (condition-case nil + (progn (xref-find-definitions identifier) + t) + (user-error nil))))) + +(defun evil-goto-definition-search (string _position) + "Find definition for STRING with evil-search." + (evil-search (format "\\_<%s\\_>" (regexp-quote string)) t t (point-min)) + t) + +(evil-define-motion evil-goto-definition () + "Go to definition or first occurrence of symbol under point. +See also `evil-goto-definition-functions'." + :jump t + :type exclusive + (let* ((match (evil--find-thing t 'symbol)) + (string (car match)) + (position (cdr match))) + (if (null string) + (user-error "No symbol under cursor") + (setq isearch-forward t) + (run-hook-with-args-until-success 'evil-goto-definition-functions + string position)))) + +;;; Folding +(defun evil-fold-action (list action) + "Perform fold ACTION for each matching major or minor mode in LIST. + +ACTION will be performed for the first matching handler in LIST. For more +information on its features and format, see the documentation for +`evil-fold-list'. + +If no matching ACTION is found in LIST, an error will signaled. + +Handler errors will be demoted, so a problem in one handler will (hopefully) +not interfere with another." + (if (null list) + (user-error + "Enable one of the following modes for folding to work: %s" + (mapconcat #'symbol-name (mapcar #'caar evil-fold-list) ", ")) + (let* ((modes (caar list))) + (if (evil--mode-p modes) + (let* ((actions (cdar list)) + (fn (plist-get actions action))) + (when fn + (with-demoted-errors "Error: %S" (funcall fn)))) + (evil-fold-action (cdr list) action))))) + +(defun evil--mode-p (modes) + "Determine whether any symbol in MODES represents the current +buffer's major mode or any of its minors." + (unless (eq modes '()) + (let ((mode (car modes))) + (or (eq major-mode mode) + (and (boundp mode) (symbol-value mode)) + (evil--mode-p (cdr modes)))))) + +(evil-define-command evil-toggle-fold () + "Open or close a fold under point. +See also `evil-open-fold' and `evil-close-fold'." + (evil-fold-action evil-fold-list :toggle)) + +(evil-define-command evil-open-folds () + "Open all folds. +See also `evil-close-folds'." + (evil-fold-action evil-fold-list :open-all)) + +(evil-define-command evil-close-folds () + "Close all folds. +See also `evil-open-folds'." + (evil-fold-action evil-fold-list :close-all)) + +(evil-define-command evil-open-fold () + "Open fold at point. +See also `evil-close-fold'." + (evil-fold-action evil-fold-list :open)) + +(evil-define-command evil-open-fold-rec () + "Open fold at point recursively. +See also `evil-open-fold' and `evil-close-fold'." + (evil-fold-action evil-fold-list :open-rec)) + +(evil-define-command evil-close-fold () + "Close fold at point. +See also `evil-open-fold'." + (evil-fold-action evil-fold-list :close)) + +;;; Ex + +(evil-define-operator evil-write (beg end _type file-or-append &optional bang) + "Save the current buffer, from BEG to END, to FILE-OR-APPEND. +If FILE-OR-APPEND is of the form \">> FILE\", append to FILE +instead of overwriting. The current buffer's filename is not +changed unless it has no associated file and no region is +specified. If the file already exists and the BANG argument is +non-nil, it is overwritten without confirmation." + :motion nil + :move-point nil + :type line + :repeat nil + (interactive "") + (let* ((append-and-filename (evil-extract-append file-or-append)) + (append (car append-and-filename)) + (filename (cdr append-and-filename)) + (bufname (buffer-file-name (buffer-base-buffer)))) + (when (zerop (length filename)) + (setq filename bufname)) + (cond + ((zerop (length filename)) + (user-error "Please specify a file name for the buffer")) + ;; execute command on region + ((eq (aref filename 0) ?!) + (shell-command-on-region beg end (substring filename 1))) + ;; with region or append, always save to file without resetting + ;; modified flag + ((or append (and beg end)) + (write-region beg end filename append nil nil (not (or append bang)))) + ;; no current file + ((null bufname) + (write-file filename (not bang))) + ;; save current buffer to its file + ((string= filename bufname) + (if (not bang) (save-buffer) (write-file filename))) + ;; save to another file + (t + (write-region nil nil filename + nil (not bufname) nil + (not bang)))))) + +(evil-define-command evil-write-all (bang) + "Save all buffers visiting a file. +If BANG is non nil then read-only buffers are saved, too, +otherwise they are skipped. " + :repeat nil + :move-point nil + (interactive "") + (if bang + (save-some-buffers t) + ;; save only buffer that are not read-only and + ;; that are visiting a file + (save-some-buffers t + #'(lambda () + (and (not buffer-read-only) + (buffer-file-name)))))) + +(evil-define-command evil-update () + "Same as `evil-write', but only write when the buffer has been modified." + :motion nil + :move-point nil + :type line + :repeat nil + (when (buffer-modified-p) + (call-interactively #'evil-write))) + +(evil-define-command evil-save (filename &optional bang) + "Save the current buffer to FILENAME. +Changes the file name of the current buffer to FILENAME. If no +FILENAME is given, the current file name is used." + :repeat nil + :move-point nil + (interactive "") + (when (zerop (length filename)) + (setq filename (buffer-file-name (buffer-base-buffer)))) + (write-file filename (not bang))) + +(evil-define-command evil-edit (file &optional bang) + "Open FILE. +If no FILE is specified, reload the current buffer from disk." + :repeat nil + (interactive "") + (if file + (find-file file) + (revert-buffer bang (or bang (not (buffer-modified-p))) t) + (read-only-mode -1))) + +(evil-define-command evil-view (file &optional bang) + "Open FILE but don't allow changes. +If no FILE is specified, reload the current buffer from disk as read-only." + :repeat nil + (interactive "") + (if file + (find-file-read-only file) + (revert-buffer bang (or bang (not (buffer-modified-p))) t) + (read-only-mode +1))) + +(evil-define-command evil-buffer-add (file) + "Add FILE to the buffer list, but don't visit it. " + :repeat nil + (interactive "") + (if (or (not file) (string= "" file)) + (user-error "No file specified") + (find-file-noselect file))) + +(evil-define-command evil-read (count file) + "Insert the contents of FILE below the current line or line COUNT." + :repeat nil + :move-point nil + (interactive "P") + (when (and file (not (zerop (length file)))) + (when count (goto-char (point-min))) + (when (or (not (zerop (forward-line (or count 1)))) + (not (bolp))) + (insert "\n")) + (cond + ((/= (aref file 0) ?!) + (when (member file '("#" "%")) + (setq file (evil-ex-replace-special-filenames file))) + (let ((result (insert-file-contents file))) + (save-excursion + (forward-char (cadr result)) + (unless (bolp) (insert "\n"))))) + (t + (shell-command (evil-ex-replace-special-filenames (substring file 1)) t) + (goto-char (mark)) + (unless (bolp) (insert "\n")) + (forward-line -1))))) + +(evil-define-command evil-show-files () + "Show the file-list. +The same as `buffer-menu', but shows only buffers visiting +files." + :repeat nil + (buffer-menu 1)) + +(evil-define-command evil-goto-error (count) + "Go to error number COUNT. +If no COUNT is supplied, move to the current error. + +Acts like `first-error' other than when given no counts, goes +to the current error instead of the first, like in Vim's :cc +command." + :repeat nil + (interactive "") + (if count + (first-error (if (eql 0 count) 1 count)) + (next-error 0))) + +(evil-define-command evil-buffer (buffer) + "Switch to another buffer." + :repeat nil + (interactive "") + (cond + ;; no buffer given, switch to "other" buffer + ((null buffer) (switch-to-buffer (other-buffer))) + ;; we are given the name of an existing buffer + ((get-buffer buffer) (switch-to-buffer buffer)) + ;; try to complete the buffer + ((let ((all-buffers (internal-complete-buffer buffer nil t))) + (when (= (length all-buffers) 1) + (switch-to-buffer (car all-buffers))))) + (t + (when (y-or-n-p + (format "No buffer with name \"%s\" exists. Create new buffer? " + buffer)) + (switch-to-buffer buffer))))) + +(evil-define-command evil-next-buffer (&optional count) + "Go to the COUNTth next buffer in the buffer list." + :repeat nil + (interactive "p") + (next-buffer count)) + +(evil-define-command evil-prev-buffer (&optional count) + "Go to the COUNTth prev buffer in the buffer list." + :repeat nil + (interactive "p") + (previous-buffer count)) + +(evil-define-command evil-delete-buffer (buffer &optional bang) + "Delete a buffer. +All windows currently showing this buffer will be closed except +for the last window in each frame." + (interactive "") + (with-current-buffer (or buffer (current-buffer)) + (when bang + (set-buffer-modified-p nil) + (dolist (process (process-list)) + (when (eq (process-buffer process) (current-buffer)) + (set-process-query-on-exit-flag process nil)))) + ;; get all windows that show this buffer + (let ((wins (get-buffer-window-list (current-buffer) nil t))) + ;; if the buffer which was initiated by emacsclient, + ;; call `server-edit' from server.el to avoid + ;; "Buffer still has clients" message + (if (and (bound-and-true-p server-buffer-clients) + (fboundp 'server-edit)) + (server-edit) + (kill-buffer nil)) + ;; close all windows that showed this buffer + (dolist (w wins) (ignore-errors (delete-window w)))))) + +(evil-define-command evil-quit (&optional force) + "Close the current window, current frame, current tab, Emacs. +If the current frame belongs to some client the client connection +is closed." + :repeat nil + (interactive "") + (condition-case nil + (evil-window-delete) + (error + (if (and (bound-and-true-p server-buffer-clients) + (fboundp 'server-edit) + (fboundp 'server-buffer-done)) + (if force + (server-buffer-done (current-buffer)) + (server-edit)) + (condition-case nil + (delete-frame) + (error + (condition-case nil + (tab-bar-close-tab) + (error + (if force + (kill-emacs) + (save-buffers-kill-emacs)))))))))) + +(evil-define-command evil-quit-all (&optional bang) + "Exit Emacs, asking for saving." + :repeat nil + (interactive "") + (if (null bang) + (save-buffers-kill-terminal) + (let ((proc (frame-parameter (selected-frame) 'client))) + (if proc + (with-no-warnings + (server-delete-client proc)) + (dolist (process (process-list)) + (set-process-query-on-exit-flag process nil)) + (kill-emacs))))) + +(evil-define-command evil-quit-all-with-error-code (&optional err-code _force) + "Exit Emacs without saving, returning an non-zero error code. +The FORCE argument is only there for compatibility and is ignored. +This function fails with an error if Emacs is run in server mode." + :repeat nil + (interactive "") + (if (bound-and-true-p server-buffer-clients) + (user-error "Cannot exit client process with error code") + (kill-emacs (or err-code 1)))) + +(evil-define-command evil-save-and-quit () + "Save all buffers and exit Emacs." + (save-buffers-kill-terminal t)) + +(evil-define-command evil-save-and-close (file &optional bang) + "Save the current buffer and close the window." + :repeat nil + (interactive "") + (evil-write nil nil nil file bang) + (evil-quit)) + +(evil-define-command evil-save-modified-and-close (file &optional bang) + "Save the current buffer and close the window." + :repeat nil + (interactive "") + (when (buffer-modified-p) + (evil-write nil nil nil file bang)) + (evil-quit)) + +(evil-define-operator evil-shell-command + (beg end _type command &optional previous) + "Execute a shell command. +If BEG, END and TYPE is specified, COMMAND is executed on the region, +which is replaced with the command's output. Otherwise, the +output is displayed in its own buffer. If PREVIOUS is non-nil, +the previous shell command is executed instead." + (interactive "") + (if (not evil-called-from-ex-p) + (let ((current-prefix-arg + (if (or current-prefix-arg (evil-visual-state-p)) + current-prefix-arg + (goto-char (min beg end)) + (count-lines beg end))) + (evil-ex-initial-input "!")) + (call-interactively #'evil-ex)) + (if (zerop (length command)) + (when previous (setq command evil-previous-shell-command)) + (setq command (evil-ex-replace-special-filenames command) + evil-previous-shell-command command)) + (cond + ((zerop (length command)) + (user-error "No%s shell command" (if previous " previous" ""))) + (evil-ex-range + (if (not evil-display-shell-error-in-message) + (shell-command-on-region beg end command nil t) + (let ((output-buffer (generate-new-buffer " *temp*")) + (error-buffer (generate-new-buffer " *temp*"))) + (unwind-protect + (if (zerop (shell-command-on-region + beg end command + output-buffer nil error-buffer)) + (progn + (delete-region beg end) + (insert-buffer-substring output-buffer) + (goto-char beg) + (evil-first-non-blank)) + (display-message-or-buffer error-buffer)) + (kill-buffer output-buffer) + (kill-buffer error-buffer))))) + (t (shell-command command))))) + +(evil-define-command evil-make (arg) + "Call a build command in the current directory. +If ARG is nil this function calls `recompile', otherwise it calls +`compile' passing ARG as build command." + (interactive "") + (if (and (fboundp 'recompile) + (not arg)) + (recompile) + (compile arg))) + +;; TODO: escape special characters (currently only \n) ... perhaps +;; there is some Emacs function doing this? +(evil-define-command evil-show-registers (registers) + "Show the contents of REGISTERS, or all registers, if none supplied." + :repeat nil + (interactive "") + (let* ((all-registers (evil-register-list)) + (reg-chars (string-to-list registers)) + (display-regs (if reg-chars + (cl-remove-if-not (lambda (r) (memq (car r) reg-chars)) + all-registers) + all-registers))) + (evil-with-view-list + :name "evil-registers" + :mode-name "Evil Registers" + :format + [("Register" 10 nil) + ("Value" 1000 nil)] + :entries + (cl-loop for (key . val) in display-regs + collect `(nil [,(char-to-string key) + ,(cond ((stringp val) + (replace-regexp-in-string "\n" "^J" val)) + ((vectorp val) + (key-description val)) + (t ""))]))))) + +(evil-define-command evil-show-marks (mrks) + "Show all marks. +If MRKS is non-nil it should be a string and only registers +corresponding to the characters of this string are shown." + :repeat nil + (interactive "") + ;; To get markers and positions, we can't rely on 'global-mark-ring' + ;; provided by Emacs (although it will be much simpler and faster), + ;; because 'global-mark-ring' does not store mark characters, but + ;; only buffer name and position. Instead, 'evil-markers-alist' is + ;; used; this is list maintained by Evil for each buffer. + (let ((all-markers + ;; get global and local marks + (append (cl-remove-if (lambda (m) + (or (evil-global-marker-p (car m)) + (not (markerp (cdr m))))) + evil-markers-alist) + (cl-remove-if (lambda (m) + (or (not (evil-global-marker-p (car m))) + (not (markerp (cdr m))))) + (default-value 'evil-markers-alist))))) + (when mrks + (setq mrks (string-to-list mrks)) + (setq all-markers (cl-delete-if (lambda (m) + (not (member (car m) mrks))) + all-markers))) + ;; map marks to list of 4-tuples (char row col file) + (setq all-markers + (mapcar (lambda (m) + (with-current-buffer (marker-buffer (cdr m)) + (save-excursion + (goto-char (cdr m)) + (list (car m) + (line-number-at-pos (point)) + (current-column) + (buffer-name))))) + all-markers)) + (evil-with-view-list + :name "evil-marks" + :mode-name "Evil Marks" + :format [("Mark" 8 nil) + ("Line" 8 nil) + ("Column" 8 nil) + ("Buffer" 1000 nil)] + :entries (cl-loop for m in (sort all-markers (lambda (a b) (< (car a) (car b)))) + collect `(nil [,(char-to-string (nth 0 m)) + ,(number-to-string (nth 1 m)) + ,(number-to-string (nth 2 m)) + (,(nth 3 m))])) + :select-action #'evil--show-marks-select-action))) + +(defun evil--show-marks-select-action (entry) + (kill-buffer) + (switch-to-buffer (car (elt entry 3))) + (evil-goto-mark (string-to-char (elt entry 0)))) + +(defun evil--parse-delmarks (to-be-parsed &optional parsed) + "Where TO-BE-PARSED can contain ranges in the form `x-y'. +PARSED is a list of characters whose marks should be deleted. +Like vim, on invalid input, preceeding valid input is still parsed." + (cl-destructuring-bind (&optional a b c &rest) to-be-parsed + (cond + ((null to-be-parsed) parsed) + ;; single mark... + ((and (not (eq ?- b)) (or (<= ?a a ?z) (<= ?A a ?Z) (<= ?0 a ?9) + (memq a '(?\" ?^ ?. ?\[ ?\] ?< ?>)))) + (evil--parse-delmarks (cdr to-be-parsed) (cons a parsed))) + ;; range of marks... + ((and (eq ?- b) c (or (<= ?a a c ?z) (<= ?A a c ?Z) (<= ?0 a c ?9))) + (evil--parse-delmarks (nthcdr 3 to-be-parsed) + (append parsed (number-sequence a c)))) + (t (progn (evil-echo "Invalid input: %s" (apply #'string (remove nil to-be-parsed))) + parsed))))) + +(evil-define-command evil-delete-marks (marks &optional force) + "MARKS is a string denoting all marks to be deleted. Mark names are +either single characters or a range of characters in the form `x-y'. +If FORCE is non-nil and MARKS is blank, all local marks except 0-9 are removed." + (interactive "") + (let ((mark-chars (remove ?\s (append marks nil)))) + (cond + ((and force mark-chars) (evil-echo "Invalid input")) + (mark-chars + (let* ((delmarks (evil--parse-delmarks mark-chars)) + (delmarkp (lambda (m) (member (car m) delmarks)))) + ;; delete all parsed marks... + (setq evil-markers-alist + (cl-remove-if delmarkp evil-markers-alist)) + ;; ensure all parsed marks are deleted globally... + (set-default 'evil-markers-alist + (cl-remove-if delmarkp (default-value 'evil-markers-alist))))) + ;; delete local marks except 0-9... + (force (setq evil-markers-alist + (cl-remove-if-not (lambda (m) (<= ?0 (car m) ?9)) + evil-markers-alist)))))) + +(declare-function ffap-file-at-point "ffap") +(defvar ffap-string-at-point-region) +(evil-define-command evil-find-file-at-point-with-line () + "Open the file at point and go to position if present. +Supports positions in the following formats: \"path:line path(line)\", +\"path:line:col\" and \"path(line,col)\"." + (require 'ffap) + (let ((fname (ffap-file-at-point))) + (unless fname + (user-error "File does not exist.")) + (let* ((line-number-pattern ":\\([0-9]+\\)\\=" ) ; path:line format + (line-number-pattern-alt "\\=(\\([0-9]+\\))") ; path(line) format + (line-and-column-numbers-pattern ":\\([0-9]+\\):\\([0-9]+\\)\\=") ; path:line:col format + (line-and-column-numbers-pattern-alt "\\=(\\([0-9]+\\),\\([0-9]+\\))") ; file(line,col) format + (get-number (lambda (pattern match-number backward) + (save-excursion + (goto-char (cadr ffap-string-at-point-region)) + (and (if backward + (re-search-backward pattern (line-beginning-position) t) + (re-search-forward pattern (line-end-position) t)) + (string-to-number (match-string match-number)))))) + (line-number (or (funcall get-number line-and-column-numbers-pattern 1 t) + (funcall get-number line-and-column-numbers-pattern-alt 1 nil) + (funcall get-number line-number-pattern 1 t) + (funcall get-number line-number-pattern-alt 1 nil))) + (column-number (or (funcall get-number line-and-column-numbers-pattern 2 t) + (funcall get-number line-and-column-numbers-pattern-alt 2 nil)))) + (evil-echo "%s, %s" + (if line-number (format "line: %s" line-number) "no line") + (if column-number (format "column: %s" column-number) "no column")) + (find-file-at-point fname) + (when line-number + (goto-char (point-min)) + (forward-line (1- line-number)) + (when column-number + (move-to-column (1- column-number))))))) + +(evil-define-command evil-find-file-at-point-visual () + "Find the filename selected by the visual region. +Signal an error if the file does not exist." + (let ((region (buffer-substring (region-beginning) (region-end)))) + (if (file-exists-p region) + (find-file-at-point region) + (user-error "Can't find file \"%s\" in path" region)))) + +(evil-ex-define-argument-type state + "Define an argument type which can take state names." + :collection + (lambda (arg predicate flag) + (let ((completions (cons '(nil) evil-state-properties))) + (when arg + (cond + ((null flag) + (try-completion arg completions predicate)) + ((eq flag t) + (all-completions arg completions predicate)) + ((eq flag 'lambda) + (test-completion arg completions predicate)) + ((eq (car-safe flag) 'boundaries) + (cons 'boundaries + (completion-boundaries arg + completions + predicate + (cdr flag))))))))) + +(evil-define-interactive-code "" + "A valid evil state." + :ex-arg state + (list (and evil-called-from-ex-p evil-ex-argument + (intern-soft evil-ex-argument)))) + +;; TODO: should we merge this command with `evil-set-initial-state'? +(evil-define-command evil-ex-set-initial-state (state) + "Set the initial state for the current major mode to STATE. +This is the state the buffer comes up in. See `evil-set-initial-state'." + :repeat nil + (interactive "") + (if (not (or (assq state evil-state-properties) + (null state))) + (user-error "State %s cannot be set as initial Evil state" state) + (let ((current-initial-state (evil-initial-state major-mode))) + (unless (eq current-initial-state state) + ;; only if we selected a new mode + (when (y-or-n-p (format "Major-mode `%s' has initial mode `%s'. \ +Change to `%s'? " + major-mode + (or current-initial-state "DEFAULT") + (or state "DEFAULT"))) + (evil-set-initial-state major-mode state) + (when (y-or-n-p "Save setting in customization file? ") + (dolist (s (list current-initial-state state)) + (when s + (let ((var (intern (format "evil-%s-state-modes" s)))) + (customize-save-variable var (symbol-value var))))))))))) + +(evil-define-command evil-force-normal-state () + "Switch to normal state without recording current command." + :repeat abort + :suppress-operator t + (evil-normal-state)) + +(evil-define-motion evil-ex-search-next (count) + "Go to the next occurrence." + :jump t + :type exclusive + (evil-ex-search count)) + +(evil-define-motion evil-ex-search-previous (count) + "Go the the previous occurrence." + :jump t + :type exclusive + (let ((evil-ex-search-direction + (if (eq evil-ex-search-direction 'backward) 'forward 'backward))) + (evil-ex-search count))) + +(defun evil-repeat-ex-search (flag) + "Called to record a search command. +FLAG is either `pre' or `post' if the function is called before resp. +after executing the command." + (cond + ((and (evil-operator-state-p) (eq flag 'pre)) + (evil-repeat-record (this-command-keys)) + (evil-clear-command-keys)) + ((and (evil-operator-state-p) (eq flag 'post)) + (let ((search-separator (car (last evil-repeat-info)))) + (evil-repeat-record (evil-ex-pattern-regex evil-ex-search-pattern)) + (when evil-ex-search-offset + (evil-repeat-record (concat search-separator evil-ex-search-offset)))) + ;; If it weren't for the fact that `exit-minibuffer' throws an `exit' + ;; tag, which bypasses the source of `this-command-keys', we'd be able + ;; to capture the key(s) in the post-command of the operator as usual. + ;; Fortunately however, `last-input-event' can see the key (by default, `return') + (when (zerop (length (this-command-keys))) + (evil-repeat-record (vector last-input-event)))) + (t (evil-repeat-motion flag)))) + +(evil-define-motion evil-ex-search-forward (count) + "Start a forward search." + :jump t + :type exclusive + :repeat evil-repeat-ex-search + (evil-ex-start-search 'forward count)) + +(evil-define-motion evil-ex-search-backward (count) + "Start a backward search." + :jump t + :repeat evil-repeat-ex-search + (evil-ex-start-search 'backward count)) + +(evil-define-motion evil-ex-search-word-forward (count &optional symbol) + "Search for the next occurrence of word under the cursor." + :jump t + :type exclusive + (interactive (list (prefix-numeric-value current-prefix-arg) + evil-symbol-word-search)) + (evil-ex-start-word-search nil 'forward count symbol)) + +(evil-define-motion evil-ex-search-word-backward (count &optional symbol) + "Search for the next occurrence of word under the cursor." + :jump t + :type exclusive + (interactive (list (prefix-numeric-value current-prefix-arg) + evil-symbol-word-search)) + (evil-ex-start-word-search nil 'backward count symbol)) + +(evil-define-motion evil-ex-search-unbounded-word-forward (count &optional symbol) + "Search for the next occurrence of word under the cursor." + :jump t + :type exclusive + (interactive (list (prefix-numeric-value current-prefix-arg) + evil-symbol-word-search)) + (evil-ex-start-word-search t 'forward count symbol)) + +(evil-define-motion evil-ex-search-unbounded-word-backward (count &optional symbol) + "Search for the next occurrence of word under the cursor." + :jump t + :type exclusive + (interactive (list (prefix-numeric-value current-prefix-arg) + evil-symbol-word-search)) + (evil-ex-start-word-search t 'backward count symbol)) + +(defun evil-revert-reveal (open-spots) + "Unconditionally close overlays in OPEN-SPOTS in current window. +Modified version of `reveal-close-old-overlays' from +reveal.el. OPEN-SPOTS is a local version of `reveal-open-spots'." + (dolist (spot open-spots) + (let ((window (car spot)) + (ol (cdr spot))) + (unless (eq window (selected-window)) + (error "evil-revert-reveal: slot with wrong window")) + (let* ((inv (overlay-get ol 'reveal-invisible)) + (open (or (overlay-get ol 'reveal-toggle-invisible) + (get inv 'reveal-toggle-invisible) + (overlay-get ol 'isearch-open-invisible-temporary)))) + (if (and (overlay-start ol) ; Check if still live + open) + (condition-case err + (funcall open ol t) + (error (message "!!Reveal-hide (funcall %s %s t): %s !!" + open ol err))) + (overlay-put ol 'invisible inv)) + ;; Remove the overlay from the list of open spots. + (overlay-put ol 'reveal-invisible nil))))) + +(defun evil--ex-substitute-final-message (nreplaced flags) + "Display message according to replacements and flags. +If FLAGS contains \"p\" or \"#\" and NREPLACED is more than 0, print +the last line to the echo area. Otherwise, print the number of +replacements made or found." + (let ((replaced-any (< 0 nreplaced))) + (cond + ((and replaced-any (memq ?p flags)) + (evil-echo "%s" (buffer-substring (line-beginning-position) + (line-end-position)))) + ((and replaced-any (memq ?# flags)) + (evil-echo "%s %s" (propertize (number-to-string (line-number-at-pos)) + 'face 'line-number-current-line) + (buffer-substring (line-beginning-position) + (line-end-position)))) + (t (evil-echo "%s %d occurrence%s" + (if (memq ?n flags) "Found" "Replaced") + nreplaced + (if (/= nreplaced 1) "s" "")))))) + +(evil-define-operator evil-ex-substitute + (beg end pattern replacement flags) + "The Ex substitute command. +\[BEG,END]substitute/PATTERN/REPLACEMENT/FLAGS" + :repeat nil + :jump t + :move-point nil + :motion evil-line + (interactive "") + (evil-ex-nohighlight) + (unless pattern (user-error "No pattern given")) + (setq replacement (or replacement "") + flags (append flags) + evil-ex-last-was-search nil + evil-ex-substitute-pattern pattern + evil-ex-substitute-replacement replacement + evil-ex-substitute-flags flags) + (let* ((inhibit-field-text-motion t) + (count-only (memq ?n flags)) + (confirm (and (memq ?c flags) (not count-only))) + (case-fold-search (evil-ex-pattern-ignore-case pattern)) + (case-replace case-fold-search) + (regex (evil-ex-pattern-regex pattern)) + (nreplaced 0) + (orig-point (point-marker)) + (last-point (point)) + (whole-line (evil-ex-pattern-whole-line pattern)) + (evil-ex-substitute-overlay (make-overlay (point) (point))) + (end-marker (move-marker (make-marker) end)) + (use-reveal confirm) + match-data + reveal-open-spots + transient-mark-mode) + (unwind-protect + (catch 'exit-search + (isearch-update-ring (setq isearch-string regex) t) + (evil-ex-hl-change 'evil-ex-substitute pattern) + (overlay-put evil-ex-substitute-overlay 'face 'isearch) + (overlay-put evil-ex-substitute-overlay 'priority 1001) + (goto-char beg) + (while (re-search-forward regex end-marker t) + (unless (and query-replace-skip-read-only + (text-property-any (match-beginning 0) (match-end 0) 'read-only t)) + (let* ((match-beg (match-beginning 0)) + (match-end (match-end 0)) + (zero-length-match (= match-beg match-end)) + match-contains-newline) + (goto-char match-beg) + (when (and (= match-beg end-marker) (> end-marker beg) (bolp)) + ;; This line is not included due to range being exclusive + (throw 'exit-search nil)) + (setq match-data (match-data t match-data) + match-contains-newline (search-forward "\n" match-end t)) + (goto-char match-end) + (if confirm + (let* ((next-replacement + (if (stringp replacement) replacement + (funcall (car replacement) (cdr replacement) nreplaced))) + (prompt + (format "Replace %s with %s (y/n/a/q/l/^E/^Y)? " + (match-string 0) + (match-substitute-replacement + next-replacement (not case-replace)))) + (search-invisible t) + response) + (move-overlay evil-ex-substitute-overlay match-beg match-end) + ;; Simulate `reveal-mode'. `reveal-mode' uses + ;; `post-command-hook' but that won't work here. + (when use-reveal + (reveal-post-command)) + (catch 'exit-read-char + (while (setq response (read-char prompt)) + (when (member response '(?y ?a ?l)) + (unless count-only + (set-match-data match-data) + (replace-match next-replacement (not case-replace))) + (cl-incf nreplaced) + (evil-ex-hl-set-region + 'evil-ex-substitute + (line-beginning-position 2) + (evil-ex-hl-get-max 'evil-ex-substitute))) + (cl-case response + ((?y ?n) (throw 'exit-read-char nil)) + (?a (setq confirm nil) + (throw 'exit-read-char nil)) + ((?q ?l ?\C-\[) (throw 'exit-search nil)) + (?\C-e (evil-scroll-line-down 1)) + (?\C-y (evil-scroll-line-up 1)))))) + (unless count-only + (let ((next-replacement + (if (stringp replacement) replacement + (funcall (car replacement) (cdr replacement) nreplaced)))) + (set-match-data match-data) + (replace-match next-replacement (not case-replace)))) + (cl-incf nreplaced)) + (setq last-point (point)) + (cond ((>= (point) end-marker) + ;; Don't want to perform multiple replacements at the end + ;; of the search region. + (throw 'exit-search nil)) + ((and (not whole-line) + (not match-contains-newline)) + (forward-line) + ;; forward-line just moves to the end of the line on the + ;; last line of the buffer. + (when (>= (point) end-marker) (throw 'exit-search nil))) + ;; For zero-length matches check to see if point won't + ;; move next time. This is a problem when matching the + ;; regexp "$" because we can enter an infinite loop, + ;; repeatedly matching the same character + ((and zero-length-match + (let ((pnt (point))) + (save-excursion + (and (re-search-forward regex end-marker t) + (= pnt (point)))))) + (when (eobp) (throw 'exit-search nil)) + (forward-char))))))) + (evil-ex-delete-hl 'evil-ex-substitute) + (delete-overlay evil-ex-substitute-overlay) + (goto-char (if count-only orig-point last-point)) + (move-marker orig-point nil) + (move-marker end-marker nil) + + (when use-reveal + (evil-revert-reveal reveal-open-spots))) + + (evil--ex-substitute-final-message nreplaced flags) + + (if (and (= nreplaced 0) evil-ex-point) + (goto-char evil-ex-point) + (evil-first-non-blank)))) + +(evil-define-operator evil-ex-repeat-substitute (beg end flags) + "Repeat last substitute command. +This is the same as \":s//~/\"." + :repeat nil + :jump t + :move-point nil + :motion evil-line + (interactive "") + (apply #'evil-ex-substitute beg end + (evil-ex-get-substitute-info (concat "//~/" flags)))) + +(evil-define-operator evil-ex-repeat-substitute-with-flags (beg end flags) + "Repeat last substitute command with last flags. +This is the same as \":s//~/&\"." + :repeat nil + :jump t + :move-point nil + :motion evil-line + (interactive "") + (apply #'evil-ex-substitute beg end + (evil-ex-get-substitute-info (concat "//~/&" flags)))) + +(evil-define-operator evil-ex-repeat-substitute-with-search (beg end flags) + "Repeat last substitute command with last search pattern. +This is the same as \":s//~/r\"." + :repeat nil + :jump t + :move-point nil + :motion evil-line + (interactive "") + (apply #'evil-ex-substitute beg end + (evil-ex-get-substitute-info (concat "//~/r" flags)))) + +(evil-define-operator evil-ex-repeat-substitute-with-search-and-flags + (beg end flags) + "Repeat last substitute command with last search pattern and last flags. +This is the same as \":s//~/&r\"." + :repeat nil + :jump t + :move-point nil + :motion evil-line + (interactive "") + (apply #'evil-ex-substitute beg end + (evil-ex-get-substitute-info (concat "//~/&r" flags)))) + +(evil-define-operator evil-ex-repeat-global-substitute () + "Repeat last substitute command on the whole buffer. +This is the same as \":%s//~/&\"." + :repeat nil + :jump t + :move-point nil + :motion evil-line + (interactive) + (apply #'evil-ex-substitute (point-min) (point-max) + (evil-ex-get-substitute-info (concat "//~/&")))) + +(declare-function hi-lock-read-face-name "hi-lock") +(evil-define-command evil-ex-match (args &optional bang) + "Define a pattern to highlight in the current buffer. +With no args, clear a highlight from the buffer +With only an ! argument, clear all highlights from the buffer. +With one arg, interpret as the pattern, and prompt for a face. +With two args, interpret as :match {face} /{pattern}/. +Unlike Vim, multiple highlights can be set at once, so there is no need for +\":2match\" and \":3match\" Ex commands." + (interactive "") + (unless args (setq args "")) + (string-match "\\` *\\([^ ]+\\)? *\\(.+\\)?\\'" args) + (let* ((face (match-string 1 args)) + (search-string (match-string 2 args)) + (raw-patterns (evil-delimited-arguments (or search-string face ""))) + (raw-pattern (or (car raw-patterns) "")) + (case-fold-search ; ignore case if non-nil + (eq (evil-ex-regex-case raw-pattern evil-ex-search-case) 'insensitive)) + search-upper-case ; bypass isearch-no-upper-case-p + (pattern-no-case (evil-ex-regex-without-case raw-pattern)) + (pattern (if evil-ex-search-vim-style-regexp + (evil-transform-vim-style-regexp pattern-no-case) + pattern-no-case))) + (cond + ((or (not face) (string= "none" face)) + (if bang + (hi-lock-unface-buffer t) + (call-interactively #'hi-lock-unface-buffer))) + ((/= 1 (length raw-patterns)) + (user-error "Invalid pattern argument")) + ((not search-string) + (require 'hi-lock) + (hi-lock-face-buffer pattern (hi-lock-read-face-name))) + (t (hi-lock-face-buffer pattern face))))) + +(defun evil-keep-lines (pattern beg end) + "Stripped down version of `keep-lines'. +Delete lines between BEG & END which don't match PATTERN." + (goto-char (min beg end)) + (setq end + (progn + (save-excursion + (goto-char (max beg end)) + (unless (or (bolp) (eobp)) + (forward-line 0)) + (point-marker)))) + (save-excursion + (or (bolp) (forward-line 1)) + (let ((start (point))) + (while (< (point) end) + ;; Start is first char not preserved by previous match. + (if (not (re-search-forward pattern end 'move)) + (delete-region start end) + (let ((end (save-excursion (goto-char (match-beginning 0)) + (forward-line 0) + (point)))) + ;; Now end is first char preserved by the new match. + (if (< start end) + (delete-region start end)))) + + (setq start (save-excursion (forward-line 1) (point))) + ;; If the match was empty, avoid matching again at same place. + (and (< (point) end) + (= (match-beginning 0) (match-end 0)) + (forward-char 1))))) + (set-marker end nil) + nil) + +(defun evil-flush-lines (pattern beg end) + "Stripped down version of `flush-lines'. +Delete lines between BEG & END which match PATTERN." + (goto-char (min beg end)) + (setq end (copy-marker (max beg end))) + (save-excursion + (while (and (< (point) end) + (re-search-forward pattern end t)) + (delete-region (save-excursion (goto-char (match-beginning 0)) + (forward-line 0) + (point)) + (progn (forward-line 1) (point))))) + (set-marker end nil)) + +(defun evil--ex-performant-global-delete (beg end pattern invert) + "Use fast functions for fast line deletion. +Delete lines between BEG & END which match PATTERN. +Use `evil-flush-lines' if INVERT is nil, or `evil-keep-lines' if not." + (goto-char end) + (re-search-backward pattern beg t) + (let ((end-marker (point-marker))) + (if invert + (evil-keep-lines pattern beg end) + (evil-flush-lines pattern beg end)) + (goto-char end-marker) + (set-marker end-marker nil))) + +(evil-define-operator evil-ex-global + (beg end pattern command &optional invert) + "The Ex global command. +\[BEG,END]global[!]/PATTERN/COMMAND" + :motion mark-whole-buffer + :move-point nil + (interactive "") + (unless pattern + (user-error "No pattern given")) + (unless command + (user-error "No command given")) + ;; TODO: `evil-ex-make-substitute-pattern' should be executed so + ;; :substitute can re-use :global's pattern depending on its `r' + ;; flag. This isn't supported currently but should be simple to add + (let* ((case-fold-search + (eq (evil-ex-regex-case pattern evil-ex-search-case) 'insensitive)) + (pattern (evil-ex-regex-without-case pattern)) + (command-form (evil-ex-parse command)) + (ex-delete (eq 'evil-ex-delete (evil-ex-completed-binding (nth 2 command-form)))) + (transient-mark-mode transient-mark-mode) + (deactivate-mark deactivate-mark) + markers) + (when (and pattern command) + (when evil-ex-search-vim-style-regexp + (setq pattern (evil-transform-vim-style-regexp pattern))) + (if (and ex-delete (not (nth 3 command-form)) (not (nth 1 command-form))) + (evil--ex-performant-global-delete beg end pattern invert) + (setq isearch-string pattern) + (isearch-update-ring pattern t) + (goto-char beg) + (evil-move-beginning-of-line) + (while (< (point) end) + (let ((match (re-search-forward pattern (line-end-position) t))) + (when (if invert (not match) match) + (push (move-marker (make-marker) + (or (and match (match-beginning 0)) + (line-beginning-position))) + markers))) + (forward-line)) + (setq markers (nreverse markers)) + (unwind-protect + (evil-with-single-undo + (let ((evil--ex-global-active-p t)) + (dolist (marker markers) + (goto-char marker) + (eval command-form t)))) + ;; ensure that all markers are deleted afterwards, + ;; even in the event of failure + (dolist (marker markers) + (set-marker marker nil)) + (run-hooks 'evil-after-global-hook)))))) + +(evil-define-operator evil-ex-global-inverted + (beg end pattern command &optional invert) + "The Ex vglobal command. +\[BEG,END]vglobal/PATTERN/COMMAND" + :motion mark-whole-buffer + :move-point nil + (interactive "") + (evil-ex-global beg end pattern command (not invert))) + +(evil-define-operator evil-ex-normal (beg end commands) + "The Ex normal command. +Execute the argument as normal command on each line in the +range. The given argument is passed straight to +`execute-kbd-macro'. The default is the current line." + :motion evil-line + (interactive "") + ;; replace ^[ by escape + (setq commands + (vconcat (mapcar (lambda (ch) (if (eq ch ?) 'escape ch)) + commands))) + (let (markers evil-called-from-ex-p prefix-arg current-prefix-arg) + (goto-char beg) + (beginning-of-line) + (while (when (< (point) end) + (push (point-marker) markers) + (and (= (forward-line) 0) (bolp)))) + (setq markers (nreverse markers)) + (deactivate-mark) + (evil-force-normal-state) + (evil-with-single-undo + (dolist (marker markers) + (goto-char marker) + (ignore-errors (execute-kbd-macro commands)) + (evil-force-normal-state) + (set-marker marker nil))))) + +(evil-define-motion evil-goto-char (position) + "Go to POSITION in the buffer. +Default position is the beginning of the buffer." + :jump t + (interactive "") + (goto-char (or position (point-min)))) + +(evil-define-operator evil-ex-line-number (beg end) + "Print the last line number." + :motion mark-whole-buffer + :move-point nil + (interactive "") + (evil-echo "%d" (count-lines (point-min) end))) + +(evil-define-command evil-show-file-info () + "Show basic file information." + (let* ((nlines (count-lines (point-min) (point-max))) + (curr (line-number-at-pos (point))) + (perc (if (> nlines 0) + (format "%d%%" (* (/ (float curr) (float nlines)) 100.0)) + "No lines in buffer")) + (file (buffer-file-name (buffer-base-buffer))) + (writable (and file (file-writable-p file))) + (readonly (if (and file (not writable)) "[readonly] " ""))) + (if file + (evil-echo "\"%s\" %d %slines --%s--" file nlines readonly perc) + (evil-echo "%d lines --%s--" nlines perc)))) + +(defvar sort-fold-case) +(evil-define-operator evil-ex-sort (beg end &optional args reverse) + "The Ex sort command. +\[BEG,END]sort[!] [/PATTERN/] [b][i][u][r][n][x][o] +The following additional options are supported: + + * i ignore case + * u remove duplicate lines + * r sort the contents of pattern + * n sort by the first decimal number + * x sort by the first hexadecimal number (with optional \"0x\" prefix) + * o sort by the first octal number + * b sort by the first binary number + +If a pattern is supplied without supplying the \"r\" option, sort +the contents of the lines after skipping the pattern. +If the pattern is empty, the last search pattern is used instead. + +The \"!\" argument means to sort in reverse order." + :motion mark-whole-buffer + :move-point nil + (interactive "") + (unless args (setq args "")) + (let ((inhibit-field-text-motion t) + options sort-fold-case unique base sort-pat pat) + ;; Handle arguments like + ;; /[^,]*,/ n + ;; and + ;; nu + (if (or (zerop (length args)) (memq (aref args 0) '(?i ?n ?x ?o ?b ?u ?r))) + (setq options args) + (setq args (evil-delimited-arguments args 2) + ;; Use last search pattern when an empty pattern is provided + pat (if (string= (car args) "") + (evil-ex-pattern-regex evil-ex-search-pattern) + (car args)) + options (cadr args))) + (cl-loop + for opt across options do + (cond + ((eq opt ?i) (setq sort-fold-case t)) + ((eq opt ?b) (setq base 2)) + ((eq opt ?o) (setq base 8)) + ((eq opt ?n) (setq base 10)) + ((eq opt ?x) (setq base 16)) + ((eq opt ?r) (setq sort-pat t)) + ((eq opt ?u) (setq unique t)) + ((eq opt ? )) + (t (user-error "Invalid sort option `%c'" opt)))) + (evil-with-restriction beg end + (goto-char beg) + (let ((num-re + (cond + ((null base) nil) + ((= base 2) "[01]+") + ((= base 8) "[0-7]+") + ((= base 10) "-?[0-9]+") + ((= base 16) "\\(-\\)?\\(?:0x\\)?\\([0-9a-f]+\\)"))) + key-end) + (sort-subr + reverse + #'forward-line + #'end-of-line + (lambda () + ;; Find the boundary of the key to match on the line + (setq key-end (line-end-position)) + (and (> (length pat) 0) + ;; When matching a pattern and one doesn't exist on the line, + ;; skip the line + (re-search-forward pat key-end 'move) + sort-pat ; Otherwise go to the start of the key + (progn (setq key-end (point)) + (goto-char (match-beginning 0)))) + ;; Return the key for the line when sorting numbers, otherwise let + ;; `sort-subr' extract the key + (when base + (let ((case-fold-search t)) + (if (not (re-search-forward num-re key-end t)) + ;; When sorting numbers and a number doesn't exist on the + ;; line, place it above all the numeric lines + most-negative-fixnum + (let ((num (string-to-number + (buffer-substring + (match-beginning (if (= base 16) 2 0)) + (match-end 0)) + base))) + (if (and (= base 16) (match-beginning 1)) + (- num) + num)))))) + ;; Only called when sorting lexicographically + (lambda () (goto-char key-end)))) + (when unique + (goto-char (point-min)) + (let ((case-fold-search sort-fold-case) + prev-line-beg) + (while (not (eobp)) + (if (and prev-line-beg + (eq 0 (compare-buffer-substrings + nil prev-line-beg (1- (point)) + nil (point) (line-end-position)))) + (delete-region (point) (line-beginning-position 2)) + (setq prev-line-beg (point)) + (forward-line))))))) + (goto-char beg)) + +;;; Window navigation + +(defvar evil--window-digit nil) + +(defun evil--window-keep-pred () + (eq 'evil-window-digit-argument this-command)) + +(defun evil--window-reset-digit () + (setq evil--window-digit nil) + (remove-hook 'post-command-hook #'evil--window-reset-digit)) + +(defun evil--window-on-exit () + (add-hook 'post-command-hook #'evil--window-reset-digit)) + +(defun evil-window-digit-argument () + "Like `digit-argument' but maintains the window map." + (interactive) + (unless (eq 'evil-window-digit-argument last-command) + (set-transient-map evil-window-map #'evil--window-keep-pred #'evil--window-on-exit)) + (let* ((char (if (integerp last-command-event) + last-command-event + (get last-command-event 'ascii-character))) + (digit (- (logand char ?\177) ?0))) + (setq evil--window-digit (+ (* 10 (or evil--window-digit 0)) digit)))) + +(defmacro evil-save-side-windows (&rest body) + "Toggle side windows, evaluate BODY, restore side windows." + (declare (indent defun) (debug (&rest form))) + (let ((sides (make-symbol "sidesvar"))) + `(let ((,sides (and (fboundp 'window-toggle-side-windows) + (window-with-parameter 'window-side)))) + ;; The compiler doesn't understand that all uses are protected + ;; by `fboundp' :-( + (declare-function window-toggle-side-windows "window") + (when ,sides + (window-toggle-side-windows)) + (unwind-protect + (progn ,@body) + (when ,sides + (window-toggle-side-windows)))))) + +(defun evil-resize-window (new-size &optional horizontal) + "Set the current window's width or height to NEW-SIZE. +If HORIZONTAL is non-nil the width of the window is changed, +otherwise its height is changed." + (let ((count (- new-size (if horizontal (window-width) (window-height))))) + (enlarge-window count horizontal))) + +(defun evil-move-window (side) + "Move the `selected-window' to SIDE. +The state of the `selected-window' is saved along with the state +of the window tree consisting of all the other windows. Then, all +windows are deleted, the remaining window is split according to +SIDE, the state of the window at SIDE is replaced with the saved +state of the `selected-window', and, finally, the state of the +saved window tree is reconstructed on the opposite side. + +SIDE has the same meaning as in `split-window'. + +Note, this function only operates on the window tree rooted in +the frame's main window and effectively preserves any side +windows (i.e. windows with a valid window-side window +parameter)." + (evil-save-side-windows + (unless (one-window-p) + (save-excursion + (let ((w (window-state-get (selected-window)))) + (delete-window) + (let ((wtree (window-state-get))) + (delete-other-windows) + (let ((subwin (selected-window)) + ;; NOTE: SIDE is new in Emacs 24 + (newwin (split-window nil nil side))) + (window-state-put wtree subwin) + (window-state-put w newwin) + (select-window newwin))))) + (balance-windows)))) + +(defun evil-alternate-buffer (&optional window) + "Return the last buffer WINDOW has displayed other than the current one. +This is equivalent to Vim's alternate buffer." + ;; If the last buffer visited has been killed, then `window-prev-buffers' + ;; returns a list with `window-buffer' at the head. + (let* ((prev-buffers (window-prev-buffers)) + (head (car prev-buffers))) + (if (eq (car head) (window-buffer window)) + (cadr prev-buffers) + head))) + +(evil-define-command evil-switch-to-windows-last-buffer () + "Switch to the last open buffer of the current window." + :repeat nil + (let ((previous-place (evil-alternate-buffer))) + (when previous-place + (switch-to-buffer (car previous-place))))) + +(evil-define-command evil-window-delete () + "Delete the current window or tab. +If `evil-auto-balance-windows' is non-nil then all children of +the deleted window's parent window are rebalanced." + (let ((p (window-parent))) + ;; If tabs are enabled and this is the only visible window, then attempt to + ;; close this tab. + (if (and (bound-and-true-p tab-bar-mode) + (null p)) + (tab-close) + (delete-window) + (when evil-auto-balance-windows + ;; balance-windows raises an error if the parent does not have + ;; any further children (then rebalancing is not necessary anyway) + (ignore-errors (balance-windows p)))))) + +(evil-define-command evil-window-split (&optional count file read-only) + "Split the current window horizontally, COUNT lines height, +editing a certain FILE. The new window will be created below +when `evil-split-window-below' is non-nil. If COUNT and +`evil-auto-balance-windows' are both non-nil then all children +of the parent of the splitted window are rebalanced." + :repeat nil + (interactive "") + (select-window + (split-window (selected-window) (when count (- count)) + (if evil-split-window-below 'below 'above))) + (when (and (not count) evil-auto-balance-windows) + (balance-windows (window-parent))) + (when file + (funcall (if read-only #'evil-view #'evil-edit) file))) + +(evil-define-command evil-window-split-view (&optional count file) + "As with `evil-window-split' but the file is opened read-only." + :repeat nil + (interactive "") + (evil-window-split count file t)) + +(evil-define-command evil-window-vsplit (&optional count file) + "Split the current window vertically, COUNT columns width, +editing a certain FILE. The new window will be created to the +right when `evil-vsplit-window-right' is non-nil. If COUNT and +`evil-auto-balance-windows'are both non-nil then all children +of the parent of the splitted window are rebalanced." + :repeat nil + (interactive "") + (select-window + (split-window (selected-window) (when count (- count)) + (if evil-vsplit-window-right 'right 'left))) + (when (and (not count) evil-auto-balance-windows) + (balance-windows (window-parent))) + (when file + (evil-edit file))) + +(evil-define-command evil-split-buffer (buffer) + "Split window and switch to another buffer." + :repeat nil + (interactive "") + (evil-window-split) + (evil-buffer buffer)) + +(evil-define-command evil-split-next-buffer (&optional count) + "Split the window and go to the COUNT-th next buffer in the buffer list." + :repeat nil + (interactive "p") + (evil-window-split) + (next-buffer count)) + +(evil-define-command evil-split-prev-buffer (&optional count) + "Split window and go to the COUNT-th prev buffer in the buffer list." + :repeat nil + (interactive "p") + (evil-window-split) + (previous-buffer count)) + +(evil-define-command evil-window-left (count) + "Move the cursor to new COUNT-th window left of the current one." + :repeat nil + (interactive "") + (dotimes (_ count) + (windmove-left))) + +(evil-define-command evil-window-right (count) + "Move the cursor to new COUNT-th window right of the current one." + :repeat nil + (interactive "") + (dotimes (_ count) + (windmove-right))) + +(evil-define-command evil-window-up (count) + "Move the cursor to new COUNT-th window above the current one." + :repeat nil + (interactive "") + (dotimes (_ (or count 1)) + (windmove-up))) + +(evil-define-command evil-window-down (count) + "Move the cursor to new COUNT-th window below the current one." + :repeat nil + (interactive "") + (dotimes (_ (or count 1)) + (windmove-down))) + +(evil-define-command evil-window-bottom-right () + "Move the cursor to bottom-right window." + :repeat nil + (let ((last-sibling (frame-root-window))) + (while (and last-sibling (not (window-live-p last-sibling))) + (setq last-sibling (window-last-child last-sibling))) + (when last-sibling + (select-window last-sibling)))) + +(evil-define-command evil-window-top-left () + "Move the cursor to top-left window." + :repeat nil + (let ((first-child (window-child (frame-root-window)))) + (while (and first-child (not (window-live-p first-child))) + (setq first-child (window-child first-child))) + (when first-child + (select-window + first-child)))) + +(evil-define-command evil-window-mru () + "Move the cursor to the previous (last accessed) buffer in another window. +More precisely, it selects the most recently used buffer that is +shown in some other window, preferably of the current frame, and +is different from the current one." + :repeat nil + (catch 'done + (dolist (buf (buffer-list (selected-frame))) + (let ((win (get-buffer-window buf))) + (when (and (not (eq buf (current-buffer))) + win + (not (eq win (selected-window)))) + (select-window win) + (throw 'done nil)))))) + +(evil-define-command evil-window-next (count) + "Move the cursor to the next window in the cyclic order. +With COUNT go to the count-th window in the order starting from +top-left." + :repeat nil + (interactive "") + (if (not count) + (other-window +1) + (evil-window-top-left) + (other-window (1- (min count (length (window-list))))))) + +(evil-define-command evil-window-prev (count) + "Move the cursor to the previous window in the cyclic order. +With COUNT go to the count-th window in the order starting from +top-left." + :repeat nil + (interactive "") + (if (not count) + (other-window -1) + (evil-window-top-left) + (other-window (1- (min count (length (window-list))))))) + +(evil-define-command evil-window-new (count file) + "Split the current window horizontally +and open a new buffer or edit a certain FILE." + :repeat nil + (interactive "") + (let ((new-window (split-window (selected-window) (when count (- count)) + (if evil-split-window-below 'below 'above)))) + (when (and (not count) evil-auto-balance-windows) + (balance-windows (window-parent))) + (select-window new-window) + (evil-buffer-new file))) + +(evil-define-command evil-window-vnew (count file) + "Split the current window vertically +and open a new buffer name or edit a certain FILE." + :repeat nil + (interactive "") + (let ((new-window (split-window (selected-window) (when count (- count)) + (if evil-vsplit-window-right 'right 'left)))) + (when (and (not count) evil-auto-balance-windows) + (balance-windows (window-parent))) + (select-window new-window) + (evil-buffer-new file))) + +(evil-define-command evil-buffer-new (&optional file) + "Edit a new unnamed buffer or FILE." + :repeat nil + (interactive "") + (if file + (evil-edit file) + (let ((buffer (generate-new-buffer "*new*"))) + (set-buffer-major-mode buffer) + (set-window-buffer nil buffer)))) + +(evil-define-command evil-window-increase-height (count) + "Increase current window height by COUNT." + :repeat nil + (interactive "") + (enlarge-window count)) + +(evil-define-command evil-window-decrease-height (count) + "Decrease current window height by COUNT." + :repeat nil + (interactive "") + (enlarge-window (- count))) + +(evil-define-command evil-window-increase-width (count) + "Increase current window width by COUNT." + :repeat nil + (interactive "") + (enlarge-window count t)) + +(evil-define-command evil-window-decrease-width (count) + "Decrease current window width by COUNT." + :repeat nil + (interactive "") + (enlarge-window (- count) t)) + +(evil-define-command evil-window-set-height (count) + "Set the height of the current window to COUNT." + :repeat nil + (interactive "") + (evil-resize-window (or count (frame-height)) nil)) + +(evil-define-command evil-window-set-width (count) + "Set the width of the current window to COUNT." + :repeat nil + (interactive "") + (evil-resize-window (or count (frame-width)) t)) + +(evil-define-command evil-ex-resize (arg) + "The ex :resize command. + +If ARG is a signed positive integer, increase the current window +height by ARG. + +If ARG is a signed negative integer, decrease the current window +height by ARG. + +If ARG is a positive integer without explicit sign, set the current +window height to ARG. + +If ARG is empty, maximize the current window height." + (interactive "") + (if (or (not arg) (= 0 (length arg))) + (evil-window-set-height nil) + (let ((n (string-to-number arg))) + (if (> n 0) + (if (= ?+ (aref arg 0)) + (evil-window-increase-height n) + (evil-window-set-height n)) + (evil-window-decrease-height (- n)))))) + +(evil-define-command evil-window-rotate-upwards () + "Rotate the windows according to the current cyclic ordering." + :repeat nil + (evil-save-side-windows + (let ((wlist (window-list)) + (slist (mapcar #'window-state-get (window-list)))) + (setq slist (append (cdr slist) (list (car slist)))) + (while (and wlist slist) + (window-state-put (car slist) (car wlist)) + (setq wlist (cdr wlist) + slist (cdr slist))) + (select-window (car (window-list)))))) + +(evil-define-command evil-window-rotate-downwards () + "Rotate the windows according to the current cyclic ordering." + :repeat nil + (evil-save-side-windows + (let ((wlist (window-list)) + (slist (mapcar #'window-state-get (window-list)))) + (setq slist (append (last slist) slist)) + (while (and wlist slist) + (window-state-put (car slist) (car wlist)) + (setq wlist (cdr wlist) + slist (cdr slist))) + (select-window (car (window-list)))))) + +(evil-define-command evil-window-exchange (&optional count) + "Exchange the current window with the next, or the COUNT-th, one." + :repeat nil + (interactive "") + (let ((original-window (selected-window))) + (evil-window-next count) + (if (fboundp 'window-swap-states) + (window-swap-states nil original-window t) + (let* ((other-window (selected-window)) + (original-state (window-state-get original-window)) + (other-state (window-state-get other-window))) + (window-state-put other-state original-window t) + (window-state-put original-state other-window t))))) + +(evil-define-command evil-window-move-very-top () + "Close the current window, split the upper-left one horizontally +and redisplay the current buffer there." + :repeat nil + (evil-move-window 'above)) + +(evil-define-command evil-window-move-far-left () + "Close the current window, split the upper-left one vertically +and redisplay the current buffer there." + :repeat nil + (evil-move-window 'left)) + +(evil-define-command evil-window-move-far-right () + "Close the current window, split the lower-right one vertically +and redisplay the current buffer there." + :repeat nil + (evil-move-window 'right)) + +(evil-define-command evil-window-move-very-bottom () + "Close the current window, split the lower-right one horizontally +and redisplay the current buffer there." + :repeat nil + (evil-move-window 'below)) + +;;; Tab commands + +(evil-define-command evil-tab-next (arg) + "Switch to the next tab. +If ARG is non-nil, parse ARG as an index and go to the tab at that +index." + :repeat nil + (interactive "") + (if arg + (tab-bar-select-tab arg) + (tab-bar-switch-to-next-tab))) + +;;; Mouse handling + +;; Large parts of this code are taken from mouse.el which is +;; distributed with GNU Emacs +(defun evil-mouse-drag-region (start-event) + "Set the region to the text that the mouse is dragged over. +Highlight the drag area as you move the mouse. +This must be bound to a button-down mouse event. + +If the click is in the echo area, display the `*Messages*' buffer. + +START-EVENT should be the event that started the drag." + (interactive "e") + ;; Give temporary modes such as isearch a chance to turn off. + (run-hooks 'mouse-leave-buffer-hook) + (evil-mouse-drag-track start-event t)) +(evil-set-command-property 'evil-mouse-drag-region :keep-visual t) + +;; Make sure `touch-screen-handle-touch' processes touch screen gestures by +;; ignoring `evil-mouse-drag-region' (bound to `down-mouse-1') during mouse +;; click emulation. +(put 'evil-mouse-drag-region 'ignored-mouse-command t) + +(defun evil-mouse-drag-track (start-event &optional + do-mouse-drag-region-post-process) + "Track mouse drags by highlighting area between point and cursor. +The region will be defined with mark and point. +DO-MOUSE-DRAG-REGION-POST-PROCESS should only be used by +`mouse-drag-region'." + (mouse-minibuffer-check start-event) + (setq mouse-selection-click-count-buffer (current-buffer)) + (deactivate-mark) + (let* ((scroll-margin 0) ; Avoid margin scrolling (Bug#9541). + (original-window (selected-window)) + ;; We've recorded what we needed from the current buffer and + ;; window, now let's jump to the place of the event, where things + ;; are happening. + (_ (mouse-set-point start-event)) + (echo-keystrokes 0) + (start-posn (event-start start-event)) + (start-point (posn-point start-posn)) + (start-window (posn-window start-posn)) + (start-window-start (window-start start-window)) + (start-hscroll (window-hscroll start-window)) + (bounds (window-edges start-window)) + (make-cursor-line-fully-visible nil) + (top (nth 1 bounds)) + (bottom (if (or (window-minibuffer-p start-window) + (not mode-line-format)) + (nth 3 bounds) + ;; Don't count the mode line. + (1- (nth 3 bounds)))) + (on-link (and mouse-1-click-follows-link + (or mouse-1-click-in-non-selected-windows + (eq start-window original-window)) + ;; Use start-point before the intangibility + ;; treatment, in case we click on a link inside an + ;; intangible text. + (mouse-on-link-p start-posn))) + (click-count (1- (event-click-count start-event))) + (remap-double-click (and on-link + (eq mouse-1-click-follows-link 'double) + (= click-count 1))) + ;; Suppress automatic hscrolling, because that is a nuisance + ;; when setting point near the right fringe (but see below). + (auto-hscroll-mode-saved auto-hscroll-mode) + (auto-hscroll-mode nil) + event end end-point) + + (setq mouse-selection-click-count click-count) + ;; In case the down click is in the middle of some intangible text, + ;; use the end of that text, and put it in START-POINT. + (if (< (point) start-point) + (goto-char start-point)) + (setq start-point (point)) + (if remap-double-click + (setq click-count 0)) + + (setq click-count (mod click-count 4)) + + ;; activate correct visual state + (let ((range (evil-mouse-start-end start-point start-point click-count))) + (set-mark (nth 0 range)) + (goto-char (nth 1 range))) + + (cond + ((= click-count 0) + (when (evil-visual-state-p) (evil-exit-visual-state))) + ((= click-count 1) + (evil-visual-char) + (evil-visual-post-command)) + ((= click-count 2) + (evil-visual-line) + (evil-visual-post-command)) + ((= click-count 3) + (evil-visual-block) + (evil-visual-post-command))) + + ;; Track the mouse until we get a non-movement event. + (track-mouse + (while (progn + (setq event (read-key)) + (or (mouse-movement-p event) + (memq (car-safe event) '(switch-frame select-window)))) + (unless (evil-visual-state-p) + (cond + ((= click-count 0) (evil-visual-char)) + ((= click-count 1) (evil-visual-char)) + ((= click-count 2) (evil-visual-line)) + ((= click-count 3) (evil-visual-block)))) + + (evil-visual-pre-command) + (unless (memq (car-safe event) '(switch-frame select-window)) + ;; Automatic hscrolling did not occur during the call to + ;; `read-event'; but if the user subsequently drags the + ;; mouse, go ahead and hscroll. + (let ((auto-hscroll-mode auto-hscroll-mode-saved)) + (redisplay)) + (setq end (event-end event) + end-point (posn-point end)) + (if (and (eq (posn-window end) start-window) + (integer-or-marker-p end-point)) + (evil-mouse--drag-set-mark-and-point start-point + end-point click-count) + (let ((mouse-row (cdr (cdr (mouse-position))))) + (cond + ((null mouse-row)) + ((< mouse-row top) + (mouse-scroll-subr start-window (- mouse-row top) + nil start-point)) + ((>= mouse-row bottom) + (mouse-scroll-subr start-window (1+ (- mouse-row bottom)) + nil start-point)))))) + (evil-visual-post-command))) + + ;; Store the fact that the current region was set by the mouse + (setq evil--region-from-mouse t) + + ;; Handle the terminating event if possible. + (when (consp event) + ;; Ensure that point is on the end of the last event. + (when (and (setq end-point (posn-point (event-end event))) + (eq (posn-window end) start-window) + (integer-or-marker-p end-point) + (/= start-point end-point)) + (evil-mouse--drag-set-mark-and-point start-point + end-point click-count)) + + ;; Find its binding. + (let* ((fun (key-binding (vector (car event)))) + (do-multi-click (and (> (event-click-count event) 0) + (functionp fun) + (not (memq fun '(mouse-set-point + mouse-set-region)))))) + (if (and (or (/= (mark) (point)) + (= click-count 1) ; word selection + (and (memq (evil-visual-type) '(line block)))) + (not do-multi-click)) + + ;; If point has moved, finish the drag. + (let (last-command this-command) + (and mouse-drag-copy-region + do-mouse-drag-region-post-process + (let (deactivate-mark) + (evil-visual-expand-region) + (copy-region-as-kill (mark) (point)) + (evil-visual-contract-region)))) + + ;; If point hasn't moved, run the binding of the + ;; terminating up-event. + (if do-multi-click + (goto-char start-point) + (deactivate-mark)) + (when (and (functionp fun) + (= start-hscroll (window-hscroll start-window)) + ;; Don't run the up-event handler if the window + ;; start changed in a redisplay after the + ;; mouse-set-point for the down-mouse event at + ;; the beginning of this function. When the + ;; window start has changed, the up-mouse event + ;; contains a different position due to the new + ;; window contents, and point is set again. + (or end-point + (= (window-start start-window) + start-window-start))) + (when (and on-link + (= start-point (point)) + (evil-mouse--remap-link-click-p start-event event)) + ;; If we rebind to mouse-2, reselect previous selected + ;; window, so that the mouse-2 event runs in the same + ;; situation as if user had clicked it directly. Fixes + ;; the bug reported by juri@jurta.org on 2005-12-27. + (if (or (vectorp on-link) (stringp on-link)) + (setq event (aref on-link 0)) + (select-window original-window) + (setcar event 'mouse-2) + ;; If this mouse click has never been done by the + ;; user, it doesn't have the necessary property to be + ;; interpreted correctly. + (put 'mouse-2 'event-kind 'mouse-click))) + (push event unread-command-events))))))) + +;; This function is a plain copy of `mouse--drag-set-mark-and-point', +;; which is only available in Emacs 24 +(defun evil-mouse--drag-set-mark-and-point (start click click-count) + (let* ((range (evil-mouse-start-end start click click-count)) + (beg (nth 0 range)) + (end (nth 1 range))) + (cond ((eq (mark) beg) + (goto-char end)) + ((eq (mark) end) + (goto-char beg)) + ((< click (mark)) + (set-mark end) + (goto-char beg)) + (t + (set-mark beg) + (goto-char end))))) + +;; This function is a plain copy of `mouse--remap-link-click-p', +;; which is only available in Emacs 23 +(defun evil-mouse--remap-link-click-p (start-event end-event) + (or (and (eq mouse-1-click-follows-link 'double) + (= (event-click-count start-event) 2)) + (and + (not (eq mouse-1-click-follows-link 'double)) + (= (event-click-count start-event) 1) + (= (event-click-count end-event) 1) + (or (not (integerp mouse-1-click-follows-link)) + (let ((t0 (posn-timestamp (event-start start-event))) + (t1 (posn-timestamp (event-end end-event)))) + (and (integerp t0) (integerp t1) + (if (> mouse-1-click-follows-link 0) + (<= (- t1 t0) mouse-1-click-follows-link) + (< (- t0 t1) mouse-1-click-follows-link)))))))) + +(defun evil-mouse-start-end (start end mode) + "Return a list of region bounds based on START and END according to MODE. +If MODE is not 1 then set point to (min START END), mark to (max +START END). If MODE is 1 then set point to start of word at (min +START END), mark to end of word at (max START END)." + (evil-sort start end) + (setq mode (mod mode 4)) + (if (/= mode 1) (list start end) + (list + (save-excursion + (goto-char (min (point-max) (1+ start))) + (if (zerop (forward-thing evil-mouse-word -1)) + (let ((bpnt (point))) + (forward-thing evil-mouse-word +1) + (if (> (point) start) bpnt (point))) + (point-min))) + (save-excursion + (goto-char end) + (1- + (if (zerop (forward-thing evil-mouse-word +1)) + (let ((epnt (point))) + (forward-thing evil-mouse-word -1) + (if (<= (point) end) epnt (point))) + (point-max))))))) + +;;; State switching + +(evil-define-command evil-exit-emacs-state (&optional buffer message) + "Exit Emacs state. +Changes the state to the previous state, or to Normal state +if the previous state was Emacs state." + :keep-visual t + :suppress-operator t + (interactive '(nil t)) + (with-current-buffer (or buffer (current-buffer)) + (when (evil-emacs-state-p) + (evil-change-to-previous-state buffer message) + (when (evil-emacs-state-p) + (evil-normal-state (and message 1)))))) + +(defun evil--restore-repeat-hooks () + "No insert-state repeat info is recorded after executing in normal state. +Restore the disabled repeat hooks on insert-state exit." + (evil-repeat-stop) + (add-hook 'pre-command-hook #'evil-repeat-pre-hook) + (add-hook 'post-command-hook #'evil-repeat-post-hook) + (remove-hook 'evil-insert-state-exit-hook #'evil--restore-repeat-hooks)) + +(defvar evil--execute-normal-return-state nil + "The state to return to after executing in normal state.") + +(evil-define-local-var evil--execute-normal-eol-pos nil + "Point position if it was at EOL before `evil-execute-in-normal-state'.") + +(defun evil-execute-in-normal-state () + "Execute the next command in Normal state." + (interactive) + (let ((buf (current-buffer)) + (state evil-state)) + (evil-with-delay (not (memq this-command + '(nil + evil-execute-in-normal-state + evil-replace-state + evil-use-register + evil-window-digit-argument + digit-argument + negative-argument + universal-argument + universal-argument-minus + universal-argument-more + universal-argument-other-key))) + post-command-hook + (with-current-buffer buf + ;; If cursor was after EOL before CTRL-O and is now at EOL, + ;; put it after EOL. + (and (or (when evil--execute-normal-eol-pos + (= (1+ (point)) (save-excursion + (goto-char evil--execute-normal-eol-pos) + (set-marker evil--execute-normal-eol-pos nil) + (line-end-position)))) + (and (eq (or goal-column temporary-goal-column) most-positive-fixnum) + (memq this-command '(next-line previous-line)))) + (not (eolp)) + (not (memq this-command + '(evil-insert evil-beginning-of-line evil-first-non-blank))) + (forward-char)) + (unless (memq evil-state '(replace insert)) + (evil-change-state state)) + (when (eq 'insert evil-state) + (remove-hook 'pre-command-hook #'evil-repeat-pre-hook) + (remove-hook 'post-command-hook #'evil-repeat-post-hook) + (add-hook 'evil-insert-state-exit-hook #'evil--restore-repeat-hooks)) + (setq evil-execute-normal-keys nil)))) + (setq evil-insert-count nil + evil--execute-normal-return-state evil-state + evil--execute-normal-eol-pos (when (eolp) (point-marker)) + evil-execute-normal-keys (this-command-keys)) + (let (evil-move-cursor-back) (evil-normal-state)) + (evil-echo "Switched to Normal state for the next command...")) + +(defun evil-stop-execute-in-emacs-state () + (when (and (not (eq this-command #'evil-execute-in-emacs-state)) + (not (minibufferp))) + (remove-hook 'post-command-hook #'evil-stop-execute-in-emacs-state) + (when (buffer-live-p evil-execute-in-emacs-state-buffer) + (with-current-buffer evil-execute-in-emacs-state-buffer + (if (and (eq evil-previous-state 'visual) + (not (use-region-p))) + (progn + (evil-change-to-previous-state) + (evil-exit-visual-state)) + (evil-change-to-previous-state)))) + (setq evil-execute-in-emacs-state-buffer nil))) + +(evil-define-command evil-execute-in-emacs-state () + "Execute the next command in Emacs state." + (add-hook 'post-command-hook #'evil-stop-execute-in-emacs-state t) + (setq evil-execute-in-emacs-state-buffer (current-buffer)) + (cond + ((evil-visual-state-p) + (let ((mrk (mark)) + (pnt (point))) + (evil-emacs-state) + (set-mark mrk) + (goto-char pnt))) + (t + (evil-emacs-state))) + (evil-echo "Switched to Emacs state for the next command ...")) + +(defun evil-exit-visual-and-repeat (event) + "Exit insert state and repeat event. +This special command should be used if some command called from +visual state should actually be called in normal-state. The main +reason for doing this is that the repeat system should *not* +record the visual state information for some command. This +command should be bound to exactly the same event in visual state +as the original command is bound in normal state. EVENT is the +event that triggered the execution of this command." + (interactive "e") + (when (evil-visual-state-p) + (evil-exit-visual-state) + (push event unread-command-events))) +(evil-declare-ignore-repeat 'evil-exit-visual-and-repeat) + +(evil-define-command evil-retab (tabstop) + "Convert all tabs to spaces or the other way around. +Replace all sequences of white-space containing a with new +strings of white-space using the new TABSTOP value given. +If you do not specify a new TABSTOP size or it is zero, Evil uses the +current value of `tab-width'." + (interactive "") + (unless (or (not tabstop) (string-match-p "^[0-9]*$" tabstop)) + (user-error "Invalid argument: %s" tabstop)) + (let ((beg (if (use-region-p) (region-beginning) (point-min))) + (end (if (use-region-p) (region-end) (point-max))) + (retab (if indent-tabs-mode #'tabify #'untabify)) + (tab-width (cond ((not tabstop) tab-width) + ((equal tabstop "0") tab-width) + (t (string-to-number tabstop))))) + (funcall retab beg end))) + +(provide 'evil-commands) + +;;; evil-commands.el ends here diff --git a/.config/emacs/lisp/evil/evil-common.el b/.config/emacs/lisp/evil/evil-common.el new file mode 100644 index 0000000..4aa1416 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-common.el @@ -0,0 +1,3843 @@ +;;; evil-common.el --- Common functions and utilities -*- lexical-binding: t -*- +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Code: + +(require 'evil-vars) +(require 'rect) +(require 'thingatpt) +(require 'cl-lib) + +(declare-function evil-visual-state-p "evil-states") +(declare-function evil-visual-restore "evil-states") +(declare-function evil-motion-state "evil-states") +(declare-function evil-replace-state-p "evil-states") +(declare-function evil-set-jump "evil-jumps") + +;;; Compatibility with different Emacs versions + +;; x-set-selection and x-get-selection have been deprecated since 25.1 +;; by gui-set-selection and gui-get-selection +(defalias 'evil-get-selection + (if (fboundp 'gui-get-selection) 'gui-get-selection 'x-get-selection)) +(defalias 'evil-set-selection + (if (fboundp 'gui-set-selection) 'gui-set-selection 'x-set-selection)) + +(defmacro evil-with-delay (condition hook &rest body) + "Execute BODY when CONDITION becomes true, checking with HOOK. +HOOK can be a simple symbol or of the form (HOOK APPEND LOCAL NAME) +where: +NAME specifies the name of the entry added to HOOK. +If APPEND is non-nil, the entry is appended to the hook. +If LOCAL is non-nil, the buffer-local value of HOOK is modified." + (declare (debug (form sexp body)) (indent 2)) + (cl-destructuring-bind (hook-sym &optional append local name) + (mapcar #'macroexp-quote (if (consp hook) hook (list hook))) + (macroexp-let2* nil + ((fun-name `(make-symbol + ,(or name (format "evil-delay-in-%s" hook-sym)))) + ;; `apply-partially' is used in case this macro is expanded + ;; (via `evil-define-key') in a file which still does not + ;; activate `lexical-binding'. + (fun `(apply-partially + (lambda (name &rest _) + (when ,(or condition t) + (remove-hook ,hook-sym name ,local) + ,@body + t)) + ,fun-name))) + `(unless ,(and condition `(funcall ,fun)) + (progn (fset ,fun-name ,fun) + ,@(when local `((put ,fun-name 'permanent-local-hook t))) + (add-hook ,hook-sym ,fun-name ,append ,local)))))) + +(defun evil-delay (condition form hook &optional append local name) + "Execute FORM when CONDITION becomes true, checking with HOOK. +NAME specifies the name of the entry added to HOOK. If APPEND is +non-nil, the entry is appended to the hook. If LOCAL is non-nil, +the buffer-local value of HOOK is modified." + (declare (obsolete evil-with-delay "1.15.0") (indent 2)) + (eval `(evil-with-delay ,condition (,hook ,append ,local ,name) ,form) t)) + +;;; List functions + +(defmacro evil--add-to-alist (alist &rest elements) + "Add the association of KEY and VAL to the value of ALIST. +If the list already contains an entry for KEY, update that entry; +otherwise prepend it to the list. + +\(fn ALIST [KEY VAL]...)" + `(progn + ,@(cl-loop + for (key val) on elements by #'cddr collect + (if (< emacs-major-version 26) + (macroexp-let2* nil ((k key) (p `(assoc ,k ,alist))) + `(if ,p (setcdr ,p ,val) + (push (cons ,k ,val) ,alist))) + `(setf (alist-get ,key ,alist nil nil #'equal) ,val))) + ,alist)) + +;; custom version of `delete-if' +(defun evil-filter-list (predicate list &optional pointer) + "Delete by side-effect all items satisfying PREDICATE in LIST. +Stop when reaching POINTER. If the first item satisfies PREDICATE, +there is no way to remove it by side-effect; therefore, write +\(setq foo (evil-filter-list #\\='predicate foo)) to be sure of +changing the value of `foo'." + (let ((tail list) elt head) + (while (and tail (not (eq tail pointer))) + (setq elt (car tail)) + (cond + ((funcall predicate elt) + (setq tail (cdr tail)) + (if head + (setcdr head tail) + (setq list tail))) + (t + (setq head tail + tail (cdr tail))))) + list)) + +(defun evil-member-if (predicate list &optional pointer) + "Find the first item satisfying PREDICATE in LIST. +Stop when reaching POINTER, which should point at a link +in the list." + (let (elt) + (catch 'done + (while (and (consp list) (not (eq list pointer))) + (setq elt (car list)) + (if (funcall predicate elt) + (throw 'done elt) + (setq list (cdr list))))))) + +(defun evil-member-recursive-if (predicate tree) + "Find the first item satisfying PREDICATE in TREE." + (declare (obsolete nil "1.15.0")) + (cond + ((funcall predicate tree) + tree) + ((listp tree) + (catch 'done + (dolist (elt tree) + (when (setq elt (evil-member-recursive-if predicate elt)) + (throw 'done elt))))))) + +(defun evil-concat-lists (&rest sequences) + "Concatenate lists, removing duplicates. +Elements are compared with `eq'." + (let (result) + (dolist (sequence sequences) + (dolist (elt sequence) + (push elt result))) + (nreverse (cl-remove-duplicates result :test #'eq)))) + +(defun evil-concat-alists (&rest sequences) + "Concatenate association lists, removing duplicates. +An alist is a list of cons cells (KEY . VALUE) where each key +may occur only once. Later values overwrite earlier values." + (let (result) + (dolist (sequence sequences) + (dolist (elt sequence) + (setq result (assq-delete-all (car-safe elt) result)) + (push elt result))) + (nreverse result))) + +(defun evil-concat-plists (&rest sequences) + "Concatenate property lists, removing duplicates. +A property list is a list (:KEYWORD1 VALUE1 :KEYWORD2 VALUE2...) +where each keyword may occur only once. Later values overwrite +earlier values." + (let (result) + (dolist (sequence sequences result) + (while sequence + (setq result + (plist-put result (pop sequence) (pop sequence))))))) + +(defun evil-concat-keymap-alists (&rest sequences) + "Concatenate keymap association lists, removing duplicates. +A keymap alist is a list of cons cells (VAR . MAP) where each keymap +may occur only once, but where the variables may be repeated +\(e.g., (VAR . MAP1) (VAR . MAP2) is allowed). The order matters, +with the highest priority keymaps being listed first." + (let (result) + (dolist (sequence sequences) + (dolist (elt sequence) + (unless (rassq (cdr-safe elt) result) + (push elt result)))) + (nreverse result))) + +(defun evil-plist-delete (prop plist) + "Delete by side effect the property PROP from PLIST. +If PROP is the first property in PLIST, there is no way +to remove it by side-effect; therefore, write +\(setq foo (evil-plist-delete :prop foo)) to be sure of +changing the value of `foo'." + (let ((tail plist) elt head) + (while tail + (setq elt (car tail)) + (cond + ((eq elt prop) + (setq tail (cdr (cdr tail))) + (if head + (setcdr (cdr head) tail) + (setq plist tail))) + (t + (setq head tail + tail (cdr (cdr tail)))))) + plist)) + +(defun evil-get-property (alist key &optional prop) + "Return property PROP for KEY in ALIST. +ALIST is an association list with entries of the form +\(KEY . PLIST), where PLIST is a property list. +If PROP is nil, return all properties for KEY. +If KEY is t, return an association list of keys +and their PROP values." + (declare (obsolete nil "1.15.0")) + (cond + ((null prop) + (cdr (assq key alist))) + ((eq key t) + (let (result val) + (dolist (entry alist result) + (setq key (car entry) + val (cdr entry)) + (when (plist-member val prop) + (setq val (plist-get val prop)) + (push (cons key val) result))))) + (t + (plist-get (cdr (assq key alist)) prop)))) + +(defun evil-put-property (alist-var key prop val &rest properties) + "Set PROP to VAL for KEY in ALIST-VAR. +ALIST-VAR points to an association list with entries of the form +\(KEY . PLIST), where PLIST is a property list storing PROP and VAL." + (declare (obsolete nil "1.15.0")) + (set alist-var + (let* ((alist (symbol-value alist-var)) + (plist (cdr (assq key alist)))) + (setq plist (plist-put plist prop val)) + (when properties + (setq plist (evil-concat-plists plist properties) + val (car (last properties)))) + (setq alist (assq-delete-all key alist)) + (push (cons key plist) alist))) + val) + +(defun evil-state-property (state prop &optional value) + "Return the value of property PROP for STATE. +PROP is a keyword as used by `evil-define-state'. STATE is the state's +symbolic name. If VALUE is non-nil and the value is a variable, return +the value of that variable. + +If STATE is t, return an association list of states and their PROP +values instead." + (if (eq state t) + (cl-loop for (key . plist) in evil-state-properties with result do + (let ((p (plist-member plist prop))) + (when p (push (cons key (cadr p)) result))) + finally return result) + (let ((val (plist-get (cdr (assq state evil-state-properties)) prop))) + (if (and value (symbolp val) (boundp val)) + (symbol-value val) + val)))) + +(eval-and-compile (defalias 'evil-swap #'cl-rotatef)) + +(defmacro evil-sort (&rest vars) + "Sort the symbol values of VARS. +Place the smallest value in the first argument and the largest in the +last, sorting in between." + (if (= (length vars) 2) + `(when (> ,@vars) (evil-swap ,@vars)) + (let ((sorted (make-symbol "sortvar"))) + `(let ((,sorted (sort (list ,@vars) #'<))) + (setq ,@(apply #'nconc + (mapcar (lambda (var) (list var `(pop ,sorted))) + vars))))))) + +(defun evil-vector-to-string (vector) + "Turn VECTOR into a string, changing to \"\\e\"." + (mapconcat (lambda (c) (if (eq c 'escape) "\e" (list c))) + vector + "")) + +;;; Command properties + +(defmacro evil-define-command (command &rest body) + "Define a command COMMAND. + +\(fn COMMAND (ARGS...) DOC [[KEY VALUE]...] BODY...)" + (declare (indent defun) + (doc-string 3) + (debug (&define name + [&optional lambda-list] + [&optional stringp] + [&rest keywordp sexp] + [&optional ("interactive" [&rest form])] + def-body))) + (let ((interactive '(interactive)) + args doc doc-form keys) + ;; collect arguments + (when (listp (car body)) + (setq args (pop body))) + ;; collect docstring + (cond ((stringp (car body)) (setq doc (pop body))) + ((eq (car-safe (car body)) #'format) (setq doc-form (pop body)))) + ;; collect keywords + (while (keywordp (car body)) + (let* ((key (pop body)) + (arg (pop body))) + (setq keys (plist-put keys key arg)))) ; TODO: add keyword check + ;; collect `interactive' form + (when (eq (caar body) 'interactive) + (cl-destructuring-bind (form . attrs) + (apply #'evil-interactive-form (cdr (pop body))) + (setq interactive `(interactive ,form) + keys (evil-concat-plists keys attrs)))) + `(progn + ;; the compiler does not recognize `defun' inside `let' + ,(when (and command body) + `(defun ,command ,args + ,@(when doc `(,doc)) + ,interactive + ,@body)) + ,(when (and command doc-form) + `(put ',command 'function-documentation ,doc-form)) + ;; set command properties for symbol or lambda function + (let ((func ,(if (and (null command) body) + `(lambda ,args + ,interactive + ,@body) + `#',command))) + (apply #'evil-set-command-properties func ',keys) + func)))) + +(define-obsolete-variable-alias + 'evil-command-properties 'evil--command-properties "1.15.0") +(defvar evil--command-properties nil + "Specifications made by `evil-define-command'.") + +(defun evil-command-properties (command) + "Return the Evil command property list for COMMAND. +See also `evil-get-command-property'." + (if (symbolp command) (get command 'evil--command-plist) + (cdr (assq command evil--command-properties)))) +(define-obsolete-function-alias + 'evil-get-command-properties #'evil-command-properties "1.15.0") + +(gv-define-setter evil-command-properties (val command) + `(if (symbolp ,command) (put ,command 'evil--command-plist ,val) + ,(macroexp-let2 nil p `(assq ,command evil--command-properties) + `(if ,p (setcdr ,p ,val) + (push (cons ,command ,val) evil--command-properties))))) + +(defalias 'evil-has-command-properties-p #'evil-command-properties + "Whether Evil properties are defined for COMMAND. +See also `evil-has-command-property-p'.") + +(defun evil-has-command-property-p (command property) + "Whether COMMAND has Evil PROPERTY. +See also `evil-has-command-properties-p'." + (plist-member (evil-command-properties command) property)) + +(defun evil-get-command-property (command property &optional default) + "Return the value of Evil PROPERTY of COMMAND. +If the command does not have the property, return DEFAULT. +See also `evil-get-command-properties'." + (let ((p (plist-member (evil-command-properties command) property))) + (if p (cadr p) default))) + +(defun evil-add-command-properties (command &rest properties) + "Set each Evil command property KEY to its VAL for COMMAND. +To replace existing properties, use `evil-set-command-properties'. + +\(fn COMMAND [KEY VAL]...)" + (let ((props (evil-command-properties command))) + (while properties + (setq props (plist-put props (pop properties) (pop properties)))) + (setf (evil-command-properties command) props))) + +(defalias 'evil-set-command-property #'evil-add-command-properties + "Set PROPERTY to VALUE for COMMAND. +To set multiple properties at once, see +`evil-set-command-properties' and `evil-add-command-properties'. + +\(fn COMMAND PROPERTY VALUE)") +(defalias 'evil-put-command-property #'evil-set-command-property) + +(defun evil-set-command-properties (command &rest properties) + "Replace all of COMMAND's properties with PROPERTIES. +PROPERTIES should be a property list. +This erases all previous properties; to only add properties, +use `evil-set-command-property'." + (setf (evil-command-properties command) properties)) + +(defun evil-remove-command-properties (command &rest properties) + "Remove PROPERTIES from COMMAND. +PROPERTIES should be a list of properties (:PROP1 :PROP2 ...). +If PROPERTIES is the empty list, all properties are removed." + (let (plist) + (when properties + (setq plist (evil-command-properties command)) + (dolist (property properties) + (setq plist (evil-plist-delete property plist)))) + (apply #'evil-set-command-properties command plist))) + +(defun evil-yank-handler (&optional motion) + "Return the yank handler for MOTION. +MOTION defaults to the current motion." + (evil-get-command-property (or motion evil-this-motion) :yank-handler)) + +(defun evil-declare-motion (command) + "Declare COMMAND to be a movement function. +This ensures that it behaves correctly in visual state." + (evil-add-command-properties command :keep-visual t :repeat 'motion)) + +(defun evil-declare-repeat (command) + "Declare COMMAND to be repeatable." + (evil-add-command-properties command :repeat t)) + +(defun evil-declare-not-repeat (command) + "Declare COMMAND to be nonrepeatable." + (evil-add-command-properties command :repeat nil)) +(defalias 'evil-declare-ignore-repeat #'evil-declare-not-repeat) + +(defun evil-declare-change-repeat (command) + "Declare COMMAND to be repeatable by buffer changes rather than +keystrokes." + (evil-add-command-properties command :repeat 'change)) + +(defun evil-declare-insert-at-point-repeat (command) + "Declare COMMAND to be repeatable by buffer changes." + (evil-add-command-properties command :repeat 'insert-at-point)) + +(defun evil-declare-abort-repeat (command) + "Declare COMMAND to be nonrepeatable." + (evil-add-command-properties command :repeat 'abort)) + +(defun evil-delimited-arguments (string &optional count) + "Parse STRING as a sequence of delimited arguments. +Return a list of COUNT strings, or as many arguments as the string +contains. The first non-blank character is taken to be the delimiter. +If some arguments are missing from STRING, the resulting list is +padded with nil values. Two delimiters following directly after each +other gives an empty string." + (unless count (setq count -1)) + (let ((idx 0) delim regexp result) + (when (string-match "^[[:space:]]*\\([^[:space:]]\\)" string) + (setq delim (match-string 1 string) + regexp (format "%s\\(\\(?:\\\\.\\|[^%s]\\)*\\)" + (regexp-quote delim) delim)) + (while (and (/= count 0) (string-match regexp string idx) + (/= (match-beginning 1) (length string))) + (setq idx (match-end 0) + count (1- count)) + (push + (if (when (= count 0) + (not (string-match-p + (concat (regexp-quote delim) "[[:space:]]*$") + string idx))) + (substring string (match-beginning 1)) + (match-string 1 string)) + result))) + (dotimes (_ count) (push nil result)) + (nreverse result))) + +(defun evil-concat-charsets (&rest sets) + "Concatenate character sets. +A character set is the part between [ and ] in a regular expression. +If any character set is complemented, the result is also complemented." + (let ((bracket "") (complement "") (hyphen "") result) + (dolist (set sets) + (when (string-match-p "^\\^" set) + (setq set (substring set 1) + complement "^")) + (when (string-match-p "^]" set) + (setq set (substring set 1) + bracket "]")) + (when (string-match-p "^-" set) + (setq set (substring set 1) + hyphen "-")) + (setq result (concat result set))) + (format "%s%s%s%s" complement bracket hyphen result))) + +;;; Key sequences + +(defun evil-keypress-parser (&optional input) + "Read from keyboard or INPUT and build a command description. +Return (CMD COUNT), where COUNT is the numeric prefix argument. +Both COUNT and CMD may be nil." + (when input (setq unread-command-events (append input unread-command-events))) + (let (count negative) + (catch 'done + (while t + (let ((seq (read-key-sequence ""))) + (when seq + (let ((cmd (key-binding seq))) + (cond + ((null cmd) (throw 'done (list nil nil))) + ((arrayp cmd) ; keyboard macro, recursive call + (let ((cmd (evil-keypress-parser cmd))) + (throw 'done + (list (car cmd) + (if (or count (cadr cmd)) + (list (car cmd) (* (or count 1) + (or (cadr cmd) 1)))))))) + ((or (eq cmd #'digit-argument) + (and (equal seq "0") count)) + (let* ((event (aref seq (1- (length seq)))) + (char (or (when (characterp event) event) + (when (symbolp event) + (get event 'ascii-character)))) + (digit (when (integerp char) (- (logand char ?\177) ?0)))) + (setq count (+ (* 10 (or count 0)) digit)))) + ((eq cmd #'negative-argument) + (setq negative (not negative))) + (t + (throw 'done (list cmd + (and count + (* count + (if negative -1 1)))))))))))))) + +(defun evil-read-key (&optional prompt) + "Read a key from the keyboard. +Translates it according to the input method." + (let ((old-global-map (current-global-map)) + (new-global-map (make-sparse-keymap)) + (overriding-terminal-local-map nil) + (overriding-local-map evil-read-key-map) + seq char cmd) + (unwind-protect + (condition-case nil + (progn + (define-key new-global-map [menu-bar] + (lookup-key global-map [menu-bar])) + (define-key new-global-map [tab-bar] + (lookup-key global-map [tab-bar])) + (define-key new-global-map [tool-bar] + (lookup-key global-map [tool-bar])) + (setq new-global-map + (append new-global-map + (list (make-char-table 'display-table + 'self-insert-command)))) + (use-global-map new-global-map) + (setq seq (read-key-sequence prompt nil t) + char (aref seq 0) + cmd (key-binding seq)) + (while (arrayp cmd) + (setq char (aref cmd 0) + cmd (key-binding cmd))) + (cond + ((eq cmd 'self-insert-command) char) + (cmd (call-interactively cmd)) + (t (user-error "No replacement character typed")))) + (quit + (when (fboundp 'evil-repeat-abort) + (evil-repeat-abort)) + (signal 'quit nil))) + (use-global-map old-global-map)))) + +(defun evil-read-quoted-char () + "Command that calls `read-quoted-char'. +This command can be used wherever `read-quoted-char' is required +as a command. Its main use is in the `evil-read-key-map'." + (interactive) + (read-quoted-char)) + +(defvar evil-digraph-read-key-keymap + (let ((map (make-sparse-keymap)) + (n ?0)) + (while (<= n ?9) + (define-key map (vector (intern (concat "kp-" (string n)))) nil) + (cl-incf n)) + map) + "By default, used to exclude otherwise disabled fallbacks.") + +(declare-function evil-digraph "evil-digraphs") +(defun evil-read-digraph-char-with-overlay (overlay) + "Read two chars, displaying the first in OVERLAY, replacing \"?\". +Return the digraph from `evil-digraph', else return second char." + (interactive) + (unwind-protect + (let ((read-key-empty-map + (let ((map (copy-keymap evil-digraph-read-key-keymap))) + (set-keymap-parent map read-key-empty-map) + ;; Disable read-key-sequence unbound fallbacks, e.g. downcasing + (define-key map [t] 'dummy) + map)) + char1 char2) + ;; create overlay prompt + (overlay-put overlay 'invisible t) + (overlay-put overlay 'after-string + #("?" 0 1 (face minibuffer-prompt cursor 1))) + (setq char1 (read-key)) + (overlay-put overlay 'after-string + (propertize (char-to-string char1) + 'face 'minibuffer-prompt + 'cursor 1)) + (setq char2 (read-key)) + + (or (evil-digraph (list char1 char2)) + ;; use the last character if undefined + char2)) + (delete-overlay overlay))) + +(defun evil-read-digraph-char (&optional hide-chars) + "Read two keys from keyboard forming a digraph. +This function creates an overlay at (point), hiding the next +HIDE-CHARS characters. HIDE-CHARS defaults to 1." + (interactive) + (let ((overlay (make-overlay + (point) (min (+ (point) (or hide-chars 1)) + (point-max))))) + (evil-read-digraph-char-with-overlay overlay))) + +(defun evil-read-motion (&optional motion count type modifier) + "Read a MOTION, motion COUNT and motion TYPE from the keyboard. +The type may be overridden with MODIFIER, which may be a type +or a Visual selection as defined by `evil-define-visual-selection'. +Return a list (MOTION COUNT [TYPE])." + (let (command prefix) + (setq evil-this-type-modified nil) + (unless motion + (while (progn + (setq command (evil-keypress-parser) + motion (pop command) + prefix (pop command)) + (when prefix + (setq count + (if count + (string-to-number + (concat (number-to-string count) + (number-to-string prefix))) + prefix))) + ;; if the command is a type modifier, read more + (when (rassq motion evil-visual-alist) + (setq modifier + (or modifier + (car (rassq motion evil-visual-alist)))))))) + (when modifier + (setq type (or type (evil-type motion 'exclusive))) + (cond + ((eq modifier 'char) + ;; TODO: this behavior could be less hard-coded + (setq type (if (eq type 'exclusive) 'inclusive 'exclusive))) + (t (setq type modifier))) + (setq evil-this-type-modified type)) + (list motion count type))) + +(defun evil-mouse-events-p (keys) + "Return non-nil iff KEYS contains a mouse event." + (cl-loop for key across keys thereis + (or (mouse-event-p key) + (mouse-movement-p key)))) + +(defun evil-extract-count (keys) + "Split the key-sequence KEYS into prefix-argument and the rest. +Return the list (PREFIX CMD SEQ REST), where PREFIX is the +prefix count, CMD the command to be executed, SEQ the subsequence +calling CMD, and REST is all remaining events in the +key-sequence. PREFIX and REST may be nil if they do not exist. +If a command is bound to some keyboard macro, it is expanded +recursively." + (catch 'done + (let ((len (length keys)) + (beg 0) + (end 1) + found-prefix) + (while (<= end len) + (let* ((seq (substring keys beg end)) + (cmd (key-binding seq))) + (cond + ((memq cmd '(undefined nil)) + (user-error "No command bound to `%s'" seq)) + ((arrayp cmd) ; keyboard macro, replace command with macro + (setq keys (vconcat (substring keys 0 beg) + cmd + (substring keys end)) + end (1+ beg) + len (length keys))) + ((functionp cmd) + (if (or (memq cmd '(digit-argument negative-argument)) + (and found-prefix + (equal (vconcat seq) (vector ?0)))) + ;; skip those commands + (setq found-prefix t ; found at least one prefix argument + beg end + end (1+ end)) + ;; a real command, finish + (throw 'done + (list (unless (zerop beg) + (string-to-number + (concat (substring keys 0 beg)))) + cmd + seq + (when (< end len) + (substring keys end)))))) + (t ; append a further event + (setq end (1+ end)))))) + (user-error "Key sequence contains no complete binding")))) + +(defun evil-extract-append (file-or-append) + "Return an (APPEND . FILENAME) pair based on FILE-OR-APPEND. +FILE-OR-APPEND should either be a filename or a \">> FILE\" +directive. APPEND will be t if FILE-OR-APPEND is an append +directive and nil otherwise. FILENAME will be the extracted +filename." + (if (and (stringp file-or-append) + (string-match "\\(>> *\\)" file-or-append)) + (cons t (substring file-or-append (match-end 1))) + (cons nil file-or-append))) + +(defun evil-set-keymap-prompt (map prompt) + "Set the prompt-string of MAP to PROMPT." + (delq (keymap-prompt map) map) + (when prompt + (setcdr map (cons prompt (cdr map))))) + +(defun evil-lookup-key (map key) + "Return non-nil value if KEY is bound in MAP." + (let ((definition (lookup-key map key))) + (unless (numberp definition) ; in-band error + definition))) + +;;; Display + +(defun evil-set-cursor (specs) + "Change the cursor's apperance according to SPECS. +SPECS may be a cursor type as per `cursor-type', a color +string as passed to `set-cursor-color', a zero-argument +function for changing the cursor, or a list of the above. + +If SPECS is nil, this function does not have an effect; +pass (list nil) instead to indicate a nil `cursor-type' +\(i.e., to disable the cursor)." + (unless (and (not (functionp specs)) + (listp specs) + (null (cdr-safe (last specs)))) + (setq specs (list specs))) + (dolist (spec specs) + (cond + ((functionp spec) + (ignore-errors (funcall spec))) + ((stringp spec) + (evil-set-cursor-color spec)) + (t + (setq cursor-type spec))))) + +(defun evil-set-cursor-color (color) + "Set the cursor color to COLOR." + (unless (equal (frame-parameter nil 'cursor-color) color) + ;; `set-cursor-color' forces a redisplay, so only + ;; call it when the color actually changes + (set-cursor-color color))) + +(defun evil-refresh-cursor (&optional state buffer) + "Refresh the cursor for STATE in BUFFER. +BUFFER defaults to the current buffer. If STATE is nil the +cursor type is either `evil-force-cursor' or the current state." + (defvar evil-local-mode) + (when evil-local-mode + (let* ((state (or state evil-force-cursor evil-state 'normal)) + (default (or evil-default-cursor t)) + (cursor (evil-state-property state :cursor t)) + (color (or (and (stringp cursor) cursor) + (and (listp cursor) (evil-member-if #'stringp cursor)) + (frame-parameter nil 'cursor-color)))) + (with-current-buffer (or buffer (current-buffer)) + ;; if both STATE and `evil-default-cursor' + ;; specify a color, don't set it twice + (evil-set-cursor (if (and color (listp default)) + (cl-remove-if #'stringp default) + default)) + (evil-set-cursor cursor))))) + +(defmacro evil-save-cursor (&rest body) + "Save the current cursor; execute BODY; restore the cursor." + (declare (indent defun) (debug t) (obsolete nil "1.15.0")) + `(let ((cursor cursor-type) + (color (frame-parameter (selected-frame) 'cursor-color)) + (inhibit-quit t)) + (unwind-protect + (progn ,@body) + (setq cursor-type cursor) + (evil-set-cursor-color color)))) + +(defun evil-echo (string &rest args) + "Display an unlogged message in the echo area. +That is, the message is not logged in the *Messages* buffer. +\(To log the message, just use `message'.)" + (unless evil-no-display + (let (message-log-max) + (apply #'message string args)))) + +(defun evil-echo-area-save () + "Save the current echo area in `evil-echo-area-message'." + (setq evil-echo-area-message (current-message))) + +(defun evil-echo-area-restore () + "Restore the echo area from `evil-echo-area-message'. +Does not restore if `evil-write-echo-area' is non-nil." + (unless evil-write-echo-area + (if evil-echo-area-message + (message "%s" evil-echo-area-message) + (message nil))) + (setq evil-echo-area-message nil + evil-write-echo-area nil)) + +;; toggleable version of `with-temp-message' +(defmacro evil-save-echo-area (&rest body) + "Save the echo area; execute BODY; restore the echo area. +Intermittent messages are not logged in the *Messages* buffer." + (declare (indent defun) (debug t)) + `(let ((inhibit-quit t) + evil-echo-area-message evil-write-echo-area) + (evil-echo-area-save) + (unwind-protect + (progn ,@body) + (evil-echo-area-restore)))) + +(defmacro evil-without-display (&rest body) + "Execute BODY without Evil displays. +Inhibits echo area messages, mode line updates and cursor changes." + (declare (indent defun) + (debug t)) + `(let ((evil-no-display t)) + ,@body)) + +(defun evil-count-lines (beg end) + "Return absolute line-number-difference betweeen BEG and END. +This should give the same results no matter where on the line BEG +and END are." + (if (= beg end) + 0 + (let ((end-at-bol (save-excursion (goto-char (max beg end)) + (bolp)))) + (if end-at-bol + (count-lines beg end) + (1- (count-lines beg end)))))) + +;;; Movement + +(defun evil-normalize-position (pos) + "Return POS if it does not exceed the buffer boundaries. +If POS is less than `point-min', return `point-min'. +Is POS is more than `point-max', return `point-max'. +If POS is a marker, return its position." + (cond + ((not (number-or-marker-p pos)) + pos) + ((< pos (point-min)) + (point-min)) + ((> pos (point-max)) + (point-max)) + ((markerp pos) + (marker-position pos)) + (t + pos))) + +(defmacro evil-save-goal-column (&rest body) + "Restore the goal column after execution of BODY. +See also `evil-save-column'." + (declare (indent defun) + (debug t)) + `(let ((goal-column goal-column) + (temporary-goal-column temporary-goal-column)) + ,@body)) + +(defmacro evil-save-column (&rest body) + "Restore the column after execution of BODY. +See also `evil-save-goal-column'." + (declare (indent defun) + (debug t)) + `(let ((col (current-column))) + (evil-save-goal-column + ,@body + (move-to-column col)))) + +(defun evil--stick-to-eol-p () + "Called by vertical movement commands to help determine cursor position." + (and evil-track-eol + (eq (or goal-column temporary-goal-column) most-positive-fixnum) + (memq last-command '(next-line previous-line)))) + +(defun evil-eolp () + "Like `eolp' but accounts for `evil-move-beyond-eol' being nil." + (ignore-errors + (save-excursion + (unless (or evil-move-beyond-eol (memq evil-state '(insert replace))) + (forward-char)) + (eolp)))) + +(defmacro evil-ensure-column (&rest body) + "Execute BODY so that column after execution is correct. +If `evil-start-of-line' is nil, treat BODY as if it were a `next-line' command. +This mostly copies the approach of Emacs' `line-move-1', but is modified +so it is more compatible with Evil's notion of EOL tracking." + (declare (indent defun) (debug t)) + `(progn + (unless evil-start-of-line + (setq this-command 'next-line + temporary-goal-column + (cond ((memq last-command '(next-line previous-line)) + temporary-goal-column) + ((and track-eol (eolp) (not (bolp))) most-positive-fixnum) + (t (current-column))))) + ,@body + (if evil-start-of-line + (evil-first-non-blank) + (line-move-to-column + (or goal-column + (if (consp temporary-goal-column) + ;; Guard against a negative value as `temporary-goal-column' + ;; may have a negative component when both `whitespace-mode' + ;; and `display-line-numbers-mode' are enabled (#1297). + (max 0 (+ (truncate (car temporary-goal-column)) + (cdr temporary-goal-column))) + temporary-goal-column)))))) + +(defun evil-narrow (beg end) + "Restrict the buffer to BEG and END. +BEG or END may be nil, specifying a one-sided restriction including +`point-min' or `point-max'. See also `evil-with-restriction.'" + (declare (obsolete nil "1.15.0")) + (narrow-to-region + (if beg (max beg (point-min)) (point-min)) + (if end (min end (point-max)) (point-max)))) + +(defmacro evil-with-restriction (beg end &rest body) + "Execute BODY with the buffer narrowed to BEG and END. +BEG or END may be nil to specify a one-sided restriction." + (declare (indent 2) (debug t)) + `(save-restriction + (let ((evil-restriction-stack + (cons (cons (point-min) (point-max)) evil-restriction-stack))) + (narrow-to-region (or ,beg (point-min)) (or ,end (point-max))) + ,@body))) + +(defmacro evil-without-restriction (&rest body) + "Execute BODY with the top-most narrowing removed. +This works only if the previous narrowing has been generated by +`evil-with-restriction'." + (declare (indent defun) (debug t)) + `(save-restriction + (widen) + (narrow-to-region (car (car evil-restriction-stack)) + (cdr (car evil-restriction-stack))) + (let ((evil-restriction-stack (cdr evil-restriction-stack))) + ,@body))) + +(defmacro evil-narrow-to-field (&rest body) + "Narrow to the current field." + (declare (indent defun) + (debug t)) + `(evil-with-restriction (field-beginning) (field-end) + ,@body)) + +(defun evil-move-beginning-of-line (&optional arg) + "Move to the beginning of the line as displayed. +Like `move-beginning-of-line', but retains the goal column." + (evil-save-goal-column + (move-beginning-of-line arg) + (beginning-of-line))) + +(defun evil-move-end-of-line (&optional arg) + "Move to the end of the line as displayed. +Like `move-end-of-line', but retains the goal column." + (evil-save-goal-column + (move-end-of-line arg) + (end-of-line))) + +(defun evil-adjust-cursor (&optional _) + "Move point one character back if at the end of a non-empty line. +This behavior is controlled by `evil-move-beyond-eol'." + (and (not evil-move-beyond-eol) + (eolp) + (evil-move-cursor-back t))) + +(defun evil-move-cursor-back (&optional force) + "Move point one character back within the current line. +Contingent on the variable `evil-move-cursor-back' or the FORCE +argument. Movement is constrained to the current field." + (or (not (or evil-move-cursor-back force)) + (bolp) + (when (bound-and-true-p visual-line-mode) + (= (point) (save-excursion (vertical-motion 0) (point)))) + (goto-char (constrain-to-field (1- (point)) (point))))) + +(defun evil-line-position (line &optional column) + "Return the position of LINE. +If COLUMN is specified, return its position on the line. +A negative number means the end of the line." + (save-excursion + (goto-char (point-min)) + (forward-line (1- line)) + (when (numberp column) + (if (< column 0) + (forward-line) + (move-to-column column))) + (point))) + +(defun evil-column (&optional pos) + "Return the horizontal position of POS. +POS defaults to point." + (save-excursion + (when pos + (goto-char pos)) + (current-column))) + +(defun evil-move-to-column (column &optional dir force) + "Move point to column COLUMN in the current line. +Places point at left of the tab character (at the right if DIR +is non-nil) and returns point." + (interactive "p") + (move-to-column column force) + (unless force + (when (or (not dir) (and (numberp dir) (< dir 1))) + (when (> (current-column) column) + (evil-move-cursor-back)))) + (point)) + +(defmacro evil-loop (spec &rest body) + "Loop with countdown variable. +Evaluate BODY with VAR counting down from COUNT to 0. +COUNT can be negative, in which case VAR counts up instead. +The return value is the value of VAR when the loop +terminates, which is 0 if the loop completes successfully. +RESULT specifies a variable for storing this value. + +\(fn (VAR COUNT [RESULT]) BODY...)" + (declare (indent defun) (debug dolist) (obsolete nil "1.15.0")) + (let* ((i (make-symbol "loopvar")) + (var (pop spec)) + (count (pop spec)) + (result (pop spec))) + (setq var (or (unless (eq var result) var) i) + result (or result var)) + `(let ((,var ,count)) + (setq ,result ,var) + (while (/= ,var 0) + ,@body + (if (> ,var 0) + (setq ,var (1- ,var)) + (setq ,var (1+ ,var))) + (setq ,result ,var)) + ,var))) + +;;; Motions + +(defmacro evil-motion-loop (spec &rest body) + "Loop a certain number of times. +Evaluate BODY repeatedly COUNT times with VAR bound to 1 or -1, +depending on the sign of COUNT. Set RESULT, if specified, to the +number of unsuccessful iterations, which is 0 if the loop completes +successfully. This is also the return value. + +Each iteration must move point; if point does not change, the loop +immediately quits. + +\(fn (VAR COUNT [RESULT]) BODY...)" + (declare (indent defun) + (debug ((symbolp form &optional symbolp) body))) + (let* ((var (or (pop spec) (make-symbol "unitvar"))) + (count (or (pop spec) 0)) + (result (or (pop spec) var)) + (i (make-symbol "loopvar"))) + `(let* ((,i ,count) + (,var (if (< ,i 0) -1 1))) + (while (and (/= ,i 0) + (/= (point) (progn ,@body (point)))) + (setq ,i (if (< ,i 0) (1+ ,i) (1- ,i)))) + (setq ,result ,i)))) + +(defun evil-signal-at-bob-or-eob (&optional count) + "Signal error if `point' is at boundaries. +If `point' is at bob and COUNT is negative this function signals +`beginning-of-buffer'. If `point' is at eob and COUNT is positive +this function signals `end-of-buffer'. This function should be used +in motions. COUNT defaults to 1." + (setq count (or count 1)) + (cond + ((< count 0) (evil-signal-at-bob)) + ((> count 0) (evil-signal-at-eob)))) + +(defun evil-signal-at-bob () + "Signal `beginning-of-buffer' if `point' is at bob. +This function should be used in backward motions. If `point' is at +bob so that no further backward motion is possible the error +`beginning-of-buffer' is raised." + (when (bobp) (signal 'beginning-of-buffer nil))) + +(defun evil-signal-at-eob () + "Signal `end-of-buffer' if `point' is at eob. +This function should be used in forward motions. If `point' is close +to eob so that no further forward motion is possible the error +`end-of-buffer' is raised. This is the case if `point' is at +`point-max' or if is one position before `point-max', +`evil-move-beyond-eol' is nil and `point' is not at the end +of a line. The latter is necessary because `point' cannot be +moved to `point-max' if `evil-move-beyond-eol' is nil and +the last line in the buffer is not empty." + (when (or (eobp) + (and (not (eolp)) + (not evil-move-beyond-eol) + (save-excursion (forward-char) (eobp)))) + (signal 'end-of-buffer nil))) + +(defmacro evil-with-hproject-point-on-window (&rest body) + "Project point after BODY to current window. +If point is on a position left or right of the current window +then it is moved to the left and right boundary of the window, +respectively. If `auto-hscroll-mode' is non-nil then the left and +right positions are increased or decreased, respectively, by +`horizontal-margin' so that no automatic scrolling occurs." + (declare (indent defun) + (debug t)) + (let ((diff (make-symbol "diff")) + (left (make-symbol "left")) + (right (make-symbol "right"))) + `(let ((,diff (if auto-hscroll-mode (1+ hscroll-margin) 0)) + auto-hscroll-mode) + ,@body + (let* ((,left (+ (window-hscroll) ,diff)) + (,right (+ (window-hscroll) (window-width) (- ,diff) -1))) + (move-to-column (min (max (current-column) ,left) ,right)))))) + +(defun evil-forward-not-thing (thing &optional count) + "Move point to the end or beginning of the complement of THING." + (evil-motion-loop (dir (or count 1)) + (let (bnd) + (cond + ((> dir 0) + (while (and (setq bnd (bounds-of-thing-at-point thing)) + (< (point) (cdr bnd))) + (goto-char (cdr bnd))) + ;; no thing at (point) + (if (zerop (forward-thing thing)) + ;; now at the end of the next thing + (let ((bnd (bounds-of-thing-at-point thing))) + (if (or (< (car bnd) (point)) ; end of a thing + (= (car bnd) (cdr bnd))) ; zero width thing + (goto-char (car bnd)) + ;; beginning of yet another thing, go back + (forward-thing thing -1))) + (goto-char (point-max)))) + (t + (while (and (not (bobp)) + (setq bnd (progn (backward-char) (bounds-of-thing-at-point thing))) + (< (point) (cdr bnd))) + (goto-char (car bnd))) + ;; either bob or no thing at point + (goto-char + (if (and (not (bobp)) + (zerop (forward-thing thing -1)) + (setq bnd (bounds-of-thing-at-point thing))) + (cdr bnd) + (point-min)))))))) + +(defun evil-bounds-of-not-thing-at-point (thing &optional which) + "Return the bounds of a complement of THING at point. +If there is a THING at point nil is returned. Otherwise if WHICH +is nil or 0 a cons cell (BEG . END) is returned. If WHICH is +negative the beginning is returned. If WHICH is positive the END +is returned." + (let ((pnt (point))) + (let ((beg (save-excursion + (and (zerop (forward-thing thing -1)) + (forward-thing thing)) + (if (> (point) pnt) (point-min) (point)))) + (end (save-excursion + (and (zerop (forward-thing thing)) + (forward-thing thing -1)) + (if (< (point) pnt) (point-max) (point))))) + (when (and (<= beg (point) end) (< beg end)) + (cond + ((or (not which) (zerop which)) (cons beg end)) + ((< which 0) beg) + ((> which 0) end)))))) + +(defun evil-forward-nearest (count &rest forwards) + "Move point forward to the first of several motions. +FORWARDS is a list of forward motion functions (i.e. each moves +point forward to the next end of a text object (if passed a +1) +or backward to the preceeding beginning of a text object (if +passed a -1)). This function calls each of these functions once +and moves point to the nearest of the resulting positions. If +COUNT is positive point is moved forward COUNT times, if negative +point is moved backward -COUNT times." + (evil-motion-loop (dir (or count 1)) + (let ((pnt (point)) + (nxt (if (< dir 0) (point-min) (point-max)))) + (dolist (fwd forwards) + (goto-char pnt) + (ignore-errors + (evil-with-restriction + (when (< dir 0) + (save-excursion + (goto-char nxt) + (line-beginning-position 0))) + (when (> dir 0) + (save-excursion + (goto-char nxt) + (line-end-position 2))) + (and (zerop (funcall fwd dir)) + (/= (point) pnt) + (if (< dir 0) (> (point) nxt) (< (point) nxt)) + (setq nxt (point)))))) + (goto-char nxt)))) + +(defun bounds-of-evil-string-at-point (&optional state) + "Return the bounds of a string at point. +If STATE is given it used a parsing state at point." + (save-excursion + (let ((state (or state (syntax-ppss)))) + (when (nth 3 state) + (cons (nth 8 state) + (when (parse-partial-sexp + (point) (point-max) nil nil state 'syntax-table) + (point))))))) +(put 'evil-string 'bounds-of-thing-at-point #'bounds-of-evil-string-at-point) + +(defun bounds-of-evil-comment-at-point () + "Return the bounds of a string at point." + (save-excursion + (let ((state (syntax-ppss))) + (when (nth 4 state) + (cons (nth 8 state) + (when (parse-partial-sexp + (point) (point-max) nil nil state 'syntax-table) + (point))))))) +(put 'evil-comment 'bounds-of-thing-at-point #'bounds-of-evil-comment-at-point) + +;; The purpose of this function is to provide line motions which +;; preserve the column. This is how `previous-line' and `next-line' +;; work, but unfortunately the behaviour is hard-coded: if and only if +;; the last command was `previous-line' or `next-line', the column is +;; preserved. Furthermore, in contrast to Vim, when we cannot go +;; further, those motions move point to the beginning resp. the end of +;; the line (we never want point to leave its column). The code here +;; comes from simple.el, and I hope it will work in future. +(declare-function evil-end-of-visual-line "evil-commands") +(defun evil-line-move (count &optional noerror) + "Like `line-move' but conserves the column. +Signal an error at buffer boundaries unless NOERROR is non-nil." + (setq this-command (if (< count 0) 'previous-line 'next-line)) + (let ((last-command + ;; Reset tmp goal column between visual/logical movement + (when (or (eq line-move-visual (consp temporary-goal-column)) + (eq temporary-goal-column most-positive-fixnum)) + last-command)) + (opoint (point))) + (if (and line-move-visual + (eq temporary-goal-column most-positive-fixnum) + (memq last-command '(next-line previous-line))) + (let (temporary-goal-column) (evil-end-of-visual-line (1+ count))) + (condition-case err + (line-move count) + ((beginning-of-buffer end-of-buffer) + (let* ((raw-col (or goal-column + (car-safe temporary-goal-column) + temporary-goal-column)) + (col (if (numberp raw-col) (max 0 raw-col) 0))) + (line-move-finish col opoint (< count 0))) + (or noerror (/= (point) opoint) (signal (car err) (cdr err)))))))) + +(defun evil-forward-syntax (syntax &optional count) + "Move point to the end or beginning of a sequence of characters in SYNTAX. +Stop on reaching a character not in SYNTAX." + (let ((notsyntax (if (= (aref syntax 0) ?^) + (substring syntax 1) + (concat "^" syntax)))) + (evil-motion-loop (dir (or count 1)) + (cond + ((< dir 0) + (skip-syntax-backward notsyntax) + (skip-syntax-backward syntax)) + (t + (skip-syntax-forward notsyntax) + (skip-syntax-forward syntax)))))) + +(defun evil-forward-chars (chars &optional count) + "Move point to the end or beginning of a sequence of CHARS. +CHARS is a character set as inside [...] in a regular expression." + (let ((notchars (if (= (aref chars 0) ?^) + (substring chars 1) + (concat "^" chars)))) + (evil-motion-loop (dir (or count 1)) + (cond + ((< dir 0) + (skip-chars-backward notchars) + (skip-chars-backward chars)) + (t + (skip-chars-forward notchars) + (skip-chars-forward chars)))))) + +(defun evil-up-block (beg end &optional count) + "Move point to the end or beginning of text enclosed by BEG and END. +BEG and END should be regular expressions matching the opening +and closing delimiters, respectively. If COUNT is greater than +zero point is moved forward otherwise it is moved +backwards. Whenever an opening delimiter is found the COUNT is +increased by one, if a closing delimiter is found the COUNT is +decreased by one. The motion stops when COUNT reaches zero. The +match-data reflects the last successful match (that caused COUNT +to reach zero). The behaviour of this functions is similar to +`up-list'." + (let* ((count (or count 1)) + (forwardp (> count 0)) + (dir (if forwardp +1 -1))) + (catch 'done + (while (not (zerop count)) + (let* ((pnt (point)) + (cl (save-excursion + (and (re-search-forward (if forwardp end beg) nil t dir) + (or (/= pnt (point)) + (progn + ;; zero size match, repeat search from + ;; the next position + (forward-char dir) + (re-search-forward (if forwardp end beg) nil t dir))) + (point)))) + (match (match-data t)) + (op (save-excursion + (and (not (equal beg end)) + (re-search-forward (if forwardp beg end) cl t dir) + (or (/= pnt (point)) + (progn + ;; zero size match, repeat search from + ;; the next position + (forward-char dir) + (re-search-forward (if forwardp beg end) cl t dir))) + (point))))) + (cond + ((not cl) + (goto-char (if forwardp (point-max) (point-min))) + (set-match-data nil) + (throw 'done count)) + (t + (if op + (progn + (setq count (if forwardp (1+ count) (1- count))) + (goto-char op)) + (setq count (if forwardp (1- count) (1+ count))) + (if (zerop count) (set-match-data match)) + (goto-char cl)))))) + 0))) + +(defun evil-up-paren (open close &optional count) + "Move point to the end or beginning of balanced parentheses. +OPEN and CLOSE should be characters identifying the opening and +closing parenthesis, respectively. If COUNT is greater than zero +point is moved forward otherwise it is moved backwards. Whenever +an opening delimiter is found the COUNT is increased by one, if a +closing delimiter is found the COUNT is decreased by one. The +motion stops when COUNT reaches zero. The match-data reflects the +last successful match (that caused COUNT to reach zero)." + ;; Always use the default `forward-sexp-function'. This is important + ;; for modes that use a custom one like `python-mode'. + ;; (addresses #364) + (let (forward-sexp-function up-list-function) + (with-syntax-table (copy-syntax-table (syntax-table)) + (modify-syntax-entry open (format "(%c" close)) + (modify-syntax-entry close (format ")%c" open)) + (let ((rest (evil-motion-loop (dir count) + (let ((pnt (point))) + (condition-case nil + (cond + ((> dir 0) + (while (progn + (up-list dir t) + (/= (char-before) close)))) + (t + (while (progn + (up-list dir t) + (/= (char-after) open))))) + (error (goto-char pnt))))))) + (cond + ((= rest count) (set-match-data nil)) + ((> count 0) (set-match-data (list (1- (point)) (point)))) + (t (set-match-data (list (point) (1+ (point)))))) + rest)))) + +(defun evil-up-xml-tag (&optional count) + "Move point to the end or beginning of balanced xml tags. +If COUNT is greater than zero point is moved forward otherwise it is moved +backwards. Whenever an opening delimiter is found the COUNT is increased by +one, if a closing delimiter is found the COUNT is decreased by one. The motion +stops when COUNT reaches zero. The match data reflects the last successful +match (that caused COUNT to reach zero)." + (let* ((dir (if (> (or count 1) 0) +1 -1)) + (count (abs (or count 1))) + (op (if (> dir 0) 1 2)) + (cl (if (> dir 0) 2 1)) + (orig (point)) + pnt tags match) + (catch 'done + (while (> count 0) + ;; find the previous opening tag + (while + (and (setq match + (re-search-forward + "<\\([^/ >\n]+\\)\\(?:=>?\\|[^\"/>]\\|\"[^\"]*\"\\)*?>\\|]+?\\)>" + nil t dir)) + (cond + ((match-beginning op) + (push (match-string op) tags)) + ((null tags) nil) ; free closing tag + ((and (< dir 0) + (string= (car tags) (match-string cl))) + ;; in backward direction we only accept matching + ;; tags. If the current tag is a free opener + ;; without matching closing tag, the subsequent + ;; test will make us ignore this tag + (pop tags)) + ((and (> dir 0)) + ;; non matching openers are considered free openers + (while (and tags + (not (string= (car tags) + (match-string cl)))) + (pop tags)) + (pop tags))))) + (unless (setq match (and match (match-data t))) + (setq match nil) + (throw 'done count)) + ;; found closing tag, look for corresponding opening tag + (cond + ((> dir 0) + (setq pnt (match-end 0)) + (goto-char (match-beginning 0))) + (t + (setq pnt (match-beginning 0)) + (goto-char (match-end 0)))) + (let* ((tag (match-string cl)) + (refwd (concat "<\\(/\\)?" + (regexp-quote tag) + "\\(?:>\\|[ \n]\\(?:[^\"/>]\\|\"[^\"]*\"\\)*?>\\)")) + (cnt 1)) + (while (and (> cnt 0) (re-search-backward refwd nil t dir)) + (setq cnt (+ cnt (if (match-beginning 1) dir (- dir))))) + (if (zerop cnt) (setq count (1- count) tags nil)) + (goto-char pnt))) + (if (> count 0) + (set-match-data nil) + (set-match-data match) + (goto-char (if (> dir 0) (match-end 0) (match-beginning 0))))) + ;; if not found, set to point-max/point-min + (unless (zerop count) + (set-match-data nil) + (goto-char (if (> dir 0) (point-max) (point-min))) + (if (/= (point) orig) (setq count (1- count)))) + (* dir count))) + +(defun evil-forward-quote (quote &optional count) + "Move point to the end or beginning of a string. +QUOTE is the character delimiting the string. If COUNT is greater +than zero point is moved forward otherwise it is moved +backwards." + (let (reset-parser) + (with-syntax-table (copy-syntax-table (syntax-table)) + (unless (= (char-syntax quote) ?\") + (modify-syntax-entry quote "\"") + (setq reset-parser t)) + ;; global parser state is out of state, use local one + (let* ((pnt (point)) + (state (save-excursion + (beginning-of-defun) + (parse-partial-sexp (point) pnt nil nil (syntax-ppss)))) + (bnd (bounds-of-evil-string-at-point state))) + (when (and bnd (< (point) (cdr bnd))) + ;; currently within a string + (if (> count 0) + (progn + (goto-char (cdr bnd)) + (setq count (1- count))) + (goto-char (car bnd)) + (setq count (1+ count)))) + ;; forward motions work with local parser state + (cond + ((> count 0) + ;; no need to reset global parser state because we only use + ;; the local one + (setq reset-parser nil) + (catch 'done + (while (and (> count 0) (not (eobp))) + (setq state (parse-partial-sexp + (point) (point-max) nil nil state 'syntax-table)) + (cond + ((nth 3 state) + (setq bnd (bounds-of-thing-at-point 'evil-string)) + (goto-char (cdr bnd)) + (setq count (1- count))) + ((eobp) (goto-char pnt) (throw 'done nil)))))) + ((< count 0) + ;; need to update global cache because of backward motion + (setq reset-parser (and reset-parser (point))) + (save-excursion + (beginning-of-defun) + (syntax-ppss-flush-cache (point))) + (catch 'done + (while (and (< count 0) (not (bobp))) + (setq pnt (point)) + (while (and (not (bobp)) + (or (eobp) (/= (char-after) quote))) + (backward-char)) + (cond + ((setq bnd (bounds-of-thing-at-point 'evil-string)) + (goto-char (car bnd)) + (setq count (1+ count))) + ((bobp) (goto-char pnt) (throw 'done nil)) + (t (backward-char)))))) + (t (setq reset-parser nil))))) + (when reset-parser + ;; reset global cache + (save-excursion + (goto-char reset-parser) + (beginning-of-defun) + (syntax-ppss-flush-cache (point)))) + count)) + +;;; Thing-at-point motion functions for Evil text objects and motions +(defun forward-evil-empty-line (&optional count) + "Move forward COUNT empty lines." + (setq count (or count 1)) + (cond + ((> count 0) + (while (and (> count 0) (not (eobp))) + (when (and (bolp) (eolp)) + (setq count (1- count))) + (forward-line 1))) + (t + (while (and (< count 0) (not (bobp)) + (zerop (forward-line -1))) + (when (and (bolp) (eolp)) + (setq count (1+ count)))))) + count) + +(defun forward-evil-space (&optional count) + "Move forward COUNT whitespace sequences [[:space:]]+." + (evil-forward-chars "[:space:]" count)) + +(defun forward-evil-word (&optional count) + "Move forward COUNT words. +Moves point COUNT words forward or (- COUNT) words backward if +COUNT is negative. Point is placed after the end of the word (if +forward) or at the first character of the word (if backward). A +word is a sequence of word characters matching +\[[:word:]] (recognized by `forward-word'), a sequence of +non-whitespace non-word characters '[^[:word:]\\n\\r\\t\\f ]', or +an empty line matching ^$." + (evil-forward-nearest + count + #'(lambda (&optional cnt) + (let ((word-separating-categories evil-cjk-word-separating-categories) + (word-combining-categories evil-cjk-word-combining-categories) + (pnt (point))) + (forward-word cnt) + (if (= pnt (point)) cnt 0))) + #'(lambda (&optional cnt) + (evil-forward-chars "^[:word:]\n\r\t\f " cnt)) + #'forward-evil-empty-line)) + +(defun forward-evil-WORD (&optional count) + "Move forward COUNT \"WORDS\". +Moves point COUNT WORDS forward or (- COUNT) WORDS backward if +COUNT is negative. Point is placed after the end of the WORD (if +forward) or at the first character of the WORD (if backward). A +WORD is a sequence of non-whitespace characters +'[^\\n\\r\\t\\f ]', or an empty line matching ^$." + (evil-forward-nearest count + #'(lambda (&optional cnt) + (evil-forward-chars "^\n\r\t\f " cnt)) + #'forward-evil-empty-line)) + +(defun forward-evil-symbol (&optional count) + "Move forward COUNT symbols. +Moves point COUNT symbols forward or (- COUNT) symbols backward +if COUNT is negative. Point is placed after the end of the +symbol (if forward) or at the first character of the symbol (if +backward). A symbol is either determined by `forward-symbol', or +is a sequence of characters not in the word, symbol or whitespace +syntax classes." + (evil-forward-nearest + count + #'(lambda (&optional cnt) + (evil-forward-syntax "^w_->" cnt)) + #'(lambda (&optional cnt) + (let ((pnt (point))) + (forward-symbol cnt) + (if (= pnt (point)) cnt 0))) + #'forward-evil-empty-line)) + +(defun forward-evil-defun (&optional count) + "Move forward COUNT defuns. +Moves point COUNT defuns forward or (- COUNT) defuns backward +if COUNT is negative. A defun is defined by +`beginning-of-defun' and `end-of-defun' functions." + (evil-motion-loop (dir (or count 1)) + (if (> dir 0) (end-of-defun) (beginning-of-defun)))) + +(defun forward-evil-sentence (&optional count) + "Move forward COUNT sentences. +Moves point COUNT sentences forward or (- COUNT) sentences +backward if COUNT is negative. This function is the same as +`forward-sentence' but returns the number of sentences that could +NOT be moved over." + (evil-motion-loop (dir (or count 1)) + (ignore-errors (forward-sentence dir)))) + +(defun forward-evil-paragraph (&optional count) + "Move forward COUNT paragraphs. +Moves point COUNT paragraphs forward or (- COUNT) paragraphs backward +if COUNT is negative. A paragraph is defined by +`start-of-paragraph-text' and `forward-paragraph' functions." + (evil-motion-loop (dir (or count 1)) + (cond + ((> dir 0) (forward-paragraph)) + ((not (bobp)) (start-of-paragraph-text) (beginning-of-line))))) + +(defvar evil-forward-quote-char ?\" + "The character to be used by `forward-evil-quote'.") + +(defun forward-evil-quote (&optional count) + "Move forward COUNT strings. +The quotation character is specified by the global variable +`evil-forward-quote-char'. This character is passed to +`evil-forward-quote'." + (evil-forward-quote evil-forward-quote-char count)) + +(defun forward-evil-quote-simple (&optional count) + "Move forward COUNT strings. +The quotation character is specified by the global variable +`evil-forward-quote-char'. This functions uses Vim's rules +parsing from the beginning of the current line for quotation +characters. It should only be used when looking for strings +within comments and buffer *must* be narrowed to the comment." + (let ((dir (if (> (or count 1) 0) 1 -1)) + (ch evil-forward-quote-char) + (pnt (point)) + (cnt 0)) + (beginning-of-line) + ;; count number of quotes before pnt + (while (< (point) pnt) + (when (= (char-after) ch) + (setq cnt (1+ cnt))) + (forward-char)) + (setq cnt (- (* 2 (abs count)) (mod cnt 2))) + (cond + ((> dir 0) + (while (and (not (eolp)) (not (zerop cnt))) + (when (= (char-after) ch) (setq cnt (1- cnt))) + (forward-char)) + (when (not (zerop cnt)) (goto-char (point-max)))) + (t + (while (and (not (bolp)) (not (zerop cnt))) + (when (= (char-before) ch) (setq cnt (1- cnt))) + (forward-char -1)) + (when (not (zerop cnt)) (goto-char (point-min))))) + (/ cnt 2))) + +;;; Motion functions +(defun evil-forward-beginning (thing &optional count) + "Move forward to beginning of THING. +The motion is repeated COUNT times." + (setq count (or count 1)) + (if (< count 0) + (forward-thing thing count) + (let ((bnd (bounds-of-thing-at-point thing)) + rest) + (when (and bnd (< (point) (cdr bnd))) + (goto-char (cdr bnd))) + (ignore-errors + (when (zerop (setq rest (forward-thing thing count))) + (when (and (bounds-of-thing-at-point thing) + (not (bobp)) + ;; handle final empty line + (not (and (bolp) (eobp)))) + (backward-char)) + (beginning-of-thing thing))) + rest))) + +(defun evil-backward-beginning (thing &optional count) + "Move backward to beginning of THING. +The motion is repeated COUNT times. This is the same as calling +`evil-backward-beginning' with -COUNT." + (evil-forward-beginning thing (- (or count 1)))) + +(defun evil-forward-end (thing &optional count) + "Move forward to end of THING. +The motion is repeated COUNT times." + (setq count (or count 1)) + (if (> count 0) + (progn (unless (eobp) (forward-char)) + (prog1 (forward-thing thing count) + (unless (bobp) (backward-char)))) + (let ((bnd (bounds-of-thing-at-point thing)) + rest) + (when (and bnd (< (point) (cdr bnd) )) + (goto-char (car bnd))) + (ignore-errors + (when (zerop (setq rest (forward-thing thing count))) + (end-of-thing thing) + (backward-char))) + rest))) + +(defun evil-backward-end (thing &optional count) + "Move backward to end of THING. +The motion is repeated COUNT times. This is the same as calling +`evil-backward-end' with -COUNT." + (evil-forward-end thing (- (or count 1)))) + +(defun evil-forward-word (&optional count) + "Move by words. +Moves point COUNT words forward or (- COUNT) words backward if +COUNT is negative. This function is the same as `forward-word' +but returns the number of words by which point could *not* be +moved." + (setq count (or count 1)) + (let* ((dir (if (>= count 0) +1 -1)) + (count (abs count))) + (while (and (> count 0) + (forward-word dir)) + (setq count (1- count))) + count)) + +(defun evil-in-comment-p (&optional pos) + "Check if POS is within a comment according to current syntax. +If POS is nil, (point) is used. The return value is the beginning +position of the comment." + (setq pos (or pos (point))) + (let ((chkpos + (cond + ((eobp) pos) + ((= (char-syntax (char-after)) ?<) (1+ pos)) + ((and (not (zerop (logand (car (syntax-after (point))) + (ash 1 16)))) + (not (zerop (logand (or (car (syntax-after (1+ (point)))) 0) + (ash 1 17))))) + (+ pos 2)) + ((and (not (zerop (logand (car (syntax-after (point))) + (ash 1 17)))) + (not (zerop (logand (or (car (syntax-after (1- (point)))) 0) + (ash 1 16))))) + (1+ pos)) + (t pos)))) + (let ((syn (save-excursion (syntax-ppss chkpos)))) + (and (nth 4 syn) (nth 8 syn))))) + +(defun evil-looking-at-start-comment (&optional move) + "Return t if point is at the start of a comment. +Point must be on one of the opening characters of a block comment +according to the current syntax table. Futhermore, these +characters must been parsed as opening characters, i.e. they +won't be considered as comment starters inside a string or +possibly another comment. Point is moved to the first character +of the comment opener if MOVE is non-nil." + (cond + ((= (point) (point-max)) nil) + ;; one character opener + ((= (char-syntax (char-after)) ?<) + (equal (point) (evil-in-comment-p (1+ (point))))) + ;; two character opener on first char + ((and (not (zerop (logand (car (syntax-after (point))) + (ash 1 16)))) + (not (zerop (logand (or (car (syntax-after (1+ (point)))) 0) + (ash 1 17))))) + (equal (point) (evil-in-comment-p (+ 2 (point))))) + ;; two character opener on second char + ((and (not (zerop (logand (car (syntax-after (point))) + (ash 1 17)))) + (not (zerop (logand (or (car (syntax-after (1- (point)))) 0) + (ash 1 16))))) + (and (equal (1- (point)) (evil-in-comment-p (1+ (point)))) + (prog1 t (when move (backward-char))))))) + +(defun evil-looking-at-end-comment (&optional move) + "Return t if point is at the end of a comment. +Point must be on one of the opening characters of a block comment +according to the current syntax table. Futhermore, these +characters must been parsed as opening characters, i.e. they +won't be considered as comment starters inside a string or +possibly another comment. Point is moved right after the comment +closer if MOVE is non-nil." + (cond + ;; one char closer + ((= (char-syntax (char-after)) ?>) + (and (evil-in-comment-p) ; in comment + (not (evil-in-comment-p (1+ (point)))) + (prog1 t (when move (forward-char))))) + ;; two char closer on first char + ((and (not (zerop (logand (car (syntax-after (point))) + (ash 1 18)))) + (not (zerop (logand (or (car (syntax-after (1+ (point)))) 0) + (ash 1 19))))) + (and (evil-in-comment-p) + (not (evil-in-comment-p (+ (point) 2))) + (prog1 t (when move (forward-char 2))))) + ;; two char closer on second char + ((and (not (zerop (logand (car (syntax-after (point))) + (ash 1 19)))) + (not (zerop (logand (or (car (syntax-after (1- (point)))) 0) + (ash 1 18))))) + (and (evil-in-comment-p) + (not (evil-in-comment-p (1+ (point)))) + (prog1 t (when move (forward-char))))))) + +(defun evil-insert-newline-above () + "Insert a new line above point and place point in that line +with regard to indentation." + (evil-narrow-to-field + (evil-move-beginning-of-line) + (insert (if use-hard-newlines hard-newline "\n")) + (forward-line -1) + (back-to-indentation))) + +(defun evil-insert-newline-below () + "Insert a new line below point and place point in that line +with regard to indentation." + (evil-narrow-to-field + (evil-move-end-of-line) + (insert (if use-hard-newlines hard-newline "\n")) + (back-to-indentation))) + +;;; Markers + +(defun evil-global-marker-p (char) + "Whether CHAR denotes a global marker." + (or (and (>= char ?A) (<= char ?Z)) + (assq char (default-value 'evil-markers-alist)))) + +(defun evil-set-marker (char &optional pos advance) + "Set the marker denoted by CHAR to position POS. +POS defaults to the current position of point. +If ADVANCE is t, the marker advances when inserting text at it; +otherwise, it stays behind." + (interactive (list (read-char))) + (catch 'done + (let ((marker (evil-get-marker char t)) alist) + (unless (markerp marker) + (cond + ((eq marker 'evil-visual-beginning) + (setq marker evil-visual-mark)) + ((eq marker 'evil-visual-goto-end) + (setq marker evil-visual-point)) + ((and marker (symbolp marker) (boundp marker)) + (set marker (or (symbol-value marker) (make-marker))) + (setq marker (symbol-value marker))) + ((eq marker 'evil-jump-backward-swap) + (evil-set-jump) + (throw 'done nil)) + ((functionp marker) + (user-error "Cannot set special marker `%c'" char)) + ((evil-global-marker-p char) + (setq alist (default-value 'evil-markers-alist) + marker (make-marker)) + (evil--add-to-alist alist char marker) + (setq-default evil-markers-alist alist)) + (t + (setq marker (make-marker)) + (evil--add-to-alist evil-markers-alist char marker)))) + (add-hook 'kill-buffer-hook #'evil-swap-out-markers nil t) + (set-marker-insertion-type marker advance) + (set-marker marker (or pos (point)))))) +(evil-set-command-properties #'evil-set-marker :suppress-operator t) + +(defun evil-get-marker (char &optional raw) + "Return the marker denoted by CHAR. +This is either a marker object as returned by `make-marker', +a number, a cons cell (FILE . POS) with FILE being a string +and POS a number, or nil. If RAW is non-nil, then the +return value may also be a variable, a movement function, +or a marker object pointing nowhere." + (let ((marker (cdr (assq char (if (evil-global-marker-p char) + (default-value 'evil-markers-alist) + evil-markers-alist))))) + (if raw + marker + (when (and (symbolp marker) (boundp marker)) + (setq marker (symbol-value marker))) + (when (functionp marker) + (save-excursion + (save-window-excursion + (funcall marker) + (setq marker (move-marker (make-marker) (point)))))) + (if (markerp marker) + (if (eq (marker-buffer marker) (current-buffer)) + (marker-position marker) + (when (marker-buffer marker) marker)) + marker)))) + +(defun evil-swap-out-markers () + "Turn markers into file references when the buffer is killed." + (when buffer-file-name + (dolist (entry evil-markers-alist) + (and (markerp (cdr entry)) + (eq (marker-buffer (cdr entry)) (current-buffer)) + (setcdr entry (cons buffer-file-name + (marker-position (cdr entry)))))))) +(put 'evil-swap-out-markers 'permanent-local-hook t) + +(defvar calc-multiplication-has-precedence) +(defun evil--eval-expr (input) + "Eval INPUT and return stringified result, if of a suitable type. +If INPUT starts with a number, +, -, or . use `calc-eval' instead." + (let* ((calcable-p (string-match-p "\\`[[:space:]]*[0-9+.-]" input)) + (result (if calcable-p + (let ((calc-multiplication-has-precedence nil)) + (calc-eval input)) + (eval (car (read-from-string input)) t)))) + (cond + ((stringp result) result) + ((or (numberp result) (symbolp result)) + (format "%s" result)) + ((sequencep result) + (mapconcat (lambda (x) (format "%s" x)) result "\n")) + (t (user-error "Using `%s' as a string" (type-of result)))))) + +(defvar evil-paste-clear-minibuffer-first nil + "`evil-paste-before' cannot have `delete-minibuffer-contents' called before +it fetches certain registers becuase this would trigger various ex-updates, +sometimes moving point, so `C-a' `C-w' etc. would miss their intended target.") + +(defun evil-ex-remove-default () + "Remove the default text shown in the ex minibuffer. +When Ex starts, the previous command is shown enclosed in +parenthesis. This function removes this text when the first key +is pressed." + (when (and (not (eq this-command 'exit-minibuffer)) + (/= (minibuffer-prompt-end) (point-max))) + (when (eq this-command 'evil-ex-delete-backward-char) + (setq this-command 'ignore)) + (if (eq this-original-command 'evil-paste-from-register) + (setq evil-paste-clear-minibuffer-first t) + (delete-minibuffer-contents))) + (remove-hook 'pre-command-hook #'evil-ex-remove-default t)) + +(defun evil-get-register (register &optional noerror) + "Return contents of REGISTER. +Signal an error if empty, unless NOERROR is non-nil. + +The following special registers are supported. + \" the unnamed register + * the clipboard contents + + the clipboard contents + the word at point (ex mode only) + the WORD at point (ex mode only) + the symbol at point (ex mode only) + the current file at point (ex mode only) + % the current file name (read only) + # the alternate file name (read only) + / the last search pattern (read only) + : the last command line (read only) + . the last inserted text (read only) + - the last small (less than a line) delete + _ the black hole register + = the expression register (read only)" + (unless (characterp register) (error "Invalid register `%S'" register)) + (condition-case err + (or (cond + ((eq register ?\") + (current-kill 0)) + ((<= ?1 register ?9) + (let ((reg (- register ?1))) + (and (< reg (length kill-ring)) + (current-kill reg t)))) + ((memq register '(?* ?+)) + (let ((what (if (eq register ?*) 'PRIMARY 'CLIPBOARD))) + (if (eval-when-compile (>= emacs-major-version 29)) + (gui--selection-value-internal what) + ;; The following code is based on `x-selection-value-internal' + ;; (now `gui--selection-value-internal') circa Emacs 24. We're + ;; unsure why exactly it's duplicated here, and it's possible + ;; it needn't be for newer versions of Emacs. + (let ((request-type (or (bound-and-true-p x-select-request-type) + '(UTF8_STRING COMPOUND_TEXT STRING))) + text) + (unless (consp request-type) + (setq request-type (list request-type))) + (while (and request-type (not text)) + (setq text (ignore-errors + (evil-get-selection what (pop request-type))))) + (when text + (remove-text-properties 0 (length text) '(foreign-selection nil) text)) + text)))) + ((eq register ?\C-W) + (with-current-buffer + (or evil-ex-original-buffer + (let ((w (minibuffer-selected-window))) (and w (window-buffer w))) + (user-error "Register is only available in Ex state")) + (thing-at-point 'evil-word))) + ((eq register ?\C-A) + (with-current-buffer + (or evil-ex-original-buffer + (let ((w (minibuffer-selected-window))) (and w (window-buffer w))) + (user-error "Register is only available in Ex state")) + (thing-at-point 'evil-WORD))) + ((eq register ?\C-O) + (with-current-buffer + (or evil-ex-original-buffer + (let ((w (minibuffer-selected-window))) (and w (window-buffer w))) + (user-error "Register is only available in Ex state")) + (thing-at-point 'evil-symbol))) + ((eq register ?\C-F) + (with-current-buffer + (or evil-ex-original-buffer + (let ((w (minibuffer-selected-window))) (and w (window-buffer w))) + (user-error "Register is only available in Ex state")) + (thing-at-point 'filename))) + ((eq register ?\C-L) + (with-current-buffer + (or evil-ex-original-buffer + (let ((w (minibuffer-selected-window))) (and w (window-buffer w))) + (user-error "Register is only available in Ex state")) + (replace-regexp-in-string "\n\\'" "" (thing-at-point 'line)))) + ((eq register ?%) + (or (buffer-file-name + (or evil-ex-original-buffer + (let ((w (minibuffer-selected-window))) (and w (window-buffer w))))) + (user-error "No file name"))) + ((= register ?#) + (or (with-current-buffer (other-buffer) (buffer-file-name)) + (user-error "No file name"))) + ((eq register ?/) + (defvar evil-search-module) + (or (car (cond + ((eq evil-search-module 'evil-search) evil-ex-search-history) + (isearch-regexp regexp-search-ring) + (t search-ring))) + (user-error "No previous regular expression"))) + ((eq register ?:) + (or (car evil-ex-history) (user-error "No previous command line"))) + ((eq register ?.) evil-last-insertion) + ((eq register ?-) evil-last-small-deletion) + ((eq register ?=) + (let ((enable-recursive-minibuffers t)) + (setq evil-last-=-register-input + (minibuffer-with-setup-hook + (lambda () + (when evil-last-=-register-input + (add-hook 'pre-command-hook #'evil-ex-remove-default nil t))) + (read-from-minibuffer + "=" + (and evil-last-=-register-input + (propertize evil-last-=-register-input 'face 'shadow)) + evil-eval-map + nil + 'evil-eval-history + evil-last-=-register-input + t))) + (evil--eval-expr evil-last-=-register-input))) + ((eq register ?_) "") ; the black hole register + (t (setq register (downcase register)) + (get-register register))) + (user-error "Register `%c' is empty" register)) + (error (unless noerror (signal (car err) (cdr err)))))) + +(defun evil-append-register (register text) + "Append TEXT to the contents of REGISTER." + (let ((content (get-register register))) + (set-register + register + (cond + ((not content) text) + ;; if the register does not contain a string treat it as a vector + ((not (stringp content)) (vconcat content text)) + ;; some non-trivial yank-handler -> always switch to line handler + ((or (text-property-not-all + 0 (length content) 'yank-handler nil content) + (text-property-not-all + 0 (length text) 'yank-handler nil text)) + ;; ensure complete lines + (setq text + (concat + content + (and (> (length content) 0) + (/= (aref content (1- (length content))) ?\n) + "\n") + text + (and (> (length text) 0) + (/= (aref text (1- (length text))) ?\n) + "\n"))) + (put-text-property 0 (length text) 'yank-handler '(evil-yank-line-handler) + text) + text) + (t (concat content text)))))) + +(defun evil-set-register (register text) + "Set the contents of REGISTER to TEXT. +If REGISTER is an upper case character then TEXT is appended to that +register instead of replacing its content." + (cond + ((not (characterp register)) (user-error "Invalid register")) + ;; don't allow modification of read-only registers + ((member register '(?: ?. ?%)) + (user-error "Can't modify read-only register")) + ((eq register ?\") (kill-new text)) + ((<= ?1 register ?9) + (if (null kill-ring) + (kill-new text) + (let ((kill-ring-yank-pointer kill-ring-yank-pointer) + interprogram-paste-function + interprogram-cut-function) + (current-kill (- register ?1)) + (setcar kill-ring-yank-pointer text)))) + ((eq register ?*) (evil-set-selection 'PRIMARY text)) + ((eq register ?+) (evil-set-selection 'CLIPBOARD text)) + ((eq register ?-) (setq evil-last-small-deletion text)) + ((eq register ?_) nil) ; the black hole register + ((and (<= ?A register) (<= register ?Z)) + (evil-append-register (downcase register) text)) + (t (set-register register text)))) + +(defun evil-register-list () + "Return an alist of all registers, but only those named +with number or character. Registers with symbol or string in names are ignored +to keep Vim compatibility with register jumps." + (sort (append (mapcar (lambda (reg) (cons reg (evil-get-register reg t))) + '(?\" ?* ?+ ?% ?# ?/ ?: ?. ?- + ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)) + (list (cons ?= evil-last-=-register-input)) + (cl-remove-if-not (lambda (reg) (number-or-marker-p (car reg))) register-alist) + nil) + #'car-less-than-car)) + +(defsubst evil-kbd-macro-suppress-motion-error () + "Return non-nil if a motion error should be suppressed. +Whether the motion error should be suppressed depends on the +variable `evil-kbd-macro-suppress-motion-error'." + (or (and defining-kbd-macro + (memq evil-kbd-macro-suppress-motion-error '(t record))) + (and executing-kbd-macro + (memq evil-kbd-macro-suppress-motion-error '(t replay))))) + +;;; Region + +;; `set-mark' does too much at once +(defun evil-move-mark (pos) + "Set buffer's mark to POS. +If POS is nil, delete the mark." + (set-marker (mark-marker) pos)) + +(defun evil-save-transient-mark-mode () + "Save Transient Mark mode and make it buffer-local. +Any changes to Transient Mark mode are now local to the current +buffer, until `evil-restore-transient-mark-mode' is called. + +Variables pertaining to Transient Mark mode are listed in +`evil-transient-vars', and their values are stored in +`evil-transient-vals'." + (dolist (var evil-transient-vars) + (when (and (boundp var) + (not (assq var evil-transient-vals))) + (push (list var (symbol-value var) + (local-variable-p var)) + evil-transient-vals) + (make-variable-buffer-local var) + (put var 'permanent-local t)))) + +(defun evil-restore-transient-mark-mode () + "Restore Transient Mark mode. +This presupposes that `evil-save-transient-mark-mode' has been +called earlier. If Transient Mark mode was disabled before but +enabled in the meantime, this function disables it; if it was +enabled before but disabled in the meantime, this function +enables it. + +The earlier settings of Transient Mark mode are stored in +`evil-transient-vals'." + (let (entry local var val) + (while (setq entry (pop evil-transient-vals)) + (setq var (pop entry) + val (pop entry) + local (pop entry)) + (unless local + (kill-local-variable var)) + (unless (equal (symbol-value var) val) + (if (fboundp var) + (funcall var (if val 1 -1)) + (setq var val)))))) + +;; In theory, an active region implies Transient Mark mode, and +;; disabling Transient Mark mode implies deactivating the region. +;; In practice, Emacs never clears `mark-active' except in Transient +;; Mark mode, so we define our own toggle functions to make things +;; more predictable. +(defun evil-transient-mark (&optional arg) + "Toggle Transient Mark mode. +Ensure that the region is properly deactivated. +Enable with positive ARG, disable with negative ARG." + (unless (numberp arg) + (setq arg (if transient-mark-mode -1 1))) + (cond + ((< arg 1) + (evil-active-region -1) + ;; Transient Mark mode cannot be disabled + ;; while CUA mode is enabled + (when (fboundp 'cua-mode) + (cua-mode -1)) + (when transient-mark-mode + (transient-mark-mode -1))) + (t + (unless transient-mark-mode + (evil-active-region -1) + (transient-mark-mode 1))))) + +(defun evil-active-region (&optional arg) + "Toggle active region. +Ensure that Transient Mark mode is properly enabled. +Enable with positive ARG, disable with negative ARG." + (unless (numberp arg) + (setq arg (if (region-active-p) -1 1))) + (cond + ((and (< arg 1)) + (when (or transient-mark-mode mark-active) + (setq mark-active nil + deactivate-mark nil) + (when (boundp 'cua--explicit-region-start) + (setq cua--explicit-region-start nil)) + (run-hooks 'deactivate-mark-hook))) + (t + (evil-transient-mark 1) + (when deactivate-mark + (setq deactivate-mark nil)) + (unless (mark t) + (evil-move-mark (point))) + (unless (region-active-p) + (set-mark (mark t))) + (when (boundp 'cua--explicit-region-start) + (setq cua--explicit-region-start t))))) + +(defmacro evil-with-transient-mark-mode (&rest body) + "Execute BODY with Transient Mark mode. +Then restore Transient Mark mode to its previous setting." + (declare (indent defun) + (debug t)) + `(let ((inhibit-quit t) + evil-transient-vals) + (unwind-protect + (progn + (evil-save-transient-mark-mode) + (evil-transient-mark 1) + ,@body) + (evil-restore-transient-mark-mode)))) + +(defmacro evil-with-active-region (beg end &rest body) + "Execute BODY with an active region from BEG to END." + (declare (indent 2) + (debug t)) + `(let ((beg ,beg) (end ,end) + evil-transient-vals) + (evil-with-transient-mark-mode + (save-excursion + (evil-active-region 1) + (evil-move-mark beg) + (goto-char end) + ,@body)))) + +(defun evil-exchange-point-and-mark () + "Exchange point and mark without activating the region." + (let* ((point (point)) + (mark (or (mark t) point))) + (set-marker (mark-marker) point) + (goto-char mark))) + +(defun evil-apply-on-block (func beg end pass-columns &rest args) + "Call FUNC for each line of a block selection. +The selection is specified by the region BEG and END. FUNC must +take at least two arguments, the beginning and end of each +line. If PASS-COLUMNS is non-nil, these values are the columns, +otherwise they are buffer positions. Extra arguments to FUNC may +be passed via ARGS." + (let (startcol startpt endcol endpt) + (save-excursion + (goto-char beg) + (setq startcol (current-column) + startpt (line-beginning-position)) + (goto-char end) + (setq endcol (current-column)) + (forward-line 1) + (setq endpt (point-marker)) + ;; ensure the start column is the left one. + (evil-sort startcol endcol) + ;; maybe extend up to EOL + (when (and (memq last-command '(next-line previous-line evil-use-register)) + (eq temporary-goal-column most-positive-fixnum)) + (goto-char startpt) + (while (< (point) endpt) + (setq endcol (max endcol (evil-column (line-end-position)))) + (forward-line 1))) + ;; start looping over lines + (goto-char startpt) + (while (< (point) endpt) + (if pass-columns + (apply func startcol endcol args) + (apply func + (save-excursion (evil-move-to-column startcol)) + (save-excursion (evil-move-to-column endcol t)) + args)) + (forward-line 1))))) + +(defun evil-apply-on-rectangle (function start end &rest args) + "Like `apply-on-rectangle' but maybe extends to eol. +If `temporary-goal-column' is set to a big number, then the +region of each line is extended to the end of each line. The end +column is set to the maximal column in all covered lines." + (apply #'evil-apply-on-block function start end t args)) + +;;; Insertion + +(defun evil-track-last-insertion (beg end old-len) + "Track the last insertion range and its text. +BEG, END and OLD-LEN are as for `after-change-functions'. +The insertion range is stored as a pair of buffer positions in +`evil-current-insertion'." + ;; For deletions left of, or insertions right of the end of the last + ;; insertion (with any changes in between), extend or contract the + ;; insertion range as appropriate. Otherwise, start a new range. + (if (when evil-current-insertion + (let ((ins-beg (car evil-current-insertion)) + (ins-end (cdr evil-current-insertion))) + (and (<= ins-beg beg) + (or (= (+ beg old-len) ins-end) + (when (evil-replace-state-p) (= beg ins-end)))))) + ;; Ignore insertion of previous char from Backspace in Replace state + (unless (and (eq this-command 'evil-replace-backspace) (= old-len 0)) + (setcdr evil-current-insertion end)) + (setq evil-current-insertion (cons beg end)))) +(put 'evil-track-last-insertion 'permanent-local-hook t) + +(defun evil-start-track-last-insertion () + "Start tracking the last insertion." + (setq evil-current-insertion nil) + (add-hook 'after-change-functions #'evil-track-last-insertion nil t)) + +(defun evil-stop-track-last-insertion () + "Stop tracking the last insertion. +The tracked insertion is set to `evil-last-insertion'." + (setq evil-last-insertion + (and evil-current-insertion + ;; Check whether the insertion range is a valid buffer + ;; range. If a buffer modification is done from within + ;; another change hook or modification-hook (yasnippet + ;; does this using overlay modification-hooks), then the + ;; insertion information may be invalid. There is no way + ;; to detect this situation, but at least we should + ;; ensure that no error occurs (see bug #272). + (>= (car evil-current-insertion) (point-min)) + (<= (cdr evil-current-insertion) (point-max)) + (buffer-substring-no-properties (car evil-current-insertion) + (cdr evil-current-insertion)))) + (remove-hook 'after-change-functions #'evil-track-last-insertion t)) + +;;; Paste + +(defun evil-yank-characters (beg end &optional register yank-handler) + "Save the characters defined by the region BEG and END in the kill-ring." + (let ((text (filter-buffer-substring beg end))) + (when yank-handler + (put-text-property 0 (length text) 'yank-handler (list yank-handler) text)) + (when register + (evil-set-register register text)) + (when evil-was-yanked-without-register + (evil-set-register ?0 text)) ; "0 register contains last yanked text + (unless (eq register ?_) + (kill-new text)))) + +(defun evil-yank-lines (beg end &optional register yank-handler) + "Save the lines in the region BEG and END into the kill-ring." + (let ((text (filter-buffer-substring beg end)) + (yank-handler (list (or yank-handler #'evil-yank-line-handler) + nil + t))) + ;; Ensure the text ends with a newline. This is required + ;; if the deleted lines were the last lines in the buffer. + (when (or (zerop (length text)) + (/= (aref text (1- (length text))) ?\n)) + (setq text (concat text "\n"))) + (put-text-property 0 (length text) 'yank-handler yank-handler text) + (when register + (evil-set-register register text)) + (when evil-was-yanked-without-register + (evil-set-register ?0 text)) ; "0 register contains last yanked text + (unless (eq register ?_) + (kill-new text)))) + +(defun evil-yank-rectangle (beg end &optional register yank-handler) + "Save the rectangle defined by region BEG and END into the kill-ring." + (let ((lines (list nil))) + (evil-apply-on-rectangle #'extract-rectangle-line beg end lines) + ;; We remove spaces from the beginning and the end of the next. + ;; Spaces are inserted explicitly in the yank-handler in order to + ;; NOT insert lines full of spaces. + (setq lines (nreverse (cdr lines))) + ;; `text' is used as default insert text when pasting this rectangle + ;; in another program, e.g., using the X clipboard. + (let ((yank-handler (list (or yank-handler #'evil-yank-block-handler) + lines + t + #'evil-delete-yanked-rectangle)) + (text (mapconcat #'identity lines "\n"))) + (put-text-property 0 (length text) 'yank-handler yank-handler text) + (when register + (evil-set-register register text)) + (when evil-was-yanked-without-register + (evil-set-register ?0 text)) ; "0 register contains last yanked text + (unless (eq register ?_) + (kill-new text)) + text))) + +(defun evil-remove-yank-excluded-properties (text) + "Remove `yank-excluded-properties' from TEXT." + (if (eq yank-excluded-properties t) + (set-text-properties 0 (length text) nil text) + (remove-list-of-text-properties 0 (length text) + yank-excluded-properties text))) + +(defun evil-yank-line-handler (text) + "Insert the current text linewise." + (let ((text (apply #'concat (make-list (or evil-paste-count 1) text))) + (opoint (point))) + (evil-remove-yank-excluded-properties text) + (cond + ((eq this-command 'evil-paste-before) + (evil-move-beginning-of-line) + (let ((beg (point))) + (insert text) + (setq evil-last-paste + (list 'evil-paste-before evil-paste-count opoint beg (point))) + (evil-set-marker ?\[ beg) + (evil-set-marker ?\] (1- (point))) + (goto-char beg)) + (back-to-indentation)) + ((eq this-command 'evil-paste-after) + (evil-move-end-of-line) + (let ((beg (point))) + (insert "\n") + (insert text) + (delete-char -1) ; delete the last newline + (setq evil-last-paste + (list 'evil-paste-after evil-paste-count opoint beg (point))) + (evil-set-marker ?\[ (1+ beg)) + (evil-set-marker ?\] (point)) + (unless evil--cursor-after + (goto-char (1+ beg)))) + (back-to-indentation)) + (t (insert text))))) + +(defun evil-yank-block-handler (lines) + "Insert the current text as block." + (let* ((count (or evil-paste-count 1)) + shifted-forward + (col (if (and (eq this-command 'evil-paste-after) + (not (and (bolp) (eolp))) + (setq shifted-forward t)) + (1+ (current-column)) + (current-column))) + (opoint (point)) + (first t)) + (dolist (line lines) + ;; maybe we have to insert a new line at eob + (if first + (setq first nil) + (when (or (> (forward-line 1) 0) + (and (eobp) (not (bolp)))) + (insert "\n"))) + ;; concat multiple copies according to count + (setq line (apply #'concat (make-list count line))) + ;; trim whitespace at beginning and end + (string-match "\\` *\\(.*?\\) *\\'" line) + (let ((text (match-string 1 line)) + (begextra (match-beginning 1)) + (endextra (- (match-end 0) (match-end 1)))) + ;; insert text unless we insert an empty line behind eol + (unless (and (< (evil-column (line-end-position)) col) + (string= text "")) + ;; if we paste behind eol, it may be sufficient to insert tabs + (if (< (evil-column (line-end-position)) col) + (move-to-column (+ col begextra) t) + (move-to-column col t) + (insert (make-string begextra ?\s))) + (evil-remove-yank-excluded-properties text) + (insert text) + (unless (eolp) + ;; text follows, so we have to insert spaces + (insert (make-string endextra ?\s)))))) + (setq evil-last-paste + (list this-command + evil-paste-count + opoint + (length lines) ; number of rows + (* count (length (car lines))))) ; number of columns + (evil-set-marker ?\[ opoint) + (evil-set-marker ?\] (1- (point))) + (if evil--cursor-after + (backward-char) + (goto-char opoint) + (when shifted-forward (forward-char))))) + +(defun evil-delete-yanked-rectangle (nrows ncols) + "Special function to delete the block yanked by a previous paste command. +Supplied as the `undo' element of a yank handler." + (let ((opoint (point)) + (col (if (eq last-command 'evil-paste-after) + (1+ (current-column)) + (current-column)))) + (dotimes (_ nrows) + (delete-region (save-excursion + (move-to-column col) + (point)) + (save-excursion + (move-to-column (+ col ncols)) + (point))) + (unless (eobp) (forward-line))) + (goto-char opoint))) + +;; TODO: if undoing is disabled in the current buffer, paste-pop won't +;; work. Although this is probably not a big problem, because usually +;; buffers where `evil-paste-pop' may be useful have undoing enabled. +;; A solution would be to temporarily enable undo when pasting and +;; store the undo information in a special variable that does not +;; interfere with `buffer-undo-list'. +(defun evil-paste-pop (count) + "Replace the just-yanked stretch of killed text with a different stretch. +This command is allowed only immediatly after a `yank', +`evil-paste-before', `evil-paste-after' or `evil-paste-pop'. +This command uses the same paste command as before, i.e., when +used after `evil-paste-after' the new text is also yanked using +`evil-paste-after', used with the same paste-count argument. + +The COUNT argument inserts the COUNTth previous kill. If COUNT +is negative this is a more recent kill." + (interactive "p") + (unless (memq last-command + '(evil-paste-after + evil-paste-before + evil-visual-paste)) + (user-error "Previous command was not an evil-paste: %s" last-command)) + (unless evil-last-paste + (user-error "Previous paste command used a register")) + (evil-undo-pop) + (goto-char (nth 2 evil-last-paste)) + (setq this-command (nth 0 evil-last-paste)) + ;; use temporary kill-ring, so the paste cannot modify it + (let ((kill-ring (list (current-kill + (if (and (> count 0) (nth 5 evil-last-paste)) + ;; if was visual paste then skip the + ;; text that has been replaced + (1+ count) + count)))) + (kill-ring-yank-pointer kill-ring)) + (when (eq last-command 'evil-visual-paste) + (let ((evil-no-display t)) + (evil-visual-restore))) + (funcall (nth 0 evil-last-paste) (nth 1 evil-last-paste)) + ;; if this was a visual paste, then mark the last paste as NOT + ;; being the first visual paste + (when (eq last-command 'evil-visual-paste) + (setcdr (nthcdr 4 evil-last-paste) nil)))) + +(defun evil-paste-pop-next (count) + "Same as `evil-paste-pop' but with negative argument." + (interactive "p") + (evil-paste-pop (- count))) + +;;; Interactive forms + +(defun evil-match-interactive-code (interactive &optional pos) + "Match an interactive code at position POS in string INTERACTIVE. +Return the first matching entry in `evil-interactive-alist', or nil." + (unless pos (setq pos 0)) + (cl-loop + with len = (length interactive) for entry in evil-interactive-alist thereis + (let* ((string (car entry)) + (end (+ pos (length string)))) + (and (<= end len) + (eq (compare-strings string nil nil interactive pos end) t) + entry)))) + +(defun evil-concatenate-interactive-forms (&rest forms) + "Concatenate interactive list expressions FORMS. +Return a single expression where successive expressions +are joined, if possible." + (let (result) + (when forms + (while (cdr forms) + (cond + ((null (car forms)) + (pop forms)) + ((and (eq (car (car forms)) 'list) + (eq (car (cadr forms)) 'list)) + (setq forms (cons (append (car forms) + (cdr (cadr forms))) + (cdr (cdr forms))))) + (t (push (pop forms) result)))) + (when (car forms) + (push (pop forms) result)) + (setq result (nreverse result)) + (cond + ((null result)) + ((null (cdr result)) (car result)) + (t `(append ,@result)))))) + +(defun evil-interactive-string (string) + "Evaluate the interactive string STRING. +The string may contain extended interactive syntax. +The return value is a cons cell (FORM . PROPERTIES), +where FORM is a single list-expression to be passed to +a standard `interactive' statement, and PROPERTIES is a +list of command properties as passed to `evil-define-command'." + (let ((len (length string)) + (pos 0) + forms properties) + (while (< pos len) + (if (eq (aref string pos) ?\n) + (setq pos (1+ pos)) + (cl-destructuring-bind (code expr . plist) + (or (evil-match-interactive-code string pos) + (user-error "Unknown interactive code: `%s'" + (substring string pos))) + (setq pos (+ pos (length code))) + (when (functionp expr) + (let ((prompt (substring + string pos + (or (string-match-p "\n" string pos) len)))) + (setq pos (+ pos (length prompt)) + expr `(funcall ,expr ,prompt)))) + (setq forms (append forms (list expr)) + properties (append properties plist))))) + (cons `(append ,@forms) properties))) + +(defun evil-interactive-form (&rest args) + "Evaluate interactive forms ARGS. +The return value is a cons cell (FORM . PROPERTIES), +where FORM is a single list-expression to be passed to +a standard `interactive' statement, and PROPERTIES is a +list of command properties as passed to `evil-define-command'." + (let (forms properties) + (dolist (arg args) + (if (not (stringp arg)) + (setq forms (append forms (list arg))) + (setq arg (evil-interactive-string arg) + forms (append forms (cdr (car arg))) + properties (append properties (cdr arg))))) + (cons (apply #'evil-concatenate-interactive-forms forms) + properties))) + +;;; Types + +(defun evil-type (object &optional default) + "Return the type of OBJECT, or DEFAULT if none." + (let (type) + (cond + ((overlayp object) + (setq type (overlay-get object :type))) + ((evil-range-p object) + (setq type (nth 2 object))) + ((listp object) + (setq type (plist-get object :type))) + ((commandp object) + (setq type (evil-get-command-property object :type))) + ((symbolp object) + (setq type (get object 'type)))) + (setq type (or type default)) + (and (evil-type-p type) type))) + +(defun evil-set-type (object type) + "Set the type of OBJECT to TYPE. +For example, (evil-set-type \\='next-line \\='line) +will make `line' the type of the `next-line' command." + (cond + ((overlayp object) + (overlay-put object :type type)) + ((evil-range-p object) + (evil-set-range-type object type)) + ((listp object) + (plist-put object :type type)) + ((commandp object) + (evil-set-command-property object :type type)) + ((symbolp object) + (put object 'type type))) + object) + +(defun evil-type-property (type prop) + "Return property PROP for TYPE." + (plist-get (cdr (assq type evil-type-properties)) prop)) + +(defun evil-type-p (sym) + "Whether SYM is the name of a type." + (assq sym evil-type-properties)) + +(defun evil-expand (beg end type &rest properties) + "Expand BEG and END as TYPE with PROPERTIES. +Return a list (BEG END TYPE PROPERTIES ...), where the tail +may contain a property list." + (apply #'evil-transform + ;; don't expand if already expanded + (unless (plist-get properties :expanded) :expand) + beg end type properties)) + +(defun evil-contract (beg end type &rest properties) + "Contract BEG and END as TYPE with PROPERTIES. +Return a list (BEG END TYPE PROPERTIES ...), where the tail +may contain a property list." + (apply #'evil-transform :contract beg end type properties)) + +(defun evil-normalize (beg end type &rest properties) + "Normalize BEG and END as TYPE with PROPERTIES. +Return a list (BEG END TYPE PROPERTIES ...), where the tail +may contain a property list." + (apply #'evil-transform :normalize beg end type properties)) + +(defun evil-transform (transform beg end type &rest properties) + "Apply TRANSFORM on BEG and END with PROPERTIES. +Return a list (BEG END TYPE PROPERTIES ...), where the tail +may contain a property list. If TRANSFORM is undefined, +return positions unchanged." + (let* ((type (or type (evil-type properties))) + (transform (when (and type transform) + (evil-type-property type transform)))) + (if transform + (apply transform beg end properties) + (apply #'evil-range beg end type properties)))) + +(defun evil-describe (beg end type &rest properties) + "Return description of BEG and END with PROPERTIES. +If no description is available, return the empty string." + (let* ((type (or type (evil-type properties))) + (properties (plist-put properties :type type)) + (describe (evil-type-property type :string))) + (or (when describe + (apply describe beg end properties)) + ""))) + +;;; Ranges + +(defun evil-range (beg end &optional type &rest properties) + "Return a list (BEG END [TYPE] PROPERTIES...). +BEG and END are buffer positions (numbers or markers), +TYPE is a type as per `evil-type-p', and PROPERTIES is +a property list." + (let ((beg (evil-normalize-position beg)) + (end (evil-normalize-position end))) + (when (and (numberp beg) (numberp end)) + (evil-sort beg end) + (nconc (list beg end) + (when (evil-type-p type) (list type)) + properties)))) + +(defun evil-range-p (object) + "Whether OBJECT is a range." + (and (listp object) + (numberp (nth 0 object)) + (numberp (nth 1 object)))) + +(defun evil-range-beginning (range) + "Return beginning of RANGE." + (when (evil-range-p range) + (let ((beg (evil-normalize-position (nth 0 range))) + (end (evil-normalize-position (nth 1 range)))) + (min beg end)))) + +(defun evil-range-end (range) + "Return end of RANGE." + (when (evil-range-p range) + (let ((beg (evil-normalize-position (nth 0 range))) + (end (evil-normalize-position (nth 1 range)))) + (max beg end)))) + +(defun evil-range-properties (range) + "Return properties of RANGE." + (when (evil-range-p range) + (nthcdr (if (evil-type range) 3 2) range))) + +(defalias 'evil-copy-range #'copy-sequence + "Return a copy of RANGE.") + +(defun evil-set-range (range &optional beg end type &rest properties) + "Set RANGE to have beginning BEG and end END. +The TYPE and additional PROPERTIES may also be specified. +If an argument is nil, it's not used; the previous value is retained. +See also `evil-set-range-beginning', `evil-set-range-end', +`evil-set-range-type' and `evil-set-range-properties'." + (when (evil-range-p range) + (let ((beg (or (evil-normalize-position beg) + (evil-range-beginning range))) + (end (or (evil-normalize-position end) + (evil-range-end range))) + (type (or type (evil-type range))) + (plist (evil-range-properties range))) + (evil-sort beg end) + (setq plist (evil-concat-plists plist properties)) + (evil-set-range-beginning range beg) + (evil-set-range-end range end) + (evil-set-range-type range type) + (evil-set-range-properties range plist) + range))) + +(defun evil-set-range-beginning (range beg &optional copy) + "Set RANGE's beginning to BEG. +If COPY is non-nil, return a copy of RANGE." + (when copy + (setq range (evil-copy-range range))) + (setcar range beg) + range) + +(defun evil-set-range-end (range end &optional copy) + "Set RANGE's end to END. +If COPY is non-nil, return a copy of RANGE." + (when copy + (setq range (evil-copy-range range))) + (setcar (cdr range) end) + range) + +(defun evil-set-range-type (range type &optional copy) + "Set RANGE's type to TYPE. +If COPY is non-nil, return a copy of RANGE." + (when copy + (setq range (evil-copy-range range))) + (if type + (setcdr (cdr range) + (cons type (evil-range-properties range))) + (setcdr (cdr range) (evil-range-properties range))) + range) + +(defun evil-set-range-properties (range properties &optional copy) + "Set RANGE's properties to PROPERTIES. +If COPY is non-nil, return a copy of RANGE." + (when copy + (setq range (evil-copy-range range))) + (setcdr (if (evil-type range) (cddr range) (cdr range)) properties) + range) + +(defun evil-range-union (range1 range2 &optional type) + "Return the union of the ranges RANGE1 and RANGE2. +If the ranges have conflicting types, use RANGE1's type. +This can be overridden with TYPE." + (when (and (evil-range-p range1) + (evil-range-p range2)) + (evil-range (min (evil-range-beginning range1) + (evil-range-beginning range2)) + (max (evil-range-end range1) + (evil-range-end range2)) + (or type + (evil-type range1) + (evil-type range2))))) + +(defun evil-subrange-p (range1 range2) + "Whether RANGE1 is contained within RANGE2." + (and (evil-range-p range1) + (evil-range-p range2) + (<= (evil-range-beginning range2) + (evil-range-beginning range1)) + (>= (evil-range-end range2) + (evil-range-end range1)))) + +(defun evil-select-inner-object (thing beg end type &optional count line) + "Return an inner text object range of COUNT objects. +If COUNT is positive, return objects following point; if COUNT is +negative, return objects preceding point. If one is unspecified, +the other is used with a negative argument. THING is a symbol +understood by `thing-at-point'. BEG, END and TYPE specify the +current selection. If LINE is non-nil, the text object should be +linewise, otherwise it is character wise." + (let* ((count (or count 1)) + (bnd (or (let ((b (bounds-of-thing-at-point thing))) + (and b (< (point) (cdr b)) b)) + (evil-bounds-of-not-thing-at-point thing) + (cons (point-min) (point-max))))) + ;; check if current object is selected + (when (or (not beg) (not end) + (> beg (car bnd)) + (< end (cdr bnd)) + (and (eq type 'inclusive) + (= (1+ beg) end))) ; empty region does not count + (when (or (not beg) (< (car bnd) beg)) (setq beg (car bnd))) + (when (or (not end) (> (cdr bnd) end)) (setq end (cdr bnd))) + (setq count (if (> count 0) (1- count) (1+ count)))) + (goto-char (if (< count 0) beg end)) + (evil-forward-nearest count + #'(lambda (cnt) (forward-thing thing cnt)) + #'(lambda (cnt) (evil-forward-not-thing thing cnt))) + (evil-range (if (>= count 0) beg (point)) + (if (< count 0) end (point)) + (if line 'line type) + :expanded t))) + +(defun evil-select-inner-restricted-object (thing beg end type &optional count line) + "Return an inner text object range of COUNT objects. +Selection is restricted to the current line, unless it is empty. +If COUNT is positive, return objects following point; if COUNT is +negative, return objects preceding point. If one is unspecified, +the other is used with a negative argument. THING is a symbol +understood by `thing-at-point'. BEG, END and TYPE specify the +current selection. If LINE is non-nil, the text object should be +linewise, otherwise it is character wise." + (save-restriction + (let ((start (line-beginning-position)) + (end (line-end-position))) + (unless (= start end) + (narrow-to-region start end))) + (evil-select-inner-object thing beg end type count line))) + +(defun evil-select-an-object (thing beg end type count &optional line) + "Return an outer text object range of COUNT objects. +If COUNT is positive, return objects following point; if COUNT is +negative, return objects preceding point. If one is unspecified, +the other is used with a negative argument. THING is a symbol +understood by thing-at-point. BEG, END and TYPE specify the +current selection. If LINE is non-nil, the text object should be +linewise, otherwise it is character wise." + (let* ((dir (if (> (or count 1) 0) +1 -1)) + (count (abs (or count 1))) + (objbnd (let ((b (bounds-of-thing-at-point thing))) + (and b (< (point) (cdr b)) b))) + (bnd (or objbnd + (evil-bounds-of-not-thing-at-point thing) + (cons (point-min) (point-max)))) + addcurrent other) + ;; check if current object is not selected + (when (or (not beg) (not end) + (> beg (car bnd)) + (< end (cdr bnd)) + (and (eq type 'inclusive) + (= (1+ beg) end))) ; empty region does not count + ;; if not, enlarge selection + (when (or (not beg) (< (car bnd) beg)) (setq beg (car bnd))) + (when (or (not end) (> (cdr bnd) end)) (setq end (cdr bnd))) + (if objbnd (setq addcurrent t))) + ;; make other and (point) reflect the selection + (cond + ((> dir 0) (goto-char end) (setq other beg)) + (t (goto-char beg) (setq other end))) + (cond + ;; do nothing more than only current is selected + ((not (and (= beg (car bnd)) (= end (cdr bnd))))) + ;; current match is thing, add whitespace + (objbnd + (let ((wsend (evil-with-restriction + ;; restrict to current line if we do non-line selection + (unless line (line-beginning-position)) + (unless line (line-end-position)) + (evil-bounds-of-not-thing-at-point thing dir)))) + (cond + (wsend + ;; add whitespace at end + (goto-char wsend) + (setq addcurrent t)) + (t + ;; no whitespace at end, try beginning + (save-excursion + (goto-char other) + (setq wsend + (evil-with-restriction + ;; restrict to current line if we do non-line selection + (unless line + (if (member thing '(evil-word evil-WORD)) + (save-excursion (back-to-indentation) (point)) + (line-beginning-position))) + (unless line (line-end-position)) + (evil-bounds-of-not-thing-at-point thing (- dir)))) + (when wsend (setq other wsend addcurrent t))))))) + ;; current match is whitespace, add thing + (t + (forward-thing thing dir) + (setq addcurrent t))) + ;; possibly count current object as selection + (if addcurrent (setq count (1- count))) + ;; move + (dotimes (_ count) + (let ((wsend (evil-bounds-of-not-thing-at-point thing dir))) + (if (and wsend (/= wsend (point))) + ;; start with whitespace + (forward-thing thing dir) + ;; start with thing + (forward-thing thing dir) + (setq wsend (evil-bounds-of-not-thing-at-point thing dir)) + (when wsend (goto-char wsend))))) + ;; return range + (evil-range (if (> dir 0) other (point)) + (if (< dir 0) other (point)) + (if line 'line type) + :expanded t))) + +(defun evil-select-a-restricted-object (thing beg end type &optional count line) + "Return an outer text object range of COUNT objects. +Selection is restricted to the current line, unless it is empty. +If COUNT is positive, return objects following point; if COUNT is +negative, return objects preceding point. If one is unspecified, +the other is used with a negative argument. THING is a symbol +understood by thing-at-point. BEG, END and TYPE specify the +current selection. If LINE is non-nil, the text object should be +linewise, otherwise it is character wise." + (save-restriction + (let ((start (line-beginning-position)) + (end (line-end-position))) + (when (/= start end) + (narrow-to-region start end))) + (evil-select-an-object thing beg end type count line))) + +(defun evil--get-block-range (op cl selection-type) + "Return the exclusive range of a visual selection. +OP and CL are pairs of buffer positions for the opening and +closing delimiter of a range. SELECTION-TYPE is the desired type +of selection. It is a symbol that determines which parts of the +block are selected. If it is `inclusive' or t the returned range +is \(cons (car OP) (cdr CL)). If it is `exclusive' or nil the +returned range is (cons (cdr OP) (car CL)). If it is +`exclusive-line' the returned range will skip whitespace at the +end of the line of OP and at the beginning of the line of CL." + (cond + ((memq selection-type '(inclusive t)) (cons (car op) (cdr cl))) + ((memq selection-type '(exclusive nil)) (cons (cdr op) (car cl))) + ((eq selection-type 'exclusive-line) + (let ((beg (cdr op)) + (end (car cl))) + (save-excursion + (goto-char beg) + (when (and (eolp) (not (eobp))) + (setq beg (line-beginning-position 2))) + (goto-char end) + (skip-chars-backward " \t") + (when (bolp) + (setq end (point)) + (goto-char beg) + (when (and (not (bolp)) (< beg end)) + (setq end (1- end))))) + (cons beg end))) + (t (user-error "Unknown selection-type `%s'" selection-type)))) + +(defun evil-select-block (thing beg end type count + &optional + selection-type + countcurrent + fixedscan) + "Return a range (BEG END) of COUNT delimited text objects. +BEG END TYPE are the currently selected (visual) range. The +delimited object must be given by THING-up function (see +`evil-up-block'). + +SELECTION-TYPE is symbol that determines which parts of the block +are selected. If it is `inclusive' or t OPEN and CLOSE are +included in the range. If it is `exclusive' or nil the delimiters +are not contained. If it is `exclusive-line' the delimiters are +not included as well as adjacent whitespace until the beginning +of the next line or the end of the previous line. If the +resulting selection consists of complete lines only and visual +state is not active, the returned selection is linewise. + +If COUNTCURRENT is non-nil an objected is counted if the current +selection matches that object exactly. + +Usually scanning for the surrounding block starts at (1+ beg) +and (1- end). If this might fail due to the behavior of THING +then FIXEDSCAN can be set to t. In this case the scan starts at +BEG and END. One example where this might fail is if BEG and END +are the delimiters of a string or comment." + (save-excursion + (save-match-data + (let* ((orig-beg beg) + (orig-end end) + (beg (or beg (point))) + (end (or end (point))) + (count (abs (or count 1))) + op cl op-end cl-end) + ;; We always assume at least one selected character. + (if (= beg end) (setq end (1+ end))) + ;; We scan twice: starting at (1+ beg) forward and at (1- end) + ;; backward. The resulting selection is the smaller one. + (goto-char (if fixedscan beg (1+ beg))) + (when (and (zerop (funcall thing +1)) (match-beginning 0)) + (setq cl (cons (match-beginning 0) (match-end 0))) + (goto-char (car cl)) + (when (and (zerop (funcall thing -1)) (match-beginning 0)) + (setq op (cons (match-beginning 0) (match-end 0))))) + ;; start scanning from end + (goto-char (if fixedscan end (1- end))) + (when (and (zerop (funcall thing -1)) (match-beginning 0)) + (setq op-end (cons (match-beginning 0) (match-end 0))) + (goto-char (cdr op-end)) + (when (and (zerop (funcall thing +1)) (match-beginning 0)) + (setq cl-end (cons (match-beginning 0) (match-end 0))))) + ;; Bug #607: use the tightest selection that contains the + ;; original selection. If non selection contains the original, + ;; use the larger one. + (cond + ((and (not op) (not cl-end)) + (error "No surrounding delimiters found")) + ((or (not op) ; first not found + (and cl-end ; second found + (>= (car op-end) (car op)) ; second smaller + (<= (cdr cl-end) (cdr cl)) + (<= (car op-end) beg) ; second contains orig + (>= (cdr cl-end) end))) + (setq op op-end cl cl-end))) + (setq op-end op cl-end cl) ; store copy + ;; if the current selection contains the surrounding + ;; delimiters, they do not count as new selection + (let ((cnt (if (and orig-beg orig-end (not countcurrent)) + (let ((sel (evil--get-block-range op cl selection-type))) + (if (and (<= orig-beg (car sel)) + (>= orig-end (cdr sel))) + count + (1- count))) + (1- count)))) + ;; starting from the innermost surrounding delimiters + ;; increase selection + (when (> cnt 0) + (setq op (progn + (goto-char (car op-end)) + (funcall thing (- cnt)) + (if (match-beginning 0) + (cons (match-beginning 0) (match-end 0)) + op)) + cl (progn + (goto-char (cdr cl-end)) + (funcall thing cnt) + (if (match-beginning 0) + (cons (match-beginning 0) (match-end 0)) + cl))))) + (let ((sel (evil--get-block-range op cl selection-type))) + (setq op (car sel) + cl (cdr sel))) + (cond + ((and (equal op orig-beg) (equal cl orig-end) + (or (not countcurrent) (/= count 1))) + (error "No surrounding delimiters found")) + ((save-excursion + (and (not (evil-visual-state-p)) + (eq type 'inclusive) + (progn (goto-char op) (bolp)) + (progn (goto-char cl) (bolp)))) + (evil-range op cl 'line :expanded t)) + (t (evil-range op cl type :expanded t))))))) + +(defun evil-select-paren (open close beg end type count &optional inclusive) + "Return a range (BEG END) of COUNT delimited text objects. +OPEN and CLOSE specify the opening and closing delimiter, +respectively. BEG END TYPE are the currently selected (visual) +range. If INCLUSIVE is non-nil, OPEN and CLOSE are included in +the range; otherwise they are excluded. + +If you aren't inside a pair of the opening and closing delimiters, +it jumps you inside the next one. If there isn't one, it errors. + +The types of OPEN and CLOSE specify which kind of THING is used +for parsing with `evil-select-block'. If OPEN and CLOSE are +characters `evil-up-paren' is used. Otherwise OPEN and CLOSE +must be regular expressions and `evil-up-block' is used. + +If the selection is exclusive, whitespace at the end or at the +beginning of the selection until the end-of-line or beginning-of-line +is ignored." + (condition-case nil + (progn + ;; we need special linewise exclusive selection + (unless inclusive (setq inclusive 'exclusive-line)) + (cond + ((and (characterp open) (characterp close)) + (let ((thing #'(lambda (&optional cnt) + (evil-up-paren open close cnt))) + (bnd (or (bounds-of-thing-at-point 'evil-string) + (bounds-of-thing-at-point 'evil-comment) + ;; If point is at the opening quote of a string, + ;; this must be handled as if point is within the + ;; string, i.e. the selection must be extended + ;; around the string. Otherwise + ;; `evil-select-block' might do the wrong thing + ;; because it accidentally moves point inside the + ;; string (for inclusive selection) when looking + ;; for the current surrounding block. (re #364) + (and (= (point) (or beg (point))) + (save-excursion + (goto-char (1+ (or beg (point)))) + (or (bounds-of-thing-at-point 'evil-string) + (bounds-of-thing-at-point 'evil-comment))))))) + (if (not bnd) + (evil-select-block thing beg end type count inclusive) + (or (evil-with-restriction (car bnd) (cdr bnd) + (ignore-errors + (evil-select-block thing beg end type count inclusive))) + (save-excursion + (setq beg (or beg (point)) + end (or end (point))) + (goto-char (car bnd)) + (let ((extbeg (min beg (car bnd))) + (extend (max end (cdr bnd)))) + (evil-select-block thing + extbeg extend + type + count + inclusive + (or (< extbeg beg) (> extend end)) + t))))))) + (t + (evil-select-block #'(lambda (&optional cnt) + (evil-up-block open close cnt)) + beg end type count inclusive)))) + (error ; we aren't in the parens, so find next instance + (save-match-data + (goto-char (or (if (and count (> 0 count)) end beg) + (point))) + (let ((re (if (characterp open) (regexp-quote (string open)) open))) + (if (and (not (looking-at-p re)) + (re-search-forward re nil t count)) + (progn + (goto-char (match-beginning 0)) + (let* ((mbeg (match-beginning 0)) + (res (evil-select-paren open close mbeg mbeg + type nil inclusive))) + (if (< (car res) mbeg) + ;; this will error if the beginning of the found parens is before the target paren + ;; this prevents things such as on the line `prova ( verder "((testo)")`, + ;; the inputs `g2ci(` from putting your cursor inside the deleted `()` after `prova` + ;; without this, it would go to the second paren (the unbalanced first paren inside the quotes) + ;; and then do a change there, changing inside the whole paren after `prova` + (error "No surrounding delimiters found") + res))) + (error "No surrounding delimiters found"))))))) + +(defun evil-select-quote-thing (thing beg end _type count &optional inclusive) + "Selection THING as if it described a quoted object. +THING is typically either `evil-quote' or `evil-chars'. This +function is called from `evil-select-quote'." + (save-excursion + (let* ((count (or count 1)) + (dir (if (> count 0) 1 -1)) + (bnd (let ((b (bounds-of-thing-at-point thing))) + (and b (< (point) (cdr b)) b))) + addcurrent + wsboth) + (if inclusive (setq inclusive t) + (when (= (abs count) 2) + (setq count dir) + (setq inclusive 'quote-only)) + ;; never extend with exclusive selection + (setq beg nil end nil)) + ;; check if the previously selected range does not contain a + ;; string + (unless (and beg end + (save-excursion + (goto-char (if (> dir 0) beg end)) + (forward-thing thing dir) + (and (<= beg (point)) (< (point) end)))) + ;; if so forget the range + (setq beg nil end nil)) + ;; check if there is a current object, if not fetch one + (when (not bnd) + (unless (and (zerop (forward-thing thing dir)) + (setq bnd (bounds-of-thing-at-point thing))) + (error "No quoted string found")) + (if (> dir 0) + (setq end (point)) + (setq beg (point))) + (setq addcurrent t)) + ;; check if current object is not selected + (when (or (not beg) (not end) (> beg (car bnd)) (< end (cdr bnd))) + ;; if not, enlarge selection + (when (or (not beg) (< (car bnd) beg)) (setq beg (car bnd))) + (when (or (not end) (> (cdr bnd) end)) (setq end (cdr bnd))) + (setq addcurrent t wsboth t)) + ;; maybe count current element + (when addcurrent + (setq count (if (> dir 0) (1- count) (1+ count)))) + ;; enlarge selection + (goto-char (if (> dir 0) end beg)) + (when (and (not addcurrent) + (= count (forward-thing thing count))) + (error "No quoted string found")) + (if (> dir 0) (setq end (point)) (setq beg (point))) + ;; add whitespace + (cond + ((not inclusive) (setq beg (1+ beg) end (1- end))) + ((not (eq inclusive 'quote-only)) + ;; try to add whitespace in forward direction + (goto-char (if (> dir 0) end beg)) + (if (setq bnd (bounds-of-thing-at-point 'evil-space)) + (if (> dir 0) (setq end (cdr bnd)) (setq beg (car bnd))) + ;; if not found try backward direction + (goto-char (if (> dir 0) beg end)) + (if (and wsboth (setq bnd (bounds-of-thing-at-point 'evil-space))) + (if (> dir 0) (setq beg (car bnd)) (setq end (cdr bnd))))))) + (evil-range beg end + ;; HACK: fixes #583 + ;; When not in visual state, an empty range is + ;; possible. However, this cannot be achieved with + ;; inclusive ranges, hence we use exclusive ranges + ;; in this case. In visual state the range must be + ;; inclusive because otherwise the selection would + ;; be wrong. + (if (evil-visual-state-p) 'inclusive 'exclusive) + :expanded t)))) + +(defun evil-select-quote (quote beg end type count &optional inclusive) + "Return a range (BEG END) of COUNT quoted text objects. +QUOTE specifies the quotation delimiter. BEG END TYPE are the +currently selected (visual) range. + +If INCLUSIVE is nil the previous selection is ignore. If there is +quoted string at point this object will be selected, otherwise +the following (if (> COUNT 0)) or preceeding object (if (< COUNT +0)) is selected. If (/= (abs COUNT) 2) the delimiting quotes are not +contained in the range, otherwise they are contained in the range. + +If INCLUSIVE is non-nil the selection depends on the previous +selection. If the currently selection contains at least one +character that is contained in a quoted string then the selection +is extended, otherwise it is thrown away. If there is a +non-selected object at point then this object is added to the +selection. Otherwise the selection is extended to the +following (if (> COUNT 0)) or preceeding object (if (< COUNT +0)). Any whitespace following (or preceeding if (< COUNT 0)) the +new selection is added to the selection. If no such whitespace +exists and the selection contains only one quoted string then the +preceeding (or following) whitespace is added to the range. " + (let ((evil-forward-quote-char quote)) + (or (let ((bnd (or (bounds-of-thing-at-point 'evil-comment) + (bounds-of-thing-at-point 'evil-string)))) + (when (and bnd (< (point) (cdr bnd)) + (/= (char-after (car bnd)) quote) + (/= (char-before (cdr bnd)) quote)) + (evil-with-restriction (car bnd) (cdr bnd) + (ignore-errors (evil-select-quote-thing + 'evil-quote-simple + beg end type + count + inclusive))))) + (let ((evil-forward-quote-char quote)) + (evil-select-quote-thing 'evil-quote + beg end type + count + inclusive))))) + +(defun evil-select-xml-tag (beg end type &optional count inclusive) + "Return a range (BEG END) of COUNT matching XML tags. +If INCLUSIVE is non-nil, the tags themselves are included +from the range." + (cond + ((and (not inclusive) (= (abs (or count 1)) 1)) + (let ((rng (evil-select-block #'evil-up-xml-tag beg end type count nil t))) + (if (or (and beg (= beg (evil-range-beginning rng)) + end (= end (evil-range-end rng)))) + (evil-select-block #'evil-up-xml-tag beg end type count t) + rng))) + (t + (evil-select-block #'evil-up-xml-tag beg end type count inclusive)))) + +(defun evil-expand-range (range &optional copy) + "Expand RANGE according to its type. +Return a new range if COPY is non-nil." + (when copy + (setq range (evil-copy-range range))) + (unless (plist-get (evil-range-properties range) :expanded) + (setq range (evil-transform-range :expand range))) + range) + +(defun evil-contract-range (range &optional copy) + "Contract RANGE according to its type. +Return a new range if COPY is non-nil." + (evil-transform-range :contract range copy)) + +(defun evil-normalize-range (range &optional copy) + "Normalize RANGE according to its type. +Return a new range if COPY is non-nil." + (evil-transform-range :normalize range copy)) + +(defun evil-transform-range (transform range &optional copy) + "Apply TRANSFORM to RANGE according to its type. +Return a new range if COPY is non-nil." + (when copy + (setq range (evil-copy-range range))) + (when (evil-type range) + (apply #'evil-set-range range + (apply #'evil-transform transform range))) + range) + +(defun evil-describe-range (range) + "Return description of RANGE. +If no description is available, return the empty string." + (apply #'evil-describe range)) + +;;; Undo + +(defvar buffer-undo-tree) +(declare-function undo-tree-current "ext:undo-tree") +(declare-function undo-tree-node-next "ext:undo-tree") +(declare-function undo-tree-node-branch "ext:undo-tree") +(declare-function undo-tree-node-branch "ext:undo-tree") +(declare-function undo-tree-undo "ext:undo-tree") +(declare-function undo-tree-snip-node "ext:undo-tree") + +(defun evil-start-undo-step (&optional continue) + "Start a undo step. +All following buffer modifications are grouped together as a +single action. If CONTINUE is non-nil, preceding modifications +are included. The step is terminated with `evil-end-undo-step'." + (when (and (listp buffer-undo-list) + (not evil-in-single-undo)) + (if evil-undo-list-pointer + (evil-refresh-undo-step) + (unless (or continue (null (car-safe buffer-undo-list))) + (undo-boundary)) + (setq evil-undo-list-pointer (or buffer-undo-list t))))) + +(defun evil-end-undo-step (&optional continue) + "End a undo step started with `evil-start-undo-step'. +Adds an undo boundary unless CONTINUE is specified." + (when (and (listp buffer-undo-list) + evil-undo-list-pointer + (not evil-in-single-undo)) + (evil-refresh-undo-step) + (unless (or continue (null (car-safe buffer-undo-list))) + (undo-boundary)) + (setq evil-undo-list-pointer nil))) + +(defun evil-refresh-undo-step () + "Refresh `buffer-undo-list' entries for current undo step. +Undo boundaries until `evil-undo-list-pointer' are removed to +make the entries undoable as a single action. See +`evil-start-undo-step'." + (when evil-undo-list-pointer + (setq buffer-undo-list + (evil-filter-list #'null buffer-undo-list evil-undo-list-pointer)) + (setq evil-undo-list-pointer (or buffer-undo-list t)))) + +(defmacro evil-with-undo (&rest body) + "Execute BODY with enabled undo. +If undo is disabled in the current buffer, the undo information +is stored in `evil-temporary-undo' instead of `buffer-undo-list'." + (declare (indent defun) (debug t)) + (let ((undo-list (make-symbol "undo-list"))) + `(let ((,undo-list buffer-undo-list) + (evil-undo-system evil-undo-system)) + (when (eq ,undo-list t) (setq buffer-undo-list nil + evil-undo-system nil)) + (unwind-protect + (progn ,@body) + ;; ensure any new undo changes we've accumulated start with + ;; exactly one undo boundary marker, i.e. nil + (when (car-safe buffer-undo-list) (push nil buffer-undo-list)) + (if (eq ,undo-list t) + ;; undo is disabled, so store undo information in + ;; evil-temporary-undo + (setq evil-temporary-undo buffer-undo-list + buffer-undo-list t) + (setq evil-temporary-undo nil)))))) + +(defmacro evil-with-single-undo (&rest body) + "Execute BODY as a single undo step." + (declare (indent defun) (debug t)) + `(let (evil-undo-list-pointer) + (evil-with-undo + (evil-start-undo-step) + (unwind-protect + (let ((evil-in-single-undo t)) ,@body) + (evil-end-undo-step))))) + +(defun evil-undo-pop () + "Undo and forget the last buffer change. +If undo is disabled in the current buffer, use the information +in `evil-temporary-undo' instead." + (if (and (eq evil-undo-system 'undo-tree) + (not (eq buffer-undo-list t))) + (let (current) + (undo-tree-undo) + (setq current (undo-tree-current buffer-undo-tree) + current (nth (undo-tree-node-branch current) + (undo-tree-node-next current))) + ;; Remove only if leaf to not have to adjust child buffer positions + (unless (undo-tree-node-next current) (undo-tree-snip-node current))) + (let ((paste-undo (list nil)) + (undo-list (if (eq buffer-undo-list t) + evil-temporary-undo + buffer-undo-list))) + (when (or (not undo-list) (car undo-list)) + (user-error "Can't undo previous change")) + (while (and undo-list (null (car undo-list))) + (pop undo-list)) ; remove nil + (while (and undo-list (car undo-list)) + (push (pop undo-list) paste-undo)) + (let ((buffer-undo-list (nreverse paste-undo))) + (evil-save-echo-area + (undo))) + (if (eq buffer-undo-list t) + (setq evil-temporary-undo nil) + (setq buffer-undo-list undo-list))))) + +;;; Search +(defun evil-transform-regexp (regexp replacements-alist) + (replace-regexp-in-string + "\\\\+[^\\\\]" + #'(lambda (txt) + (let* ((b (match-beginning 0)) + (e (match-end 0)) + (ch (aref txt (1- e))) + (repl (assoc ch replacements-alist))) + (if (and repl (zerop (mod (length txt) 2))) + (concat (substring txt b (- e 2)) + (cdr repl)) + txt))) + regexp nil t)) + +(defun evil-transform-magic (str magic quote transform &optional _start) + "Transform STR with magic characters. +MAGIC is a regexp that matches all potential magic +characters. Each occurence of CHAR as magic character within str +is replaced by the result of calling the associated TRANSFORM +function. TRANSFORM is a function taking two arguments, the +character to be transformed and the rest of string after the +character. The function should return a triple (REPLACEMENT REST +. STOP) where REPLACEMENT is the replacement and REST is the rest +of the string that has not been transformed. If STOP is non-nil +then the substitution stops immediately. The replacement starts +at position START, everything before that position is returned +literally. The result is a pair (RESULT . REST). RESULT is a +list containing the transformed parts in order. If two +subsequents parts are both strings, they are concatenated. REST +is the untransformed rest string (usually \"\" but may be more if +TRANSFORM stopped the substitution). Which characters are +considered as magic characters (i.e. the transformation happens +if the character is NOT preceeded by a backslash) is determined +by `evil-magic'. The special tokens \\v, \\V, \\m and \\M have +always a special meaning (like in Vim) and should not be +contained in TRANSFORMS, otherwise their meaning is overwritten. + +The parameter QUOTE is a quoting function applied to literal +transformations, usually `regexp-quote' or `replace-quote'." + (save-match-data + (let ((regexp (concat "\\(?:\\`\\|[^\\]\\)\\(\\\\\\(?:\\(" magic "\\)\\|\\(.\\)\\)\\|\\(" magic "\\)\\)")) + (magic-chars (evil-get-magic evil-magic)) + (evil-magic evil-magic) + (quote (or quote #'identity)) + result stop) + (while (and (not stop) str (string-match regexp str)) + (unless (zerop (match-beginning 1)) + (push (substring str 0 (match-beginning 1)) result)) + (let ((char (or (match-string 2 str) + (match-string 3 str) + (match-string 4 str))) + (rest (substring str (match-end 0)))) + (cond + ((match-beginning 4) + ;; magic character without backslash + (if (string-match magic-chars char) + ;; magic, do transform + (let ((trans (funcall transform (aref char 0) rest))) + (push (car trans) result) + (setq str (cadr trans) stop (nthcdr 2 trans))) + ;; non-magic, literal transformation + (push (funcall quote char) result) + (setq str rest))) + ((match-beginning 2) + ;; magic character with backslash + (if (not (string-match magic-chars char)) + ;; non-magic, do transform + (let ((trans (funcall transform (aref char 0) rest))) + (push (car trans) result) + (setq str (cadr trans) stop (nthcdr 2 trans))) + ;; magic, literal transformation + (push (funcall quote char) result) + (setq str rest))) + ((memq (aref char 0) '(?m ?M ?v ?V)) + (setq evil-magic (cdr (assq (aref char 0) + '((?m . t) + (?M . nil) + (?v . very-magic) + (?V . very-nomagic))))) + (setq magic-chars (evil-get-magic evil-magic)) + (setq str rest)) + (t + ;; non-magic char with backslash, literal transformation + (push (funcall quote char) result) + (setq str rest))))) + (cond + ((and str (not stop)) + (push str result) + (setq str "")) + ((not str) + (setq str ""))) + ;; concatenate subsequent strings + ;; note that result is in reverse order + (let (repl) + (while result + (cond + ((and (stringp (car result)) + (zerop (length (car result)))) + (pop result)) + ((and (stringp (car result)) + (stringp (cadr result))) + (setq result (cons (concat (cadr result) + (car result)) + (nthcdr 2 result)))) + (t + (push (pop result) repl)))) + (cons repl str))))) + +(defconst evil-vim-regexp-replacements + '((?n . "\n") (?r . "\r") + (?t . "\t") (?b . "\b") + (?s . "[[:space:]]") (?S . "[^[:space:]]") + (?d . "[[:digit:]]") (?D . "[^[:digit:]]") + (?x . "[[:xdigit:]]") (?X . "[^[:xdigit:]]") + (?o . "[0-7]") (?O . "[^0-7]") + (?a . "[[:alpha:]]") (?A . "[^[:alpha:]]") + (?l . "[a-z]") (?L . "[^a-z]") + (?u . "[A-Z]") (?U . "[^A-Z]") + (?y . "\\s") (?Y . "\\S") + (?\( . "\\(") (?\) . "\\)") + (?{ . "\\{") (?} . "\\}") + (?\[ . "[") (?\] . "]") + (?< . "\\<") (?> . "\\>") + (?_ . "\\_") + (?* . "*") (?+ . "+") + (?? . "?") (?= . "?") + (?. . ".") + (?` . "`") (?^ . "^") + (?$ . "$") (?| . "\\|"))) + +(defconst evil-regexp-magic "[][(){}<>_dDsSxXoOaAlLuUwWyY.*+?=^$`|nrtb0-9]") + +(defun evil-transform-vim-style-regexp (regexp) + "Transform vim-style backslash codes to Emacs regexp. +This includes the backslash codes \\d, \\D, \\s, \\S, \\x, \\X, +\\o, \\O, \\a, \\A, \\l, \\L, \\u, \\U and \\w, \\W. The new +codes \\y and \\Y can be used instead of the Emacs code \\s and +\\S which have a different meaning in Vim-style." + (car + (car + (evil-transform-magic + regexp evil-regexp-magic #'regexp-quote + #'(lambda (char rest) + (let ((repl (assoc char evil-vim-regexp-replacements))) + (if repl + (list (cdr repl) rest) + (list (concat "\\" (char-to-string char)) rest)))))))) + +;;; Substitute + +(defun evil-downcase-first (str) + "Return STR with the first letter downcased." + (if (zerop (length str)) + str + (concat (downcase (substring str 0 1)) + (substring str 1)))) + +(defun evil-upcase-first (str) + "Return STR with the first letter upcased." + (if (zerop (length str)) + str + (concat (upcase (substring str 0 1)) + (substring str 1)))) + +(defun evil-get-magic (magic) + "Return a regexp matching the magic characters according to MAGIC. +Depending on the value of MAGIC the following characters are +considered magic. + t [][{}*+?.&~$^ + nil [][{}*+?$^ + `very-magic' not 0-9A-Za-z_ + `very-nomagic' empty." + (cond + ((eq magic t) "[][}{*+?.&~$^]") + ((eq magic 'very-magic) "[^0-9A-Za-z_]") + ((eq magic 'very-nomagic) "\\\\") + (t "[][}{*+?$^]"))) + +;; TODO: support magic characters in patterns +(defconst evil-replacement-magic "[eElLuU0-9&#,rnbt=]" + "All magic characters in a replacement string") + +(defun evil-compile-subreplacement (to &optional start) + "Convert a regexp replacement TO to Lisp from START until \\e or \\E. +Return a pair (RESULT . REST). RESULT is a list suitable for +`perform-replace' if necessary, the original string if not. +REST is the unparsed remainder of TO." + (let ((result + (evil-transform-magic + to evil-replacement-magic #'replace-quote + #'(lambda (char rest) + (cond + ((eq char ?#) + (list '(number-to-string replace-count) rest)) + ((eq char ?r) (list "\r" rest)) + ((eq char ?n) (list "\n" rest)) + ((eq char ?b) (list "\b" rest)) + ((eq char ?t) (list "\t" rest)) + ((memq char '(?e ?E)) + `("" ,rest . t)) + ((memq char '(?l ?L ?u ?U)) + (let ((result (evil-compile-subreplacement rest)) + (func (cdr (assoc char + '((?l . evil-downcase-first) + (?L . downcase) + (?u . evil-upcase-first) + (?U . upcase)))))) + (list `(,func + (replace-quote + (evil-match-substitute-replacement + ,(car result) + (not case-replace)))) + (cdr result)))) + ((eq char ?=) + (when (or (zerop (length rest)) + (not (eq (aref rest 0) ?@))) + (user-error "Expected @ after \\=")) + (when (< (length rest) 2) + (user-error "Expected register after \\=@")) + (list (evil-get-register (aref rest 1)) + (substring rest 2))) + ((eq char ?,) + (let* ((obj (read-from-string rest)) + (result `(replace-quote ,(car obj))) + (end + ;; swallow a space after a symbol + (if (and (or (symbolp (car obj)) + ;; swallow a space after 'foo, + ;; but not after (quote foo) + (and (eq (car-safe (car obj)) 'quote) + (not (= ?\( (aref rest 0))))) + (eq (string-match " " rest (cdr obj)) + (cdr obj))) + (1+ (cdr obj)) + (cdr obj)))) + (list result (substring rest end)))) + ((eq char ?0) + (list "\\&" rest)) + (t + (list (concat "\\" (char-to-string char)) rest)))) + start))) + (let ((rest (cdr result)) + (result (car result))) + (replace-match-string-symbols result) + (cons (if (cdr result) + (cons 'concat result) + (or (car result) "")) + rest)))) + +(defun evil-compile-replacement (to) + "Maybe convert a regexp replacement TO to Lisp. +Return a list suitable for `perform-replace' if necessary, the +original string if not. Currently the following magic characters +in replacements are supported: 0-9&#lLuUrnbt, +The magic character , (comma) start an Emacs-lisp expression." + (when (stringp to) + (save-match-data + (cons 'replace-eval-replacement + (car (evil-compile-subreplacement to)))))) + +(defun evil-match-substitute-replacement (replacement &optional fixedcase string) + "Return REPLACEMENT as it will be inserted by `evil-replace-match'. +If REPLACEMENT is an expression it will be evaluated to compute the +replacement text first." + (match-substitute-replacement + (if (stringp replacement) + replacement + (funcall (car replacement) + (cdr replacement) + 0)) + fixedcase nil string)) + +;;; Alignment + +(defun evil-justify-lines (beg end justify position) + "Justify all lines in a range. +BEG and END specify the range of those lines to be +justified. JUSTIFY is either `left', `right' or `center' according +to the justification type. POSITION is the maximal text width for +right and center justification or the column at which the lines +should be left-aligned for left justification." + (let ((fill-column position) + adaptive-fill-mode fill-prefix) + (evil-with-restriction + (save-excursion (goto-char beg) (line-beginning-position)) + (save-excursion (goto-char end) (line-end-position (if (bolp) 0 1))) + (goto-char (point-min)) + (while (progn + (if (eq justify 'left) + (indent-line-to position) + (when (re-search-forward "^[[:space:]]*" nil t) + (delete-region (match-beginning 0) + (match-end 0))) + (justify-current-line justify nil t)) + (and (zerop (forward-line)) (bolp)))) + (goto-char (point-min)) + (back-to-indentation)))) + +;;; View helper + +(defvar-local evil-list-view-select-action nil) + +(define-derived-mode evil-list-view-mode tabulated-list-mode + "Evil List View" + (tabulated-list-init-header) + (tabulated-list-print)) + +(defun evil-list-view-goto-entry () + (interactive) + (when (and evil-list-view-select-action + (not (eobp))) + (let* ((line (line-number-at-pos (point))) + (entry (elt tabulated-list-entries (1- line)))) + (funcall evil-list-view-select-action (nth 1 entry))))) + +(defun evil-list-view-quit () + (interactive) + (quit-window 'kill)) + +(define-key evil-list-view-mode-map (kbd "q") #'evil-list-view-quit) +(define-key evil-list-view-mode-map [follow-link] nil) ;; allows mouse-1 to be activated +(define-key evil-list-view-mode-map [mouse-1] #'evil-list-view-goto-entry) +(define-key evil-list-view-mode-map [return] #'evil-list-view-goto-entry) + +(defmacro evil-with-view-list (&rest properties) + "Open a new list view buffer. +PROPERTIES is a property-list which supports the following properties: + +:name (required) The name of the buffer. +:mode-name (required) The name for the mode line. +:format (required) The value for `tabulated-list-format'. +:entries (required) The value for `tabulated-list-entries'. +:select-action (optional) A function for row selection. + It takes a single parameter, which is the + selected row's vector value that is + passed into `:entries'." + (declare (indent defun) (debug t)) + `(let ((bufname (concat "*" ,(plist-get properties :name) "*")) + (inhibit-read-only t)) + (and (get-buffer bufname) + (kill-buffer bufname)) + (let ((buf (get-buffer-create bufname))) + (with-current-buffer buf + (setq tabulated-list-format ,(plist-get properties :format)) + (setq tabulated-list-entries ,(plist-get properties :entries)) + (setq evil-list-view-select-action ,(plist-get properties :select-action)) + (evil-list-view-mode) + (setq mode-name ,(plist-get properties :mode-name)) + (evil-motion-state)) + (switch-to-buffer-other-window buf)))) + +(provide 'evil-common) + +;;; evil-common.el ends here diff --git a/.config/emacs/lisp/evil/evil-core.el b/.config/emacs/lisp/evil/evil-core.el new file mode 100644 index 0000000..ae3f949 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-core.el @@ -0,0 +1,1322 @@ +;;; evil-core.el --- Core functionality -*- lexical-binding: t -*- +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; Evil is defined as a globalized minor mode, enabled with the toggle +;; function `evil-mode'. This in turn enables `evil-local-mode' in +;; every buffer, which sets up the buffer's state. +;; +;; Each state has its own keymaps, and these keymaps have status as +;; "emulation keymaps" with priority over regular keymaps. Emacs +;; maintains the following keymap hierarchy (highest priority first): +;; +;; * Overriding keymaps/overlay keymaps... +;; * Emulation mode keymaps... +;; - Evil keymaps... +;; * Minor mode keymaps... +;; * Local keymap (`local-set-key') +;; * Global keymap (`global-set-key') +;; +;; Within this hierarchy, Evil arranges the keymaps for the current +;; state as shown below: +;; +;; * Intercept keymaps... +;; * Local state keymap +;; * Minor-mode keymaps... +;; * Auxiliary keymaps... +;; * Overriding keymaps... +;; * Global state keymap +;; * Keymaps for other states... +;; +;; These keymaps are listed in `evil-mode-map-alist', which is listed +;; in `emulation-mode-map-alist'. +;; +;; Most of the key bindings for a state are stored in its global +;; keymap, which has a name such as `evil-normal-state-map'. (See the +;; file evil-maps.el, which contains all the default key bindings.) A +;; state also has a local keymap (`evil-normal-state-local-map'), +;; which may contain user customizations for the current buffer. +;; Furthermore, any Emacs mode may be assigned state bindings of its +;; own by passing the mode's keymap to the function `evil-define-key' +;; or `evil-define-minor-mode-key'. The former uses a specific map to +;; define the key in while the latter associates the key with a +;; particular mode. These mode-specific bindings are ultimately stored +;; in so-called auxiliary and minor-mode keymaps respectively, which +;; are sandwiched between the local keymap and the global keymap. +;; Finally, the state may also activate the keymaps of other states +;; (e.g., Normal state inherits bindings from Motion state). +;; +;; For integration purposes, a regular Emacs keymap may be "elevated" +;; to emulation status by passing it to `evil-make-intercept-map' or +;; `evil-make-overriding-map'. An "intercept" keymap has priority over +;; all other Evil keymaps. (Evil uses this facility when debugging and +;; for handling the "ESC" key in the terminal.) More common is the +;; "overriding" keymap, which only has priority over the global state +;; keymap. (This is useful for adapting key-heavy modes such as Dired, +;; where all but a few keys should be left as-is and should not be +;; shadowed by Evil's default bindings.) +;; +;; States are defined with the macro `evil-define-state', which +;; creates a command for switching to the state. This command, +;; for example `evil-normal-state' for Normal state, performs +;; the following tasks: +;; +;; * Setting `evil-state' to the new state. +;; * Refreshing the keymaps in `evil-mode-map-alist'. +;; * Updating the mode line. +;; - Normal state depends on `evil-normal-state-tag'. +;; * Adjusting the cursor's appearance. +;; - Normal state depends on `evil-normal-state-cursor'. +;; * Displaying a message in the echo area. +;; - Normal state depends on `evil-normal-state-message'. +;; * Running hooks. +;; - Normal state runs `evil-normal-state-entry-hook' when +;; entering, and `evil-normal-state-exit-hook' when exiting. +;; +;; The various properties of a state can be accessed through their +;; respective variables, or by passing a keyword and the state's name +;; to the `evil-state-property' function. Evil defines the states +;; Normal state ("normal"), Insert state ("insert"), Visual state +;; ("visual"), Replace state ("replace"), Operator-Pending state +;; ("operator"), Motion state ("motion") and Emacs state ("emacs"). + +;;; Code: + +(require 'evil-common) + +(declare-function evil-emacs-state-p "evil-states") +(declare-function evil-ex-p "evil-ex") + +(define-minor-mode evil-local-mode + "Minor mode for setting up Evil in a single buffer." + :init-value nil + (if evil-local-mode + (progn + (cl-pushnew 'evil-mode-map-alist emulation-mode-map-alists) + (evil-initialize-local-keymaps) + (when (minibufferp) + (setq-local evil-default-state 'insert) + (setq-local evil-echo-state nil)) + (setq evil-input-method current-input-method) + (evil-initialize-state) + (add-hook 'input-method-activate-hook #'evil-activate-input-method t t) + (add-hook 'input-method-deactivate-hook #'evil-deactivate-input-method t t) + (add-hook 'activate-mark-hook 'evil-visual-activate-hook nil t) + ;; FIXME: Add these hooks buffer-locally and remove when disabling + (add-hook 'pre-command-hook 'evil-repeat-pre-hook) + (add-hook 'post-command-hook 'evil-repeat-post-hook)) + (evil-refresh-mode-line) + (remove-hook 'activate-mark-hook 'evil-visual-activate-hook t) + (remove-hook 'input-method-activate-hook #'evil-activate-input-method t) + (remove-hook 'input-method-deactivate-hook #'evil-deactivate-input-method t) + (activate-input-method evil-input-method) + (evil-change-state nil))) + +;; Make the variable permanent local. This is particular useful in +;; conjunction with nXhtml/mumamo because mumamo does not touch these +;; variables. +(put 'evil-local-mode 'permanent-local t) + +(defun turn-on-evil-mode () + "Turn on Evil in the current buffer." + (interactive) + (evil-local-mode)) + +(defun turn-off-evil-mode () + "Turn off Evil in the current buffer." + (interactive) + (evil-local-mode -1)) + +;; The function `evil-initialize' should only be used to initialize +;; `evil-local-mode' from the globalized minor-mode `evil-mode'. It is +;; called whenever evil is enabled in a buffer for the first time or +;; when evil is active and the major-mode of the buffer changes. +(defun evil-initialize () + "Enable Evil in the current buffer, if appropriate. +To enable Evil globally, do (evil-mode)." + (if evil-local-mode + ;; Set Evil state according to new major-mode + (evil-initialize-state) + (or (and (minibufferp) (not evil-want-minibuffer)) + (evil-disabled-buffer-p) + (evil-local-mode)))) + +(defalias 'evil--fundamental-mode #'fundamental-mode) + +(defvar evil--advices ()) + +;;;###autoload (autoload 'evil-mode "evil" nil t) +(define-globalized-minor-mode evil-mode evil-local-mode evil-initialize + :group 'evil) + +;; `define-globalized-minor-mode' supports a BODY argument but only since +;; GNU Emacs 27.1, so resort to this ugly advice in the mean time. +(define-advice evil-mode (:after (&optional _arg) body) + ;; Hooks used to not run in Fundamental buffers (bug#23827), so + ;; other measures are necessary to initialize Evil there. When Evil + ;; is enabled globally, the default value of `major-mode' is set to + ;; the `evil--fundamental-mode' alias, sidestepping the restriction. + (if evil-mode + (progn + (and (eval-when-compile (version< emacs-version "26.1")) + (eq (default-value 'major-mode) 'fundamental-mode) + (setq-default major-mode #'evil--fundamental-mode)) + (dolist (advice evil--advices) (apply #'advice-add advice)) + (with-no-warnings (evil-esc-mode 1))) + (when (eq (default-value 'major-mode) #'evil--fundamental-mode) + (setq-default major-mode 'fundamental-mode)) + (pcase-dolist (`(,funname ,_where ,adfun) evil--advices) + (advice-remove funname adfun)) + (with-no-warnings (evil-esc-mode -1)))) + +(defun evil--advice-add (&rest args) + "Like `advice-add' for advices active only in `evil-mode'." + (when evil-mode (apply #'advice-add args)) + (cl-pushnew args evil--advices :test #'equal)) + +(defun evil-change-state (state &optional message) + "Change the state to STATE. +If STATE is nil, disable all states." + (let ((func (evil-state-property (or state evil-state) :toggle))) + (when (and (functionp func) + (or message (not (eq state evil-state)))) + (funcall func (if state (and message 1) -1))))) + +(defmacro evil-save-state (&rest body) + "Save the current state; execute BODY; restore the state." + (declare (indent defun) + (debug t)) + `(let ((evil-state evil-state) + (evil-previous-state evil-previous-state) + (evil-previous-state-alist (copy-tree evil-previous-state-alist)) + (evil-next-state evil-next-state) + (old-state evil-state) + (inhibit-quit t) + (buf (current-buffer))) + (unwind-protect + (progn ,@body) + (when (buffer-live-p buf) + (with-current-buffer buf + (evil-change-state old-state)))))) + +(defmacro evil-with-state (state &rest body) + "Change to STATE and execute BODY without refreshing the display. +Restore the previous state afterwards." + (declare (indent defun) + (debug t)) + `(evil-without-display + (evil-save-state + (evil-change-state ',state) + ,@body))) + +(defun evil-initialize-state () + "Set up the initial state for the current buffer. +See also `evil-set-initial-state'." + (evil-change-state (evil-initial-state-for-buffer))) + +(defun evil-initial-state-for-buffer-name (&optional name default) + "Return the initial Evil state to use for a buffer with name NAME. +Matches the name against the regular expressions in +`evil-buffer-regexps'. If none matches, returns DEFAULT." + (let ((name (if (stringp name) name (buffer-name name)))) + (when name + (catch 'done + (dolist (entry evil-buffer-regexps default) + (let ((regexp (car entry)) + (state (cdr entry))) + (when (string-match-p regexp name) + (throw 'done state)))))))) + +(defun evil-disabled-buffer-p (&optional buffer) + "Whether Evil should be disabled in BUFFER." + (null (evil-initial-state-for-buffer-name buffer 'undefined))) + +(defun evil-initial-state-for-buffer (&optional buffer) + "Return the initial Evil state to use for BUFFER. +BUFFER defaults to the current buffer. See also `evil-initial-state'." + (with-current-buffer (or buffer (current-buffer)) + (or (evil-initial-state-for-buffer-name) + (cl-loop for (mode) in minor-mode-map-alist + when (and (boundp mode) (symbol-value mode)) + thereis (evil-initial-state mode)) + (evil-initial-state major-mode nil t) + evil-default-state))) + +(defun evil-initial-state (mode &optional default follow-parent checked-modes) + "Return the Evil state to use for MODE or its alias. +Return DEFAULT if no initial state is associated with MODE. +The initial state for a mode can be set with +`evil-set-initial-state'. + +If FOLLOW-PARENT is non-nil, also check parent modes of MODE and +its alias. CHECKED-MODES is used internally and should not be set +initially." + (when (memq mode checked-modes) + (error "Circular reference detected in ancestors of `%s'\n%s" + major-mode checked-modes)) + (let ((mode-alias (let ((func (symbol-function mode))) + (when (symbolp func) func)))) + (or (cl-dolist (entry (evil-state-property t :modes) default) + (let ((state (car entry)) + (modes (symbol-value (cdr entry)))) + (when (or (memq mode modes) + (and mode-alias (memq mode-alias modes))) + (cl-return state)))) + (and follow-parent (get mode 'derived-mode-parent) + (evil-initial-state (get mode 'derived-mode-parent) + nil t (cons mode checked-modes))) + (and follow-parent mode-alias + (get mode-alias 'derived-mode-parent) + (evil-initial-state (get mode-alias 'derived-mode-parent) + nil t (cons mode-alias checked-modes)))))) + +(defun evil-set-initial-state (mode state) + "Set the initial state for major mode MODE to STATE. +This is the state the buffer comes up in." + (dolist (modes (evil-state-property t :modes)) + (setq modes (cdr-safe modes)) + (set modes (delq mode (symbol-value modes)))) + (when state + (add-to-list (evil-state-property state :modes) mode))) + +(evil-define-command evil-change-to-initial-state + (&optional buffer message) + "Change the state of BUFFER to its initial state. +This is the state the buffer came up in. If Evil is not activated +then this function does nothing." + :keep-visual t + :suppress-operator t + (when evil-local-mode + (with-current-buffer (or buffer (current-buffer)) + (evil-change-state (evil-initial-state-for-buffer buffer) + message)))) + +(evil-define-command evil-change-to-previous-state + (&optional buffer message) + "Change the state of BUFFER to its previous state." + :keep-visual t + :repeat abort + :suppress-operator t + (with-current-buffer (or buffer (current-buffer)) + (let ((prev-state evil-previous-state) + (prev-prev-state (cdr-safe (assoc evil-previous-state + evil-previous-state-alist)))) + (evil-change-state nil) + (when prev-prev-state + (setq evil-previous-state prev-prev-state)) + (evil-change-state (or prev-state evil-default-state 'normal) + message)))) + +;; When a buffer is created in a low-level way, it is invisible to +;; Evil (as well as other globalized minor modes) because no hooks are +;; run. This is appropriate since many buffers are used for throwaway +;; purposes. Passing the buffer to `set-window-buffer' indicates +;; otherwise, though, so advise this function to initialize Evil. +(evil--advice-add 'set-window-buffer :before #'evil--swb-initialize) +(defun evil--swb-initialize (_window buffer &rest _) + "Initialize Evil in the displayed buffer." + (when (and evil-mode (get-buffer buffer)) + (with-current-buffer buffer + (unless evil-local-mode + (save-match-data (evil-initialize)))))) + +;; Refresh cursor color. +;; Cursor color can only be set for each frame but not for each buffer. +;; FIXME: Shouldn't this belong in `evil-(local-)mode'? +(add-hook 'window-configuration-change-hook #'evil-refresh-cursor) +(advice-add 'select-window :after #'evil--sw-refresh-cursor) +(defun evil--sw-refresh-cursor (&rest _) + (evil-refresh-cursor)) + +(defun evil-generate-mode-line-tag (&optional state) + "Generate the evil mode-line tag for STATE." + (let ((tag (evil-state-property state :tag t))) + (when (functionp tag) + (setq tag (funcall tag))) + ;; prepare mode-line: add tooltip + (if (stringp tag) + (propertize tag + 'help-echo (evil-state-property state :name) + 'mouse-face 'mode-line-highlight) + tag))) + +(defun evil-refresh-mode-line (&optional state) + "Refresh mode line tag." + (when (listp mode-line-format) + (setq evil-mode-line-tag (evil-generate-mode-line-tag state)) + ;; refresh mode line data structure + ;; first remove evil from mode-line + (setq mode-line-format (delq 'evil-mode-line-tag mode-line-format)) + (let ((mlpos mode-line-format) + pred which where) + ;; determine before/after which symbol the tag should be placed + (cond + ((eq evil-mode-line-format 'before) + (setq where 'after which 'mode-line-position)) + ((eq evil-mode-line-format 'after) + (setq where 'after which 'mode-line-modes)) + ((consp evil-mode-line-format) + (setq where (car evil-mode-line-format) + which (cdr evil-mode-line-format)))) + ;; find the cons-cell of the symbol before/after which the tag + ;; should be placed + (while (and mlpos + (let ((sym (or (car-safe (car mlpos)) (car mlpos)))) + (not (eq which sym)))) + (setq pred mlpos + mlpos (cdr mlpos))) + ;; put evil tag at the right position in the mode line + (cond + ((not mlpos)) ;; position not found, so do not add the tag + ((eq where 'before) + (if pred + (setcdr pred (cons 'evil-mode-line-tag mlpos)) + (setq mode-line-format + (cons 'evil-mode-line-tag mode-line-format)))) + ((eq where 'after) + (setcdr mlpos (cons 'evil-mode-line-tag (cdr mlpos))))) + (force-mode-line-update)))) + +;; input methods should be disabled in non-insertion states +(defun evil-activate-input-method () + "Enable input method in states with :input-method non-nil." + (let (input-method-activate-hook + input-method-deactivate-hook) + (when (and evil-local-mode evil-state) + (setq evil-input-method current-input-method) + (unless (evil-state-property evil-state :input-method) + (deactivate-input-method))))) +(put 'evil-activate-input-method 'permanent-local-hook t) + +(defun evil-deactivate-input-method () + "Disable input method in all states." + (let (input-method-activate-hook + input-method-deactivate-hook) + (when (and evil-local-mode evil-state) + (setq evil-input-method nil)))) +(put 'evil-deactivate-input-method 'permanent-local-hook t) + +(defmacro evil-without-input-method-hooks (&rest body) + "Execute body with evil's activate/deactivate-input-method hooks deactivated. +This allows input methods to be used in normal-state." + (declare (indent defun)) + `(unwind-protect + (progn + (remove-hook 'input-method-activate-hook #'evil-activate-input-method t) + (remove-hook 'input-method-deactivate-hook #'evil-deactivate-input-method t) + ,@body) + (add-hook 'input-method-activate-hook #'evil-activate-input-method nil t) + (add-hook 'input-method-deactivate-hook #'evil-deactivate-input-method nil t))) + +(evil--advice-add 'toggle-input-method :around #'evil--refresh-input-method) +(defun evil--refresh-input-method (orig-fun &rest args) + "Refresh `evil-input-method'." + (cond + ((not evil-local-mode) + (apply orig-fun args)) + ((evil-state-property evil-state :input-method) + (apply orig-fun args)) + (t + (let ((current-input-method evil-input-method)) + (apply orig-fun args))))) + +;; Local keymaps are implemented using buffer-local variables. +;; However, unless a buffer-local value already exists, +;; `define-key' acts on the variable's default (global) value. +;; So we need to initialize the variable whenever we enter a +;; new buffer or when the buffer-local values are reset. +(defun evil-initialize-local-keymaps () + "Initialize a buffer-local value for local keymaps as necessary. +The initial value is that of `make-sparse-keymap'." + (dolist (entry evil-local-keymaps-alist) + (let ((map (cdr entry))) + (unless (and (keymapp (symbol-value map)) + (local-variable-p map)) + (set map (make-sparse-keymap)))))) + +(defun evil-make-overriding-map (keymap &optional state copy) + "Give KEYMAP precedence over the global keymap of STATE. +The keymap will have lower precedence than custom STATE bindings. +If STATE is nil, give it precedence over all states. +If COPY is t, create a copy of KEYMAP and give that +higher precedence. See also `evil-make-intercept-map'." + (let ((key [override-state])) + (if (not copy) + (define-key keymap key (or state 'all)) + (unless (keymapp copy) + (setq copy (assq-delete-all 'menu-bar (copy-keymap keymap)))) + (define-key copy key (or state 'all)) + (define-key keymap key copy)))) + +(defun evil-make-intercept-map (keymap &optional state aux) + "Give KEYMAP precedence over all Evil keymaps in STATE. +If STATE is nil, give it precedence over all states. If AUX is non-nil, make the +auxiliary keymap corresponding to KEYMAP in STATE an intercept keymap instead of +KEYMAP itself. See also `evil-make-overriding-map'." + (let ((key [intercept-state]) + (keymap (if aux + (evil-get-auxiliary-keymap keymap state t t) + keymap))) + (define-key keymap key (or state 'all)))) + +(defmacro evil-define-keymap (keymap doc &rest body) + "Define a keymap KEYMAP listed in `evil-mode-map-alist'. +That means it will have precedence over regular keymaps. + +DOC is the documentation for the variable. BODY, if specified, +is executed after toggling the mode. Optional keyword arguments +may be specified before the body code: + +:mode VAR Mode variable. If unspecified, the variable + is based on the keymap name. +:local BOOLEAN Whether the keymap should be buffer-local, that is, + reinitialized for each buffer. +:func BOOLEAN Create a toggle function even if BODY is empty. + +\(fn KEYMAP DOC [[KEY VAL]...] BODY...)" + (declare (indent defun) + (doc-string 2) + (debug (&define name + [&optional stringp] + [&rest [keywordp sexp]] + def-body))) + (let ((func t) + arg intercept key local mode overriding) + (while (keywordp (car-safe body)) + (setq key (pop body) + arg (pop body)) + (cond + ((eq key :mode) (setq mode arg)) + ((eq key :local) (setq local arg)) + ((eq key :func) (setq func arg)) + ((eq key :intercept) (setq intercept arg)) + ((eq key :overriding) (setq overriding arg)))) + (setq mode (or mode + (intern (replace-regexp-in-string + "\\(?:-\\(?:mode-\\)?\\(?:key\\)?map\\)?$" + "-mode" + (symbol-name keymap))))) + `(progn + (defvar ,keymap ,(unless local '(make-sparse-keymap))) + (unless (get ',keymap 'variable-documentation) + (put ',keymap 'variable-documentation ,doc)) + (defvar ,mode nil) + (unless (get ',mode 'variable-documentation) + (put ',mode 'variable-documentation ,doc)) + (make-variable-buffer-local ',mode) + (put ',mode 'permanent-local t) + (when ,intercept + (evil-make-intercept-map ,keymap)) + (when ,overriding + (evil-make-overriding-map ,keymap)) + ,@(if local + `((make-variable-buffer-local ',keymap) + (put ',keymap 'permanent-local t) + (evil--add-to-alist evil-local-keymaps-alist ',mode ',keymap)) + `((evil--add-to-alist evil-global-keymaps-alist ',mode ',keymap) + (evil--add-to-alist evil-mode-map-alist ',mode ,keymap))) + ,(when (or body func) + `(defun ,mode (&optional arg) + ,@(when doc `(,doc)) + (interactive) + (cond + ((numberp arg) (setq ,mode (> arg 0))) + (t (setq ,mode (not ,mode)))) + ,@body)) + ',keymap))) + +;; The ESC -> escape translation code has been provided by Stefan +;; Monnier in the discussion of GNU Emacs bug #13793. +(defun evil-esc-mode (&optional arg) + "Toggle interception of \\e (escape). +Enable with positive ARG and disable with negative ARG. + +When enabled, `evil-esc-mode' modifies the entry of \\e in +`input-decode-map'. If such an event arrives, it is translated to +a plain `escape' event if no further event occurs within +`evil-esc-delay' seconds. Otherwise no translation happens and +the ESC prefix map (i.e. the map originally bound to \\e in +`input-decode-map`) is returned." + (cond + ((or (null arg) (eq arg 0)) + (evil-esc-mode (if evil-esc-mode -1 +1))) + ((> arg 0) + (unless evil-esc-mode + (setq evil-esc-mode t) + (add-hook 'after-make-frame-functions #'evil-init-esc) + (mapc #'evil-init-esc (frame-list)))) + ((< arg 0) + (when evil-esc-mode + (remove-hook 'after-make-frame-functions #'evil-init-esc) + (mapc #'evil-deinit-esc (frame-list)) + (setq evil-esc-mode nil))))) + +(defun evil-init-esc (frame) + "Update `input-decode-map' in terminal." + (with-selected-frame frame + (let ((term (frame-terminal frame))) + (when (and + (or (eq evil-intercept-esc 'always) + (and evil-intercept-esc + (eq (terminal-live-p term) t))) ; only patch tty + (not (terminal-parameter term 'evil-esc-map))) + (let ((evil-esc-map (lookup-key input-decode-map [?\e]))) + (set-terminal-parameter term 'evil-esc-map evil-esc-map) + (define-key input-decode-map [?\e] + `(menu-item "" ,evil-esc-map :filter ,#'evil-esc))))))) + +(defun evil-deinit-esc (frame) + "Restore `input-decode-map' in terminal." + (with-selected-frame frame + (let ((term (frame-terminal frame))) + (when (terminal-live-p term) + (let ((evil-esc-map (terminal-parameter term 'evil-esc-map))) + (when evil-esc-map + (define-key input-decode-map [?\e] evil-esc-map) + (set-terminal-parameter term 'evil-esc-map nil))))))) + +(defun evil-esc (map) + "Translate \\e to `escape' if no further event arrives. +This function is used to translate a \\e event either to `escape' +or to the standard ESC prefix translation map. If \\e arrives, +this function waits for `evil-esc-delay' seconds for another +event. If no other event arrives, the event is translated to +`escape', otherwise it is translated to the standard ESC prefix +map stored in `input-decode-map'. If `evil-inhibit-esc' is +non-nil or if evil is in emacs state, the event is always +translated to the ESC prefix. + +The translation to `escape' happens only if the current command +has indeed been triggered by \\e. In other words, this will only +happen when the keymap is accessed from `read-key-sequence'. In +particular, if it is access from `define-key' the returned +mapping will always be the ESC prefix map." + (if (and (not evil-inhibit-esc) + (or evil-local-mode (evil-ex-p) + (active-minibuffer-window)) + (not (evil-emacs-state-p)) + (let ((keys (this-single-command-keys))) + (and (> (length keys) 0) + (= (aref keys (1- (length keys))) ?\e))) + (sit-for evil-esc-delay)) + (prog1 [escape] + (when defining-kbd-macro + (end-kbd-macro) + (setq last-kbd-macro (vconcat last-kbd-macro [escape])) + (start-kbd-macro t t))) + map)) + +(defun evil-state-p (sym) + "Whether SYM is the name of a state." + (assq sym evil-state-properties)) + +(defun evil-state-keymaps (state &rest excluded) + "Return a keymap alist of keymaps activated by STATE. +If STATE references other states in its :enable property, +these states are recursively processed and added to the list. +\(The EXCLUDED argument is an internal safeguard against +infinite recursion, keeping track of processed states.)" + (let* ((state (or state evil-state)) + (enable (evil-state-property state :enable)) + (map (cons + (evil-state-property state :mode) + (evil-state-property state :keymap t))) + (local-map (cons + (evil-state-property state :local) + (evil-state-property state :local-keymap t))) + (minor-mode-maps (evil-state-minor-mode-keymaps state)) + (aux-maps (evil-state-auxiliary-keymaps state)) + (overriding-maps + (evil-state-overriding-keymaps state)) + (intercept-maps + (evil-state-intercept-keymaps state)) + (result `(,intercept-maps)) + (remove-duplicates (null excluded))) + (unless (memq state enable) + (setq enable (cons state enable))) + ;; process STATE's :enable property + (dolist (entry enable) + (cond + ((memq entry excluded)) + ;; the keymaps for STATE + ((eq entry state) + (setq result `(,@result + (,local-map) + ,minor-mode-maps + ,aux-maps + ,overriding-maps + (,map))) + (push state excluded)) + ;; the keymaps for another state: call `evil-state-keymaps' + ;; recursively, but keep track of processed states + ((evil-state-p entry) + (setq result `(,@result + ,(apply #'evil-state-keymaps entry excluded)))) + ;; a single keymap + ((or (keymapp entry) + (and (keymapp (symbol-value entry)) + (setq entry (symbol-value entry))) + (setq entry (evil-keymap-for-mode entry))) + (setq result `(,@result + ((,(evil-mode-for-keymap entry t) . + ,entry))))))) + ;; postpone the expensive filtering of duplicates to the top level + (if remove-duplicates + (apply #'evil-concat-keymap-alists result) + (apply #'append result)))) + +(defun evil-normalize-keymaps (&optional state) + "Create a buffer-local value for `evil-mode-map-alist'. +This is a keymap alist, determined by the current state +\(or by STATE if specified)." + (let ((state (or state evil-state)) + (excluded '(nil t)) + map mode temp) + ;; initialize buffer-local keymaps as necessary + (evil-initialize-local-keymaps) + ;; deactivate keymaps of previous state + (dolist (entry evil-mode-map-alist) + (setq mode (car entry) + map (cdr entry)) + ;; don't deactivate overriding keymaps; + ;; they are toggled by their associated mode + (if (or (memq mode excluded) + (evil-intercept-keymap-p map) + (evil-overriding-keymap-p map) + (evil-auxiliary-keymap-p map) + (evil-minor-mode-keymap-p map)) + (push mode excluded) + (and (fboundp mode) (symbol-value mode) (funcall mode -1)) + (set mode nil))) + (setq evil-mode-map-alist nil) + ;; activate keymaps of current state + (when state + (setq temp (evil-state-keymaps state)) + (dolist (entry temp) + (setq mode (car entry) + map (cdr entry)) + (unless (or (and (boundp mode) (symbol-value mode)) + ;; the minor-mode keymaps include modes that are not + ;; necessarily active + (evil-minor-mode-keymap-p map)) + (when (fboundp mode) + (funcall mode 1)) + (set mode t)) + ;; refresh the keymap in case it has changed + ;; (e.g., `evil-operator-shortcut-map' is + ;; reset on toggling) + (if (or (memq mode excluded) + (evil-intercept-keymap-p map) + (evil-overriding-keymap-p map) + (evil-auxiliary-keymap-p map) + (evil-minor-mode-keymap-p map)) + (push mode excluded) + (setcdr entry (or (evil-keymap-for-mode mode) map)))) + ;; update `evil-mode-map-alist' + (setq evil-mode-map-alist temp)))) + +(defun evil-mode-for-keymap (keymap &optional default) + "Return the minor mode associated with KEYMAP. +Return DEFAULT if no mode is found. +See also `evil-keymap-for-mode'." + (let ((map (if (keymapp keymap) keymap (symbol-value keymap))) + (var (when (symbolp keymap) keymap))) + ;; Check Evil variables first for speed purposes. + ;; If all else fails, check `minor-mode-map-alist'. + (or (when var + (or (car (rassq var evil-global-keymaps-alist)) + (car (rassq var evil-local-keymaps-alist)))) + (car (rassq map (mapcar #'(lambda (e) + ;; from (MODE-VAR . MAP-VAR) + ;; to (MODE-VAR . MAP) + (cons (car-safe e) + (symbol-value (cdr-safe e)))) + (append evil-global-keymaps-alist + evil-local-keymaps-alist)))) + (car (rassq map minor-mode-map-alist)) + default))) + +(defun evil-keymap-for-mode (mode &optional variable) + "Return the keymap associated with MODE. +Return the keymap variable if VARIABLE is non-nil. +See also `evil-mode-for-keymap'." + (let* ((var (or (cdr (assq mode evil-global-keymaps-alist)) + (cdr (assq mode evil-local-keymaps-alist)))) + (map (or (symbol-value var) + (cdr (assq mode minor-mode-map-alist))))) + (if variable var map))) + +(defun evil-state-auxiliary-keymaps (state) + "Return a keymap alist of auxiliary keymaps for STATE." + (let ((state (or state evil-state)) + aux result) + (dolist (map (current-active-maps) (nreverse result)) + (when (setq aux (evil-get-auxiliary-keymap map state)) + (push (cons (evil-mode-for-keymap map t) aux) result))))) + +(defun evil-state-minor-mode-keymaps (state) + "Return a keymap alist of minor-mode keymaps for STATE." + (cdr (assq (or state evil-state) evil-minor-mode-keymaps-alist))) + +(defun evil-state-overriding-keymaps (&optional state) + "Return a keymap alist of overriding keymaps for STATE." + (let* ((state (or state evil-state)) + result) + (dolist (map (current-active-maps)) + (when (setq map (evil-overriding-keymap-p map state)) + (push (cons (evil-mode-for-keymap map t) map) result))) + (nreverse result))) + +(defun evil-state-intercept-keymaps (&optional state) + "Return a keymap alist of intercept keymaps for STATE." + (let* ((state (or state evil-state)) + result) + (dolist (map (current-active-maps)) + (when (setq map (or (evil-intercept-keymap-p map state) + (evil-intercept-keymap-p + (evil-get-auxiliary-keymap map state) state))) + (push (cons (evil-mode-for-keymap map t) map) result))) + (setq result (nreverse result)) + result)) + +(defun evil-set-auxiliary-keymap (map state &optional aux) + "Set the auxiliary keymap for MAP in STATE to AUX. +If AUX is nil, create a new auxiliary keymap." + (unless aux (setq aux (make-sparse-keymap))) + (unless (evil-auxiliary-keymap-p aux) + (evil-set-keymap-prompt + aux (format "Auxiliary keymap for %s" + (or (evil-state-property state :name) + (format "%s state" state))))) + (define-key map (vector (intern (format "%s-state" state))) aux) + aux) + +(defun evil-get-auxiliary-keymap (map state &optional create ignore-parent) + "Get the auxiliary keymap for MAP in STATE. +If CREATE is non-nil, create an auxiliary keymap +if MAP does not have one. If CREATE and +IGNORE-PARENT are non-nil then a new auxiliary +keymap is created even if the parent of MAP has +one already." + (when state + (let* ((key (vector (intern (format "%s-state" state)))) + (parent-aux (when (and ignore-parent + (keymap-parent map)) + (lookup-key (keymap-parent map) key))) + (aux (lookup-key map key))) + (cond + ((and ignore-parent + (equal parent-aux aux) + create) + (evil-set-auxiliary-keymap map state)) + ((evil-auxiliary-keymap-p aux) + aux) + (create + (evil-set-auxiliary-keymap map state)))))) + +(defun evil-get-minor-mode-keymap (state mode) + "Get the auxiliary keymap for MODE in STATE, creating one if it +does not already exist." + (let ((state-entry (assq state evil-minor-mode-keymaps-alist))) + (if (and state-entry + (assq mode state-entry)) + (cdr (assq mode state-entry)) + (let ((map (make-sparse-keymap))) + (evil-set-keymap-prompt + map (format "Minor-mode keymap for %s in %s" + mode + (or (evil-state-property state :name) + (format "%s state" state)))) + (if state-entry + (setcdr state-entry + (append (list (cons mode map)) (cdr state-entry))) + (push (cons state (list (cons mode map))) + evil-minor-mode-keymaps-alist)) + map)))) + +(defun evil-auxiliary-keymap-p (map) + "Whether MAP is an auxiliary keymap." + (let ((prompt (keymap-prompt map))) + (when prompt (string-prefix-p "Auxiliary keymap" prompt)))) + +(defun evil-minor-mode-keymap-p (map) + "Whether MAP is a minor-mode keymap." + (let ((prompt (keymap-prompt map))) + (when prompt (string-prefix-p "Minor-mode keymap" prompt)))) + +(defun evil-intercept-keymap-p (map &optional state) + "Whether MAP is an intercept keymap for STATE. +If STATE is nil, it means any state." + (let ((entry (and (keymapp map) + (lookup-key map [intercept-state])))) + (cond + ((null entry) nil) + ((null state) map) + ((eq entry state) map) + ((eq entry 'all) map)))) + +(defun evil-overriding-keymap-p (map &optional state) + "Whether MAP is an overriding keymap for STATE. +If STATE is nil, it means any state." + (let ((entry (and (keymapp map) + (lookup-key map [override-state])))) + (cond + ((null entry) nil) + ((keymapp entry) (evil-overriding-keymap-p entry state)) + ((null state) map) + ((eq entry state) map) + ((eq entry 'all) map)))) + +(defun evil-intercept-keymap-state (map) + "Return the state for the intercept keymap MAP. +A return value of t means all states." + (let ((state (lookup-key map [intercept-state] map))) + (cond + ((keymapp state) + (evil-intercept-keymap-state state)) + ((eq state 'all) + t) + (t + state)))) + +(defun evil-overriding-keymap-state (map) + "Return the state for the overriding keymap MAP. +A return value of t means all states." + (let ((state (lookup-key map [override-state] map))) + (cond + ((keymapp state) + (evil-overriding-keymap-state state)) + ((eq state 'all) + t) + (t + state)))) + +(defun evil-set-leader (state key &optional localleader) + "Set KEY to trigger leader bindings in STATE. +KEY should be in the form produced by `kbd'. STATE is one of +`normal', `insert', `visual', `replace', `operator', `motion', +`emacs', a list of one or more of these, or `nil', which means +all of the above. If LOCALLEADER is non-nil, set the local leader +instead." + (let* ((all-states '(normal insert visual replace operator motion emacs)) + (states (cond ((null state) all-states) + ((consp state) state) + (t (list state)))) + (leaderkey (if localleader [localleader] [leader])) + (binding + `(menu-item "" nil :filter ,(lambda (_cmd) (key-binding leaderkey))))) + (dolist (state states) + (evil-global-set-key state key binding)))) + +(defmacro evil-define-key (state keymap key def &rest bindings) + "Create a STATE binding from KEY to DEF for KEYMAP. +STATE is one of `normal', `insert', `visual', `replace', +`operator', `motion', `emacs', or a list of one or more of +these. Omitting a state by using `nil' corresponds to a standard +Emacs binding using `define-key'. The remaining arguments are +like those of `define-key'. For example: + + (evil-define-key \\='normal foo-map \"a\" \\='bar) + +This creates a binding from `a' to `bar' in normal state, which +is active whenever `foo-map' is active. Using nil for the state, +the following lead to identical bindings: + + (evil-define-key nil foo-map \"a\" \\='bar) + (define-key foo-map \"a\" \\='bar) + +It is possible to specify multiple states and/or bindings at +once: + + (evil-define-key \\='(normal visual) foo-map + \"a\" \\='bar + \"b\" \\='foo) + +If `foo-map' has not been initialized yet, this macro adds an +entry to `after-load-functions', delaying execution as necessary. + +KEYMAP may also be a quoted symbol. If the symbol is `global', the +global evil keymap corresponding to the state(s) is used, meaning +the following lead to identical bindings: + + (evil-define-key \\='normal \\='global \"a\" \\='bar) + (evil-global-set-key \\='normal \"a\" \\='bar) + +The symbol `local' may also be used, which corresponds to using +`evil-local-set-key'. If a quoted symbol is used that is not +`global' or `local', it is assumed to be the name of a minor +mode, in which case `evil-define-minor-mode-key' is used. + +KEY is an internal Emacs representation of a key, as for +`define-key'. To bind key sequences that use modifier keys such +as \"C-a\" or \"M-a\", convert the key sequences using `kbd'. +For example: + + (evil-define-key \\='normal foo-map (kbd \"C-a\") \\='bar)" + (declare (indent defun)) + (cond + ((member keymap '('global 'local)) + `(evil-define-key* ,state ,keymap ,key ,def ,@bindings)) + ((eq (car-safe keymap) 'quote) + `(evil-define-minor-mode-key ,state ,keymap ,key ,def ,@bindings)) + (t `(evil-with-delay ,(if (symbolp keymap) + ;; BEWARE: Can't work for lexically scoped vars + `(and (boundp ',keymap) (keymapp ,keymap)) + `(keymapp ,keymap)) + (after-load-functions + t nil ,(format "evil-define-key-in-%s" + (if (symbolp keymap) keymap 'keymap))) + (with-demoted-errors "Error in evil-define-key: %S" + (evil-define-key* ,state ,keymap ,key ,def ,@bindings)))))) +(defalias 'evil-declare-key #'evil-define-key) + +(defun evil-define-key* (state keymap key def &rest bindings) + "Create a STATE binding from KEY to DEF for KEYMAP. +STATE is one of normal, insert, visual, replace, operator, +motion, emacs, or a list of one or more of these. Omitting a +state by using nil corresponds to a standard Emacs binding using +`define-key' The remaining arguments are like those of +`define-key'. For example: + + (evil-define-key* \\='normal foo-map \"a\" \\='bar) + +This creates a binding from \"a\" to bar in Normal state, which +is active whenever foo-map is active. Using nil for the state, +the following are equivalent: + + (evil-define-key* nil foo-map \"a\" \\='bar) + + (define-key foo-map \"a\" \\='bar) + + It is possible to specify multiple states and/or bindings at + once: + + (evil-define-key* \\='(normal visual) foo-map + \"a\" \\='bar + \"b\" \\='foo) + +KEYMAP may also be a quoted symbol. If the symbol is global, the +global evil keymap corresponding to the state(s) is used, meaning +the following are equivalent: + + (evil-define-key* \\='normal \\='global \"a\" \\='bar) + + (evil-global-set-key \\='normal \"a\" \\='bar) + +The symbol local may also be used, which corresponds to using +`evil-local-set-key'. + +The use is nearly identical to `evil-define-key' with the +exception that this is a function and not a macro (and so will +not be expanded when compiled which can have unintended +consequences). `evil-define-key*' also does not defer any +bindings like `evil-define-key' does using `evil-with-delay'. This +allows errors in the bindings to be caught immediately, and makes +its behavior more predictable." + (declare (indent defun)) + (let ((maps + (if state + (mapcar + (lambda (st) + (cond ((eq keymap 'global) + (evil-state-property st :keymap t)) + ((eq keymap 'local) + (evil-state-property st :local-keymap t)) + (t + (evil-get-auxiliary-keymap keymap st t t)))) + (if (listp state) state (list state))) + (list + (cond ((eq keymap 'global) + global-map) + ((eq keymap 'local) + ;; see `local-set-key' + (or (current-local-map) + (let ((map (make-sparse-keymap))) + (use-local-map map) + map))) + (t + keymap)))))) + (while key + (dolist (map maps) + (define-key map key def)) + (setq key (pop bindings) + def (pop bindings))) + ;; ensure the prompt string comes first + (dolist (map maps) + (evil-set-keymap-prompt map (keymap-prompt map))))) + +(defun evil-define-minor-mode-key (state mode key def &rest bindings) + "Similar to `evil-define-key' but the bindings are associated +with the minor-mode symbol MODE instead of a particular map. +Associating bindings with a mode symbol instead of a map allows +evil to use Emacs' built-in mechanisms to enable the bindings +automatically when MODE is active without relying on calling +`evil-normalize-keymaps'. Another less significant difference is +that the bindings can be created immediately, because this +function only uses the symbol MODE and does not rely on its +value. + +See `evil-define-key' for the usage of STATE, KEY, DEF and +BINDINGS." + (declare (indent defun)) + (let ((maps (mapcar + (lambda (st) + (evil-get-minor-mode-keymap st mode)) + (if (listp state) state (list state))))) + (while key + (dolist (map maps) + (define-key map key def)) + (setq key (pop bindings) + def (pop bindings))))) + +(defmacro evil-add-hjkl-bindings (keymap &optional state &rest bindings) + "Add \"h\", \"j\", \"k\", \"l\" bindings to KEYMAP in STATE. +Add additional BINDINGS if specified." + (declare (indent defun)) + `(evil-define-key ,state ,keymap + "h" (lookup-key evil-motion-state-map "h") + "j" (lookup-key evil-motion-state-map "j") + "k" (lookup-key evil-motion-state-map "k") + "l" (lookup-key evil-motion-state-map "l") + ":" (lookup-key evil-motion-state-map ":") + ,@bindings)) + +;; may be useful for programmatic purposes +(defun evil-global-set-key (state key def) + "Bind KEY to DEF in STATE." + (define-key (evil-state-property state :keymap t) key def)) + +(defun evil-local-set-key (state key def) + "Bind KEY to DEF in STATE in the current buffer." + (define-key (evil-state-property state :local-keymap t) key def)) + +;; Advise these functions as they may activate an overriding keymap or +;; a keymap with state bindings; if so, refresh `evil-mode-map-alist'. +(advice-add 'use-global-map :after #'evil--do-normalize-keymaps) +(advice-add 'use-local-map :after #'evil--do-normalize-keymaps) +(defun evil--do-normalize-keymaps (&rest _) + "Refresh Evil keymaps." + (evil-normalize-keymaps)) + + +(defmacro evil-define-state (state doc &rest body) + "Define an Evil state STATE. +DOC is a general description and shows up in all docstrings; +the first line of the string should be the full name of the state. + +BODY is executed each time the state is enabled or disabled. + +Optional keyword arguments: +- `:tag' - the mode line indicator, e.g. \"\". +- `:message' - string shown in the echo area when the state is + activated. +- `:cursor' - default cursor specification. +- `:enable' - list of other state keymaps to enable when in this + state. +- `:entry-hook' - list of functions to run when entering this state. +- `:exit-hook' - list of functions to run when exiting this state. +- `:suppress-keymap' - if non-nil, effectively disables bindings to + `self-insert-command' by making `evil-suppress-map' the parent of + the global state keymap. + +The global keymap of this state will be `evil-test-state-map', +the local keymap will be `evil-test-state-local-map', and so on. + +\(fn STATE DOC [[KEY VAL]...] BODY...)" + (declare (indent defun) + (doc-string 2) + (debug (&define name + [&optional stringp] + [&rest [keywordp sexp]] + def-body))) + (let* ((name (and (string-match "^\\(.+\\)\\(\\(?:.\\|\n\\)*\\)" doc) + (match-string 1 doc))) + (doc (match-string 2 doc)) + (name (and (string-match "^\\(.+?\\)\\.?$" name) + (match-string 1 name))) + (doc (if (or (null doc) (string= doc "")) "" + (format "\n%s" doc))) + (toggle (intern (format "evil-%s-state" state))) + (mode (intern (format "%s-minor-mode" toggle))) + (keymap (intern (format "%s-map" toggle))) + (local (intern (format "%s-local-minor-mode" toggle))) + (local-keymap (intern (format "%s-local-map" toggle))) + (tag (intern (format "%s-tag" toggle))) + (message (intern (format "%s-message" toggle))) + (cursor (intern (format "%s-cursor" toggle))) + (entry-hook (intern (format "%s-entry-hook" toggle))) + (exit-hook (intern (format "%s-exit-hook" toggle))) + (modes (intern (format "%s-modes" toggle))) + (predicate (intern (format "%s-p" toggle))) + arg cursor-value enable entry-hook-value exit-hook-value + input-method key message-value suppress-keymap tag-value) + ;; collect keywords + (while (keywordp (car-safe body)) + (setq key (pop body) + arg (pop body)) + (cond + ((eq key :tag) + (setq tag-value arg)) + ((eq key :message) + (setq message-value arg)) + ((eq key :cursor) + (setq cursor-value arg)) + ((eq key :entry-hook) + (setq entry-hook-value arg) + (unless (listp entry-hook-value) + (setq entry-hook-value (list entry-hook-value)))) + ((eq key :exit-hook) + (setq exit-hook-value arg) + (unless (listp exit-hook-value) + (setq exit-hook-value (list exit-hook-value)))) + ((eq key :enable) + (setq enable arg)) + ((eq key :input-method) + (setq input-method arg)) + ((eq key :suppress-keymap) + (setq suppress-keymap arg)))) + + ;; macro expansion + `(progn + ;; Save the state's properties in `evil-state-properties' for + ;; runtime lookup. Among other things, this information is used + ;; to determine what keymaps should be activated by the state + ;; (and, when processing :enable, what keymaps are activated by + ;; other states). We cannot know this at compile time because + ;; it depends on the current buffer and its active keymaps + ;; (to which we may have assigned state bindings), as well as + ;; states whose definitions may not have been processed yet. + (let ((plist (list + :name ',name + :toggle ',toggle + :mode (defvar ,mode nil + ,(format "Non-nil if %s is enabled. +Use the command `%s' to change this variable." name toggle)) + :keymap (defvar ,keymap (make-sparse-keymap) + ,(format "Keymap for %s." name)) + :local (defvar ,local nil + ,(format "Non-nil if %s is enabled. +Use the command `%s' to change this variable." name toggle)) + :local-keymap (defvar ,local-keymap nil + ,(format "Buffer-local keymap for %s." name)) + :tag (defvar ,tag ,tag-value + ,(format "Mode line tag for %s." name)) + :message (defvar ,message ,message-value + ,(format "Echo area message for %s." name)) + :cursor (defvar ,cursor ',cursor-value + ,(format "Cursor for %s. +May be a cursor type as per `cursor-type', a color string as passed +to `set-cursor-color', a zero-argument function for changing the +cursor, or a list of the above. + +If set to nil, the value of `evil-default-cursor' is used; if you +want the nil `cursor-type' (i.e., you want to disable the cursor), +set this variable to (list nil)." name)) + :entry-hook (defvar ,entry-hook nil + ,(format "Hooks to run when entering %s." name)) + :exit-hook (defvar ,exit-hook nil + ,(format "Hooks to run when exiting %s." name)) + :modes (defvar ,modes nil + ,(format "Modes that should come up in %s." name)) + :input-method ',input-method + :predicate ',predicate + :enable ',enable))) + (evil--add-to-alist evil-state-properties ',state plist)) + + ,@(when suppress-keymap + `((set-keymap-parent ,keymap evil-suppress-map))) + + (dolist (func ',entry-hook-value) (add-hook ',entry-hook func)) + (dolist (func ',exit-hook-value) (add-hook ',exit-hook func)) + + (defun ,predicate (&optional state) + ,(format "Whether the current state is %s. +\(That is, whether `evil-state' is `%s'.)" name state) + (and evil-local-mode + (eq (or state evil-state) ',state))) + + ;; define state function + (defun ,toggle (&optional arg) + ,(format "Enable %s. Disable with negative ARG. +If ARG is nil, don't display a message in the echo area.%s" name doc) + (interactive) + (cond + ((and (numberp arg) (< arg 1)) + (setq evil-previous-state evil-state + evil-state nil) + (let ((evil-state ',state)) + (run-hooks ',exit-hook) + (setq evil-state nil) + (evil-normalize-keymaps) + ,@body)) + (t + (unless evil-local-mode (evil-local-mode)) + (let ((evil-next-state ',state) + input-method-activate-hook input-method-deactivate-hook) + (evil-change-state nil) + (setq evil-state ',state) + (evil--add-to-alist evil-previous-state-alist + ',state evil-previous-state) + (let ((evil-state ',state)) + (evil-normalize-keymaps) + (if ',input-method + (activate-input-method evil-input-method) + ;; BUG #475: Deactivate the current input method only + ;; if there is a function to deactivate it, otherwise + ;; an error would be raised. This strange situation + ;; should not arise in general and there should + ;; probably be a better way to handle this situation. + (when deactivate-current-input-method-function + (deactivate-input-method))) + (unless evil-no-display + (when (eq (window-buffer) (current-buffer)) + (evil-refresh-cursor ',state)) + (evil-refresh-mode-line ',state)) + ,@body + (run-hooks ',entry-hook) + (when (and evil-echo-state + arg (not evil-no-display) ,message) + (if (functionp ,message) + (funcall ,message) + (evil-echo "%s" ,message)))))))) + + (evil-add-command-properties ',toggle :keep-visual t :suppress-operator t) + + (evil-define-keymap ,keymap nil + :mode ,mode + :func nil) + + (evil-define-keymap ,local-keymap nil + :mode ,local + :local t + :func nil) + + ',state))) + +(provide 'evil-core) + +;;; evil-core.el ends here diff --git a/.config/emacs/lisp/evil/evil-development.el b/.config/emacs/lisp/evil/evil-development.el new file mode 100644 index 0000000..60da92d --- /dev/null +++ b/.config/emacs/lisp/evil/evil-development.el @@ -0,0 +1,50 @@ +;;; evil-development.el --- Useful features for Evil developers -*- lexical-binding: t -*- + +;; Author: Justin Burkett + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Code: + +;;; Teach imenu about evil macros + +(with-eval-after-load 'lisp-mode + (when (boundp 'lisp-imenu-generic-expression) + (dolist (macro '("interactive-code" + "type" + "text-object" + "motion" + "command" + "operator")) + (let ((macro-name (format "evil-%s" macro))) + (unless (assoc macro-name lisp-imenu-generic-expression) + (push (list + macro-name + (format "^\\s-*(evil-define-%s\\s-+\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" + macro) + 1) + lisp-imenu-generic-expression)))))) + +(provide 'evil-development) + +;;; evil-development.el ends here diff --git a/.config/emacs/lisp/evil/evil-digraphs.el b/.config/emacs/lisp/evil/evil-digraphs.el new file mode 100644 index 0000000..2bd3687 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-digraphs.el @@ -0,0 +1,1735 @@ +;;; evil-digraphs.el --- Digraphs -*- lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Code: + +(require 'evil-vars) + +(defgroup evil-digraphs nil + "Digraph support based on RFC 1345." + :group 'evil + :prefix "evil-digraph-") + +(defcustom evil-digraphs-table-user nil + "List of user-defined digraphs. +Entries have the form ((?CHAR1 ?CHAR2) . ?DIGRAPH). That is, +a cons cell of the digraph and its character replacement, +where the digraph is a list of two characters. +See also `evil-digraphs-table'." + :type '(alist :key-type (list character character) + :value-type character)) + +(defconst evil-digraphs-table + '(((?N ?U) . ?\x00) + ((?S ?H) . ?\x01) + ((?S ?X) . ?\x02) + ((?E ?X) . ?\x03) + ((?E ?T) . ?\x04) + ((?E ?Q) . ?\x05) + ((?A ?K) . ?\x06) + ((?B ?L) . ?\x07) + ((?B ?S) . ?\x08) + ((?H ?T) . ?\x09) + ((?L ?F) . ?\x0a) + ((?V ?T) . ?\x0b) + ((?F ?F) . ?\x0c) + ((?C ?R) . ?\x0d) + ((?S ?O) . ?\x0e) + ((?S ?I) . ?\x0f) + ((?D ?L) . ?\x10) + ((?D ?1) . ?\x11) + ((?D ?2) . ?\x12) + ((?D ?3) . ?\x13) + ((?D ?4) . ?\x14) + ((?N ?K) . ?\x15) + ((?S ?Y) . ?\x16) + ((?E ?B) . ?\x17) + ((?C ?N) . ?\x18) + ((?E ?M) . ?\x19) + ((?S ?B) . ?\x1a) + ((?E ?C) . ?\x1b) + ((?F ?S) . ?\x1c) + ((?G ?S) . ?\x1d) + ((?R ?S) . ?\x1e) + ((?U ?S) . ?\x1f) + ((?S ?P) . ?\x20) + ((?N ?b) . ?\x23) + ((?D ?O) . ?\x24) + ((?A ?t) . ?\x40) + ((?< ?\() . ?\x5b) + ((?/ ?/) . ?\x5c) + ((?\) ?>) . ?\x5d) + ((?' ?>) . ?\x5e) + ((?' ?!) . ?\x60) + ((?\( ?!) . ?\x7b) + ((?! ?!) . ?\x7c) + ((?! ?\)) . ?\x7d) + ((?' ??) . ?\x7e) + ((?D ?T) . ?\x7f) + ((?P ?A) . ?\x80) + ((?H ?O) . ?\x81) + ((?B ?H) . ?\x82) + ((?N ?H) . ?\x83) + ((?I ?N) . ?\x84) + ((?N ?L) . ?\x85) + ((?S ?A) . ?\x86) + ((?E ?S) . ?\x87) + ((?H ?S) . ?\x88) + ((?H ?J) . ?\x89) + ((?V ?S) . ?\x8a) + ((?P ?D) . ?\x8b) + ((?P ?U) . ?\x8c) + ((?R ?I) . ?\x8d) + ((?S ?2) . ?\x8e) + ((?S ?3) . ?\x8f) + ((?D ?C) . ?\x90) + ((?P ?1) . ?\x91) + ((?P ?2) . ?\x92) + ((?T ?S) . ?\x93) + ((?C ?C) . ?\x94) + ((?M ?W) . ?\x95) + ((?S ?G) . ?\x96) + ((?E ?G) . ?\x97) + ((?S ?S) . ?\x98) + ((?G ?C) . ?\x99) + ((?S ?C) . ?\x9a) + ((?C ?I) . ?\x9b) + ((?S ?T) . ?\x9c) + ((?O ?C) . ?\x9d) + ((?P ?M) . ?\x9e) + ((?A ?C) . ?\x9f) + ((?N ?S) . ?\xa0) + ((?! ?I) . ?\xa1) + ((?C ?t) . ?\xa2) + ((?P ?d) . ?\xa3) + ((?C ?u) . ?\xa4) + ((?Y ?e) . ?\xa5) + ((?B ?B) . ?\xa6) + ((?S ?E) . ?\xa7) + ((?' ?:) . ?\xa8) + ((?C ?o) . ?\xa9) + ((?- ?a) . ?\xaa) + ((?< ?<) . ?\xab) + ((?N ?O) . ?\xac) + ((?- ?-) . ?\xad) + ((?R ?g) . ?\xae) + ((?' ?m) . ?\xaf) + ((?D ?G) . ?\xb0) + ((?+ ?-) . ?\xb1) + ((?2 ?S) . ?\xb2) + ((?3 ?S) . ?\xb3) + ((?' ?') . ?\xb4) + ((?M ?y) . ?\xb5) + ((?P ?I) . ?\xb6) + ((?. ?M) . ?\xb7) + ((?' ?,) . ?\xb8) + ((?1 ?S) . ?\xb9) + ((?- ?o) . ?\xba) + ((?> ?>) . ?\xbb) + ((?1 ?4) . ?\xbc) + ((?1 ?2) . ?\xbd) + ((?3 ?4) . ?\xbe) + ((?? ?I) . ?\xbf) + ((?A ?!) . ?\xc0) + ((?A ?') . ?\xc1) + ((?A ?>) . ?\xc2) + ((?A ??) . ?\xc3) + ((?A ?:) . ?\xc4) + ((?A ?A) . ?\xc5) + ((?A ?E) . ?\xc6) + ((?C ?,) . ?\xc7) + ((?E ?!) . ?\xc8) + ((?E ?') . ?\xc9) + ((?E ?>) . ?\xca) + ((?E ?:) . ?\xcb) + ((?I ?!) . ?\xcc) + ((?I ?') . ?\xcd) + ((?I ?>) . ?\xce) + ((?I ?:) . ?\xcf) + ((?D ?-) . ?\xd0) + ((?N ??) . ?\xd1) + ((?O ?!) . ?\xd2) + ((?O ?') . ?\xd3) + ((?O ?>) . ?\xd4) + ((?O ??) . ?\xd5) + ((?O ?:) . ?\xd6) + ((?* ?X) . ?\xd7) + ((?O ?/) . ?\xd8) + ((?U ?!) . ?\xd9) + ((?U ?') . ?\xda) + ((?U ?>) . ?\xdb) + ((?U ?:) . ?\xdc) + ((?Y ?') . ?\xdd) + ((?T ?H) . ?\xde) + ((?s ?s) . ?\xdf) + ((?a ?!) . ?\xe0) + ((?a ?') . ?\xe1) + ((?a ?>) . ?\xe2) + ((?a ??) . ?\xe3) + ((?a ?:) . ?\xe4) + ((?a ?a) . ?\xe5) + ((?a ?e) . ?\xe6) + ((?c ?,) . ?\xe7) + ((?e ?!) . ?\xe8) + ((?e ?') . ?\xe9) + ((?e ?>) . ?\xea) + ((?e ?:) . ?\xeb) + ((?i ?!) . ?\xec) + ((?i ?') . ?\xed) + ((?i ?>) . ?\xee) + ((?i ?:) . ?\xef) + ((?d ?-) . ?\xf0) + ((?n ??) . ?\xf1) + ((?o ?!) . ?\xf2) + ((?o ?') . ?\xf3) + ((?o ?>) . ?\xf4) + ((?o ??) . ?\xf5) + ((?o ?:) . ?\xf6) + ((?- ?:) . ?\xf7) + ((?o ?/) . ?\xf8) + ((?u ?!) . ?\xf9) + ((?u ?') . ?\xfa) + ((?u ?>) . ?\xfb) + ((?u ?:) . ?\xfc) + ((?y ?') . ?\xfd) + ((?t ?h) . ?\xfe) + ((?y ?:) . ?\xff) + ((?A ?-) . ?\x0100) + ((?a ?-) . ?\x0101) + ((?A ?\() . ?\x0102) + ((?a ?\() . ?\x0103) + ((?A ?\;) . ?\x0104) + ((?a ?\;) . ?\x0105) + ((?C ?') . ?\x0106) + ((?c ?') . ?\x0107) + ((?C ?>) . ?\x0108) + ((?c ?>) . ?\x0109) + ((?C ?.) . ?\x010a) + ((?c ?.) . ?\x010b) + ((?C ?<) . ?\x010c) + ((?c ?<) . ?\x010d) + ((?D ?<) . ?\x010e) + ((?d ?<) . ?\x010f) + ((?D ?/) . ?\x0110) + ((?d ?/) . ?\x0111) + ((?E ?-) . ?\x0112) + ((?e ?-) . ?\x0113) + ((?E ?\() . ?\x0114) + ((?e ?\() . ?\x0115) + ((?E ?.) . ?\x0116) + ((?e ?.) . ?\x0117) + ((?E ?\;) . ?\x0118) + ((?e ?\;) . ?\x0119) + ((?E ?<) . ?\x011a) + ((?e ?<) . ?\x011b) + ((?G ?>) . ?\x011c) + ((?g ?>) . ?\x011d) + ((?G ?\() . ?\x011e) + ((?g ?\() . ?\x011f) + ((?G ?.) . ?\x0120) + ((?g ?.) . ?\x0121) + ((?G ?,) . ?\x0122) + ((?g ?,) . ?\x0123) + ((?H ?>) . ?\x0124) + ((?h ?>) . ?\x0125) + ((?H ?/) . ?\x0126) + ((?h ?/) . ?\x0127) + ((?I ??) . ?\x0128) + ((?i ??) . ?\x0129) + ((?I ?-) . ?\x012a) + ((?i ?-) . ?\x012b) + ((?I ?\() . ?\x012c) + ((?i ?\() . ?\x012d) + ((?I ?\;) . ?\x012e) + ((?i ?\;) . ?\x012f) + ((?I ?.) . ?\x0130) + ((?i ?.) . ?\x0131) + ((?I ?J) . ?\x0132) + ((?i ?j) . ?\x0133) + ((?J ?>) . ?\x0134) + ((?j ?>) . ?\x0135) + ((?K ?,) . ?\x0136) + ((?k ?,) . ?\x0137) + ((?k ?k) . ?\x0138) + ((?L ?') . ?\x0139) + ((?l ?') . ?\x013a) + ((?L ?,) . ?\x013b) + ((?l ?,) . ?\x013c) + ((?L ?<) . ?\x013d) + ((?l ?<) . ?\x013e) + ((?L ?.) . ?\x013f) + ((?l ?.) . ?\x0140) + ((?L ?/) . ?\x0141) + ((?l ?/) . ?\x0142) + ((?N ?') . ?\x0143) + ((?n ?') . ?\x0144) + ((?N ?,) . ?\x0145) + ((?n ?,) . ?\x0146) + ((?N ?<) . ?\x0147) + ((?n ?<) . ?\x0148) + ((?' ?n) . ?\x0149) + ((?N ?G) . ?\x014a) + ((?n ?g) . ?\x014b) + ((?O ?-) . ?\x014c) + ((?o ?-) . ?\x014d) + ((?O ?\() . ?\x014e) + ((?o ?\() . ?\x014f) + ((?O ?\") . ?\x0150) + ((?o ?\") . ?\x0151) + ((?O ?E) . ?\x0152) + ((?o ?e) . ?\x0153) + ((?R ?') . ?\x0154) + ((?r ?') . ?\x0155) + ((?R ?,) . ?\x0156) + ((?r ?,) . ?\x0157) + ((?R ?<) . ?\x0158) + ((?r ?<) . ?\x0159) + ((?S ?') . ?\x015a) + ((?s ?') . ?\x015b) + ((?S ?>) . ?\x015c) + ((?s ?>) . ?\x015d) + ((?S ?,) . ?\x015e) + ((?s ?,) . ?\x015f) + ((?S ?<) . ?\x0160) + ((?s ?<) . ?\x0161) + ((?T ?,) . ?\x0162) + ((?t ?,) . ?\x0163) + ((?T ?<) . ?\x0164) + ((?t ?<) . ?\x0165) + ((?T ?/) . ?\x0166) + ((?t ?/) . ?\x0167) + ((?U ??) . ?\x0168) + ((?u ??) . ?\x0169) + ((?U ?-) . ?\x016a) + ((?u ?-) . ?\x016b) + ((?U ?\() . ?\x016c) + ((?u ?\() . ?\x016d) + ((?U ?0) . ?\x016e) + ((?u ?0) . ?\x016f) + ((?U ?\") . ?\x0170) + ((?u ?\") . ?\x0171) + ((?U ?\;) . ?\x0172) + ((?u ?\;) . ?\x0173) + ((?W ?>) . ?\x0174) + ((?w ?>) . ?\x0175) + ((?Y ?>) . ?\x0176) + ((?y ?>) . ?\x0177) + ((?Y ?:) . ?\x0178) + ((?Z ?') . ?\x0179) + ((?z ?') . ?\x017a) + ((?Z ?.) . ?\x017b) + ((?z ?.) . ?\x017c) + ((?Z ?<) . ?\x017d) + ((?z ?<) . ?\x017e) + ((?O ?9) . ?\x01a0) + ((?o ?9) . ?\x01a1) + ((?O ?I) . ?\x01a2) + ((?o ?i) . ?\x01a3) + ((?y ?r) . ?\x01a6) + ((?U ?9) . ?\x01af) + ((?u ?9) . ?\x01b0) + ((?Z ?/) . ?\x01b5) + ((?z ?/) . ?\x01b6) + ((?E ?D) . ?\x01b7) + ((?A ?<) . ?\x01cd) + ((?a ?<) . ?\x01ce) + ((?I ?<) . ?\x01cf) + ((?i ?<) . ?\x01d0) + ((?O ?<) . ?\x01d1) + ((?o ?<) . ?\x01d2) + ((?U ?<) . ?\x01d3) + ((?u ?<) . ?\x01d4) + ((?A ?1) . ?\x01de) + ((?a ?1) . ?\x01df) + ((?A ?7) . ?\x01e0) + ((?a ?7) . ?\x01e1) + ((?A ?3) . ?\x01e2) + ((?a ?3) . ?\x01e3) + ((?G ?/) . ?\x01e4) + ((?g ?/) . ?\x01e5) + ((?G ?<) . ?\x01e6) + ((?g ?<) . ?\x01e7) + ((?K ?<) . ?\x01e8) + ((?k ?<) . ?\x01e9) + ((?O ?\;) . ?\x01ea) + ((?o ?\;) . ?\x01eb) + ((?O ?1) . ?\x01ec) + ((?o ?1) . ?\x01ed) + ((?E ?Z) . ?\x01ee) + ((?e ?z) . ?\x01ef) + ((?j ?<) . ?\x01f0) + ((?G ?') . ?\x01f4) + ((?g ?') . ?\x01f5) + ((?\; ?S) . ?\x02bf) + ((?' ?<) . ?\x02c7) + ((?' ?\() . ?\x02d8) + ((?' ?.) . ?\x02d9) + ((?' ?0) . ?\x02da) + ((?' ?\;) . ?\x02db) + ((?' ?\") . ?\x02dd) + ((?A ?%) . ?\x0386) + ((?E ?%) . ?\x0388) + ((?Y ?%) . ?\x0389) + ((?I ?%) . ?\x038a) + ((?O ?%) . ?\x038c) + ((?U ?%) . ?\x038e) + ((?W ?%) . ?\x038f) + ((?i ?3) . ?\x0390) + ((?A ?*) . ?\x0391) + ((?B ?*) . ?\x0392) + ((?G ?*) . ?\x0393) + ((?D ?*) . ?\x0394) + ((?E ?*) . ?\x0395) + ((?Z ?*) . ?\x0396) + ((?Y ?*) . ?\x0397) + ((?H ?*) . ?\x0398) + ((?I ?*) . ?\x0399) + ((?K ?*) . ?\x039a) + ((?L ?*) . ?\x039b) + ((?M ?*) . ?\x039c) + ((?N ?*) . ?\x039d) + ((?C ?*) . ?\x039e) + ((?O ?*) . ?\x039f) + ((?P ?*) . ?\x03a0) + ((?R ?*) . ?\x03a1) + ((?S ?*) . ?\x03a3) + ((?T ?*) . ?\x03a4) + ((?U ?*) . ?\x03a5) + ((?F ?*) . ?\x03a6) + ((?X ?*) . ?\x03a7) + ((?Q ?*) . ?\x03a8) + ((?W ?*) . ?\x03a9) + ((?J ?*) . ?\x03aa) + ((?V ?*) . ?\x03ab) + ((?a ?%) . ?\x03ac) + ((?e ?%) . ?\x03ad) + ((?y ?%) . ?\x03ae) + ((?i ?%) . ?\x03af) + ((?u ?3) . ?\x03b0) + ((?a ?*) . ?\x03b1) + ((?b ?*) . ?\x03b2) + ((?g ?*) . ?\x03b3) + ((?d ?*) . ?\x03b4) + ((?e ?*) . ?\x03b5) + ((?z ?*) . ?\x03b6) + ((?y ?*) . ?\x03b7) + ((?h ?*) . ?\x03b8) + ((?i ?*) . ?\x03b9) + ((?k ?*) . ?\x03ba) + ((?l ?*) . ?\x03bb) + ((?m ?*) . ?\x03bc) + ((?n ?*) . ?\x03bd) + ((?c ?*) . ?\x03be) + ((?o ?*) . ?\x03bf) + ((?p ?*) . ?\x03c0) + ((?r ?*) . ?\x03c1) + ((?* ?s) . ?\x03c2) + ((?s ?*) . ?\x03c3) + ((?t ?*) . ?\x03c4) + ((?u ?*) . ?\x03c5) + ((?f ?*) . ?\x03c6) + ((?x ?*) . ?\x03c7) + ((?q ?*) . ?\x03c8) + ((?w ?*) . ?\x03c9) + ((?j ?*) . ?\x03ca) + ((?v ?*) . ?\x03cb) + ((?o ?%) . ?\x03cc) + ((?u ?%) . ?\x03cd) + ((?w ?%) . ?\x03ce) + ((?' ?G) . ?\x03d8) + ((?, ?G) . ?\x03d9) + ((?T ?3) . ?\x03da) + ((?t ?3) . ?\x03db) + ((?M ?3) . ?\x03dc) + ((?m ?3) . ?\x03dd) + ((?K ?3) . ?\x03de) + ((?k ?3) . ?\x03df) + ((?P ?3) . ?\x03e0) + ((?p ?3) . ?\x03e1) + ((?' ?%) . ?\x03f4) + ((?j ?3) . ?\x03f5) + ((?I ?O) . ?\x0401) + ((?D ?%) . ?\x0402) + ((?G ?%) . ?\x0403) + ((?I ?E) . ?\x0404) + ((?D ?S) . ?\x0405) + ((?I ?I) . ?\x0406) + ((?Y ?I) . ?\x0407) + ((?J ?%) . ?\x0408) + ((?L ?J) . ?\x0409) + ((?N ?J) . ?\x040a) + ((?T ?s) . ?\x040b) + ((?K ?J) . ?\x040c) + ((?V ?%) . ?\x040e) + ((?D ?Z) . ?\x040f) + ((?A ?=) . ?\x0410) + ((?B ?=) . ?\x0411) + ((?V ?=) . ?\x0412) + ((?G ?=) . ?\x0413) + ((?D ?=) . ?\x0414) + ((?E ?=) . ?\x0415) + ((?Z ?%) . ?\x0416) + ((?Z ?=) . ?\x0417) + ((?I ?=) . ?\x0418) + ((?J ?=) . ?\x0419) + ((?K ?=) . ?\x041a) + ((?L ?=) . ?\x041b) + ((?M ?=) . ?\x041c) + ((?N ?=) . ?\x041d) + ((?O ?=) . ?\x041e) + ((?P ?=) . ?\x041f) + ((?R ?=) . ?\x0420) + ((?S ?=) . ?\x0421) + ((?T ?=) . ?\x0422) + ((?U ?=) . ?\x0423) + ((?F ?=) . ?\x0424) + ((?H ?=) . ?\x0425) + ((?C ?=) . ?\x0426) + ((?C ?%) . ?\x0427) + ((?S ?%) . ?\x0428) + ((?S ?c) . ?\x0429) + ((?= ?\") . ?\x042a) + ((?Y ?=) . ?\x042b) + ((?% ?\") . ?\x042c) + ((?J ?E) . ?\x042d) + ((?J ?U) . ?\x042e) + ((?J ?A) . ?\x042f) + ((?a ?=) . ?\x0430) + ((?b ?=) . ?\x0431) + ((?v ?=) . ?\x0432) + ((?g ?=) . ?\x0433) + ((?d ?=) . ?\x0434) + ((?e ?=) . ?\x0435) + ((?z ?%) . ?\x0436) + ((?z ?=) . ?\x0437) + ((?i ?=) . ?\x0438) + ((?j ?=) . ?\x0439) + ((?k ?=) . ?\x043a) + ((?l ?=) . ?\x043b) + ((?m ?=) . ?\x043c) + ((?n ?=) . ?\x043d) + ((?o ?=) . ?\x043e) + ((?p ?=) . ?\x043f) + ((?r ?=) . ?\x0440) + ((?s ?=) . ?\x0441) + ((?t ?=) . ?\x0442) + ((?u ?=) . ?\x0443) + ((?f ?=) . ?\x0444) + ((?h ?=) . ?\x0445) + ((?c ?=) . ?\x0446) + ((?c ?%) . ?\x0447) + ((?s ?%) . ?\x0448) + ((?s ?c) . ?\x0449) + ((?= ?') . ?\x044a) + ((?y ?=) . ?\x044b) + ((?% ?') . ?\x044c) + ((?j ?e) . ?\x044d) + ((?j ?u) . ?\x044e) + ((?j ?a) . ?\x044f) + ((?i ?o) . ?\x0451) + ((?d ?%) . ?\x0452) + ((?g ?%) . ?\x0453) + ((?i ?e) . ?\x0454) + ((?d ?s) . ?\x0455) + ((?i ?i) . ?\x0456) + ((?y ?i) . ?\x0457) + ((?j ?%) . ?\x0458) + ((?l ?j) . ?\x0459) + ((?n ?j) . ?\x045a) + ((?t ?s) . ?\x045b) + ((?k ?j) . ?\x045c) + ((?v ?%) . ?\x045e) + ((?d ?z) . ?\x045f) + ((?Y ?3) . ?\x0462) + ((?y ?3) . ?\x0463) + ((?O ?3) . ?\x046a) + ((?o ?3) . ?\x046b) + ((?F ?3) . ?\x0472) + ((?f ?3) . ?\x0473) + ((?V ?3) . ?\x0474) + ((?v ?3) . ?\x0475) + ((?C ?3) . ?\x0480) + ((?c ?3) . ?\x0481) + ((?G ?3) . ?\x0490) + ((?g ?3) . ?\x0491) + ((?A ?+) . ?\x05d0) + ((?B ?+) . ?\x05d1) + ((?G ?+) . ?\x05d2) + ((?D ?+) . ?\x05d3) + ((?H ?+) . ?\x05d4) + ((?W ?+) . ?\x05d5) + ((?Z ?+) . ?\x05d6) + ((?X ?+) . ?\x05d7) + ((?T ?j) . ?\x05d8) + ((?J ?+) . ?\x05d9) + ((?K ?%) . ?\x05da) + ((?K ?+) . ?\x05db) + ((?L ?+) . ?\x05dc) + ((?M ?%) . ?\x05dd) + ((?M ?+) . ?\x05de) + ((?N ?%) . ?\x05df) + ((?N ?+) . ?\x05e0) + ((?S ?+) . ?\x05e1) + ((?E ?+) . ?\x05e2) + ((?P ?%) . ?\x05e3) + ((?P ?+) . ?\x05e4) + ((?Z ?j) . ?\x05e5) + ((?Z ?J) . ?\x05e6) + ((?Q ?+) . ?\x05e7) + ((?R ?+) . ?\x05e8) + ((?S ?h) . ?\x05e9) + ((?T ?+) . ?\x05ea) + ((?, ?+) . ?\x060c) + ((?\; ?+) . ?\x061b) + ((?? ?+) . ?\x061f) + ((?H ?') . ?\x0621) + ((?a ?M) . ?\x0622) + ((?a ?H) . ?\x0623) + ((?w ?H) . ?\x0624) + ((?a ?h) . ?\x0625) + ((?y ?H) . ?\x0626) + ((?a ?+) . ?\x0627) + ((?b ?+) . ?\x0628) + ((?t ?m) . ?\x0629) + ((?t ?+) . ?\x062a) + ((?t ?k) . ?\x062b) + ((?g ?+) . ?\x062c) + ((?h ?k) . ?\x062d) + ((?x ?+) . ?\x062e) + ((?d ?+) . ?\x062f) + ((?d ?k) . ?\x0630) + ((?r ?+) . ?\x0631) + ((?z ?+) . ?\x0632) + ((?s ?+) . ?\x0633) + ((?s ?n) . ?\x0634) + ((?c ?+) . ?\x0635) + ((?d ?d) . ?\x0636) + ((?t ?j) . ?\x0637) + ((?z ?H) . ?\x0638) + ((?e ?+) . ?\x0639) + ((?i ?+) . ?\x063a) + ((?+ ?+) . ?\x0640) + ((?f ?+) . ?\x0641) + ((?q ?+) . ?\x0642) + ((?k ?+) . ?\x0643) + ((?l ?+) . ?\x0644) + ((?m ?+) . ?\x0645) + ((?n ?+) . ?\x0646) + ((?h ?+) . ?\x0647) + ((?w ?+) . ?\x0648) + ((?j ?+) . ?\x0649) + ((?y ?+) . ?\x064a) + ((?: ?+) . ?\x064b) + ((?\" ?+) . ?\x064c) + ((?= ?+) . ?\x064d) + ((?/ ?+) . ?\x064e) + ((?' ?+) . ?\x064f) + ((?1 ?+) . ?\x0650) + ((?3 ?+) . ?\x0651) + ((?0 ?+) . ?\x0652) + ((?a ?S) . ?\x0670) + ((?p ?+) . ?\x067e) + ((?v ?+) . ?\x06a4) + ((?g ?f) . ?\x06af) + ((?0 ?a) . ?\x06f0) + ((?1 ?a) . ?\x06f1) + ((?2 ?a) . ?\x06f2) + ((?3 ?a) . ?\x06f3) + ((?4 ?a) . ?\x06f4) + ((?5 ?a) . ?\x06f5) + ((?6 ?a) . ?\x06f6) + ((?7 ?a) . ?\x06f7) + ((?8 ?a) . ?\x06f8) + ((?9 ?a) . ?\x06f9) + ((?B ?.) . ?\x1e02) + ((?b ?.) . ?\x1e03) + ((?B ?_) . ?\x1e06) + ((?b ?_) . ?\x1e07) + ((?D ?.) . ?\x1e0a) + ((?d ?.) . ?\x1e0b) + ((?D ?_) . ?\x1e0e) + ((?d ?_) . ?\x1e0f) + ((?D ?,) . ?\x1e10) + ((?d ?,) . ?\x1e11) + ((?F ?.) . ?\x1e1e) + ((?f ?.) . ?\x1e1f) + ((?G ?-) . ?\x1e20) + ((?g ?-) . ?\x1e21) + ((?H ?.) . ?\x1e22) + ((?h ?.) . ?\x1e23) + ((?H ?:) . ?\x1e26) + ((?h ?:) . ?\x1e27) + ((?H ?,) . ?\x1e28) + ((?h ?,) . ?\x1e29) + ((?K ?') . ?\x1e30) + ((?k ?') . ?\x1e31) + ((?K ?_) . ?\x1e34) + ((?k ?_) . ?\x1e35) + ((?L ?_) . ?\x1e3a) + ((?l ?_) . ?\x1e3b) + ((?M ?') . ?\x1e3e) + ((?m ?') . ?\x1e3f) + ((?M ?.) . ?\x1e40) + ((?m ?.) . ?\x1e41) + ((?N ?.) . ?\x1e44) + ((?n ?.) . ?\x1e45) + ((?N ?_) . ?\x1e48) + ((?n ?_) . ?\x1e49) + ((?P ?') . ?\x1e54) + ((?p ?') . ?\x1e55) + ((?P ?.) . ?\x1e56) + ((?p ?.) . ?\x1e57) + ((?R ?.) . ?\x1e58) + ((?r ?.) . ?\x1e59) + ((?R ?_) . ?\x1e5e) + ((?r ?_) . ?\x1e5f) + ((?S ?.) . ?\x1e60) + ((?s ?.) . ?\x1e61) + ((?T ?.) . ?\x1e6a) + ((?t ?.) . ?\x1e6b) + ((?T ?_) . ?\x1e6e) + ((?t ?_) . ?\x1e6f) + ((?V ??) . ?\x1e7c) + ((?v ??) . ?\x1e7d) + ((?W ?!) . ?\x1e80) + ((?W ?`) . ?\x1e80) + ((?w ?!) . ?\x1e81) + ((?w ?`) . ?\x1e81) + ((?W ?') . ?\x1e82) + ((?w ?') . ?\x1e83) + ((?W ?:) . ?\x1e84) + ((?w ?:) . ?\x1e85) + ((?W ?.) . ?\x1e86) + ((?w ?.) . ?\x1e87) + ((?X ?.) . ?\x1e8a) + ((?x ?.) . ?\x1e8b) + ((?X ?:) . ?\x1e8c) + ((?x ?:) . ?\x1e8d) + ((?Y ?.) . ?\x1e8e) + ((?y ?.) . ?\x1e8f) + ((?Z ?>) . ?\x1e90) + ((?z ?>) . ?\x1e91) + ((?Z ?_) . ?\x1e94) + ((?z ?_) . ?\x1e95) + ((?h ?_) . ?\x1e96) + ((?t ?:) . ?\x1e97) + ((?w ?0) . ?\x1e98) + ((?y ?0) . ?\x1e99) + ((?A ?2) . ?\x1ea2) + ((?a ?2) . ?\x1ea3) + ((?E ?2) . ?\x1eba) + ((?e ?2) . ?\x1ebb) + ((?E ??) . ?\x1ebc) + ((?e ??) . ?\x1ebd) + ((?I ?2) . ?\x1ec8) + ((?i ?2) . ?\x1ec9) + ((?O ?2) . ?\x1ece) + ((?o ?2) . ?\x1ecf) + ((?U ?2) . ?\x1ee6) + ((?u ?2) . ?\x1ee7) + ((?Y ?!) . ?\x1ef2) + ((?Y ?`) . ?\x1ef2) + ((?y ?!) . ?\x1ef3) + ((?y ?`) . ?\x1ef3) + ((?Y ?2) . ?\x1ef6) + ((?y ?2) . ?\x1ef7) + ((?Y ??) . ?\x1ef8) + ((?y ??) . ?\x1ef9) + ((?\; ?') . ?\x1f00) + ((?, ?') . ?\x1f01) + ((?\; ?!) . ?\x1f02) + ((?, ?!) . ?\x1f03) + ((?? ?\;) . ?\x1f04) + ((?? ?,) . ?\x1f05) + ((?! ?:) . ?\x1f06) + ((?? ?:) . ?\x1f07) + ((?1 ?N) . ?\x2002) + ((?1 ?M) . ?\x2003) + ((?3 ?M) . ?\x2004) + ((?4 ?M) . ?\x2005) + ((?6 ?M) . ?\x2006) + ((?1 ?T) . ?\x2009) + ((?1 ?H) . ?\x200a) + ((?- ?1) . ?\x2010) + ((?- ?N) . ?\x2013) + ((?- ?M) . ?\x2014) + ((?- ?3) . ?\x2015) + ((?! ?2) . ?\x2016) + ((?= ?2) . ?\x2017) + ((?' ?6) . ?\x2018) + ((?' ?9) . ?\x2019) + ((?. ?9) . ?\x201a) + ((?9 ?') . ?\x201b) + ((?\" ?6) . ?\x201c) + ((?\" ?9) . ?\x201d) + ((?: ?9) . ?\x201e) + ((?9 ?\") . ?\x201f) + ((?/ ?-) . ?\x2020) + ((?/ ?=) . ?\x2021) + ((?o ?o) . ?\x2022) + ((?. ?.) . ?\x2025) + ((?, ?.) . ?\x2026) + ((?% ?0) . ?\x2030) + ((?1 ?') . ?\x2032) + ((?2 ?') . ?\x2033) + ((?3 ?') . ?\x2034) + ((?1 ?\") . ?\x2035) + ((?2 ?\") . ?\x2036) + ((?3 ?\") . ?\x2037) + ((?C ?a) . ?\x2038) + ((?< ?1) . ?\x2039) + ((?> ?1) . ?\x203a) + ((?: ?X) . ?\x203b) + ((?' ?-) . ?\x203e) + ((?/ ?f) . ?\x2044) + ((?0 ?S) . ?\x2070) + ((?4 ?S) . ?\x2074) + ((?5 ?S) . ?\x2075) + ((?6 ?S) . ?\x2076) + ((?7 ?S) . ?\x2077) + ((?8 ?S) . ?\x2078) + ((?9 ?S) . ?\x2079) + ((?+ ?S) . ?\x207a) + ((?- ?S) . ?\x207b) + ((?= ?S) . ?\x207c) + ((?\( ?S) . ?\x207d) + ((?\) ?S) . ?\x207e) + ((?n ?S) . ?\x207f) + ((?0 ?s) . ?\x2080) + ((?1 ?s) . ?\x2081) + ((?2 ?s) . ?\x2082) + ((?3 ?s) . ?\x2083) + ((?4 ?s) . ?\x2084) + ((?5 ?s) . ?\x2085) + ((?6 ?s) . ?\x2086) + ((?7 ?s) . ?\x2087) + ((?8 ?s) . ?\x2088) + ((?9 ?s) . ?\x2089) + ((?+ ?s) . ?\x208a) + ((?- ?s) . ?\x208b) + ((?= ?s) . ?\x208c) + ((?\( ?s) . ?\x208d) + ((?\) ?s) . ?\x208e) + ((?L ?i) . ?\x20a4) + ((?P ?t) . ?\x20a7) + ((?W ?=) . ?\x20a9) + ((?= ?e) . ?\x20ac) + ((?E ?u) . ?\x20ac) + ((?= ?R) . ?\x20bd) + ((?= ?P) . ?\x20bd) + ((?o ?C) . ?\x2103) + ((?c ?o) . ?\x2105) + ((?o ?F) . ?\x2109) + ((?N ?0) . ?\x2116) + ((?P ?O) . ?\x2117) + ((?R ?x) . ?\x211e) + ((?S ?M) . ?\x2120) + ((?T ?M) . ?\x2122) + ((?O ?m) . ?\x2126) + ((?A ?O) . ?\x212b) + ((?1 ?3) . ?\x2153) + ((?2 ?3) . ?\x2154) + ((?1 ?5) . ?\x2155) + ((?2 ?5) . ?\x2156) + ((?3 ?5) . ?\x2157) + ((?4 ?5) . ?\x2158) + ((?1 ?6) . ?\x2159) + ((?5 ?6) . ?\x215a) + ((?1 ?8) . ?\x215b) + ((?3 ?8) . ?\x215c) + ((?5 ?8) . ?\x215d) + ((?7 ?8) . ?\x215e) + ((?1 ?R) . ?\x2160) + ((?2 ?R) . ?\x2161) + ((?3 ?R) . ?\x2162) + ((?4 ?R) . ?\x2163) + ((?5 ?R) . ?\x2164) + ((?6 ?R) . ?\x2165) + ((?7 ?R) . ?\x2166) + ((?8 ?R) . ?\x2167) + ((?9 ?R) . ?\x2168) + ((?a ?R) . ?\x2169) + ((?b ?R) . ?\x216a) + ((?c ?R) . ?\x216b) + ((?1 ?r) . ?\x2170) + ((?2 ?r) . ?\x2171) + ((?3 ?r) . ?\x2172) + ((?4 ?r) . ?\x2173) + ((?5 ?r) . ?\x2174) + ((?6 ?r) . ?\x2175) + ((?7 ?r) . ?\x2176) + ((?8 ?r) . ?\x2177) + ((?9 ?r) . ?\x2178) + ((?a ?r) . ?\x2179) + ((?b ?r) . ?\x217a) + ((?c ?r) . ?\x217b) + ((?< ?-) . ?\x2190) + ((?- ?!) . ?\x2191) + ((?- ?>) . ?\x2192) + ((?- ?v) . ?\x2193) + ((?< ?>) . ?\x2194) + ((?U ?D) . ?\x2195) + ((?< ?=) . ?\x21d0) + ((?= ?>) . ?\x21d2) + ((?= ?=) . ?\x21d4) + ((?F ?A) . ?\x2200) + ((?d ?P) . ?\x2202) + ((?T ?E) . ?\x2203) + ((?/ ?0) . ?\x2205) + ((?D ?E) . ?\x2206) + ((?N ?B) . ?\x2207) + ((?\( ?-) . ?\x2208) + ((?- ?\)) . ?\x220b) + ((?* ?P) . ?\x220f) + ((?+ ?Z) . ?\x2211) + ((?- ?2) . ?\x2212) + ((?- ?+) . ?\x2213) + ((?* ?-) . ?\x2217) + ((?O ?b) . ?\x2218) + ((?S ?b) . ?\x2219) + ((?R ?T) . ?\x221a) + ((?0 ?\() . ?\x221d) + ((?0 ?0) . ?\x221e) + ((?- ?L) . ?\x221f) + ((?- ?V) . ?\x2220) + ((?P ?P) . ?\x2225) + ((?A ?N) . ?\x2227) + ((?O ?R) . ?\x2228) + ((?\( ?U) . ?\x2229) + ((?\) ?U) . ?\x222a) + ((?I ?n) . ?\x222b) + ((?D ?I) . ?\x222c) + ((?I ?o) . ?\x222e) + ((?. ?:) . ?\x2234) + ((?: ?.) . ?\x2235) + ((?: ?R) . ?\x2236) + ((?: ?:) . ?\x2237) + ((?? ?1) . ?\x223c) + ((?C ?G) . ?\x223e) + ((?? ?-) . ?\x2243) + ((?? ?=) . ?\x2245) + ((?? ?2) . ?\x2248) + ((?= ??) . ?\x224c) + ((?H ?I) . ?\x2253) + ((?! ?=) . ?\x2260) + ((?= ?3) . ?\x2261) + ((?= ?<) . ?\x2264) + ((?> ?=) . ?\x2265) + ((?< ?*) . ?\x226a) + ((?* ?>) . ?\x226b) + ((?! ?<) . ?\x226e) + ((?! ?>) . ?\x226f) + ((?\( ?C) . ?\x2282) + ((?\) ?C) . ?\x2283) + ((?\( ?_) . ?\x2286) + ((?\) ?_) . ?\x2287) + ((?0 ?.) . ?\x2299) + ((?0 ?2) . ?\x229a) + ((?- ?T) . ?\x22a5) + ((?. ?P) . ?\x22c5) + ((?: ?3) . ?\x22ee) + ((?. ?3) . ?\x22ef) + ((?E ?h) . ?\x2302) + ((?< ?7) . ?\x2308) + ((?> ?7) . ?\x2309) + ((?7 ?<) . ?\x230a) + ((?7 ?>) . ?\x230b) + ((?N ?I) . ?\x2310) + ((?\( ?A) . ?\x2312) + ((?T ?R) . ?\x2315) + ((?I ?u) . ?\x2320) + ((?I ?l) . ?\x2321) + ((?< ?/) . ?\x2329) + ((?/ ?>) . ?\x232a) + ((?V ?s) . ?\x2423) + ((?1 ?h) . ?\x2440) + ((?3 ?h) . ?\x2441) + ((?2 ?h) . ?\x2442) + ((?4 ?h) . ?\x2443) + ((?1 ?j) . ?\x2446) + ((?2 ?j) . ?\x2447) + ((?3 ?j) . ?\x2448) + ((?4 ?j) . ?\x2449) + ((?1 ?.) . ?\x2488) + ((?2 ?.) . ?\x2489) + ((?3 ?.) . ?\x248a) + ((?4 ?.) . ?\x248b) + ((?5 ?.) . ?\x248c) + ((?6 ?.) . ?\x248d) + ((?7 ?.) . ?\x248e) + ((?8 ?.) . ?\x248f) + ((?9 ?.) . ?\x2490) + ((?h ?h) . ?\x2500) + ((?H ?H) . ?\x2501) + ((?v ?v) . ?\x2502) + ((?V ?V) . ?\x2503) + ((?3 ?-) . ?\x2504) + ((?3 ?_) . ?\x2505) + ((?3 ?!) . ?\x2506) + ((?3 ?/) . ?\x2507) + ((?4 ?-) . ?\x2508) + ((?4 ?_) . ?\x2509) + ((?4 ?!) . ?\x250a) + ((?4 ?/) . ?\x250b) + ((?d ?r) . ?\x250c) + ((?d ?R) . ?\x250d) + ((?D ?r) . ?\x250e) + ((?D ?R) . ?\x250f) + ((?d ?l) . ?\x2510) + ((?d ?L) . ?\x2511) + ((?D ?l) . ?\x2512) + ((?L ?D) . ?\x2513) + ((?u ?r) . ?\x2514) + ((?u ?R) . ?\x2515) + ((?U ?r) . ?\x2516) + ((?U ?R) . ?\x2517) + ((?u ?l) . ?\x2518) + ((?u ?L) . ?\x2519) + ((?U ?l) . ?\x251a) + ((?U ?L) . ?\x251b) + ((?v ?r) . ?\x251c) + ((?v ?R) . ?\x251d) + ((?V ?r) . ?\x2520) + ((?V ?R) . ?\x2523) + ((?v ?l) . ?\x2524) + ((?v ?L) . ?\x2525) + ((?V ?l) . ?\x2528) + ((?V ?L) . ?\x252b) + ((?d ?h) . ?\x252c) + ((?d ?H) . ?\x252f) + ((?D ?h) . ?\x2530) + ((?D ?H) . ?\x2533) + ((?u ?h) . ?\x2534) + ((?u ?H) . ?\x2537) + ((?U ?h) . ?\x2538) + ((?U ?H) . ?\x253b) + ((?v ?h) . ?\x253c) + ((?v ?H) . ?\x253f) + ((?V ?h) . ?\x2542) + ((?V ?H) . ?\x254b) + ((?F ?D) . ?\x2571) + ((?B ?D) . ?\x2572) + ((?T ?B) . ?\x2580) + ((?L ?B) . ?\x2584) + ((?F ?B) . ?\x2588) + ((?l ?B) . ?\x258c) + ((?R ?B) . ?\x2590) + ((?. ?S) . ?\x2591) + ((?: ?S) . ?\x2592) + ((?? ?S) . ?\x2593) + ((?f ?S) . ?\x25a0) + ((?O ?S) . ?\x25a1) + ((?R ?O) . ?\x25a2) + ((?R ?r) . ?\x25a3) + ((?R ?F) . ?\x25a4) + ((?R ?Y) . ?\x25a5) + ((?R ?H) . ?\x25a6) + ((?R ?Z) . ?\x25a7) + ((?R ?K) . ?\x25a8) + ((?R ?X) . ?\x25a9) + ((?s ?B) . ?\x25aa) + ((?S ?R) . ?\x25ac) + ((?O ?r) . ?\x25ad) + ((?U ?T) . ?\x25b2) + ((?u ?T) . ?\x25b3) + ((?P ?R) . ?\x25b6) + ((?T ?r) . ?\x25b7) + ((?D ?t) . ?\x25bc) + ((?d ?T) . ?\x25bd) + ((?P ?L) . ?\x25c0) + ((?T ?l) . ?\x25c1) + ((?D ?b) . ?\x25c6) + ((?D ?w) . ?\x25c7) + ((?L ?Z) . ?\x25ca) + ((?0 ?m) . ?\x25cb) + ((?0 ?o) . ?\x25ce) + ((?0 ?M) . ?\x25cf) + ((?0 ?L) . ?\x25d0) + ((?0 ?R) . ?\x25d1) + ((?S ?n) . ?\x25d8) + ((?I ?c) . ?\x25d9) + ((?F ?d) . ?\x25e2) + ((?B ?d) . ?\x25e3) + ((?* ?2) . ?\x2605) + ((?* ?1) . ?\x2606) + ((?< ?H) . ?\x261c) + ((?> ?H) . ?\x261e) + ((?0 ?u) . ?\x263a) + ((?0 ?U) . ?\x263b) + ((?S ?U) . ?\x263c) + ((?F ?m) . ?\x2640) + ((?M ?l) . ?\x2642) + ((?c ?S) . ?\x2660) + ((?c ?H) . ?\x2661) + ((?c ?D) . ?\x2662) + ((?c ?C) . ?\x2663) + ((?M ?d) . ?\x2669) + ((?M ?8) . ?\x266a) + ((?M ?2) . ?\x266b) + ((?M ?b) . ?\x266d) + ((?M ?x) . ?\x266e) + ((?M ?X) . ?\x266f) + ((?O ?K) . ?\x2713) + ((?X ?X) . ?\x2717) + ((?- ?X) . ?\x2720) + ((?I ?S) . ?\x3000) + ((?, ?_) . ?\x3001) + ((?. ?_) . ?\x3002) + ((?+ ?\") . ?\x3003) + ((?+ ?_) . ?\x3004) + ((?* ?_) . ?\x3005) + ((?\; ?_) . ?\x3006) + ((?0 ?_) . ?\x3007) + ((?< ?+) . ?\x300a) + ((?> ?+) . ?\x300b) + ((?< ?') . ?\x300c) + ((?> ?') . ?\x300d) + ((?< ?\") . ?\x300e) + ((?> ?\") . ?\x300f) + ((?\( ?\") . ?\x3010) + ((?\) ?\") . ?\x3011) + ((?= ?T) . ?\x3012) + ((?= ?_) . ?\x3013) + ((?\( ?') . ?\x3014) + ((?\) ?') . ?\x3015) + ((?\( ?I) . ?\x3016) + ((?\) ?I) . ?\x3017) + ((?- ??) . ?\x301c) + ((?A ?5) . ?\x3041) + ((?a ?5) . ?\x3042) + ((?I ?5) . ?\x3043) + ((?i ?5) . ?\x3044) + ((?U ?5) . ?\x3045) + ((?u ?5) . ?\x3046) + ((?E ?5) . ?\x3047) + ((?e ?5) . ?\x3048) + ((?O ?5) . ?\x3049) + ((?o ?5) . ?\x304a) + ((?k ?a) . ?\x304b) + ((?g ?a) . ?\x304c) + ((?k ?i) . ?\x304d) + ((?g ?i) . ?\x304e) + ((?k ?u) . ?\x304f) + ((?g ?u) . ?\x3050) + ((?k ?e) . ?\x3051) + ((?g ?e) . ?\x3052) + ((?k ?o) . ?\x3053) + ((?g ?o) . ?\x3054) + ((?s ?a) . ?\x3055) + ((?z ?a) . ?\x3056) + ((?s ?i) . ?\x3057) + ((?z ?i) . ?\x3058) + ((?s ?u) . ?\x3059) + ((?z ?u) . ?\x305a) + ((?s ?e) . ?\x305b) + ((?z ?e) . ?\x305c) + ((?s ?o) . ?\x305d) + ((?z ?o) . ?\x305e) + ((?t ?a) . ?\x305f) + ((?d ?a) . ?\x3060) + ((?t ?i) . ?\x3061) + ((?d ?i) . ?\x3062) + ((?t ?U) . ?\x3063) + ((?t ?u) . ?\x3064) + ((?d ?u) . ?\x3065) + ((?t ?e) . ?\x3066) + ((?d ?e) . ?\x3067) + ((?t ?o) . ?\x3068) + ((?d ?o) . ?\x3069) + ((?n ?a) . ?\x306a) + ((?n ?i) . ?\x306b) + ((?n ?u) . ?\x306c) + ((?n ?e) . ?\x306d) + ((?n ?o) . ?\x306e) + ((?h ?a) . ?\x306f) + ((?b ?a) . ?\x3070) + ((?p ?a) . ?\x3071) + ((?h ?i) . ?\x3072) + ((?b ?i) . ?\x3073) + ((?p ?i) . ?\x3074) + ((?h ?u) . ?\x3075) + ((?b ?u) . ?\x3076) + ((?p ?u) . ?\x3077) + ((?h ?e) . ?\x3078) + ((?b ?e) . ?\x3079) + ((?p ?e) . ?\x307a) + ((?h ?o) . ?\x307b) + ((?b ?o) . ?\x307c) + ((?p ?o) . ?\x307d) + ((?m ?a) . ?\x307e) + ((?m ?i) . ?\x307f) + ((?m ?u) . ?\x3080) + ((?m ?e) . ?\x3081) + ((?m ?o) . ?\x3082) + ((?y ?A) . ?\x3083) + ((?y ?a) . ?\x3084) + ((?y ?U) . ?\x3085) + ((?y ?u) . ?\x3086) + ((?y ?O) . ?\x3087) + ((?y ?o) . ?\x3088) + ((?r ?a) . ?\x3089) + ((?r ?i) . ?\x308a) + ((?r ?u) . ?\x308b) + ((?r ?e) . ?\x308c) + ((?r ?o) . ?\x308d) + ((?w ?A) . ?\x308e) + ((?w ?a) . ?\x308f) + ((?w ?i) . ?\x3090) + ((?w ?e) . ?\x3091) + ((?w ?o) . ?\x3092) + ((?n ?5) . ?\x3093) + ((?v ?u) . ?\x3094) + ((?\" ?5) . ?\x309b) + ((?0 ?5) . ?\x309c) + ((?* ?5) . ?\x309d) + ((?+ ?5) . ?\x309e) + ((?a ?6) . ?\x30a1) + ((?A ?6) . ?\x30a2) + ((?i ?6) . ?\x30a3) + ((?I ?6) . ?\x30a4) + ((?u ?6) . ?\x30a5) + ((?U ?6) . ?\x30a6) + ((?e ?6) . ?\x30a7) + ((?E ?6) . ?\x30a8) + ((?o ?6) . ?\x30a9) + ((?O ?6) . ?\x30aa) + ((?K ?a) . ?\x30ab) + ((?G ?a) . ?\x30ac) + ((?K ?i) . ?\x30ad) + ((?G ?i) . ?\x30ae) + ((?K ?u) . ?\x30af) + ((?G ?u) . ?\x30b0) + ((?K ?e) . ?\x30b1) + ((?G ?e) . ?\x30b2) + ((?K ?o) . ?\x30b3) + ((?G ?o) . ?\x30b4) + ((?S ?a) . ?\x30b5) + ((?Z ?a) . ?\x30b6) + ((?S ?i) . ?\x30b7) + ((?Z ?i) . ?\x30b8) + ((?S ?u) . ?\x30b9) + ((?Z ?u) . ?\x30ba) + ((?S ?e) . ?\x30bb) + ((?Z ?e) . ?\x30bc) + ((?S ?o) . ?\x30bd) + ((?Z ?o) . ?\x30be) + ((?T ?a) . ?\x30bf) + ((?D ?a) . ?\x30c0) + ((?T ?i) . ?\x30c1) + ((?D ?i) . ?\x30c2) + ((?T ?U) . ?\x30c3) + ((?T ?u) . ?\x30c4) + ((?D ?u) . ?\x30c5) + ((?T ?e) . ?\x30c6) + ((?D ?e) . ?\x30c7) + ((?T ?o) . ?\x30c8) + ((?D ?o) . ?\x30c9) + ((?N ?a) . ?\x30ca) + ((?N ?i) . ?\x30cb) + ((?N ?u) . ?\x30cc) + ((?N ?e) . ?\x30cd) + ((?N ?o) . ?\x30ce) + ((?H ?a) . ?\x30cf) + ((?B ?a) . ?\x30d0) + ((?P ?a) . ?\x30d1) + ((?H ?i) . ?\x30d2) + ((?B ?i) . ?\x30d3) + ((?P ?i) . ?\x30d4) + ((?H ?u) . ?\x30d5) + ((?B ?u) . ?\x30d6) + ((?P ?u) . ?\x30d7) + ((?H ?e) . ?\x30d8) + ((?B ?e) . ?\x30d9) + ((?P ?e) . ?\x30da) + ((?H ?o) . ?\x30db) + ((?B ?o) . ?\x30dc) + ((?P ?o) . ?\x30dd) + ((?u ?R) . ?\x2515) + ((?U ?r) . ?\x2516) + ((?U ?R) . ?\x2517) + ((?u ?l) . ?\x2518) + ((?u ?L) . ?\x2519) + ((?U ?l) . ?\x251a) + ((?U ?L) . ?\x251b) + ((?v ?r) . ?\x251c) + ((?v ?R) . ?\x251d) + ((?V ?r) . ?\x2520) + ((?V ?R) . ?\x2523) + ((?v ?l) . ?\x2524) + ((?v ?L) . ?\x2525) + ((?V ?l) . ?\x2528) + ((?V ?L) . ?\x252b) + ((?d ?h) . ?\x252c) + ((?d ?H) . ?\x252f) + ((?D ?h) . ?\x2530) + ((?D ?H) . ?\x2533) + ((?u ?h) . ?\x2534) + ((?u ?H) . ?\x2537) + ((?U ?h) . ?\x2538) + ((?U ?H) . ?\x253b) + ((?v ?h) . ?\x253c) + ((?v ?H) . ?\x253f) + ((?V ?h) . ?\x2542) + ((?V ?H) . ?\x254b) + ((?F ?D) . ?\x2571) + ((?B ?D) . ?\x2572) + ((?T ?B) . ?\x2580) + ((?L ?B) . ?\x2584) + ((?F ?B) . ?\x2588) + ((?l ?B) . ?\x258c) + ((?R ?B) . ?\x2590) + ((?. ?S) . ?\x2591) + ((?: ?S) . ?\x2592) + ((?? ?S) . ?\x2593) + ((?f ?S) . ?\x25a0) + ((?O ?S) . ?\x25a1) + ((?R ?O) . ?\x25a2) + ((?R ?r) . ?\x25a3) + ((?R ?F) . ?\x25a4) + ((?R ?Y) . ?\x25a5) + ((?R ?H) . ?\x25a6) + ((?R ?Z) . ?\x25a7) + ((?R ?K) . ?\x25a8) + ((?R ?X) . ?\x25a9) + ((?s ?B) . ?\x25aa) + ((?S ?R) . ?\x25ac) + ((?O ?r) . ?\x25ad) + ((?U ?T) . ?\x25b2) + ((?u ?T) . ?\x25b3) + ((?P ?R) . ?\x25b6) + ((?T ?r) . ?\x25b7) + ((?D ?t) . ?\x25bc) + ((?d ?T) . ?\x25bd) + ((?P ?L) . ?\x25c0) + ((?T ?l) . ?\x25c1) + ((?D ?b) . ?\x25c6) + ((?D ?w) . ?\x25c7) + ((?L ?Z) . ?\x25ca) + ((?0 ?m) . ?\x25cb) + ((?0 ?o) . ?\x25ce) + ((?0 ?M) . ?\x25cf) + ((?0 ?L) . ?\x25d0) + ((?0 ?R) . ?\x25d1) + ((?S ?n) . ?\x25d8) + ((?I ?c) . ?\x25d9) + ((?F ?d) . ?\x25e2) + ((?B ?d) . ?\x25e3) + ((?* ?2) . ?\x2605) + ((?* ?1) . ?\x2606) + ((?< ?H) . ?\x261c) + ((?> ?H) . ?\x261e) + ((?0 ?u) . ?\x263a) + ((?0 ?U) . ?\x263b) + ((?S ?U) . ?\x263c) + ((?F ?m) . ?\x2640) + ((?M ?l) . ?\x2642) + ((?c ?S) . ?\x2660) + ((?c ?H) . ?\x2661) + ((?c ?D) . ?\x2662) + ((?c ?C) . ?\x2663) + ((?M ?d) . ?\x2669) + ((?M ?8) . ?\x266a) + ((?M ?2) . ?\x266b) + ((?M ?b) . ?\x266d) + ((?M ?x) . ?\x266e) + ((?M ?X) . ?\x266f) + ((?O ?K) . ?\x2713) + ((?X ?X) . ?\x2717) + ((?- ?X) . ?\x2720) + ((?I ?S) . ?\x3000) + ((?, ?_) . ?\x3001) + ((?. ?_) . ?\x3002) + ((?+ ?\") . ?\x3003) + ((?+ ?_) . ?\x3004) + ((?* ?_) . ?\x3005) + ((?\; ?_) . ?\x3006) + ((?0 ?_) . ?\x3007) + ((?< ?+) . ?\x300a) + ((?> ?+) . ?\x300b) + ((?< ?') . ?\x300c) + ((?> ?') . ?\x300d) + ((?< ?\") . ?\x300e) + ((?> ?\") . ?\x300f) + ((?\( ?\") . ?\x3010) + ((?\) ?\") . ?\x3011) + ((?= ?T) . ?\x3012) + ((?= ?_) . ?\x3013) + ((?\( ?') . ?\x3014) + ((?\) ?') . ?\x3015) + ((?\( ?I) . ?\x3016) + ((?\) ?I) . ?\x3017) + ((?- ??) . ?\x301c) + ((?A ?5) . ?\x3041) + ((?a ?5) . ?\x3042) + ((?I ?5) . ?\x3043) + ((?i ?5) . ?\x3044) + ((?U ?5) . ?\x3045) + ((?u ?5) . ?\x3046) + ((?E ?5) . ?\x3047) + ((?e ?5) . ?\x3048) + ((?O ?5) . ?\x3049) + ((?o ?5) . ?\x304a) + ((?k ?a) . ?\x304b) + ((?g ?a) . ?\x304c) + ((?k ?i) . ?\x304d) + ((?g ?i) . ?\x304e) + ((?k ?u) . ?\x304f) + ((?g ?u) . ?\x3050) + ((?k ?e) . ?\x3051) + ((?g ?e) . ?\x3052) + ((?k ?o) . ?\x3053) + ((?g ?o) . ?\x3054) + ((?s ?a) . ?\x3055) + ((?z ?a) . ?\x3056) + ((?s ?i) . ?\x3057) + ((?z ?i) . ?\x3058) + ((?s ?u) . ?\x3059) + ((?z ?u) . ?\x305a) + ((?s ?e) . ?\x305b) + ((?z ?e) . ?\x305c) + ((?s ?o) . ?\x305d) + ((?z ?o) . ?\x305e) + ((?t ?a) . ?\x305f) + ((?d ?a) . ?\x3060) + ((?t ?i) . ?\x3061) + ((?d ?i) . ?\x3062) + ((?t ?U) . ?\x3063) + ((?t ?u) . ?\x3064) + ((?d ?u) . ?\x3065) + ((?t ?e) . ?\x3066) + ((?d ?e) . ?\x3067) + ((?t ?o) . ?\x3068) + ((?d ?o) . ?\x3069) + ((?n ?a) . ?\x306a) + ((?n ?i) . ?\x306b) + ((?n ?u) . ?\x306c) + ((?n ?e) . ?\x306d) + ((?n ?o) . ?\x306e) + ((?h ?a) . ?\x306f) + ((?b ?a) . ?\x3070) + ((?p ?a) . ?\x3071) + ((?h ?i) . ?\x3072) + ((?b ?i) . ?\x3073) + ((?p ?i) . ?\x3074) + ((?h ?u) . ?\x3075) + ((?b ?u) . ?\x3076) + ((?p ?u) . ?\x3077) + ((?h ?e) . ?\x3078) + ((?b ?e) . ?\x3079) + ((?p ?e) . ?\x307a) + ((?h ?o) . ?\x307b) + ((?b ?o) . ?\x307c) + ((?p ?o) . ?\x307d) + ((?m ?a) . ?\x307e) + ((?m ?i) . ?\x307f) + ((?m ?u) . ?\x3080) + ((?m ?e) . ?\x3081) + ((?m ?o) . ?\x3082) + ((?y ?A) . ?\x3083) + ((?y ?a) . ?\x3084) + ((?y ?U) . ?\x3085) + ((?y ?u) . ?\x3086) + ((?y ?O) . ?\x3087) + ((?y ?o) . ?\x3088) + ((?r ?a) . ?\x3089) + ((?r ?i) . ?\x308a) + ((?r ?u) . ?\x308b) + ((?r ?e) . ?\x308c) + ((?r ?o) . ?\x308d) + ((?w ?A) . ?\x308e) + ((?w ?a) . ?\x308f) + ((?w ?i) . ?\x3090) + ((?w ?e) . ?\x3091) + ((?w ?o) . ?\x3092) + ((?n ?5) . ?\x3093) + ((?v ?u) . ?\x3094) + ((?\" ?5) . ?\x309b) + ((?0 ?5) . ?\x309c) + ((?* ?5) . ?\x309d) + ((?+ ?5) . ?\x309e) + ((?a ?6) . ?\x30a1) + ((?A ?6) . ?\x30a2) + ((?i ?6) . ?\x30a3) + ((?I ?6) . ?\x30a4) + ((?u ?6) . ?\x30a5) + ((?U ?6) . ?\x30a6) + ((?e ?6) . ?\x30a7) + ((?E ?6) . ?\x30a8) + ((?o ?6) . ?\x30a9) + ((?O ?6) . ?\x30aa) + ((?K ?a) . ?\x30ab) + ((?G ?a) . ?\x30ac) + ((?K ?i) . ?\x30ad) + ((?G ?i) . ?\x30ae) + ((?K ?u) . ?\x30af) + ((?G ?u) . ?\x30b0) + ((?K ?e) . ?\x30b1) + ((?G ?e) . ?\x30b2) + ((?K ?o) . ?\x30b3) + ((?G ?o) . ?\x30b4) + ((?S ?a) . ?\x30b5) + ((?Z ?a) . ?\x30b6) + ((?S ?i) . ?\x30b7) + ((?Z ?i) . ?\x30b8) + ((?S ?u) . ?\x30b9) + ((?Z ?u) . ?\x30ba) + ((?S ?e) . ?\x30bb) + ((?Z ?e) . ?\x30bc) + ((?S ?o) . ?\x30bd) + ((?Z ?o) . ?\x30be) + ((?T ?a) . ?\x30bf) + ((?D ?a) . ?\x30c0) + ((?T ?i) . ?\x30c1) + ((?D ?i) . ?\x30c2) + ((?T ?U) . ?\x30c3) + ((?T ?u) . ?\x30c4) + ((?D ?u) . ?\x30c5) + ((?T ?e) . ?\x30c6) + ((?D ?e) . ?\x30c7) + ((?T ?o) . ?\x30c8) + ((?D ?o) . ?\x30c9) + ((?N ?a) . ?\x30ca) + ((?N ?i) . ?\x30cb) + ((?N ?u) . ?\x30cc) + ((?N ?e) . ?\x30cd) + ((?N ?o) . ?\x30ce) + ((?H ?a) . ?\x30cf) + ((?B ?a) . ?\x30d0) + ((?P ?a) . ?\x30d1) + ((?H ?i) . ?\x30d2) + ((?B ?i) . ?\x30d3) + ((?P ?i) . ?\x30d4) + ((?H ?u) . ?\x30d5) + ((?B ?u) . ?\x30d6) + ((?P ?u) . ?\x30d7) + ((?H ?e) . ?\x30d8) + ((?B ?e) . ?\x30d9) + ((?P ?e) . ?\x30da) + ((?H ?o) . ?\x30db) + ((?B ?o) . ?\x30dc) + ((?P ?o) . ?\x30dd) + ((?M ?a) . ?\x30de) + ((?M ?i) . ?\x30df) + ((?M ?u) . ?\x30e0) + ((?M ?e) . ?\x30e1) + ((?M ?o) . ?\x30e2) + ((?Y ?A) . ?\x30e3) + ((?Y ?a) . ?\x30e4) + ((?Y ?U) . ?\x30e5) + ((?Y ?u) . ?\x30e6) + ((?Y ?O) . ?\x30e7) + ((?Y ?o) . ?\x30e8) + ((?R ?a) . ?\x30e9) + ((?R ?i) . ?\x30ea) + ((?R ?u) . ?\x30eb) + ((?R ?e) . ?\x30ec) + ((?R ?o) . ?\x30ed) + ((?W ?A) . ?\x30ee) + ((?W ?a) . ?\x30ef) + ((?W ?i) . ?\x30f0) + ((?W ?e) . ?\x30f1) + ((?W ?o) . ?\x30f2) + ((?N ?6) . ?\x30f3) + ((?V ?u) . ?\x30f4) + ((?K ?A) . ?\x30f5) + ((?K ?E) . ?\x30f6) + ((?V ?a) . ?\x30f7) + ((?V ?i) . ?\x30f8) + ((?V ?e) . ?\x30f9) + ((?V ?o) . ?\x30fa) + ((?. ?6) . ?\x30fb) + ((?- ?6) . ?\x30fc) + ((?* ?6) . ?\x30fd) + ((?+ ?6) . ?\x30fe) + ((?b ?4) . ?\x3105) + ((?p ?4) . ?\x3106) + ((?m ?4) . ?\x3107) + ((?f ?4) . ?\x3108) + ((?d ?4) . ?\x3109) + ((?t ?4) . ?\x310a) + ((?n ?4) . ?\x310b) + ((?l ?4) . ?\x310c) + ((?g ?4) . ?\x310d) + ((?k ?4) . ?\x310e) + ((?h ?4) . ?\x310f) + ((?j ?4) . ?\x3110) + ((?q ?4) . ?\x3111) + ((?x ?4) . ?\x3112) + ((?z ?h) . ?\x3113) + ((?c ?h) . ?\x3114) + ((?s ?h) . ?\x3115) + ((?r ?4) . ?\x3116) + ((?z ?4) . ?\x3117) + ((?c ?4) . ?\x3118) + ((?s ?4) . ?\x3119) + ((?a ?4) . ?\x311a) + ((?o ?4) . ?\x311b) + ((?e ?4) . ?\x311c) + ((?a ?i) . ?\x311e) + ((?e ?i) . ?\x311f) + ((?a ?u) . ?\x3120) + ((?o ?u) . ?\x3121) + ((?a ?n) . ?\x3122) + ((?e ?n) . ?\x3123) + ((?a ?N) . ?\x3124) + ((?e ?N) . ?\x3125) + ((?e ?r) . ?\x3126) + ((?i ?4) . ?\x3127) + ((?u ?4) . ?\x3128) + ((?i ?u) . ?\x3129) + ((?v ?4) . ?\x312a) + ((?n ?G) . ?\x312b) + ((?g ?n) . ?\x312c) + ((?1 ?c) . ?\x3220) + ((?2 ?c) . ?\x3221) + ((?3 ?c) . ?\x3222) + ((?4 ?c) . ?\x3223) + ((?5 ?c) . ?\x3224) + ((?6 ?c) . ?\x3225) + ((?7 ?c) . ?\x3226) + ((?8 ?c) . ?\x3227) + ((?9 ?c) . ?\x3228) + ((?\s ?\s) . ?\xe000) + ((?/ ?c) . ?\xe001) + ((?U ?A) . ?\xe002) + ((?U ?B) . ?\xe003) + ((?\" ?3) . ?\xe004) + ((?\" ?1) . ?\xe005) + ((?\" ?!) . ?\xe006) + ((?\" ?') . ?\xe007) + ((?\" ?>) . ?\xe008) + ((?\" ??) . ?\xe009) + ((?\" ?-) . ?\xe00a) + ((?\" ?\() . ?\xe00b) + ((?\" ?.) . ?\xe00c) + ((?\" ?:) . ?\xe00d) + ((?\" ?0) . ?\xe00e) + ((?\" ?\") . ?\xe00f) + ((?\" ?<) . ?\xe010) + ((?\" ?,) . ?\xe011) + ((?\" ?\;) . ?\xe012) + ((?\" ?_) . ?\xe013) + ((?\" ?=) . ?\xe014) + ((?\" ?/) . ?\xe015) + ((?\" ?i) . ?\xe016) + ((?\" ?d) . ?\xe017) + ((?\" ?p) . ?\xe018) + ((?\; ?\;) . ?\xe019) + ((?, ?,) . ?\xe01a) + ((?b ?3) . ?\xe01b) + ((?C ?i) . ?\xe01c) + ((?f ?\() . ?\xe01d) + ((?e ?d) . ?\xe01e) + ((?a ?m) . ?\xe01f) + ((?p ?m) . ?\xe020) + ((?F ?l) . ?\xe023) + ((?G ?F) . ?\xe024) + ((?> ?V) . ?\xe025) + ((?! ?*) . ?\xe026) + ((?? ?*) . ?\xe027) + ((?J ?<) . ?\xe028) + ((?f ?f) . ?\xfb00) + ((?f ?i) . ?\xfb01) + ((?f ?l) . ?\xfb02) + ((?f ?t) . ?\xfb05) + ((?s ?t) . ?\xfb06) + ((?~ ?!) . ?\x00a1) + ((?c ?|) . ?\x00a2) + ((?$ ?$) . ?\x00a3) + ((?o ?x) . ?\x00a4) + ((?Y ?-) . ?\x00a5) + ((?| ?|) . ?\x00a6) + ((?c ?O) . ?\x00a9) + ((?- ?,) . ?\x00ac) + ((?- ?=) . ?\x00af) + ((?~ ?o) . ?\x00b0) + ((?2 ?2) . ?\x00b2) + ((?3 ?3) . ?\x00b3) + ((?p ?p) . ?\x00b6) + ((?~ ?.) . ?\x00b7) + ((?1 ?1) . ?\x00b9) + ((?~ ??) . ?\x00bf) + ((?A ?`) . ?\x00c0) + ((?A ?^) . ?\x00c2) + ((?A ?~) . ?\x00c3) + ((?A ?\") . ?\x00c4) + ((?A ?@) . ?\x00c5) + ((?E ?`) . ?\x00c8) + ((?E ?^) . ?\x00ca) + ((?E ?\") . ?\x00cb) + ((?I ?`) . ?\x00cc) + ((?I ?^) . ?\x00ce) + ((?I ?\") . ?\x00cf) + ((?N ?~) . ?\x00d1) + ((?O ?`) . ?\x00d2) + ((?O ?^) . ?\x00d4) + ((?O ?~) . ?\x00d5) + ((?/ ?\\) . ?\x00d7) + ((?U ?`) . ?\x00d9) + ((?U ?^) . ?\x00db) + ((?I ?p) . ?\x00de) + ((?a ?`) . ?\x00e0) + ((?a ?^) . ?\x00e2) + ((?a ?~) . ?\x00e3) + ((?a ?\") . ?\x00e4) + ((?a ?@) . ?\x00e5) + ((?e ?`) . ?\x00e8) + ((?e ?^) . ?\x00ea) + ((?e ?\") . ?\x00eb) + ((?i ?`) . ?\x00ec) + ((?i ?^) . ?\x00ee) + ((?n ?~) . ?\x00f1) + ((?o ?`) . ?\x00f2) + ((?o ?^) . ?\x00f4) + ((?o ?~) . ?\x00f5) + ((?u ?`) . ?\x00f9) + ((?u ?^) . ?\x00fb) + ((?y ?\") . ?\x00ff)) + "Table of default digraphs. +This includes all digraphs defined in RFC 1345, +as well as miscellaneous digraphs for multi-byte characters. +See also `evil-digraphs-table-user'.") + +;;;###autoload +(defun evil-digraph (digraph) + "Convert DIGRAPH to character or list representation. +If DIGRAPH is a list (CHAR1 CHAR2), return the corresponding character; +if DIGRAPH is a character, return the corresponding list. +Searches in `evil-digraphs-table-user' and `evil-digraphs-table'." + (if (listp digraph) + (or (cdr (assoc digraph evil-digraphs-table-user)) + (cdr (assoc digraph evil-digraphs-table)) + (cl-destructuring-bind (char1 char2) digraph + (unless (eq char1 char2) + (or (cdr (assoc (list char2 char1) evil-digraphs-table-user)) + (cdr (assoc (list char2 char1) evil-digraphs-table)))))) + (or (car (rassoc digraph evil-digraphs-table-user)) + (car (rassoc digraph evil-digraphs-table))))) + +(provide 'evil-digraphs) + +;;; evil-digraphs.el ends here diff --git a/.config/emacs/lisp/evil/evil-ex.el b/.config/emacs/lisp/evil/evil-ex.el new file mode 100644 index 0000000..94edd6e --- /dev/null +++ b/.config/emacs/lisp/evil/evil-ex.el @@ -0,0 +1,940 @@ +;;; evil-ex.el --- Ex mode -*- lexical-binding: t; -*- + +;; Author: Frank Fischer +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; Ex is implemented as an extensible minilanguage, whose grammar +;; is stored in `evil-ex-grammar'. Ex commands are defined with +;; `evil-ex-define-cmd', which creates a binding from a string +;; to an interactive function. It is also possible to define key +;; sequences which execute a command immediately when entered: +;; such shortcuts go in `evil-ex-shortcut-map'. + +;; To provide buffer and filename completion, as well as interactive +;; feedback, Ex defines the concept of an argument handler, specified +;; with `evil-ex-define-argument-type'. In the case of the +;; substitution command (":s/foo/bar"), the handler incrementally +;; highlights matches in the buffer as the substitution is typed. + +;;; Code: + +(require 'evil-common) +(require 'evil-states) + +(declare-function evil-goto-line "evil-commands") + +(eval-when-compile + (defconst evil-ex-grammar + '((expression + (count command argument #'evil-ex-call-command) + ((\? range) command argument #'evil-ex-call-command) + (line #'evil-goto-line) + (sexp #'eval-expression)) + (count + number) + (command #'evil-ex-parse-command) + (binding + "[~&*@<>=:#]+\\|[[:alpha:]_]+\\|!") + (emacs-binding + "[[:alpha:]-][[:alnum:][:punct:]]*") + (argument + ((\? "\\(?:.\\|\n\\)+") #'$1)) + (range + ("%" #'(evil-ex-full-range)) + ("*" #'(evil-ex-last-visual-range)) + ((\? line) "[,;]" (\? line) + #'(let ((l1 $1)) + (save-excursion + (and l1 (string= $2 ";") (goto-line l1)) + (evil-ex-range l1 $3)))) + (line #'evil-ex-range) + ("`" marker-name ",`" marker-name + #'(evil-ex-char-marker-range $2 $4))) + (line + ((\? base) (\? offset) search (\? offset) + #'(save-excursion + (goto-line (evil-ex-line $1 $2)) + (evil-ex-line $3 $4))) + (base (\? offset) #'evil-ex-line) + (nil offset #'evil-ex-line)) + (base + number + ("'" marker-name #'(evil-ex-marker $2)) + search + ("\\^" #'(evil-ex-first-line)) + ("\\$" #'(evil-ex-last-line)) + ("\\." #'(evil-ex-current-line))) + (offset + (+ signed-number #'+)) + ;; TODO - handle offset & ;next-pattern search elements + (search + forward + backward + next + prev + subst) + (forward + ("/" "\\(?:\\\\.\\|[^/]\\)+" "/\\|$" #'(evil-ex-re-fwd $2))) + (backward + ("?" "\\(?:\\\\.\\|[^?]\\)+" "?\\|$" #'(evil-ex-re-bwd $2))) + (marker-name + "[]\\[-a-zA-Z_<>'}{)(]") + (next + "\\\\/" #'(evil-ex-prev-search)) + (prev + "\\\\\\?" #'(evil-ex-prev-search)) + (subst + "\\\\&" #'(evil-ex-prev-search)) + (signed-number + (sign (\? number) #'evil-ex-signed-number)) + (sign + "[+-]" #'intern) + (number + "[0-9]+" #'string-to-number) + (sexp + "(.*)?" #'(car (read-from-string $0)))) + "Grammar for Ex. +An association list of syntactic symbols and their definitions. +The first entry is the start symbol. A symbol's definition may +reference other symbols, but the grammar cannot contain +left recursion. See `evil-parser' for a detailed explanation +of the syntax.") + + (defun evil-parser--dexp (obj) + "Parse a numerical dollar-sign symbol. +Given e.g. $4, return 4." + (when (symbolp obj) + (let ((str (symbol-name obj))) + (when (string-match "\\`\\$\\([0-9]+\\)\\'" str) + (string-to-number (match-string 1 str)))))) + + (defmacro evil-parser (grammar &rest entrypoints) + "Construct a parser for GRAMMAR with ENTRYPOINTS. +The result is a function taking the arguments SYMBOL and SYNTAX, that +parses the text after point. SYMBOL should be one of ENTRYPOINTS. + +If parsing succeeds, point is moved to the end of the parsed text and +a 1-tuple (RESULT) is returned. Otherwise, the return value is nil. + +GRAMMAR is an association list of symbols and their definitions. +A definition is a list of production rules, which are tried in +succession. A production rule can be one of the following: + + nil matches the empty string. + A regular expression matches a substring. + A symbol matches a production for that symbol. + (X Y) matches X followed by Y. + (\\? X) matches zero or one of X. + (* X) matches zero or more of X. + (+ X) matches one or more of X. + (& X) matches X, but does not consume. + (! X) matches anything but X, but does not consume. + +Thus, a simple grammar may look like: + + ((plus \"\\\\+\") ; plus <- \"+\" + (minus \"-\") ; minus <- \"-\" + (operator plus minus)) ; operator <- plus / minus + +All input-consuming rules have a value. A regular expression evaluates +to the text matched, while a list evaluates to a list of values. +The value of a list may be overridden with a semantic action, which is +specified with a #\\='-quoted expression at the end: + + (X Y #\\='foo) + +The value of this rule is the result of calling foo with the values +of X and Y as arguments. Alternatively, the function call may be +specified explicitly: + + (X Y #\\='(foo $1 $2)) + +Here, $1 refers to X and $2 refers to Y. $0 refers to the whole list. +Dollar expressions can also be used directly: + + (X Y #\\='$1) + +This matches X followed by Y, but ignores the value of Y; +the value of the list is the same as the value of X. + +If the SYNTAX argument is non-nil, then all semantic actions +are ignored, and a syntax tree is constructed instead. The +syntax tree obeys the property that all the leaf nodes are +parts of the input string. Thus, by traversing the syntax tree, +one can determine how each character was parsed. + +The following symbols have reserved meanings within a grammar: +`\\?', `*', `+', `&', `!', `function', `alt', `seq' and nil." + (cl-labels + ;; Return code for parsing PRODUCTION. + ((compile + (production) + (pcase production + ('nil '(list (when syntax (point)))) ; Epsilon + ((and (pred stringp) regexp) ; Token + `(when (progn + (skip-chars-forward " \t\n\r") ; Ignore leading whitespace + (looking-at ,regexp)) + (goto-char (match-end 0)) + (list (if syntax (point) (match-string 0))))) + ((and (pred symbolp) symbol) ; Symbol + `(let ((result (,symbol syntax))) + (and syntax result (push ',symbol (car result))) + result)) + (`(function ,fun) ; Function + `(let ((result (funcall #',fun))) + (and syntax result (setcar result (point))) + result)) + ;; Positive lookahead + (`(& . ,rule) `(when ,(compile rule) ,(compile nil))) + ;; Negative lookahead + (`(! . ,rule) `(unless ,(compile rule) ,(compile nil))) + ;; Zero or one + (`(\? . ,(or `(,rule) rule)) (compile `(alt ,rule nil))) + ;; Zero or more + (`(* . ,rules) (compile `(alt (+ ,@rules) nil))) + ;; Lists + ((or `(,(and (or '+ 'alt 'seq) symbol) . ,rules) + (and (pred listp) rules (let symbol 'seq))) + (let ((func (unless (eq symbol 'alt) #'list))) + (pcase (when (> (length rules) 1) (car (last rules))) + (`(function ,x) (setq func x + rules (butlast rules)))) + `(let ((cell + ,(pcase symbol + ('+ ; One or more + (when (cdr rules) (error "Too many `+' rules")) + `(cl-loop for x = ,(compile (car rules)) + while x collect (car x) into result until (eobp) + finally return (when result (list result)))) + ('alt `(or ,@(mapcar #'compile rules))) + ('seq + (cl-loop + for rule in rules collect + (macroexp-let2 nil x (compile rule) + (if (memq (car-safe rule) '(& !)) x + `(when ,x (push (car ,x) result)))) + into items finally return + `(let ((pos (point)) result) + (if (and ,@items) (list (nreverse result)) + (goto-char pos) + nil))))))) + ;; Semantic action + (when (and ',func cell (not syntax)) + (setcar + cell + (let ((result (car cell))) + (ignore result) ; Suppress unused var warning + ,(pcase func + ;; Dollar expression + ((or (pred evil-parser--dexp) (pred listp)) + (dval func)) + ((pred symbolp) + `(,(if (eq symbol 'alt) #'list #'cons) #',func result)) + (_ (error "Invalid semantic action `%S'" func)))))) + cell))))) + ;; Substitute all dollar-sign symbols in X. + ;; Each dollar-sign symbol is replaced with the corresponding + ;; element in RESULT, so that $1 becomes the first element, etc. + ;; The special value $0 is substituted with the whole list RESULT. + (dval + (x) + (if (listp x) (cons #'list (mapcar #'dval x)) + (let ((num (evil-parser--dexp x))) + (cond ((null num) `',x) + ((eq num 0) 'result) + (t `(nth (1- ,num) result))))))) + `(lambda (symbol &optional syntax) + (cl-labels + (,@(cl-loop for (symbol . def) in (eval grammar t) collect + `(,symbol (syntax) ,(compile `(alt . ,def)))) + (evil-ex-parse-command + () + (let* ((result (binding nil)) + (command (car result))) + (cond + ((not result) nil) + ;; Check whether the parsed command is followed by a slash, + ;; dash or number and either the part before is NOT known to be + ;; a binding, or the complete string IS known to be a binding. + ((and (let ((ch (char-after))) + (and ch (or (memq ch '(?- ?/)) (<= ?0 ch ?9)))) + (or (evil-ex-binding + (concat command (buffer-substring (point) (point-max))) t) + (not (evil-ex-binding command t)))) + (backward-char (length command)) + (emacs-binding nil)) + ;; Parse a following "!" as bang only if the command + ;; has the property :ex-bang t. + ((when (eq (char-after) ?!) + (let ((binding (evil-ex-completed-binding command t))) + (and binding (evil-get-command-property binding :ex-bang)))) + (forward-char) + (setcar result (concat command "!")) + result) + (t result))))) + (pcase symbol + ,@(cl-loop for sym in entrypoints collect `(',sym ,(compile sym))) + (_ (error "Unknown entrypoint `%s'" symbol)))))))) + +(defvar evil-ex-argument-types nil + "Association list of argument handlers.") + +(defvar evil-ex-commands nil + "Association list of command bindings and functions.") + +(defvar evil-ex-reverse-range nil + "Whether the current Ex range was entered reversed.") + +(defvar evil--ex-cmd nil + "The current Ex command string.") + +(defvar evil--ex-argument-handler nil + "The argument handler for the current Ex command.") + +(defvar evil--ex-shortcut-command nil + "Interactive command to use instead of an Ex command.") + +(define-error 'evil-ex-error "Ex syntax error") + +(defun evil-ex-p () + "Return non-nil if currently in Ex state." + (when evil-ex-original-buffer t)) + +(evil-define-command evil-ex (&optional initial-input) + "Enter an Ex command. +The Ex command line is initialized with the value of INITIAL-INPUT. If +the command is called interactively the initial input depends on the +current state. In Normal state if a prefix count is given then the +initial input is \".,.+count\", otherwise it is empty. In Visual state +the initial input is the visual region '<,'> or `<,`>. The variable +`evil-ex-initial-input', if non-nil, is appended to the line." + :keep-visual t + :repeat abort + (interactive + (let ((s (concat + (cond + ((and (evil-visual-state-p) + evil-ex-visual-char-range + (memq (evil-visual-type) '(inclusive exclusive))) + "`<,`>") + ((evil-visual-state-p) "'<,'>") + (current-prefix-arg + (let ((arg (prefix-numeric-value current-prefix-arg))) + (cond ((< arg 0) (setq arg (1+ arg))) + ((> arg 0) (setq arg (1- arg)))) + (if (= arg 0) "." (format ".,.%+d" arg))))) + evil-ex-initial-input))) + (list (unless (string= s "") s)))) + (let ((buffer (current-buffer)) + (previous-command (when evil-want-empty-ex-last-command + (car evil-ex-history))) + s evil--ex-cmd evil--ex-argument-handler evil--ex-shortcut-command) + (minibuffer-with-setup-hook + (lambda () + (setq-local evil-ex-original-buffer buffer) + (evil-ex-setup) + (if initial-input (evil--ex-update) + (when previous-command + (add-hook 'pre-command-hook #'evil-ex-remove-default nil t)))) + (setq s (read-from-minibuffer + ":" + (or initial-input + (and previous-command (propertize previous-command 'face 'shadow))) + evil-ex-completion-map nil 'evil-ex-history))) + (if evil--ex-shortcut-command + (call-interactively evil--ex-shortcut-command) + (when (string= s "") (setq s previous-command)) + (unless (string= s "") (evil-ex-execute s))))) + +(defun evil-ex-execute (string) + "Execute STRING as an Ex command." + (eval (or (evil-ex-parse string) (signal 'evil-ex-error string)) t)) + +(defun evil-ex-parse (&optional string syntax entrypoint) + "Parse STRING as an Ex expression and return its evaluation tree. +If STRING is nil, parse the text after point instead. If SYNTAX is +non-nil, return a syntax tree instead. ENTRYPOINT is the start +symbol, which defaults to `expression'." + (if string + (with-temp-buffer + (insert string) + (goto-char (point-min)) + (evil-ex-parse nil syntax entrypoint)) + (let ((result (funcall (evil-parser evil-ex-grammar expression range) + (or entrypoint 'expression) syntax))) + (and result + ;; Disallow incomplete matches (ignore trailing WS) + (not (search-forward "[^ \t\n\r]" nil t)) + (car result))))) + +(defun evil-ex-delete-backward-char () + "Close the minibuffer if it is empty, otherwise call `delete-backward-char'." + (interactive) + (call-interactively + (if (< (minibuffer-prompt-end) (point-max)) + #'delete-backward-char + #'abort-recursive-edit))) + +(cl-defstruct (evil-ex-argument-handler (:type list) (:constructor nil) + (:copier nil) (:predicate nil)) + (runner nil :read-only t) (completer nil :read-only t)) + +(defun evil-ex-setup () + "Initialize Ex minibuffer. +This function registers hooks that are used for the interactive +actions during Ex state." + (add-hook 'after-change-functions #'evil--ex-update nil t) + (add-hook 'minibuffer-exit-hook #'evil-ex-teardown nil t) + (add-hook 'completion-at-point-functions #'evil-ex-completion-at-point nil t)) + +(defsubst evil--ex-bang-p (command) + "Return non-nil if the string COMMAND has a \"!\" suffix." + (and (> (length command) 1) (eq (aref command (1- (length command))) ?!))) + +(defun evil--ex-update (&optional beg _end _old-len) + "Update Ex variables when the minibuffer changes. +This function is usually called from `after-change-functions' +hook. If BEG is non-nil (which is the case when called from +`after-change-functions'), then an error description is shown +in case of incomplete or unknown commands." + (when (and beg (eq this-command #'self-insert-command)) + (let ((cmd (lookup-key evil-ex-shortcut-map (minibuffer-contents-no-properties)))) + (when (commandp cmd) + (setq evil--ex-shortcut-command cmd) + (exit-minibuffer)))) + + (setq evil--ex-cmd nil) + (let ((expr (save-excursion (goto-char (minibuffer-prompt-end)) + (evil-ex-parse))) + func handler current-prefix-arg evil-ex-range evil-ex-bang evil-ex-argument) + (when (eq (car expr) #'evil-ex-call-command) + (with-current-buffer evil-ex-original-buffer + (let* ((range (eval (nth 1 expr) t)) + (count (when (integerp range) range))) + (setq current-prefix-arg count + evil-ex-range (if count (evil-ex-range count count) range) + evil--ex-cmd (eval (nth 2 expr) t) + evil-ex-bang (evil--ex-bang-p evil--ex-cmd) + evil-ex-argument (eval (nth 3 expr) t)))) + (cond + ((not beg)) + ;; Test the current command when called from `after-change-functions' + ((setq func (evil-ex-completed-binding evil--ex-cmd t)) + ;; Update argument handler + (let ((type (evil-get-command-property func :ex-arg))) + (when type (setq handler (cdr (assq type evil-ex-argument-types))))) + (if (eq handler evil--ex-argument-handler) + (let ((runner (evil-ex-argument-handler-runner handler))) + (when runner (funcall runner 'update evil-ex-argument))) + (let ((runner (evil-ex-argument-handler-runner evil--ex-argument-handler))) + (when runner (funcall runner 'stop))) + (setq evil--ex-argument-handler handler) + (let ((runner (evil-ex-argument-handler-runner handler))) + (when runner (funcall runner 'start evil-ex-argument))))) + (t (let* ((evil-ex-complete-emacs-commands 'in-turn) + (prefix (try-completion evil--ex-cmd (evil-ex-completion-table)))) + (cond ((stringp prefix) (evil-ex-echo "Incomplete command")) + ((null prefix) (evil-ex-echo "Unknown command"))))))))) + +(defun evil-ex-teardown () + "Deinitialize Ex minibuffer. +Clean up everything set up by `evil-ex-setup'." + (let ((runner (evil-ex-argument-handler-runner evil--ex-argument-handler))) + (when runner (funcall runner 'stop)))) + +(defvar-local evil--ex-echo-overlay nil + "Overlay for displaying info messages during Ex.") + +(defun evil-ex-echo (string &rest args) + "Display a message after the current Ex command." + ;; Differs from minibuffer-message, which see, in that it does not + ;; use sit-for, since it may be called in the middle of a command. + (unless (or evil-no-display (zerop (length string))) + (let ((string (concat " [" (apply #'format string args) "]"))) + (put-text-property 0 1 'cursor t string) ; Place cursor before message + (put-text-property 1 (length string) 'face 'evil-ex-info string) + (with-selected-window (minibuffer-window) + (if evil--ex-echo-overlay + (move-overlay evil--ex-echo-overlay (point-max) (point-max)) + (setq evil--ex-echo-overlay (make-overlay (point-max) (point-max) nil t t))) + (add-hook 'pre-command-hook #'evil--ex-remove-echo-overlay nil t) + (overlay-put evil--ex-echo-overlay 'after-string string))))) + +(defun evil--ex-remove-echo-overlay () + "Remove echo overlay from Ex minibuffer." + (delete-overlay evil--ex-echo-overlay) + (setq evil--ex-echo-overlay nil) + (remove-hook 'pre-command-hook #'evil--ex-remove-echo-overlay t)) + +(cl-defun evil-ex-completion-at-point () + "Function used for `completion-at-point-functions' in Ex state." + (cl-flet ((fix-beg (b) (min (save-excursion + (+ (goto-char b) (skip-chars-forward " \t\n\r"))) + (point)))) + (pcase (nreverse (evil--ex-syntactic-context)) + ((or (and 'nil (let beg (minibuffer-prompt-end))) + `((expression) (command . ,beg) . ,_) + (and `((expression) (line) . ,_) + (guard (looking-at-p "[ \t\n\r]*\\'")) + (let beg (point)))) + (list (fix-beg beg) (point) (evil-ex-completion-table))) + (`((expression) (argument . ,beg)) + (setq beg (fix-beg beg)) + ;; If it's an autoload, load the function; this allows external + ;; packages to register autoloaded Ex commands which will be + ;; loaded when ex argument completion is triggered. + (let ((binding (evil-ex-binding evil--ex-cmd t))) (autoload-do-load binding)) + + (let* ((binding (evil-ex-completed-binding evil--ex-cmd)) + (arg-type (evil-get-command-property binding :ex-arg)) + (arg-handler (cdr (assq arg-type evil-ex-argument-types)))) + (pcase (evil-ex-argument-handler-completer arg-handler) + (`(collection . ,table) (list beg (point-max) table)) + (`(completion-at-point . ,completer) + (save-restriction (narrow-to-region beg (point-max)) + (funcall completer)))))) + (`((expression) (sexp . ,_)) + (when (fboundp 'elisp-completion-at-point) (elisp-completion-at-point)))))) + +(defun evil-ex-completion-table () + (let ((ex-cmds + (cl-loop + for (cmd . fun) in evil-ex-commands unless (stringp fun) + collect cmd + ;; Append ! to all commands that may take a bang argument + when (evil-get-command-property fun :ex-bang) + collect (concat cmd "!"))) + (emacs-cmds (lambda (str pred action) + (completion-table-with-predicate + obarray #'commandp t str pred action)))) + (cond + ((null evil-ex-complete-emacs-commands) ex-cmds) + ((eq evil-ex-complete-emacs-commands 'in-turn) + (completion-table-in-turn ex-cmds emacs-cmds)) + (t (cl-loop for s in-ref ex-cmds do + (setf s (propertize s 'face 'evil-ex-commands))) + (evil-completion-table-concat ex-cmds emacs-cmds))))) + +(defun evil-completion-table-concat (table1 table2) + (lambda (string pred action) + (cond + ((null action) + (let (matches) + (dolist (table (list table1 table2) (try-completion string matches)) + (let ((x (try-completion string table pred))) + (when x (push (if (eq x 't) string x) matches)))))) + ((eq action t) + (delete-dups + (append (all-completions string table1 pred) + (all-completions string table2 pred)))) + ((eq action 'lambda) + (when (or (test-completion string table1 pred) + (test-completion string table2 pred)) + t)) + ((eq (car-safe action) 'boundaries) + (or (completion-boundaries string table1 pred (cdr action)) + (completion-boundaries string table2 pred (cdr action)))) + ((eq action 'metadata) + '(metadata (display-sort-function . evil-ex-sort-completions)))))) + +(defun evil-ex-sort-completions (completions) + (sort completions + (lambda (str1 str2) + (let ((p1 (eq (get-text-property 0 'face str1) 'evil-ex-commands)) + (p2 (eq (get-text-property 0 'face str2) 'evil-ex-commands))) + (if (eq p1 p2) (string< str1 str2) p1))))) + +(define-obsolete-function-alias 'evil-ex-completion #'completion-at-point "1.15.0") + +(define-obsolete-function-alias + 'evil-ex-command-completion-at-point #'evil-ex-completion-at-point "1.15.0") + +(defalias 'evil-ex-argument-completion-at-point #'ignore) +(make-obsolete + 'evil-ex-argument-completion-at-point #'evil-ex-completion-at-point "1.15.0") + +(define-obsolete-function-alias + 'evil-ex-elisp-completion-at-point #'elisp-completion-at-point "1.15.0") + +(defun evil-ex-define-cmd (cmd function) + "Bind the function FUNCTION to the command CMD." + (if (not (string-match-p "\\[" cmd)) + (evil--add-to-alist evil-ex-commands cmd function) + (string-match "\\(.*\\)\\[\\(.*\\)\\]" cmd) + (let ((mandatory (match-string 1 cmd)) + (optional (match-string 2 cmd))) + (dotimes (n (1+ (length optional))) + (evil--add-to-alist evil-ex-commands + (concat mandatory (substring optional 0 n)) + function))))) + +(defmacro evil-ex-define-argument-type (arg-type doc &rest body) + "Define a new handler for argument-type ARG-TYPE. +DOC is the documentation string. It is followed by a list of keywords +and function: + +:collection COLLECTION + + A collection for completion as required by `all-completions'. + +:completion-at-point FUNC + + Function to be called to initialize a potential completion. FUNC + must match the requirements as described for the variable + `completion-at-point-functions'. When FUNC is called the minibuffer + content is narrowed to exactly match the argument. + +:runner FUNC + + Function to be called when the type of the current argument changes + or when the content of this argument changes. This function should + take one obligatory argument FLAG followed by an optional argument + ARG. FLAG is one of three symbol `start', `stop' or `update'. When + the argument type is recognized for the first time and this handler + is started the FLAG is `start'. If the argument type changes to + something else or ex state finished the handler FLAG is `stop'. If + the content of the argument has changed FLAG is `update'. If FLAG is + either `start' or `update' then ARG is the current value of this + argument. If FLAG is `stop' then arg is nil." + (declare (indent defun) + (doc-string 2) + (debug (&define name + [&optional stringp] + [&rest [keywordp function-form]]))) + (unless (stringp doc) (push doc body)) + (let (runner completer) + (while (keywordp (car body)) + (let ((key (pop body)) + (func (pop body))) + (cond + ((eq key :runner) + (setq runner `#',func)) + ((eq key :collection) + (setq completer `(cons 'collection #',func))) + ((eq key :completion-at-point) + (setq completer `(cons 'completion-at-point #',func)))))) + `(evil--add-to-alist evil-ex-argument-types + ',arg-type (list ,runner ,completer)))) + +(evil-ex-define-argument-type file + "Handle a file argument." + :collection read-file-name-internal) + +(evil-ex-define-argument-type buffer + "Called to complete a buffer name argument." + :collection internal-complete-buffer) + +(declare-function comint-completion-at-point "comint") +(declare-function shell-completion-vars "shell" ()) + +(defvar-local evil--ex-shell-argument-initialized nil + "This variable is set to t if shell command completion has been initialized. +See `evil-ex-init-shell-argument-completion'.") + +(defun evil-ex-init-shell-argument-completion (flag &optional _arg) + "Prepare the current minibuffer for completion of shell commands. +This function must be called from the :runner function of some +argument handler that requires shell completion." + (when (and (eq flag 'start) + (not evil--ex-shell-argument-initialized)) + (setq evil--ex-shell-argument-initialized t) + (require 'shell) + ;; Set up Comint for Shell mode, except + ;; `comint-completion-at-point' will be called manually. + (shell-completion-vars) + (remove-hook 'completion-at-point-functions #'comint-completion-at-point t))) + +(evil-ex-define-argument-type shell + "Shell argument type, supports completion." + :completion-at-point comint-completion-at-point + :runner evil-ex-init-shell-argument-completion) + +(defun evil-ex-file-or-shell-command-completion-at-point () + (if (eq (char-after (point-min)) ?!) + (save-restriction + (narrow-to-region (1+ (point-min)) (point-max)) + (comint-completion-at-point)) + (list (point-min) (point-max) #'read-file-name-internal))) + +(evil-ex-define-argument-type file-or-shell + "File or shell argument type. +If the current argument starts with a ! the rest of the argument +is considered a shell command, otherwise a file-name. Completion +works accordingly." + :completion-at-point evil-ex-file-or-shell-command-completion-at-point + :runner evil-ex-init-shell-argument-completion) + +(defun evil-ex-binding (command &optional noerror) + "Return the final binding of COMMAND." + (let ((binding (if (evil--ex-bang-p command) (substring command 0 -1) + command))) + (while (stringp + (setq binding (cdr (assoc binding evil-ex-commands))))) + (unless binding (setq binding (intern-soft command))) + (if (commandp binding) + (or (command-remapping binding) binding) + (unless noerror (user-error "Unknown command: `%s'" command))))) + +(defun evil-ex-completed-binding (command &optional noerror) + "Return the final binding of the completion of COMMAND." + (let ((completion (try-completion command evil-ex-commands))) + (evil-ex-binding (if (eq completion t) command + (or completion command)) + noerror))) + +;; TODO: extensions like :p :~ ... +(defun evil-ex-replace-special-filenames (file-name) + "Replace special symbols in FILE-NAME. +Replaces % by the current file-name, +Replaces # by the alternate file-name in FILE-NAME." + (let ((remote (file-remote-p file-name)) + (current-fname (buffer-file-name)) + (alternate-fname (and (other-buffer) + (buffer-file-name (other-buffer))))) + (setq file-name (or (file-remote-p file-name 'localname) file-name)) + (when current-fname + (setq current-fname (or (file-remote-p current-fname 'localname) + current-fname)) + (setq file-name + (replace-regexp-in-string "\\(^\\|[^\\\\]\\)\\(%\\)" + current-fname file-name + t t 2))) + (when alternate-fname + (setq alternate-fname (or (file-remote-p alternate-fname 'localname) + alternate-fname)) + (setq file-name + (replace-regexp-in-string "\\(^\\|[^\\\\]\\)\\(#\\)" + alternate-fname file-name + t t 2))) + (setq file-name + (replace-regexp-in-string "\\\\\\([#%]\\)" + "\\1" file-name t)) + (setq file-name (concat remote file-name))) + file-name) + +(defun evil-ex-file-arg () + "Return the current Ex argument as a file name. +This function interprets special file names like # and %." + (unless (zerop (length evil-ex-argument)) + (evil-ex-replace-special-filenames evil-ex-argument))) + +(define-error 'evil-ex-no-previous-cmd "No previous command line") + +(defun evil-ex-repeat (&optional count) + "Repeat the last Ex command." + (interactive "P") + (when count + (goto-char (point-min)) + (forward-line (1- count))) + (cl-loop + with evil-ex-original-buffer = (current-buffer) + for last-cmd in evil-ex-history do + (condition-case nil + (cl-return (let (evil-ex-history) (evil-ex-execute last-cmd))) + (evil-ex-no-previous-cmd)) + finally (signal 'evil-ex-no-previous-cmd nil))) + +(defun evil-ex-call-command (range command argument) + "Execute the given command COMMAND." + (when evil-ex-reverse-range + (setq evil-ex-reverse-range nil) + (unless (y-or-n-p "Backward range given, OK to swap? ") + (user-error ""))) + (let* ((count (when (integerp range) range)) + (current-prefix-arg count) + (evil-ex-range (if count (evil-ex-range count count) range)) + (cmd (evil-ex-completed-binding command)) + (evil-ex-bang (evil--ex-bang-p command)) + (evil-ex-argument argument) + (evil-this-type (evil-type evil-ex-range)) + (evil-ex-point (point)) + (restore-point + (and evil-ex-range + (evil-get-command-property cmd :restore-point) + (if (evil-visual-state-p) evil-visual-beginning evil-ex-point))) + (evil-called-from-ex-p t)) + (evil-exit-visual-state) + (deactivate-mark) + (when evil-ex-range + ;; Set region if an explicit range has been specified + (cl-destructuring-bind (beg end &rest) (evil-expand-range evil-ex-range t) + (goto-char beg) + (set-marker (mark-marker) end))) + (setq this-command cmd) + (let ((mark-active evil-ex-range)) + (run-hooks 'pre-command-hook) + (call-interactively cmd)) + (run-hooks 'post-command-hook) + (when restore-point (goto-char restore-point)))) + +(defun evil-ex-line (base &optional offset) + "Return the line number of BASE plus OFFSET." + (+ (or base (line-number-at-pos)) + (or offset 0))) + +(defun evil-ex-first-line () + "Return the line number of the first line." + (line-number-at-pos (point-min))) + +(defun evil-ex-current-line () + "Return the line number of the current line." + (line-number-at-pos)) + +(defun evil-ex-last-line () + "Return the line number of the last line." + (save-excursion + (goto-char (point-max)) + (when (bolp) + (forward-line -1)) + (line-number-at-pos))) + +(defun evil-ex-range (beg-line &optional end-line) + "Return the first and last position of the current range." + (let* ((beg (if beg-line (evil-line-position beg-line) + (line-beginning-position))) + (end (if end-line (evil-line-position (1+ end-line)) + (save-excursion (goto-char beg) (line-beginning-position 2))))) + (when (< end beg) + (setq evil-ex-reverse-range t) + (evil-swap beg end)) + (evil-range beg end 'line :expanded t))) + +(defun evil-ex-full-range () + "Return a range encompassing the whole buffer." + (evil-range (point-min) (point-max) 'line :expanded t)) + +(defun evil-ex-last-visual-range () + "Return a linewise range of the last visual selection." + (evil-range evil-visual-mark evil-visual-point 'line)) + +(defun evil-ex-marker (marker) + "Return MARKER's line number in the current buffer. +Signal an error if MARKER is in a different buffer." + (setq marker (evil-get-marker + (if (stringp marker) (aref marker 0) marker))) + (if (numberp marker) + (line-number-at-pos marker) + (user-error "Ex does not support markers in other files"))) + +(defun evil-ex-char-marker-range (beg end) + (setq beg (evil-get-marker (if (stringp beg) (aref beg 0) beg)) + end (evil-get-marker (if (stringp end) (aref end 0) end))) + (unless (and (numberp beg) (numberp end)) + (user-error "Ex does not support markers in other files")) + (evil-expand-range + (evil-range beg end + (if (evil-visual-state-p) + (evil-visual-type) + 'inclusive)))) + +(declare-function evil-ex-make-search-pattern "evil-search") + +(defun evil-ex-re-fwd (pattern) + "Search forward for PATTERN. +Return the line number of the match." + (when evil-ex-search-vim-style-regexp + (setq pattern (evil-transform-vim-style-regexp pattern))) + (set-text-properties 0 (length pattern) nil pattern) + (setq evil-ex-search-pattern (evil-ex-make-search-pattern pattern) + evil-ex-search-direction 'forward) + (condition-case err + (save-excursion + (evil-move-beginning-of-line 2) + (when (or (re-search-forward pattern nil t) + (progn + (goto-char (point-min)) + (re-search-forward pattern nil t))) + (line-number-at-pos (match-beginning 0)))) + (invalid-regexp + (evil-ex-echo (cadr err)) + nil))) + +(defun evil-ex-re-bwd (pattern) + "Search backward for PATTERN. +Return the line number of the match." + (when evil-ex-search-vim-style-regexp + (setq pattern (evil-transform-vim-style-regexp pattern))) + (set-text-properties 0 (length pattern) nil pattern) + (setq evil-ex-search-pattern (evil-ex-make-search-pattern pattern) + evil-ex-search-direction 'backward) + (condition-case err + (save-excursion + (evil-move-beginning-of-line) + (when (or (re-search-backward pattern nil t) + (progn + (goto-char (point-max)) + (re-search-backward pattern nil t))) + (line-number-at-pos (match-beginning 0)))) + (invalid-regexp + (evil-ex-echo (cadr err)) + nil))) + +(defun evil-ex-prev-search () + (error "Previous search not yet implemented")) + +(defun evil-ex-signed-number (sign &optional number) + "Return a signed number like -3 and +1. +NUMBER defaults to 1." + (funcall sign (or number 1))) + +(defun evil-ex-command-force-p (command) + "Whether COMMAND accepts the bang argument." + (declare (obsolete evil-get-command-property "1.15.0")) + (let ((binding (evil-ex-completed-binding command t))) + (when binding + (evil-get-command-property binding :ex-bang)))) + +(cl-defun evil--ex-syntactic-context + (&optional (pos (point)) + (tree (save-excursion (goto-char (minibuffer-prompt-end)) + (evil-ex-parse nil t))) + &aux i result) + "Return the syntactical context in TREE of the character at POS. +POS defaults to the current position of point." + ;; Iterate over syntax tree leaves (i.e. the strings), and return + ;; the path to the leaf containing the cursor. Or, if not found, + ;; e.g. because of trailing whitespace, the leaf at most one char + ;; past the rightmost non-empty string. + (cl-labels + ((traverse + (node path) + (while (and (consp node) (symbolp (car node))) + (push (cons (pop node) i) path)) + (if (listp node) + (dolist (child node) (traverse child path)) + ;; NODE is the end position of a parsed string + (when (>= node pos) (cl-return-from evil--ex-syntactic-context path)) + (when (or (null result) (> node i)) + (setq i node + result path))))) + (traverse tree ())) + result) + +(provide 'evil-ex) + +;;; evil-ex.el ends here diff --git a/.config/emacs/lisp/evil/evil-integration.el b/.config/emacs/lisp/evil/evil-integration.el new file mode 100644 index 0000000..710d69d --- /dev/null +++ b/.config/emacs/lisp/evil/evil-integration.el @@ -0,0 +1,468 @@ +;;; evil-integration.el --- Integrate Evil with other modules -*- lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; This provides evil integration for various emacs modes. +;; Additional keybindings (or default state) should go into evil-keybindings.el. + +;;; Code: + +(require 'evil-maps) +(require 'evil-core) +(require 'evil-macros) +(require 'evil-types) +(require 'evil-repeat) + +;;; Evilize some commands + +;; unbound keys should be ignored +(evil-declare-ignore-repeat 'undefined) + +(mapc #'(lambda (cmd) + (evil-set-command-property cmd :keep-visual t) + (evil-declare-not-repeat cmd)) + '(digit-argument + negative-argument + universal-argument + universal-argument-minus + universal-argument-more + universal-argument-other-key)) +(mapc #'evil-declare-not-repeat + '(what-cursor-position)) +(mapc #'evil-declare-change-repeat + '(dabbrev-expand + hippie-expand + quoted-insert)) +(mapc #'evil-declare-abort-repeat + '(balance-windows + eval-expression + execute-extended-command + exit-minibuffer + compile + delete-window + delete-other-windows + find-file-at-point + ffap-other-window + recompile + redo + save-buffer + split-window + split-window-horizontally + split-window-vertically + undo + undo-tree-redo + undo-tree-undo)) + +(evil-set-type #'previous-line 'line) +(evil-set-type #'next-line 'line) + +(dolist (cmd '(keyboard-quit keyboard-escape-quit)) + (evil-set-command-property cmd :suppress-operator t)) + +;;; Mouse +(evil-declare-insert-at-point-repeat 'mouse-yank-primary) +(evil-declare-insert-at-point-repeat 'mouse-yank-secondary) + +;;; key-binding + +;; Calling `keyboard-quit' should cancel repeat +(advice-add 'keyboard-quit :before #'evil-repeat-abort) + +(add-hook 'wdired-mode-hook #'evil-change-to-initial-state) +(advice-add 'wdired-change-to-dired-mode :after + #'evil--change-to-initial-state-with-msg) +(defun evil--change-to-initial-state-with-msg (&rest _) + (evil-change-to-initial-state nil t)) + +;;; Parentheses + +(evil--advice-add 'show-paren-function :around #'evil--match-paren-in-normal-state) +(defun evil--match-paren-in-normal-state (orig-fun &rest args) + "Match parentheses in Normal state." + (if (eq (not (memq 'not evil-highlight-closing-paren-at-point-states)) + (not (memq evil-state evil-highlight-closing-paren-at-point-states))) + (apply orig-fun args) + (let* ((orig-spdf show-paren-data-function) + (show-paren-data-function + (lambda () + (let ((pos (point)) narrow) + (setq + pos (cl-dotimes (i (1+ (* 2 evil-show-paren-range))) + (setq pos (+ pos (if (cl-evenp i) i (- i)))) + (pcase (syntax-class (syntax-after pos)) + (4 (setq narrow pos) (cl-return pos)) + (5 (cl-return (1+ pos)))))) + (when pos + (save-excursion + (goto-char pos) + (save-restriction + (when narrow (narrow-to-region narrow (point-max))) + (funcall orig-spdf)))))))) + (apply orig-fun args)))) + +;;; Undo tree +(eval-after-load 'undo-tree + '(with-no-warnings + (evil-ex-define-cmd "undol[ist]" 'undo-tree-visualize) + (evil-ex-define-cmd "ul" 'undo-tree-visualize) + + (define-key undo-tree-visualizer-mode-map + [remap evil-backward-char] 'undo-tree-visualize-switch-branch-left) + (define-key undo-tree-visualizer-mode-map + [remap evil-forward-char] 'undo-tree-visualize-switch-branch-right) + (define-key undo-tree-visualizer-mode-map + [remap evil-next-line] 'undo-tree-visualize-redo) + (define-key undo-tree-visualizer-mode-map + [remap evil-previous-line] 'undo-tree-visualize-undo) + (define-key undo-tree-visualizer-mode-map + [remap evil-ret] 'undo-tree-visualizer-set) + + (define-key undo-tree-visualizer-selection-mode-map + [remap evil-backward-char] 'undo-tree-visualizer-select-left) + (define-key undo-tree-visualizer-selection-mode-map + [remap evil-forward-char] 'undo-tree-visualizer-select-right) + (define-key undo-tree-visualizer-selection-mode-map + [remap evil-next-line] 'undo-tree-visualizer-select-next) + (define-key undo-tree-visualizer-selection-mode-map + [remap evil-previous-line] 'undo-tree-visualizer-select-previous) + (define-key undo-tree-visualizer-selection-mode-map + [remap evil-ret] 'undo-tree-visualizer-set))) + +;;; Auto-complete +(eval-after-load 'auto-complete + '(progn + (evil-add-command-properties 'auto-complete :repeat 'evil-ac-repeat) + (evil-add-command-properties 'ac-complete :repeat 'evil-ac-repeat) + (evil-add-command-properties 'ac-expand :repeat 'evil-ac-repeat) + (evil-add-command-properties 'ac-next :repeat 'ignore) + (evil-add-command-properties 'ac-previous :repeat 'ignore) + + (defvar evil-ac-prefix-len nil + "The length of the prefix of the current item to be completed.") + + (defvar ac-prefix) + (defun evil-ac-repeat (flag) + "Record the changes for auto-completion." + (cond + ((eq flag 'pre) + (setq evil-ac-prefix-len (length ac-prefix)) + (evil-repeat-start-record-changes)) + ((eq flag 'post) + ;; Add change to remove the prefix + (evil-repeat-record-change (- evil-ac-prefix-len) + "" + evil-ac-prefix-len) + ;; Add change to insert the full completed text + (evil-repeat-record-change + (- evil-ac-prefix-len) + (buffer-substring-no-properties (- evil-repeat-pos + evil-ac-prefix-len) + (point)) + 0) + ;; Finish repeation + (evil-repeat-finish-record-changes)))))) + +;;; Company +(eval-after-load 'company + '(progn + (mapc #'evil-declare-change-repeat + '(company-complete-mouse + company-complete-number + company-complete-selection + company-complete-common)) + + (mapc #'evil-declare-ignore-repeat + '(company-abort + company-select-next + company-select-previous + company-select-next-or-abort + company-select-previous-or-abort + company-select-mouse + company-show-doc-buffer + company-show-location + company-search-candidates + company-filter-candidates)))) + +;;; Eval last sexp +(defun evil--preceding-sexp (command &rest args) + "In Normal- or Motion-state, allow last sexp to end at point." + (save-excursion + (or evil-move-beyond-eol + (eolp) + (not (or (evil-normal-state-p) (evil-motion-state-p))) + (forward-char)) + (apply command args))) +(advice-add 'elisp--preceding-sexp :around #'evil--preceding-sexp) +(advice-add 'pp-last-sexp :around #'evil--preceding-sexp) +(advice-add 'lisp-eval-last-sexp :around #'evil--preceding-sexp) + +;;; Show key +(advice-add 'quail-show-key :around #'evil--in-emacs-state) +(advice-add 'describe-char :around #'evil--in-emacs-state) +(defun evil--in-emacs-state (orig-fun &rest args) + "Temporarily enter Emacs-state and apply ORIG-FUN to ARGS." + (evil-with-state emacs (apply orig-fun args))) + +;;; ace-jump-mode +(declare-function ace-jump-char-mode "ext:ace-jump-mode") +(declare-function ace-jump-word-mode "ext:ace-jump-mode") +(declare-function ace-jump-line-mode "ext:ace-jump-mode") +(defvar ace-jump-mode-scope) + +(defvar evil-ace-jump-active nil) + +(defmacro evil-enclose-ace-jump-for-motion (&rest body) + "Enclose ace-jump to make it suitable for motions. +This includes restricting `ace-jump-mode' to the current window +in visual and operator state, deactivating visual updates, saving +the mark and entering `recursive-edit'." + (declare (indent defun) + (debug t)) + `(let ((old-mark (mark)) + (ace-jump-mode-scope + (if (and (not (memq evil-state '(visual operator))) + (boundp 'ace-jump-mode-scope)) + ace-jump-mode-scope + 'window))) + (remove-hook 'pre-command-hook #'evil-visual-pre-command t) + (remove-hook 'post-command-hook #'evil-visual-post-command t) + (unwind-protect + (let ((evil-ace-jump-active 'prepare)) + (add-hook 'ace-jump-mode-end-hook + #'evil-ace-jump-exit-recursive-edit) + ,@body + (when evil-ace-jump-active + (setq evil-ace-jump-active t) + (recursive-edit))) + (remove-hook 'post-command-hook + #'evil-ace-jump-exit-recursive-edit) + (remove-hook 'ace-jump-mode-end-hook + #'evil-ace-jump-exit-recursive-edit) + (if (evil-visual-state-p) + (progn + (add-hook 'pre-command-hook #'evil-visual-pre-command nil t) + (add-hook 'post-command-hook #'evil-visual-post-command nil t) + (set-mark old-mark)) + (push-mark old-mark))))) + +(advice-add 'ace-jump-done :after #'evil--after-ace-jump-done) +(defun evil--after-ace-jump-done (&rest _) + (when evil-ace-jump-active + (add-hook 'post-command-hook #'evil-ace-jump-exit-recursive-edit))) + +(defun evil-ace-jump-exit-recursive-edit () + "Exit a recursive edit caused by an evil jump." + (cond + ((eq evil-ace-jump-active 'prepare) + (setq evil-ace-jump-active nil)) + (evil-ace-jump-active + (remove-hook 'post-command-hook #'evil-ace-jump-exit-recursive-edit) + (exit-recursive-edit)))) + +(evil-define-motion evil-ace-jump-char-mode (_count) + "Jump visually directly to a char using ace-jump." + :type inclusive + (evil-without-repeat + (let ((pnt (point)) + (buf (current-buffer))) + (evil-enclose-ace-jump-for-motion + (call-interactively 'ace-jump-char-mode)) + ;; if we jump backwards, motion type is exclusive, analogously + ;; to `evil-find-char-backward' + (when (and (equal buf (current-buffer)) + (< (point) pnt)) + (setq evil-this-type + (cond + ((eq evil-this-type 'exclusive) 'inclusive) + ((eq evil-this-type 'inclusive) 'exclusive))))))) + +(evil-define-motion evil-ace-jump-char-to-mode (_count) + "Jump visually to the char in front of a char using ace-jump." + :type inclusive + (evil-without-repeat + (let ((pnt (point)) + (buf (current-buffer))) + (evil-enclose-ace-jump-for-motion + (call-interactively 'ace-jump-char-mode)) + (if (and (equal buf (current-buffer)) + (< (point) pnt)) + (progn + (or (eobp) (forward-char)) + (setq evil-this-type + (cond + ((eq evil-this-type 'exclusive) 'inclusive) + ((eq evil-this-type 'inclusive) 'exclusive)))) + (backward-char))))) + +(evil-define-motion evil-ace-jump-line-mode (_count) + "Jump visually to the beginning of a line using ace-jump." + :type line + :repeat abort + (evil-without-repeat + (evil-enclose-ace-jump-for-motion + (call-interactively 'ace-jump-line-mode)))) + +(evil-define-motion evil-ace-jump-word-mode (_count) + "Jump visually to the beginning of a word using ace-jump." + :type exclusive + :repeat abort + (evil-without-repeat + (evil-enclose-ace-jump-for-motion + (call-interactively 'ace-jump-word-mode)))) + +(define-key evil-motion-state-map [remap ace-jump-char-mode] #'evil-ace-jump-char-mode) +(define-key evil-motion-state-map [remap ace-jump-line-mode] #'evil-ace-jump-line-mode) +(define-key evil-motion-state-map [remap ace-jump-word-mode] #'evil-ace-jump-word-mode) + +;;; avy +(declare-function avy-goto-word-or-subword-1 "ext:avy") +(declare-function avy-goto-line "ext:avy") +(declare-function avy-goto-char "ext:avy") +(declare-function avy-goto-char-2 "ext:avy") +(declare-function avy-goto-char-2-above "ext:avy") +(declare-function avy-goto-char-2-below "ext:avy") +(declare-function avy-goto-char-in-line "ext:avy") +(declare-function avy-goto-word-0 "ext:avy") +(declare-function avy-goto-word-1 "ext:avy") +(declare-function avy-goto-word-1-above "ext:avy") +(declare-function avy-goto-word-1-below "ext:avy") +(declare-function avy-goto-subword-0 "ext:avy") +(declare-function avy-goto-subword-1 "ext:avy") +(declare-function avy-goto-char-timer "ext:avy") +(defvar avy-all-windows) + +(defmacro evil-enclose-avy-for-motion (&rest body) + "Enclose avy to make it suitable for motions. +Based on `evil-enclose-ace-jump-for-motion'." + (declare (indent defun) + (debug t)) + `(let ((avy-all-windows + (if (and (not (memq evil-state '(visual operator))) + (boundp 'avy-all-windows)) + avy-all-windows + nil))) + ,@body)) + +(defmacro evil-define-avy-motion (command type) + (declare (indent defun) + (debug t)) + (let ((name (intern (format "evil-%s" command)))) + `(evil-define-motion ,name (_count) + ,(format "Evil motion for `%s'." command) + :type ,type + :jump t + :repeat abort + (evil-without-repeat + (evil-enclose-avy-for-motion + (call-interactively ',command)))))) + +;; define evil-avy-* motion commands for avy-* commands +(evil-define-avy-motion avy-goto-char inclusive) +(evil-define-avy-motion avy-goto-char-2 inclusive) +(evil-define-avy-motion avy-goto-char-2-above inclusive) +(evil-define-avy-motion avy-goto-char-2-below inclusive) +(evil-define-avy-motion avy-goto-char-in-line inclusive) +(evil-define-avy-motion avy-goto-char-timer inclusive) +(evil-define-avy-motion avy-goto-line line) +(evil-define-avy-motion avy-goto-line-above line) +(evil-define-avy-motion avy-goto-line-below line) +(evil-define-avy-motion avy-goto-subword-0 exclusive) +(evil-define-avy-motion avy-goto-subword-1 exclusive) +(evil-define-avy-motion avy-goto-symbol-1 exclusive) +(evil-define-avy-motion avy-goto-symbol-1-above exclusive) +(evil-define-avy-motion avy-goto-symbol-1-below exclusive) +(evil-define-avy-motion avy-goto-word-0 exclusive) +(evil-define-avy-motion avy-goto-word-1 exclusive) +(evil-define-avy-motion avy-goto-word-1-above exclusive) +(evil-define-avy-motion avy-goto-word-1-below exclusive) +(evil-define-avy-motion avy-goto-word-or-subword-1 exclusive) + +;; remap avy-* commands to evil-avy-* commands +(dolist (command '(avy-goto-char + avy-goto-char-2 + avy-goto-char-2-above + avy-goto-char-2-below + avy-goto-char-in-line + avy-goto-char-timer + avy-goto-line + avy-goto-line-above + avy-goto-line-below + avy-goto-subword-0 + avy-goto-subword-1 + avy-goto-symbol-1 + avy-goto-symbol-1-above + avy-goto-symbol-1-below + avy-goto-word-0 + avy-goto-word-1 + avy-goto-word-1-above + avy-goto-word-1-below + avy-goto-word-or-subword-1)) + (define-key evil-motion-state-map + (vector 'remap command) (intern-soft (format "evil-%s" command)))) + +;;; nXhtml/mumamo +;; ensure that mumamo does not toggle evil through its globalized mode +(eval-after-load 'mumamo + '(with-no-warnings + (push 'evil-mode-cmhh mumamo-change-major-mode-no-nos))) + +;; visual-line-mode integration +(when evil-respect-visual-line-mode + (evil-define-minor-mode-key 'motion 'visual-line-mode + "j" 'evil-next-visual-line + "gj" 'evil-next-line + "k" 'evil-previous-visual-line + "gk" 'evil-previous-line + "0" 'evil-beginning-of-visual-line + "g0" 'evil-beginning-of-line + "$" 'evil-end-of-visual-line + "g$" 'evil-end-of-line + "V" 'evil-visual-screen-line)) + +;;; abbrev.el +(defun evil-maybe-expand-abbrev () + (when (and abbrev-mode evil-want-abbrev-expand-on-insert-exit) + (expand-abbrev))) + +(eval-after-load 'abbrev + '(add-hook 'evil-insert-state-exit-hook #'evil-maybe-expand-abbrev)) + +;;; ElDoc +(eval-after-load 'eldoc + '(when (fboundp 'eldoc-add-command-completions) + (eldoc-add-command-completions "evil-window-"))) + +;;; XRef +(eval-after-load 'xref + '(progn + (evil-set-command-property 'xref-find-definitions :jump t) + (evil-set-command-property 'xref-find-references :jump t))) + +(provide 'evil-integration) + +;;; evil-integration.el ends here diff --git a/.config/emacs/lisp/evil/evil-jumps.el b/.config/emacs/lisp/evil/evil-jumps.el new file mode 100644 index 0000000..a5ad8ba --- /dev/null +++ b/.config/emacs/lisp/evil/evil-jumps.el @@ -0,0 +1,358 @@ +;;; evil-jumps.el --- Jump list implementation -*- lexical-binding: t -*- + +;; Author: Bailey Ling + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +(require 'cl-lib) +(require 'evil-core) +(require 'evil-states) + +;;; Code: + +(defgroup evil-jumps nil + "Evil jump list configuration options." + :prefix "evil-jumps" + :group 'evil) + +(defcustom evil-jumps-cross-buffers t + "When non-nil, the jump commands can cross borders between buffers. +Otherwise the jump commands act only within the current buffer." + :type 'boolean) + +(defcustom evil-jumps-max-length 100 + "The maximum number of jumps to keep track of." + :type 'integer) + +(defcustom evil-jumps-pre-jump-hook nil + "Hooks to run just before jumping to a location in the jump list." + :type 'hook) + +(defcustom evil-jumps-post-jump-hook nil + "Hooks to run just after jumping to a location in the jump list." + :type 'hook) + +(defcustom evil-jumps-ignored-file-patterns '("COMMIT_EDITMSG$" "TAGS$") + "List of regexps to exclude file path from inclusion in the jump list." + :type '(repeat string)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar savehist-additional-variables) + +(defvar evil--jumps-jumping nil) + +(defvar evil--jumps-jump-command nil + "Set by `evil--jump-backward', used and cleared in the +`post-command-hook' by `evil--jump-handle-buffer-crossing'") + +(eval-when-compile (defvar evil--jumps-debug nil)) + +(defvar evil--jumps-buffer-targets "\\*\\(new\\|scratch\\)\\*" + "Regexp to determine if buffer with `buffer-name' is a valid jump target.") + +(defvar evil--jumps-window-jumps (make-hash-table) + "Hashtable which stores all jumps on a per window basis.") + +(defvar evil-jumps-history nil + "History of `evil-mode' jumps that are persisted with `savehist'.") + +(cl-defstruct evil-jumps-struct + ring + (idx -1) + previous-pos) + +;; Is inlining this really worth it? +(defsubst evil--jumps-message (format &rest args) + (when (eval-when-compile evil--jumps-debug) + (with-current-buffer (get-buffer-create "*evil-jumps*") + (goto-char (point-max)) + (insert (apply #'format format args) "\n")))) + +(defun evil--jumps-get-current (&optional window) + (unless window (setq window (selected-window))) + (let ((jump-struct (gethash window evil--jumps-window-jumps))) + (unless jump-struct + (setq jump-struct (make-evil-jumps-struct)) + (puthash window jump-struct evil--jumps-window-jumps)) + jump-struct)) + +(defun evil--jumps-get-jumps (struct) + (let ((ring (evil-jumps-struct-ring struct))) + (unless ring + (setq ring (make-ring evil-jumps-max-length)) + (setf (evil-jumps-struct-ring struct) ring)) + ring)) + +(defun evil--jumps-get-window-jump-list () + (let ((struct (evil--jumps-get-current))) + (evil--jumps-get-jumps struct))) + +(defun evil--jumps-savehist-load () + (add-to-list 'savehist-additional-variables 'evil-jumps-history) + (let ((ring (make-ring evil-jumps-max-length))) + (cl-loop for jump in (reverse evil-jumps-history) + do (ring-insert ring jump)) + (setf (evil-jumps-struct-ring (evil--jumps-get-current)) ring)) + (add-hook 'savehist-save-hook #'evil--jumps-savehist-sync) + (remove-hook 'savehist-mode-hook #'evil--jumps-savehist-load)) + +(defun evil--jumps-savehist-sync () + "Update the printable value of window jumps for `savehist'." + (setq evil-jumps-history + (delq nil (mapcar #'(lambda (jump) + (let* ((mark (car jump)) + (pos (if (markerp mark) + (marker-position mark) + mark)) + (file-name (cadr jump))) + (when (and (not (file-remote-p file-name)) + (file-exists-p file-name) + pos) + (list pos file-name)))) + (ring-elements (evil--jumps-get-window-jump-list)))))) + +(defun evil--jumps-current-file-name () + "Get the current buffer file name for `evil--jumps-push'." + (or buffer-file-name + (when (derived-mode-p 'dired-mode) default-directory))) + +(defun evil--jumps-jump (idx shift) + (let ((target-list (evil--jumps-get-window-jump-list))) + (evil--jumps-message "jumping from %s by %s" idx shift) + (evil--jumps-message "target list = %s" target-list) + (setq idx (+ idx shift)) + (let ((current-file-name (or (evil--jumps-current-file-name) (buffer-name))) + (size (ring-length target-list))) + (unless evil-jumps-cross-buffers + ;; skip jump marks pointing to other buffers + (while (and (< idx size) (>= idx 0) + (not (string= current-file-name (cadr (ring-ref target-list idx))))) + (setq idx (+ idx shift)))) + (when (and (< idx size) (>= idx 0)) + ;; actual jump + (run-hooks 'evil-jumps-pre-jump-hook) + (let* ((place (ring-ref target-list idx)) + (pos (car place)) + (file-name (cadr place))) + (setq evil--jumps-jumping t) + (unless (string= current-file-name file-name) + (if (string-match-p evil--jumps-buffer-targets file-name) + (switch-to-buffer file-name) + (find-file file-name))) + (setq evil--jumps-jumping nil) + (goto-char pos) + (setf (evil-jumps-struct-idx (evil--jumps-get-current)) idx) + (run-hooks 'evil-jumps-post-jump-hook)))))) + +(defun evil--jumps-push () + "Push the current cursor/file position to the jump list." + (let ((target-list (evil--jumps-get-window-jump-list)) + (file-name (evil--jumps-current-file-name)) + (buffer-name (buffer-name)) + (current-pos (point-marker)) + (first-pos nil) + (first-file-name nil) + (excluded nil)) + (when (and (not file-name) + (string-match-p evil--jumps-buffer-targets buffer-name)) + (setq file-name buffer-name)) + (when file-name + (dolist (pattern evil-jumps-ignored-file-patterns) + (when (string-match-p pattern file-name) + (setq excluded t))) + (unless excluded + (unless (ring-empty-p target-list) + (setq first-pos (car (ring-ref target-list 0))) + (setq first-file-name (car (cdr (ring-ref target-list 0))))) + (unless (and (equal first-pos current-pos) + (equal first-file-name file-name)) + (evil--jumps-message "pushing %s on %s" current-pos file-name) + (ring-insert target-list `(,current-pos ,file-name))))) + (evil--jumps-message "%s %s" + (selected-window) + (and (not (ring-empty-p target-list)) + (ring-ref target-list 0))))) + +(evil-define-command evil-show-jumps () + "Display the contents of the jump list." + :repeat nil + (evil-with-view-list + :name "evil-jumps" + :mode "Evil Jump List" + :format [("Jump" 5 nil) + ("Marker" 8 nil) + ("File/text" 1000 t)] + :entries (let* ((jumps (evil--jumps-savehist-sync)) + (count 0)) + (cl-loop for jump in jumps + collect `(nil [,(number-to-string (cl-incf count)) + ,(number-to-string (car jump)) + (,(cadr jump))]))) + :select-action #'evil--show-jumps-select-action)) + +(defun evil--show-jumps-select-action (jump) + (let ((position (string-to-number (elt jump 1))) + (file (car (elt jump 2)))) + (kill-buffer) + (switch-to-buffer (find-file file)) + (goto-char position))) + +(defun evil-set-jump (&optional pos) + "Set jump point at POS. +POS defaults to point." + (save-excursion + (when (markerp pos) + (set-buffer (marker-buffer pos))) + + (unless (or (region-active-p) (evil-visual-state-p)) + (push-mark pos t)) + + (unless evil--jumps-jumping + ;; clear out intermediary jumps when a new one is set + (let* ((struct (evil--jumps-get-current)) + (target-list (evil--jumps-get-jumps struct)) + (idx (evil-jumps-struct-idx struct))) + (cl-loop repeat idx + do (ring-remove target-list)) + (setf (evil-jumps-struct-idx struct) -1)) + (when pos + (goto-char pos)) + (evil--jumps-push)))) +(put 'evil-set-jump 'permanent-local-hook t) + +(defun evil--jump-backward (count) + (setq evil--jumps-jump-command t) + (let ((count (or count 1))) + (evil-motion-loop (nil count) + (let* ((struct (evil--jumps-get-current)) + (idx (evil-jumps-struct-idx struct))) + (evil--jumps-message "jumping back %s" idx) + (when (= idx -1) + (setq idx 0) + (setf (evil-jumps-struct-idx struct) 0) + (evil--jumps-push)) + (evil--jumps-jump idx 1))))) + +(defun evil--jump-forward (count) + (setq evil--jumps-jump-command t) + (let ((count (or count 1))) + (evil-motion-loop (nil count) + (let* ((struct (evil--jumps-get-current)) + (idx (evil-jumps-struct-idx struct))) + (when (= idx -1) + (setq idx 0) + (setf (evil-jumps-struct-idx struct) 0) + (evil--jumps-push)) + (evil--jumps-jump idx -1))))) + +(defun evil--jumps-window-configuration-hook (&rest _args) + (let* ((window-list (window-list-1 nil nil t)) + (existing-window (selected-window)) + (new-window (previous-window))) + (when (and (not (eq existing-window new-window)) + (> (length window-list) 1)) + (let* ((target-jump-struct (evil--jumps-get-current new-window))) + (if (not (ring-empty-p (evil--jumps-get-jumps target-jump-struct))) + (evil--jumps-message "target window %s already has %s jumps" new-window + (ring-length (evil--jumps-get-jumps target-jump-struct))) + (evil--jumps-message "new target window detected; copying %s to %s" existing-window new-window) + (let* ((source-jump-struct (evil--jumps-get-current existing-window)) + (source-list (evil--jumps-get-jumps source-jump-struct))) + (when (= (ring-length (evil--jumps-get-jumps target-jump-struct)) 0) + (setf (evil-jumps-struct-previous-pos target-jump-struct) (evil-jumps-struct-previous-pos source-jump-struct)) + (setf (evil-jumps-struct-idx target-jump-struct) (evil-jumps-struct-idx source-jump-struct)) + (setf (evil-jumps-struct-ring target-jump-struct) (ring-copy source-list))))))) + ;; delete obsolete windows + (maphash (lambda (key _val) + (unless (member key window-list) + (evil--jumps-message "removing %s" key) + (remhash key evil--jumps-window-jumps))) + evil--jumps-window-jumps))) +(put 'evil--jumps-window-configuration-hook 'permanent-local-hook t) + +(defun evil--jump-hook (&optional command) + "`pre-command-hook' for evil-jumps. +Set jump point if COMMAND has a non-nil `:jump' property. Otherwise, +save the current position in case the command being executed will +change the current buffer." + (setq command (or command this-command)) + (if (evil-get-command-property command :jump) + (evil-set-jump) + (setf (evil-jumps-struct-previous-pos (evil--jumps-get-current)) + (point-marker)))) +(put 'evil--jump-hook 'permanent-local-hook t) + +(defun evil--jump-handle-buffer-crossing () + (let ((jump-command evil--jumps-jump-command)) + (setq evil--jumps-jump-command nil) + (dolist (frame (frame-list)) + (dolist (window (window-list frame)) + (let* ((struct (evil--jumps-get-current window)) + (previous-pos (evil-jumps-struct-previous-pos struct))) + (when previous-pos + (setf (evil-jumps-struct-previous-pos struct) nil) + (if (and + ;; `evil-jump-backward' and 'evil-jump-forward' need + ;; to be handled specially. When jumping backward + ;; multiple times, calling `evil-set-jump' is always + ;; wrong: If you jump back twice and we call + ;; `evil-set-jump' after the second time, we clear + ;; the forward jump list and `evil--jump-forward' + ;; won't work. + + ;; The first time you jump backward, setting a jump + ;; point is sometimes correct. But we don't do it + ;; here because this function is called after + ;; `evil--jump-backward' has updated our position in + ;; the jump list so, again, `evil-set-jump' would + ;; break `evil--jump-forward'. + (not jump-command) + (let ((previous-buffer (marker-buffer previous-pos))) + (and previous-buffer + (not (eq previous-buffer (window-buffer window)))))) + (evil-set-jump previous-pos) + (set-marker previous-pos nil)))))))) +(put 'evil--jump-handle-buffer-crossing 'permanent-local-hook t) + +(if (bound-and-true-p savehist-loaded) + (evil--jumps-savehist-load) + (add-hook 'savehist-mode-hook #'evil--jumps-savehist-load)) + +(defun evil--jumps-install-or-uninstall () + (if evil-local-mode + (progn + (add-hook 'pre-command-hook #'evil--jump-hook nil t) + (add-hook 'post-command-hook #'evil--jump-handle-buffer-crossing nil t) + (add-hook 'next-error-hook #'evil-set-jump nil t) + (add-hook 'window-configuration-change-hook #'evil--jumps-window-configuration-hook nil t)) + (remove-hook 'pre-command-hook #'evil--jump-hook t) + (remove-hook 'post-command-hook #'evil--jump-handle-buffer-crossing t) + (remove-hook 'next-error-hook #'evil-set-jump t) + (remove-hook 'window-configuration-change-hook #'evil--jumps-window-configuration-hook t))) + +(add-hook 'evil-local-mode-hook #'evil--jumps-install-or-uninstall) + +(provide 'evil-jumps) + +;;; evil-jumps.el ends here diff --git a/.config/emacs/lisp/evil/evil-keybindings.el b/.config/emacs/lisp/evil/evil-keybindings.el new file mode 100644 index 0000000..81a8b00 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-keybindings.el @@ -0,0 +1,124 @@ +;;; evil-keybindings.el --- Add some Evil keybindings to other modules -*- lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; This provides a set of keybindings for other emacs modes. This also includes +;; setting up the initial evil state of those other modes. + +;;; Code: + +(require 'evil-maps) +(require 'evil-core) +(require 'evil-macros) +(require 'evil-types) +(require 'evil-repeat) + +;; etags-select +;; FIXME: probably etags-select should be recomended in docs +(eval-after-load 'etags-select + '(progn + (define-key evil-motion-state-map "g]" 'etags-select-find-tag-at-point))) + +;;; Buffer-menu + +(evil-add-hjkl-bindings Buffer-menu-mode-map 'motion) + +;; dictionary.el + +(evil-add-hjkl-bindings dictionary-mode-map 'motion + "?" 'dictionary-help ; "h" + "C-o" 'dictionary-previous) ; "l" + +;;; Dired + +(eval-after-load 'dired + '(progn + ;; use the standard Dired bindings as a base + (defvar dired-mode-map) + (evil-make-overriding-map dired-mode-map 'normal) + (evil-add-hjkl-bindings dired-mode-map 'normal + "J" 'dired-goto-file ; "j" + "K" 'dired-do-kill-lines ; "k" + "r" 'dired-do-redisplay ; "l" + ;; ":d", ":v", ":s", ":e" + ";" (lookup-key dired-mode-map ":")))) + +;;; ERT + +(evil-add-hjkl-bindings ert-results-mode-map 'motion) + +;;; Info + +(evil-add-hjkl-bindings Info-mode-map 'motion + "0" 'evil-beginning-of-line + (kbd "\M-h") 'Info-help ; "h" + "\C-t" 'Info-history-back ; "l" + "\C-o" 'Info-history-back + " " 'Info-scroll-up + "\C-]" 'Info-follow-nearest-node + (kbd "DEL") 'Info-scroll-down) + +;;; Speedbar + +(evil-add-hjkl-bindings speedbar-mode-map 'motion + "h" 'backward-char + "j" 'speedbar-next + "k" 'speedbar-prev + "l" 'forward-char + "i" 'speedbar-item-info + "r" 'speedbar-refresh + "u" 'speedbar-up-directory + "o" 'speedbar-toggle-line-expansion + (kbd "RET") 'speedbar-edit-line) + +;; Ibuffer +(eval-after-load 'ibuffer + '(progn + (defvar ibuffer-mode-map) + (evil-make-overriding-map ibuffer-mode-map 'normal) + (evil-define-key 'normal ibuffer-mode-map + "j" 'evil-next-line + "k" 'evil-previous-line + (kbd "RET") 'ibuffer-visit-buffer))) + +;;; ag.el +(eval-after-load 'ag + '(progn + (defvar ag-mode-map) + (add-to-list 'evil-motion-state-modes 'ag-mode) + (evil-add-hjkl-bindings ag-mode-map 'motion))) + +;;; ELP + +(advice-add 'elp-results :after #'evil--set-motion-state) +(defun evil--set-motion-state (&rest _) + (evil-motion-state)) + +(provide 'evil-keybindings) + +;;; evil-keybindings.el ends here diff --git a/.config/emacs/lisp/evil/evil-macros.el b/.config/emacs/lisp/evil/evil-macros.el new file mode 100644 index 0000000..0f7fd59 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-macros.el @@ -0,0 +1,772 @@ +;;; evil-macros.el --- Macros -*- lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Code: + +(require 'evil-common) +(require 'evil-states) +(require 'evil-repeat) + +(declare-function evil-ex-p "evil-ex") +(declare-function evil-line-or-visual-line "evil-commands") + +;; set some error codes +(put 'beginning-of-line 'error-conditions '(beginning-of-line error)) +(put 'beginning-of-line 'error-message "Beginning of line") +(put 'end-of-line 'error-conditions '(end-of-line error)) +(put 'end-of-line 'error-message "End of line") +;; we don't want line boundaries to trigger the debugger +;; when `debug-on-error' is t +(cl-pushnew 'beginning-of-line debug-ignored-errors) +(cl-pushnew 'end-of-line debug-ignored-errors) + +(defun evil-motion-range (motion &optional count type) + "Execute a motion and return the buffer positions. +The return value is a list (BEG END TYPE)." + (let ((opoint (point)) + (omark (mark t)) + (obuffer (current-buffer)) + (evil-motion-marker (move-marker (make-marker) (point))) + range) + (evil-with-transient-mark-mode + (evil-narrow-to-field + (unwind-protect + (let ((current-prefix-arg count) + ;; Store type in global variable `evil-this-type'. + ;; If necessary, motions can change their type + ;; during execution by setting this variable. + (evil-this-type + (or type (evil-get-command-property motion :type 'exclusive)))) + (condition-case err + (let ((repeat-type (evil--repeat-type motion))) + (when (functionp repeat-type) (funcall repeat-type 'pre)) + (unless (with-local-quit + (setq range (call-interactively motion)) + t) + (evil-repeat-abort) + (setq quit-flag t)) + (when (functionp repeat-type) (funcall repeat-type 'post))) + (error + (evil-repeat-abort) + ;; some operators depend on succeeding motions, in + ;; particular for `evil-forward-char' (e.g., used by + ;; `evil-substitute'), therefore we let end-of-line + ;; and end-of-buffer pass + (if (memq (car err) '(end-of-line end-of-buffer)) + (message (error-message-string err)) + (signal (car err) (cdr err))))) + (cond + ;; the motion returned a range + ((evil-range-p range)) + ;; the motion made a Visual selection + ((evil-visual-state-p) + (setq range (evil-visual-range))) + ;; the motion made an active region + ((region-active-p) + (setq range (evil-range (region-beginning) (region-end) + evil-this-type))) + ;; default: range from previous position to current + (t (setq range (evil-expand-range + (evil-normalize evil-motion-marker + (point) + evil-this-type))))) + (unless (or (null type) (eq (evil-type range) type)) + (evil-set-type range type) + (evil-expand-range range)) + (evil-set-range-properties range nil) + range) + ;; restore point and mark like `save-excursion', + ;; but only if the motion hasn't disabled the operator + (unless evil-inhibit-operator + (set-buffer obuffer) + (evil-move-mark omark) + (goto-char opoint)) + ;; delete marker so it doesn't slow down editing + (move-marker evil-motion-marker nil)))))) + +(defmacro evil-define-motion (motion args &rest body) + "Define a motion command MOTION. +ARGS is a list of arguments. Motions can have any number of +arguments, but the first (if any) has the predefined meaning of +count. BODY must execute the motion by moving point. + +Optional keyword arguments are: +- `:type' - determines how the motion works after an operator (one of + `inclusive', `line', `block' and `exclusive', or a self-defined + motion type) +- `:jump' - if non-nil, the previous position is stored in the jump + list, so that it can be restored with \ +\\\\[evil-jump-backward] + +\(fn MOTION (COUNT ARGS...) DOC [[KEY VALUE]...] BODY...)" + (declare (indent defun) + (doc-string 3) + (debug (&define name lambda-list + [&optional stringp] + [&rest keywordp sexp] + [&optional ("interactive" [&rest form])] + def-body))) + (let (arg doc interactive key keys) + (when args + (setq args `(&optional ,@(delq '&optional args)) + ;; the count is either numerical or nil + interactive '(""))) + ;; collect docstring + (when (and (> (length body) 1) + (or (eq (car-safe (car body)) #'format) + (stringp (car body)))) + (setq doc (pop body))) + ;; collect keywords + (setq keys (plist-put keys :repeat 'motion)) + (while (keywordp (car body)) + (setq key (pop body) + arg (pop body) + keys (plist-put keys key arg))) + ;; collect `interactive' specification + (when (eq (car-safe (car body)) 'interactive) + (setq interactive (cdr (pop body)))) + ;; macro expansion + `(progn + ;; refresh echo area in Eldoc mode + (when ',motion + (eval-after-load 'eldoc + '(and (fboundp 'eldoc-add-command) + (eldoc-add-command ',motion)))) + (evil-define-command ,motion (,@args) + ,@(when doc `(,doc)) ; avoid nil before `interactive' + ,@keys + :keep-visual t + (interactive ,@interactive) + ,@body)))) + +(defmacro evil-narrow-to-line (&rest body) + "Narrow BODY to the current line. +BODY will signal `beginning-of-line' or `end-of-line' upon reaching +the beginning or end of the current line." + (declare (indent defun) (debug t)) + `(cl-destructuring-bind (beg end &rest) (evil-line-expand (point) (point)) + (when (save-excursion (goto-char end) (bolp)) + (setq end (max beg (1- end)))) + ;; Do not include the newline in Normal state + (or evil-move-beyond-eol + (evil-visual-state-p) (evil-operator-state-p) + (setq end (max beg (1- end)))) + (evil-with-restriction beg end + (condition-case err + (progn ,@body) + (beginning-of-buffer + (signal (if (= (point-min) beg) 'beginning-of-line (car err)) + (cdr err))) + (end-of-buffer + (signal (if (= (point-max) end) 'end-of-line (car err)) + (cdr err))))))) + +(defun evil-eobp (&optional pos) + "Whether point is at end-of-buffer with regard to end-of-line." + (save-excursion + (when pos (goto-char pos)) + (cond + ((eobp)) + ;; the rest only pertains to Normal state + ((not (evil-normal-state-p)) nil) + ;; at the end of the last line + ((eolp) + (forward-char) + (eobp)) + ;; at the last character of the last line + (t + (forward-char) + (cond + ((eobp)) + ((eolp) + (forward-char) + (eobp))))))) + +(defun evil-move-beginning (count forward &optional backward) + "Move to the beginning of the COUNT next object. +If COUNT is negative, move to the COUNT previous object. +FORWARD is a function which moves to the end of the object, and +BACKWARD is a function which moves to the beginning. +If one is unspecified, the other is used with a negative argument." + (let* ((count (or count 1)) + (backward (or backward + #'(lambda (count) + (funcall forward (- count))))) + (forward (or forward + #'(lambda (count) + (funcall backward (- count))))) + (opoint (point))) + (cond + ((< count 0) + (when (bobp) + (signal 'beginning-of-buffer nil)) + (unwind-protect + (evil-motion-loop (nil count count) + (funcall backward 1)) + (unless (zerop count) + (goto-char (point-min))))) + ((> count 0) + (when (evil-eobp) + (signal 'end-of-buffer nil)) + ;; Do we need to move past the current object? + (when (<= (save-excursion + (funcall forward 1) + (funcall backward 1) + (point)) + opoint) + (setq count (1+ count))) + (unwind-protect + (evil-motion-loop (nil count count) + (funcall forward 1)) + (if (zerop count) + ;; go back to beginning of object + (funcall backward 1) + (goto-char (point-max))))) + (t count)))) + +(defun evil-move-end (count forward &optional backward inclusive) + "Move to the end of the COUNT next object. +If COUNT is negative, move to the COUNT previous object. +FORWARD is a function which moves to the end of the object, and +BACKWARD is a function which moves to the beginning. +If one is unspecified, the other is used with a negative argument. +If INCLUSIVE is non-nil, then point is placed at the last character +of the object; otherwise it is placed at the end of the object." + (let* ((count (or count 1)) + (backward (or backward + #'(lambda (count) + (funcall forward (- count))))) + (forward (or forward + #'(lambda (count) + (funcall backward (- count))))) + (opoint (point))) + (cond + ((< count 0) + (when (bobp) + (signal 'beginning-of-buffer nil)) + ;; Do we need to move past the current object? + (when (>= (save-excursion + (funcall backward 1) + (funcall forward 1) + (point)) + (if inclusive + (1+ opoint) + opoint)) + (setq count (1- count))) + (unwind-protect + (evil-motion-loop (nil count count) + (funcall backward 1)) + (if (not (zerop count)) + (goto-char (point-min)) + ;; go to end of object + (funcall forward 1) + (when inclusive + (unless (bobp) (backward-char))) + (when (or (evil-normal-state-p) + (evil-motion-state-p)) + (evil-adjust-cursor))))) + ((> count 0) + (when (evil-eobp) + (signal 'end-of-buffer nil)) + (when inclusive + (forward-char)) + (unwind-protect + (evil-motion-loop (nil count count) + (funcall forward 1)) + (if (not (zerop count)) + (goto-char (point-max)) + (when inclusive + (unless (bobp) (backward-char))) + (when (or (evil-normal-state-p) + (evil-motion-state-p)) + (evil-adjust-cursor))))) + (t count)))) + +(defun evil-text-object-make-linewise (range) + "Turn the text object selection RANGE to linewise. +The selection is adjusted in a sensible way so that the selected +lines match the user intent. In particular, whitespace-only parts +at the first and last lines are omitted. This function returns +the new range." + ;; Bug #607 + ;; If new type is linewise and the selection of the + ;; first line consists of whitespace only, the + ;; beginning is moved to the start of the next line. If + ;; the selections of the last line consists of + ;; whitespace only, the end is moved to the end of the + ;; previous line. + (if (eq (evil-type range) 'line) + range + (let ((expanded (plist-get (evil-range-properties range) :expanded)) + (newrange (evil-expand-range range t))) + (save-excursion + ;; skip whitespace at the beginning + (goto-char (evil-range-beginning newrange)) + (skip-chars-forward " \t") + (when (and (not (bolp)) (eolp)) + (evil-set-range-beginning newrange (1+ (point)))) + ;; skip whitepsace at the end + (goto-char (evil-range-end newrange)) + (skip-chars-backward " \t") + (when (and (not (eolp)) (bolp)) + (evil-set-range-end newrange (1- (point)))) + ;; only modify range if result is not empty + (if (> (evil-range-beginning newrange) + (evil-range-end newrange)) + range + (unless expanded + (evil-contract-range newrange)) + newrange))))) + +(defmacro evil-define-text-object (object args &rest body) + "Define a text object command OBJECT. +BODY should return a range (BEG END) to the right of point +if COUNT is positive, and to the left of it if negative. + +Optional keyword arguments: +- `:type' - determines how the range applies after an operator + (`inclusive', `line', `block', and `exclusive', or a self-defined + motion type). +- `:extend-selection' - if non-nil (default), the text object always + enlarges the current selection. Otherwise, it replaces the current + selection. + +\(fn OBJECT (COUNT) DOC [[KEY VALUE]...] BODY...)" + (declare (indent defun) + (doc-string 3) + (debug (&define name lambda-list + [&optional stringp] + [&rest keywordp sexp] + def-body))) + (let* ((args (delq '&optional args)) + (count (or (pop args) 'count)) + (args (when args `(&optional ,@args))) + (interactive '(interactive "")) + doc keys) + ;; collect docstring + (when (stringp (car body)) + (setq doc (pop body))) + ;; collect keywords + (setq keys (plist-put keys :extend-selection t)) + (while (keywordp (car body)) + (setq keys (plist-put keys (pop body) (pop body)))) + ;; interactive + (when (eq (car-safe (car body)) 'interactive) + (setq interactive (pop body))) + ;; macro expansion + `(evil-define-motion ,object (,count ,@args) + ,@(when doc `(,doc)) + ,@keys + ,interactive + (setq ,count (or ,count 1)) + (when (/= ,count 0) + ;; FIXME: These let-bindings shadow variables in args + (let ((type (evil-type ',object evil-visual-char)) + (extend (and (evil-visual-state-p) + (evil-get-command-property + ',object :extend-selection + ',(plist-get keys :extend-selection)))) + (dir evil-visual-direction) + mark point range selection) + (cond + ;; Visual state: extend the current selection + ((and (evil-visual-state-p) + (called-interactively-p 'any)) + ;; if we are at the beginning of the Visual selection, + ;; go to the left (negative COUNT); if at the end, + ;; go to the right (positive COUNT) + (setq dir evil-visual-direction + ,count (* ,count dir)) + (setq range (progn ,@body)) + (when (evil-range-p range) + (setq range (evil-expand-range range)) + (evil-set-type range (evil-type range type)) + (setq range (evil-contract-range range)) + ;; the beginning is mark and the end is point + ;; unless the selection goes the other way + (setq mark (evil-range-beginning range) + point (evil-range-end range) + type (evil-type + (if evil-text-object-change-visual-type + range + (evil-visual-range)))) + (when (and (eq type 'line) + (not (eq type (evil-type range)))) + (let ((newrange (evil-text-object-make-linewise range))) + (setq mark (evil-range-beginning newrange) + point (evil-range-end newrange)))) + (when (< dir 0) + (evil-swap mark point)) + ;; select the union + (evil-visual-make-selection mark point type))) + ;; not Visual state: return a pair of buffer positions + (t + (setq range (progn ,@body)) + (unless (evil-range-p range) + (setq ,count (- ,count) + range (progn ,@body))) + (when (evil-range-p range) + (setq selection (evil-range (point) (point) type)) + (if extend + (setq range (evil-range-union range selection)) + (evil-set-type range (evil-type range type))) + ;; possibly convert to linewise + (when (eq evil-this-type-modified 'line) + (setq range (evil-text-object-make-linewise range))) + (evil-set-range-properties range nil) + range)))))))) + +(defmacro evil-define-operator (operator args &rest body) + "Define an operator command OPERATOR. +The operator acts on the range of characters BEG through +END. BODY must execute the operator by potentially manipulating +the buffer contents, or otherwise causing side effects to happen. + +Optional keyword arguments are: +- `:type' - force the input range to be of a given type (`inclusive', + `line', `block', and `exclusive', or a self-defined motion type). +- `:motion' - use a predetermined motion instead of waiting for one + from the keyboard. This does not affect the behavior in visual + state, where selection boundaries are always used. +- `:repeat' - if non-nil (default), then \ + \\\\[evil-repeat] will repeat the + operator. +- `:move-point' - if non-nil (default), the cursor will be moved to + the beginning of the range before the body executes +- `:keep-visual' - if non-nil, the selection is not disabled when the + operator is executed in visual state. By default, visual state is + exited automatically. +- `:restore-point' - if non-nil, point is restored when the + operator is executed from ex. + +\(fn OPERATOR (BEG END ARGS...) DOC [[KEY VALUE]...] BODY...)" + (declare (indent defun) + (doc-string 3) + (debug (&define name lambda-list + [&optional stringp] + [&rest keywordp sexp] + [&optional ("interactive" [&rest form])] + def-body))) + (let* ((args (delq '&optional args)) + (interactive (if (> (length args) 2) '("") '(""))) + (args (if (> (length args) 2) + `(,(nth 0 args) ,(nth 1 args) + &optional ,@(nthcdr 2 args)) + args)) + (end-marker (make-symbol "end-marker")) + arg doc key keys visual) + ;; collect docstring + (when (and (> (length body) 1) + (or (eq (car-safe (car-safe body)) 'format) + (stringp (car-safe body)))) + (setq doc (pop body))) + ;; collect keywords + (setq keys (plist-put keys :move-point t)) + (while (keywordp (car-safe body)) + (setq key (pop body) + arg (pop body)) + (cond + ((eq key :keep-visual) + (setq visual arg)) + (t + (setq keys (plist-put keys key arg))))) + ;; collect `interactive' specification + (when (eq (car-safe (car-safe body)) 'interactive) + (setq interactive (cdr-safe (pop body)))) + ;; transform extended interactive specs + (setq interactive (apply #'evil-interactive-form interactive)) + (setq keys (evil-concat-plists keys (cdr-safe interactive)) + interactive (car-safe interactive)) + ;; macro expansion + `(evil-define-command ,operator ,args + ,@(when doc `(,doc)) + ,@keys + :keep-visual t + :suppress-operator t + (interactive + (let* ((props (evil-command-properties ',operator)) + (evil-operator-range-motion + (let ((p (plist-member props :motion))) + ;; :motion nil is equivalent to :motion undefined + (when p (or (cadr p) #'undefined)))) + (evil-operator-range-type (plist-get props :type)) + evil-operator-range-beginning evil-operator-range-end + evil-inhibit-operator) + (setq evil-inhibit-operator-value nil + evil-this-operator this-command + evil-operator-start-col (current-column)) + (prog1 ,interactive + (setq evil-inhibit-operator-value evil-inhibit-operator) + (if ,visual + (when (evil-visual-state-p) (evil-visual-expand-region)) + (setq deactivate-mark t)) + (cond + ((evil-visual-state-p) + (evil-visual-rotate 'upper-left)) + ((plist-get props :move-point) + (when evil-operator-range-beginning + (goto-char evil-operator-range-beginning))))))) + (unwind-protect + (let ((evil-inhibit-operator evil-inhibit-operator-value) + (,end-marker (make-marker))) + (set-marker ,end-marker ,(cadr args)) + (unless (and evil-inhibit-operator + (called-interactively-p 'any)) + ,@body) + (evil-set-marker ?\[ (or ,(car args) (point-max))) + (evil-set-marker ?\] (max (or ,(car args) (point-max)) + (1- (or (marker-position ,end-marker) (point-max))))) + (set-marker ,end-marker nil)) + (setq evil-inhibit-operator-value nil))))) + +;; this is used in the `interactive' specification of an operator command +(defun evil-operator-range (&optional return-type) + "Read a motion from the keyboard and return its buffer positions. +The return value is a list (BEG END), or (BEG END TYPE) if +RETURN-TYPE is non-nil." + (let ((motion (or evil-operator-range-motion + (when evil-called-from-ex-p 'evil-line))) + (type evil-operator-range-type) + range count) + (setq evil-this-type-modified nil) + (cond + ;; Ex command + ((and evil-called-from-ex-p evil-ex-range) + (setq range evil-ex-range)) + ;; Visual selection + ((and (not evil-called-from-ex-p) (evil-visual-state-p)) + (setq range (evil-visual-range))) + ;; active region + ((and (not evil-called-from-ex-p) (region-active-p)) + (setq range (evil-range (region-beginning) (region-end) + (or evil-this-type 'exclusive)))) + ;; motion + (t + (evil-save-echo-area + (evil-save-state + (unless motion + ;; Make linewise operator shortcuts. E.g., "d" yields the + ;; shortcut "dd", and "g?" yields shortcuts "g??" and "g?g?". + (let ((keys (nth 2 (evil-extract-count (this-command-keys))))) + (evil-change-state 'operator) + (cl-loop for keys on (listify-key-sequence keys) do + (define-key evil-operator-shortcut-map + (vconcat keys) #'evil-line-or-visual-line))) + ;; read motion from keyboard + (let ((command (evil-read-motion motion))) + (setq motion (car command) + count (cadr command) + type (or type (nth 2 command))))) + (cond + ((eq motion #'undefined) + (setq range (list nil nil) + motion nil)) + ((or (null motion) ; keyboard-quit + (evil-get-command-property motion :suppress-operator)) + (evil-repeat-abort) + (setq quit-flag t + range (evil-range (point) (point)) ; zero-len range + motion nil)) + (evil-repeat-count + (setq count evil-repeat-count + ;; only the first operator's count is overwritten + evil-repeat-count nil)) + ((or count current-prefix-arg) + ;; multiply operator count and motion count together + (setq count + (* (prefix-numeric-value count) + (prefix-numeric-value current-prefix-arg))))) + (when motion + (let ((evil-state 'operator) + mark-active) + ;; calculate motion range + (setq range (evil-motion-range motion count type)))) + ;; update global variables + (setq evil-this-motion motion + evil-this-motion-count count + type (evil-type range type) + evil-this-type type))))) + (unless (or (null type) (eq (evil-type range) type)) + (evil-contract-range range) + (evil-set-range-type range type) + (evil-expand-range range)) + (setq evil-operator-range-beginning (evil-range-beginning range) + evil-operator-range-end (evil-range-end range) + evil-operator-range-type (evil-type range)) + (setcdr (cdr range) + (when return-type (list (evil-type range)))) + range)) + +(defmacro evil-define-type (type doc &rest body) + "Define type TYPE. +DOC is a general description and shows up in all docstrings. + +Optional keyword arguments: +- `:expand' - expansion function. This function should accept two + positions in the current buffer, BEG and END,and return a pair of + expanded buffer positions. +- `:contract' - the opposite of `:expand'. Optional. +- `:one-to-one' - non-nil if expansion is one-to-one. This means that + `:expand' followed by `:contract' always return the original range. +- `:normalize' - normalization function. This function should accept + two unexpanded positions and adjust them before expansion. May be + used to deal with buffer boundaries. +- `:string' - description function. Takes two buffer positions and + returns a human-readable string. For example \"2 lines\" + +If further keywords and functions are specified, they are assumed to +be transformations on buffer positions, like `:expand' and `:contract'. + +\(fn TYPE DOC [[KEY FUNC]...])" + (declare (indent defun) + (doc-string 2) + (debug (&define name + [&optional stringp] + [&rest [keywordp function-form]]))) + (let ((plist (list :one-to-one t)) ; standard values + args defun-forms func key name string sym val) + ;; keywords + (while (keywordp (car-safe body)) + (setq key (pop body) + val (pop body)) + (if (plist-member plist key) ; not a function + (setq plist (plist-put plist key val)) + (setq func val + sym (intern (replace-regexp-in-string + "^:" "" (symbol-name key))) + name (intern (format "evil-%s-%s" type sym)) + args (car (cdr-safe func)) + string (cadr (cdr-safe func)) + string (if (stringp string) + (format "%s\n\n" string) "") + plist (plist-put plist key `',name)) + (push + (cond + ((eq key :string) + `(defun ,name (beg end &rest properties) + ,(format "Return size of %s from BEG to END \ +with PROPERTIES.\n\n%s%s" type string doc) + (let ((type ',type) + plist range) + (when (and beg end) + (evil-sort beg end) + (save-excursion + (unless (plist-get properties :expanded) + (setq range (apply #'evil-expand + beg end type properties) + beg (evil-range-beginning range) + end (evil-range-end range) + type (evil-type range type) + plist (evil-range-properties range)) + (setq properties + (evil-concat-plists properties plist))) + (or (apply #',func beg end + (when ,(> (length args) 2) + properties)) + "")))))) + (t + `(defun ,name (beg end &rest properties) + ,(format "Perform %s transformation on %s from BEG to END \ +with PROPERTIES.\n\n%s%s" sym type string doc) + (let ((type ',type) + plist range) + (when (and beg end) + (evil-sort beg end) + (save-excursion + (when (memq ,key '(:expand :contract)) + (setq properties + (plist-put properties + :expanded ,(eq key :expand)))) + (setq range (or (apply #',func beg end + (when ,(> (length args) 2) + properties)) + (apply #'evil-range + beg end type properties)) + beg (evil-range-beginning range) + end (evil-range-end range) + type (evil-type range type) + plist (evil-range-properties range)) + (setq properties + (evil-concat-plists properties plist)) + (apply #'evil-range beg end type properties))))))) + defun-forms))) + ;; :one-to-one requires both or neither of :expand and :contract + (when (plist-get plist :expand) + (setq plist (plist-put plist :one-to-one + (and (plist-get plist :contract) + (plist-get plist :one-to-one))))) + `(progn + (evil--add-to-alist evil-type-properties ',type (list ,@plist)) + ,@defun-forms + ',type))) + +(defmacro evil-define-interactive-code (code &rest body) + "Define an interactive code. +PROMPT, if given, is the remainder of the interactive string +up to the next newline. Command properties may be specified +via KEY-VALUE pairs. BODY should evaluate to a list of values. + +\(fn CODE (PROMPT) [[KEY VALUE]...] BODY...)" + (declare (indent defun)) + (let* ((args (and (> (length body) 1) (listp (car body)) + (pop body))) + (doc (when (stringp (car body)) (pop body))) + (properties + (cl-loop while (keywordp (car body)) + collect (pop body) collect (pop body))) + (func (if args + `(lambda ,args + ,@(when doc `(,doc)) + ,@body) + `',(macroexp-progn body)))) + `(eval-and-compile + (evil--add-to-alist + evil-interactive-alist ,code (cons ,func ',properties)) + ,code))) + +;;; Highlighting + +(when (fboundp 'font-lock-add-keywords) + (font-lock-add-keywords + 'emacs-lisp-mode + ;; Match all `evil-define-' forms except `evil-define-key'. + ;; (In the interests of speed, this expression is incomplete + ;; and does not match all three-letter words.) + '(("(\\(evil-\\(?:ex-\\)?define-\ +\\(?:[^ k][^ e][^ y]\\|[-[:word:]]\\{4,\\}\\)\\)\ +\\>[ \f\t\n\r\v]*\\(\\(?:\\sw\\|\\s_\\)+\\)?" + (1 'font-lock-keyword-face) + (2 'font-lock-function-name-face nil t)) + ("(\\(evil-\\(?:delay\\|narrow\\|signal\\|save\\|with\\(?:out\\)?\\)\ +\\(?:-[-[:word:]]+\\)?\\)\\>\[ \f\t\n\r\v]+" + 1 'font-lock-keyword-face) + ("(\\(evil-\\(?:[-[:word:]]\\)*loop\\)\\>[ \f\t\n\r\v]+" + 1 'font-lock-keyword-face)))) + +(provide 'evil-macros) + +;;; evil-macros.el ends here diff --git a/.config/emacs/lisp/evil/evil-maps.el b/.config/emacs/lisp/evil/evil-maps.el new file mode 100644 index 0000000..6778023 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-maps.el @@ -0,0 +1,686 @@ +;;; evil-maps.el --- Default keymaps -*- lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Code: + +(require 'evil-states) +(require 'evil-ex) +(require 'evil-commands) +(require 'evil-command-window) +(require 'evil-common) + +;;; Normal state + +(define-key evil-normal-state-map "a" 'evil-append) +(define-key evil-normal-state-map "A" 'evil-append-line) +(define-key evil-normal-state-map "c" 'evil-change) +(define-key evil-normal-state-map "C" 'evil-change-line) +(define-key evil-normal-state-map "d" 'evil-delete) +(define-key evil-normal-state-map "D" 'evil-delete-line) +(define-key evil-normal-state-map "i" 'evil-insert) +(define-key evil-normal-state-map (kbd "") 'evil-insert) +(define-key evil-normal-state-map (kbd "") 'evil-insert) +(define-key evil-normal-state-map "I" 'evil-insert-line) +(define-key evil-normal-state-map "J" 'evil-join) +(define-key evil-normal-state-map "m" 'evil-set-marker) +(define-key evil-normal-state-map "o" 'evil-open-below) +(define-key evil-normal-state-map "O" 'evil-open-above) +(define-key evil-normal-state-map "p" 'evil-paste-after) +(define-key evil-normal-state-map "P" 'evil-paste-before) +(define-key evil-normal-state-map "q" 'evil-record-macro) +(define-key evil-normal-state-map "Q" 'evil-execute-last-recorded-macro) +(define-key evil-normal-state-map "r" 'evil-replace) +(define-key evil-normal-state-map "R" 'evil-enter-replace-state) +(define-key evil-normal-state-map "s" 'evil-substitute) +(define-key evil-normal-state-map "S" 'evil-change-whole-line) +(define-key evil-normal-state-map "x" 'evil-delete-char) +(define-key evil-normal-state-map "X" 'evil-delete-backward-char) +(define-key evil-normal-state-map [deletechar] 'evil-delete-char) +(define-key evil-normal-state-map "y" 'evil-yank) +(define-key evil-normal-state-map "Y" 'evil-yank-line) +(define-key evil-normal-state-map "&" 'evil-ex-repeat-substitute) +(define-key evil-normal-state-map "g&" 'evil-ex-repeat-global-substitute) +(define-key evil-normal-state-map "g8" 'what-cursor-position) +(define-key evil-normal-state-map "ga" 'what-cursor-position) +(define-key evil-normal-state-map "gi" 'evil-insert-resume) +(define-key evil-normal-state-map "gI" 'evil-insert-0-line) +(define-key evil-normal-state-map "gJ" 'evil-join-whitespace) +(define-key evil-normal-state-map "gp" 'evil-paste-after-cursor-after) +(define-key evil-normal-state-map "gP" 'evil-paste-before-cursor-after) +(define-key evil-normal-state-map "gq" 'evil-fill-and-move) +(define-key evil-normal-state-map "gw" 'evil-fill) +(define-key evil-normal-state-map "gu" 'evil-downcase) +(define-key evil-normal-state-map "gU" 'evil-upcase) +(define-key evil-normal-state-map "gf" 'find-file-at-point) +(define-key evil-normal-state-map "]f" 'find-file-at-point) +(define-key evil-normal-state-map "[f" 'find-file-at-point) +(define-key evil-normal-state-map "gF" 'evil-find-file-at-point-with-line) +(define-key evil-normal-state-map "]F" 'evil-find-file-at-point-with-line) +(define-key evil-normal-state-map "[F" 'evil-find-file-at-point-with-line) +(define-key evil-normal-state-map "gx" 'browse-url-at-point) +(define-key evil-normal-state-map "g?" 'evil-rot13) +(define-key evil-normal-state-map "g~" 'evil-invert-case) +(define-key evil-normal-state-map "zo" 'evil-open-fold) +(define-key evil-normal-state-map "zO" 'evil-open-fold-rec) +(define-key evil-normal-state-map "zc" 'evil-close-fold) +(define-key evil-normal-state-map "za" 'evil-toggle-fold) +(define-key evil-normal-state-map "zr" 'evil-open-folds) +(define-key evil-normal-state-map "zm" 'evil-close-folds) +(define-key evil-normal-state-map "z=" 'ispell-word) +(define-key evil-normal-state-map "\C-n" 'evil-paste-pop-next) +(define-key evil-normal-state-map "\C-p" 'evil-paste-pop) +(define-key evil-normal-state-map "\C-t" 'pop-tag-mark) +(define-key evil-normal-state-map (kbd "C-.") 'evil-repeat-pop) +(define-key evil-normal-state-map (kbd "M-.") 'evil-repeat-pop-next) +(define-key evil-normal-state-map "." 'evil-repeat) +(define-key evil-normal-state-map "@" 'evil-execute-macro) +(define-key evil-normal-state-map "\"" 'evil-use-register) +(define-key evil-normal-state-map "~" 'evil-invert-char) +(define-key evil-normal-state-map "=" 'evil-indent) +(define-key evil-normal-state-map "<" 'evil-shift-left) +(define-key evil-normal-state-map ">" 'evil-shift-right) +(define-key evil-normal-state-map "ZZ" 'evil-save-modified-and-close) +(define-key evil-normal-state-map "ZQ" 'evil-quit) +(define-key evil-normal-state-map (kbd "DEL") 'evil-backward-char) +(define-key evil-normal-state-map [escape] 'evil-force-normal-state) +(define-key evil-normal-state-map [remap cua-paste-pop] 'evil-paste-pop) +(define-key evil-normal-state-map [remap yank-pop] 'evil-paste-pop) + +;; go to last change +(define-key evil-normal-state-map "g;" 'evil-goto-last-change) +(define-key evil-normal-state-map "g," 'evil-goto-last-change-reverse) + +;; undo +(define-key evil-normal-state-map "u" 'evil-undo) +(define-key evil-normal-state-map "\C-r" 'evil-redo) + +;; window commands +(define-prefix-command 'evil-window-map) +(define-key evil-window-map "b" 'evil-window-bottom-right) +(define-key evil-window-map "c" 'evil-window-delete) +(define-key evil-window-map "f" 'ffap-other-window) +(define-key evil-window-map "h" 'evil-window-left) +(define-key evil-window-map "H" 'evil-window-move-far-left) +(define-key evil-window-map "j" 'evil-window-down) +(define-key evil-window-map "J" 'evil-window-move-very-bottom) +(define-key evil-window-map "k" 'evil-window-up) +(define-key evil-window-map "K" 'evil-window-move-very-top) +(define-key evil-window-map "l" 'evil-window-right) +(define-key evil-window-map "L" 'evil-window-move-far-right) +(define-key evil-window-map "n" 'evil-window-new) +(define-key evil-window-map "o" 'delete-other-windows) +(define-key evil-window-map "p" 'evil-window-mru) +(define-key evil-window-map "q" 'evil-quit) +(define-key evil-window-map "r" 'evil-window-rotate-downwards) +(define-key evil-window-map "R" 'evil-window-rotate-upwards) +(define-key evil-window-map "s" 'evil-window-split) +(define-key evil-window-map "S" 'evil-window-split) +(define-key evil-window-map "t" 'evil-window-top-left) +(when (featurep 'tab-bar) + (define-key evil-window-map "T" 'tab-window-detach)) +(define-key evil-window-map "v" 'evil-window-vsplit) +(define-key evil-window-map "w" 'evil-window-next) +(define-key evil-window-map "W" 'evil-window-prev) +(define-key evil-window-map "x" 'evil-window-exchange) +(define-key evil-window-map "+" 'evil-window-increase-height) +(define-key evil-window-map "-" 'evil-window-decrease-height) +(define-key evil-window-map ":" 'evil-ex) +(define-key evil-window-map "_" 'evil-window-set-height) +(define-key evil-window-map "<" 'evil-window-decrease-width) +(define-key evil-window-map ">" 'evil-window-increase-width) +(define-key evil-window-map "=" 'balance-windows) +(define-key evil-window-map (kbd "C-=") 'balance-windows) +(define-key evil-window-map "|" 'evil-window-set-width) +(define-key evil-window-map [down] 'evil-window-down) +(define-key evil-window-map [up] 'evil-window-up) +(define-key evil-window-map [left] 'evil-window-left) +(define-key evil-window-map [right] 'evil-window-right) +(define-key evil-window-map "\C-b" 'evil-window-bottom-right) +(define-key evil-window-map "\C-c" 'evil-window-delete) +(define-key evil-window-map "\C-f" 'ffap-other-window) +(define-key evil-window-map "\C-h" 'evil-window-left) +(define-key evil-window-map (kbd "C-S-h") 'evil-window-move-far-left) +(define-key evil-window-map "\C-j" 'evil-window-down) +(define-key evil-window-map (kbd "C-S-j") 'evil-window-move-very-bottom) +(define-key evil-window-map "\C-k" 'evil-window-up) +(define-key evil-window-map (kbd "C-S-k") 'evil-window-move-very-top) +(define-key evil-window-map "\C-l" 'evil-window-right) +(define-key evil-window-map (kbd "C-S-l") 'evil-window-move-far-right) +(define-key evil-window-map "\C-n" 'evil-window-new) +(define-key evil-window-map "\C-o" 'delete-other-windows) +(define-key evil-window-map "\C-p" 'evil-window-mru) +(define-key evil-window-map "\C-q" 'evil-quit) +(define-key evil-window-map "\C-r" 'evil-window-rotate-downwards) +(define-key evil-window-map (kbd "C-S-r") 'evil-window-rotate-upwards) +(define-key evil-window-map "\C-s" 'evil-window-split) +(define-key evil-window-map (kbd "C-S-s") 'evil-window-split) +(define-key evil-window-map "\C-t" 'evil-window-top-left) +(define-key evil-window-map "\C-v" 'evil-window-vsplit) +(define-key evil-window-map "\C-w" 'evil-window-next) +(define-key evil-window-map (kbd "C-S-W") 'evil-window-prev) +(define-key evil-window-map "\C-x" 'evil-window-exchange) +(define-key evil-window-map "\C-_" 'evil-window-set-height) +(define-key evil-window-map [C-down] 'evil-window-down) +(define-key evil-window-map [C-up] 'evil-window-up) +(define-key evil-window-map [C-left] 'evil-window-left) +(define-key evil-window-map [C-right] 'evil-window-right) +(define-key evil-window-map "0" 'evil-window-digit-argument) +(define-key evil-window-map "1" 'evil-window-digit-argument) +(define-key evil-window-map "2" 'evil-window-digit-argument) +(define-key evil-window-map "3" 'evil-window-digit-argument) +(define-key evil-window-map "4" 'evil-window-digit-argument) +(define-key evil-window-map "5" 'evil-window-digit-argument) +(define-key evil-window-map "6" 'evil-window-digit-argument) +(define-key evil-window-map "7" 'evil-window-digit-argument) +(define-key evil-window-map "8" 'evil-window-digit-argument) +(define-key evil-window-map "9" 'evil-window-digit-argument) + +(when (featurep 'tab-bar) + (define-key evil-motion-state-map "gt" 'evil-tab-next) + (define-key evil-window-map "gt" 'evil-tab-next) + (define-key evil-motion-state-map "gT" 'tab-bar-switch-to-prev-tab) + (define-key evil-window-map "gT" 'tab-bar-switch-to-prev-tab)) + +;;; Motion state + +;; "0" is a special command when called first +(define-key evil-motion-state-map "0" 'evil-beginning-of-line) +(define-key evil-motion-state-map "1" 'digit-argument) +(define-key evil-motion-state-map "2" 'digit-argument) +(define-key evil-motion-state-map "3" 'digit-argument) +(define-key evil-motion-state-map "4" 'digit-argument) +(define-key evil-motion-state-map "5" 'digit-argument) +(define-key evil-motion-state-map "6" 'digit-argument) +(define-key evil-motion-state-map "7" 'digit-argument) +(define-key evil-motion-state-map "8" 'digit-argument) +(define-key evil-motion-state-map "9" 'digit-argument) +(define-key evil-motion-state-map "b" 'evil-backward-word-begin) +(define-key evil-motion-state-map "B" 'evil-backward-WORD-begin) +(define-key evil-motion-state-map "e" 'evil-forward-word-end) +(define-key evil-motion-state-map "E" 'evil-forward-WORD-end) +(define-key evil-motion-state-map "f" 'evil-find-char) +(define-key evil-motion-state-map "F" 'evil-find-char-backward) +(define-key evil-motion-state-map "G" 'evil-goto-line) +(define-key evil-motion-state-map "h" 'evil-backward-char) +(define-key evil-motion-state-map "H" 'evil-window-top) +(define-key evil-motion-state-map "j" 'evil-next-line) +(define-key evil-motion-state-map "k" 'evil-previous-line) +(define-key evil-motion-state-map "l" 'evil-forward-char) +(define-key evil-motion-state-map " " 'evil-forward-char) +(define-key evil-motion-state-map "K" 'evil-lookup) +(define-key evil-motion-state-map "L" 'evil-window-bottom) +(define-key evil-motion-state-map "M" 'evil-window-middle) +(define-key evil-motion-state-map "n" 'evil-search-next) +(define-key evil-motion-state-map "N" 'evil-search-previous) +(define-key evil-motion-state-map "t" 'evil-find-char-to) +(define-key evil-motion-state-map "T" 'evil-find-char-to-backward) +(define-key evil-motion-state-map "w" 'evil-forward-word-begin) +(define-key evil-motion-state-map "W" 'evil-forward-WORD-begin) +(define-key evil-motion-state-map "y" 'evil-yank) +(define-key evil-motion-state-map "Y" 'evil-yank-line) +(define-key evil-motion-state-map "gd" 'evil-goto-definition) +(define-key evil-motion-state-map "ge" 'evil-backward-word-end) +(define-key evil-motion-state-map "gE" 'evil-backward-WORD-end) +(define-key evil-motion-state-map "gg" 'evil-goto-first-line) +(define-key evil-motion-state-map "gj" 'evil-next-visual-line) +(define-key evil-motion-state-map (vconcat "g" [down]) 'evil-next-visual-line) +(define-key evil-motion-state-map "gk" 'evil-previous-visual-line) +(define-key evil-motion-state-map (vconcat "g" [up]) 'evil-previous-visual-line) +(define-key evil-motion-state-map "g0" 'evil-beginning-of-visual-line) +(define-key evil-motion-state-map "g_" 'evil-last-non-blank) +(define-key evil-motion-state-map "g^" 'evil-first-non-blank-of-visual-line) +(define-key evil-motion-state-map (vconcat "g" [home]) 'evil-first-non-blank-of-visual-line) +(define-key evil-motion-state-map "gm" 'evil-middle-of-visual-line) +(define-key evil-motion-state-map "gM" 'evil-percentage-of-line) +(define-key evil-motion-state-map "go" 'evil-goto-char) +(define-key evil-motion-state-map "g$" 'evil-end-of-visual-line) +(define-key evil-motion-state-map (vconcat "g" [end]) 'evil-end-of-visual-line) +(define-key evil-motion-state-map "g\C-]" 'evil-jump-to-tag) +(define-key evil-motion-state-map "{" 'evil-backward-paragraph) +(define-key evil-motion-state-map "}" 'evil-forward-paragraph) +(define-key evil-motion-state-map "#" 'evil-search-word-backward) +(define-key evil-motion-state-map "g#" 'evil-search-unbounded-word-backward) +(define-key evil-motion-state-map "$" 'evil-end-of-line) +(define-key evil-motion-state-map [end] 'evil-end-of-line) +(define-key evil-motion-state-map [home] 'evil-beginning-of-line) +(define-key evil-motion-state-map "%" 'evil-jump-item) +(define-key evil-motion-state-map "`" 'evil-goto-mark) +(define-key evil-motion-state-map "'" 'evil-goto-mark-line) +(define-key evil-motion-state-map "(" 'evil-backward-sentence-begin) +(define-key evil-motion-state-map ")" 'evil-forward-sentence-begin) +(define-key evil-motion-state-map "]]" 'evil-forward-section-begin) +(define-key evil-motion-state-map "][" 'evil-forward-section-end) +(define-key evil-motion-state-map "[[" 'evil-backward-section-begin) +(define-key evil-motion-state-map "[]" 'evil-backward-section-end) +(define-key evil-motion-state-map "[(" 'evil-previous-open-paren) +(define-key evil-motion-state-map "])" 'evil-next-close-paren) +(define-key evil-motion-state-map "[{" 'evil-previous-open-brace) +(define-key evil-motion-state-map "]}" 'evil-next-close-brace) +(define-key evil-motion-state-map "]'" 'evil-next-mark-line) +(define-key evil-motion-state-map "]`" 'evil-next-mark) +(define-key evil-motion-state-map "['" 'evil-previous-mark-line) +(define-key evil-motion-state-map "[`" 'evil-previous-mark) +(define-key evil-motion-state-map "]s" 'evil-next-flyspell-error) +(define-key evil-motion-state-map "[s" 'evil-prev-flyspell-error) +(define-key evil-motion-state-map "*" 'evil-search-word-forward) +(define-key evil-motion-state-map "g*" 'evil-search-unbounded-word-forward) +(define-key evil-motion-state-map "," 'evil-repeat-find-char-reverse) +(define-key evil-motion-state-map "/" 'evil-search-forward) +(define-key evil-motion-state-map ";" 'evil-repeat-find-char) +(define-key evil-motion-state-map "?" 'evil-search-backward) +(define-key evil-motion-state-map "|" 'evil-goto-column) +(define-key evil-motion-state-map "^" 'evil-first-non-blank) +(define-key evil-motion-state-map "+" 'evil-next-line-first-non-blank) +(define-key evil-motion-state-map "_" 'evil-next-line-1-first-non-blank) +(define-key evil-motion-state-map "-" 'evil-previous-line-first-non-blank) +(define-key evil-motion-state-map "\C-w" 'evil-window-map) +(define-key evil-motion-state-map (kbd "C-6") 'evil-switch-to-windows-last-buffer) +(define-key evil-motion-state-map "\C-]" 'evil-jump-to-tag) +(define-key evil-motion-state-map (kbd "C-b") 'evil-scroll-page-up) +(define-key evil-motion-state-map (kbd "C-e") 'evil-scroll-line-down) +(define-key evil-motion-state-map (kbd "C-f") 'evil-scroll-page-down) +(define-key evil-motion-state-map (kbd "C-o") 'evil-jump-backward) +(define-key evil-motion-state-map (kbd "C-y") 'evil-scroll-line-up) +(define-key evil-motion-state-map (kbd "RET") 'evil-ret) +(define-key evil-motion-state-map "\\" 'evil-execute-in-emacs-state) +(define-key evil-motion-state-map "z^" 'evil-scroll-top-line-to-bottom) +(define-key evil-motion-state-map "z+" 'evil-scroll-bottom-line-to-top) +(define-key evil-motion-state-map "zt" 'evil-scroll-line-to-top) +;; TODO: z RET has an advanced form taking an count before the RET +;; but this requires again a special state with a single command +;; bound to RET +(define-key evil-motion-state-map (vconcat "z" [return]) 'evil-scroll-line-to-top-first-non-blank) +(define-key evil-motion-state-map (kbd "z RET") 'evil-scroll-line-to-top-first-non-blank) +(define-key evil-motion-state-map "zz" 'evil-scroll-line-to-center) +(define-key evil-motion-state-map "z." 'evil-scroll-line-to-center-first-non-blank) +(define-key evil-motion-state-map "zb" 'evil-scroll-line-to-bottom) +(define-key evil-motion-state-map "z-" 'evil-scroll-line-to-bottom-first-non-blank) +(define-key evil-motion-state-map "v" 'evil-visual-char) +(define-key evil-motion-state-map "V" 'evil-visual-line) +(define-key evil-motion-state-map "\C-v" 'evil-visual-block) +(define-key evil-motion-state-map "gv" 'evil-visual-restore) +(define-key evil-motion-state-map (kbd "C-^") 'evil-buffer) +(define-key evil-motion-state-map [left] 'evil-backward-char) +(define-key evil-motion-state-map [right] 'evil-forward-char) +(define-key evil-motion-state-map [up] 'evil-previous-line) +(define-key evil-motion-state-map [down] 'evil-next-line) +(define-key evil-motion-state-map "zl" 'evil-scroll-column-right) +(define-key evil-motion-state-map [?z right] 'evil-scroll-column-right) +(define-key evil-motion-state-map "zh" 'evil-scroll-column-left) +(define-key evil-motion-state-map [?z left] 'evil-scroll-column-left) +(define-key evil-motion-state-map "ze" 'evil-scroll-end-column) +(define-key evil-motion-state-map "zs" 'evil-scroll-start-column) +(define-key evil-motion-state-map "zH" 'evil-scroll-left) +(define-key evil-motion-state-map [?z S-left] 'evil-scroll-left) +(define-key evil-motion-state-map "zL" 'evil-scroll-right) +(define-key evil-motion-state-map [?z S-right] 'evil-scroll-right) +(define-key evil-motion-state-map + (read-kbd-macro evil-toggle-key) 'evil-emacs-state) + +;; text objects +(define-key evil-outer-text-objects-map "w" 'evil-a-word) +(define-key evil-outer-text-objects-map "W" 'evil-a-WORD) +(define-key evil-outer-text-objects-map "s" 'evil-a-sentence) +(define-key evil-outer-text-objects-map "p" 'evil-a-paragraph) +(define-key evil-outer-text-objects-map "b" 'evil-a-paren) +(define-key evil-outer-text-objects-map "(" 'evil-a-paren) +(define-key evil-outer-text-objects-map ")" 'evil-a-paren) +(define-key evil-outer-text-objects-map "[" 'evil-a-bracket) +(define-key evil-outer-text-objects-map "]" 'evil-a-bracket) +(define-key evil-outer-text-objects-map "B" 'evil-a-curly) +(define-key evil-outer-text-objects-map "{" 'evil-a-curly) +(define-key evil-outer-text-objects-map "}" 'evil-a-curly) +(define-key evil-outer-text-objects-map "<" 'evil-an-angle) +(define-key evil-outer-text-objects-map ">" 'evil-an-angle) +(define-key evil-outer-text-objects-map "'" 'evil-a-single-quote) +(define-key evil-outer-text-objects-map "\"" 'evil-a-double-quote) +(define-key evil-outer-text-objects-map "`" 'evil-a-back-quote) +(define-key evil-outer-text-objects-map "t" 'evil-a-tag) +(define-key evil-outer-text-objects-map "o" 'evil-a-symbol) +(define-key evil-inner-text-objects-map "w" 'evil-inner-word) +(define-key evil-inner-text-objects-map "W" 'evil-inner-WORD) +(define-key evil-inner-text-objects-map "s" 'evil-inner-sentence) +(define-key evil-inner-text-objects-map "p" 'evil-inner-paragraph) +(define-key evil-inner-text-objects-map "b" 'evil-inner-paren) +(define-key evil-inner-text-objects-map "(" 'evil-inner-paren) +(define-key evil-inner-text-objects-map ")" 'evil-inner-paren) +(define-key evil-inner-text-objects-map "[" 'evil-inner-bracket) +(define-key evil-inner-text-objects-map "]" 'evil-inner-bracket) +(define-key evil-inner-text-objects-map "B" 'evil-inner-curly) +(define-key evil-inner-text-objects-map "{" 'evil-inner-curly) +(define-key evil-inner-text-objects-map "}" 'evil-inner-curly) +(define-key evil-inner-text-objects-map "<" 'evil-inner-angle) +(define-key evil-inner-text-objects-map ">" 'evil-inner-angle) +(define-key evil-inner-text-objects-map "'" 'evil-inner-single-quote) +(define-key evil-inner-text-objects-map "\"" 'evil-inner-double-quote) +(define-key evil-inner-text-objects-map "`" 'evil-inner-back-quote) +(define-key evil-inner-text-objects-map "t" 'evil-inner-tag) +(define-key evil-inner-text-objects-map "o" 'evil-inner-symbol) +(define-key evil-motion-state-map "gn" 'evil-next-match) +(define-key evil-motion-state-map "gN" 'evil-previous-match) + +(when evil-want-C-i-jump + (define-key evil-motion-state-map (kbd "C-i") 'evil-jump-forward)) + +(when evil-want-C-u-scroll + (define-key evil-motion-state-map (kbd "C-u") 'evil-scroll-up)) + +(when evil-want-C-d-scroll + (define-key evil-motion-state-map (kbd "C-d") 'evil-scroll-down)) + +(when evil-want-C-g-bindings + (define-key evil-motion-state-map "g\C-g" 'count-words)) + +;;; Visual state + +(define-key evil-visual-state-map "A" 'evil-append) +(define-key evil-visual-state-map "I" 'evil-insert) +(define-key evil-visual-state-map "o" 'exchange-point-and-mark) +(define-key evil-visual-state-map "O" 'evil-visual-exchange-corners) +(define-key evil-visual-state-map "R" 'evil-change-whole-line) +(define-key evil-visual-state-map "u" 'evil-downcase) +(define-key evil-visual-state-map "U" 'evil-upcase) +(define-key evil-visual-state-map "X" 'evil-delete-line) +(define-key evil-visual-state-map "a" evil-outer-text-objects-map) +(define-key evil-visual-state-map "i" evil-inner-text-objects-map) +(define-key evil-visual-state-map (kbd "") 'undefined) +(define-key evil-visual-state-map (kbd "") 'undefined) +(define-key evil-visual-state-map [remap evil-repeat] 'undefined) +(define-key evil-visual-state-map [escape] 'evil-exit-visual-state) +(define-key evil-visual-state-map "gf" 'evil-find-file-at-point-visual) + +;;; Operator-Pending state + +(define-key evil-operator-state-map "a" evil-outer-text-objects-map) +(define-key evil-operator-state-map "i" evil-inner-text-objects-map) +;; (define-key evil-operator-state-map [escape] 'keyboard-quit) + +;;; Insert state + +(defvar evil-insert-state-bindings + `(([insert] . evil-enter-replace-state) + ("\C-q" . evil-quoted-insert) + ("\C-v" . evil-quoted-insert) + ("\C-k" . evil-insert-digraph) + ("\C-o" . evil-execute-in-normal-state) + ("\C-r" . evil-paste-from-register) + ("\C-y" . evil-copy-from-above) + ("\C-e" . evil-copy-from-below) + ("\C-n" . evil-complete-next) ;; Completion commands + ("\C-p" . evil-complete-previous) ;; don't yet behave correctly + ("\C-x\C-n" . evil-complete-next-line) ;; in replace state + ("\C-x\C-p" . evil-complete-previous-line) ;; TODO - fix this + ("\C-t" . evil-shift-right-line) + ("\C-d" . evil-shift-left-line) + ("\C-a" . evil-paste-last-insertion) + ("\C-@" . evil-paste-last-insertion-and-stop-insert) + ([remap delete-backward-char] . evil-delete-backward-char-and-join) + ([S-left] . evil-backward-word-begin) + ([S-right] . evil-forward-word-begin) + ,(if evil-want-C-w-delete + '("\C-w" . evil-delete-backward-word) + '("\C-w" . evil-window-map)) + ,@(when evil-want-C-u-delete + '(("\C-u" . evil-delete-back-to-indentation))) + ,@(when evil-want-C-h-delete + '(("\C-h" . evil-delete-backward-char-and-join))) + ([mouse-2] . mouse-yank-primary)) + "Evil's bindings for insert & replace states. +Used in `evil-insert-state-map' and `evil-replace-state-map', +excluding , , and `evil-toggle-key'.") + +(defun evil-update-insert-state-bindings (&optional _option-name remove force) + "Update bindings in `evil-insert-state-map'. +If no arguments are given add the bindings specified in +`evil-insert-state-bindings'. If REMOVE is non nil, remove only +these bindings. Unless FORCE is non nil, this will not +overwriting existing bindings, which means bindings will not be +added if one already exists for a key and only default bindings +are removed. + +Note that , and `evil-toggle-key' are not +included in `evil-insert-state-bindings' by default." + (interactive) + (dolist (binding evil-insert-state-bindings) + (cond + ((and remove + (or force + ;; Only remove if the default binding has not changed + (eq (evil-lookup-key evil-insert-state-map (car binding)) + (cdr binding)))) + (define-key evil-insert-state-map (car binding) nil)) + ((and (null remove) + (or force + ;; Check to see that nothing is bound here before adding + (not (evil-lookup-key evil-insert-state-map (car binding))))) + (define-key evil-insert-state-map (car binding) (cdr binding)))))) + +(define-key evil-insert-state-map [delete] 'delete-char) +(define-key evil-insert-state-map [escape] 'evil-normal-state) +(define-key evil-insert-state-map + (read-kbd-macro evil-toggle-key) 'evil-emacs-state) + +;;; Replace state + +(dolist (binding evil-insert-state-bindings) + (define-key evil-replace-state-map (car binding) (cdr binding))) +(define-key evil-replace-state-map (kbd "DEL") 'evil-replace-backspace) +(when evil-want-C-h-delete + (define-key evil-replace-state-map "\C-h" 'evil-replace-backspace)) +(define-key evil-replace-state-map [escape] 'evil-normal-state) +(define-key evil-replace-state-map [insert] 'evil-append) + +;;; Emacs state + +(define-key evil-emacs-state-map + (read-kbd-macro evil-toggle-key) 'evil-exit-emacs-state) + +(when evil-want-C-w-in-emacs-state + (define-key evil-emacs-state-map "\C-w" 'evil-window-map)) + +;;; Mouse +(define-key evil-motion-state-map [down-mouse-1] 'evil-mouse-drag-region) +(define-key evil-visual-state-map [mouse-2] 'evil-exit-visual-and-repeat) +(define-key evil-normal-state-map [mouse-2] 'mouse-yank-primary) + +;; Ex +(define-key evil-motion-state-map ":" 'evil-ex) +(define-key evil-motion-state-map "!" 'evil-shell-command) + +(evil-ex-define-cmd "e[dit]" 'evil-edit) +(evil-ex-define-cmd "vie[w]" 'evil-view) +(evil-ex-define-cmd "w[rite]" 'evil-write) +(evil-ex-define-cmd "up[date]" 'evil-update) +(evil-ex-define-cmd "wa[ll]" 'evil-write-all) +(evil-ex-define-cmd "sav[eas]" 'evil-save) +(evil-ex-define-cmd "r[ead]" 'evil-read) +(evil-ex-define-cmd "b[uffer]" 'evil-buffer) +(evil-ex-define-cmd "bad[d]" 'evil-buffer-add) +(evil-ex-define-cmd "bn[ext]" 'evil-next-buffer) +(evil-ex-define-cmd "bp[revious]" 'evil-prev-buffer) +(evil-ex-define-cmd "bN[ext]" "bprevious") +(evil-ex-define-cmd "sb[uffer]" 'evil-split-buffer) +(evil-ex-define-cmd "sbn[ext]" 'evil-split-next-buffer) +(evil-ex-define-cmd "sbp[revious]" 'evil-split-prev-buffer) +(evil-ex-define-cmd "sbN[ext]" "sbprevious") +(evil-ex-define-cmd "buffers" 'buffer-menu) +(evil-ex-define-cmd "files" 'evil-show-files) +(evil-ex-define-cmd "ls" "buffers") + +(evil-ex-define-cmd "c[hange]" 'evil-change) +(evil-ex-define-cmd "co[py]" 'evil-copy) +(evil-ex-define-cmd "t" "copy") +(evil-ex-define-cmd "m[ove]" 'evil-move) +(evil-ex-define-cmd "d[elete]" 'evil-ex-delete) +(evil-ex-define-cmd "y[ank]" 'evil-ex-yank) +(evil-ex-define-cmd "pu[t]" 'evil-ex-put) +(evil-ex-define-cmd "go[to]" 'evil-goto-char) +(evil-ex-define-cmd "j[oin]" 'evil-ex-join) +(evil-ex-define-cmd "le[ft]" 'evil-align-left) +(evil-ex-define-cmd "ri[ght]" 'evil-align-right) +(evil-ex-define-cmd "ce[nter]" 'evil-align-center) +(evil-ex-define-cmd "sp[lit]" 'evil-window-split) +(evil-ex-define-cmd "sv[iew]" 'evil-window-split-view) +(evil-ex-define-cmd "vs[plit]" 'evil-window-vsplit) +(evil-ex-define-cmd "new" 'evil-window-new) +(evil-ex-define-cmd "ene[w]" 'evil-buffer-new) +(evil-ex-define-cmd "vne[w]" 'evil-window-vnew) +(evil-ex-define-cmd "clo[se]" 'evil-window-delete) +(evil-ex-define-cmd "on[ly]" 'delete-other-windows) +(evil-ex-define-cmd "q[uit]" 'evil-quit) +(evil-ex-define-cmd "wq" 'evil-save-and-close) +(evil-ex-define-cmd "quita[ll]" 'evil-quit-all) +(evil-ex-define-cmd "qa[ll]" "quitall") +(evil-ex-define-cmd "cq[uit]" 'evil-quit-all-with-error-code) +(evil-ex-define-cmd "wqa[ll]" 'evil-save-and-quit) +(evil-ex-define-cmd "xa[ll]" "wqall") +(evil-ex-define-cmd "x[it]" 'evil-save-modified-and-close) +(evil-ex-define-cmd "exi[t]" 'evil-save-modified-and-close) +(evil-ex-define-cmd "bd[elete]" 'evil-delete-buffer) +(evil-ex-define-cmd "bw[ipeout]" 'evil-delete-buffer) +(evil-ex-define-cmd "g[lobal]" 'evil-ex-global) +(evil-ex-define-cmd "v[global]" 'evil-ex-global-inverted) +(evil-ex-define-cmd "norm[al]" 'evil-ex-normal) +(evil-ex-define-cmd "s[ubstitute]" 'evil-ex-substitute) +(evil-ex-define-cmd "&" 'evil-ex-repeat-substitute) +(evil-ex-define-cmd "&&" 'evil-ex-repeat-substitute-with-flags) +(evil-ex-define-cmd "~" 'evil-ex-repeat-substitute-with-search) +(evil-ex-define-cmd "~&" 'evil-ex-repeat-substitute-with-search-and-flags) +(evil-ex-define-cmd "mat[ch]" 'evil-ex-match) +(evil-ex-define-cmd "registers" 'evil-show-registers) +(evil-ex-define-cmd "di[splay]" "registers") +(evil-ex-define-cmd "ma[rk]" 'evil-set-col-0-mark) +(evil-ex-define-cmd "marks" 'evil-show-marks) +(evil-ex-define-cmd "delm[arks]" 'evil-delete-marks) +(evil-ex-define-cmd "ju[mps]" 'evil-show-jumps) +(evil-ex-define-cmd "noh[lsearch]" 'evil-ex-nohighlight) +(evil-ex-define-cmd "f[ile]" 'evil-show-file-info) +(evil-ex-define-cmd "<" 'evil-shift-left) +(evil-ex-define-cmd ">" 'evil-shift-right) +(evil-ex-define-cmd "=" 'evil-ex-line-number) +(evil-ex-define-cmd "!" 'evil-shell-command) +(evil-ex-define-cmd "@:" 'evil-ex-repeat) +(evil-ex-define-cmd "mak[e]" 'evil-make) +(evil-ex-define-cmd "cc" 'evil-goto-error) +(evil-ex-define-cmd "cfir[st]" 'first-error) +(evil-ex-define-cmd "cr[ewind]" 'first-error) +(evil-ex-define-cmd "cn[ext]" 'next-error) +(evil-ex-define-cmd "cp[revious]" 'previous-error) +(evil-ex-define-cmd "set-initial-state" 'evil-ex-set-initial-state) +(evil-ex-define-cmd "show-digraphs" 'evil-ex-show-digraphs) +(evil-ex-define-cmd "sor[t]" 'evil-ex-sort) +(evil-ex-define-cmd "res[ize]" 'evil-ex-resize) +(evil-ex-define-cmd "u[ndo]" 'evil-undo) +(evil-ex-define-cmd "red[o]" 'evil-redo) +(evil-ex-define-cmd "p[rint]" 'evil-ex-print) +(evil-ex-define-cmd "P[rint]" 'evil-ex-print) +(evil-ex-define-cmd "nu[mber]" 'evil-ex-numbered-print) +(evil-ex-define-cmd "#" 'evil-ex-numbered-print) +(evil-ex-define-cmd "z" 'evil-ex-z) +(evil-ex-define-cmd "ret[ab]" 'evil-retab) + +(when (featurep 'tab-bar) + (evil-ex-define-cmd "tabnew" 'tab-bar-new-tab) + (evil-ex-define-cmd "tabc[lose]" 'tab-bar-close-tab) + (evil-ex-define-cmd "tabo[nly]" 'tab-bar-close-other-tabs) + (evil-ex-define-cmd "tabn[ext]" 'evil-tab-next) + (evil-ex-define-cmd "tabp[revious]" 'tab-bar-switch-to-prev-tab)) + +;; Command-line editing +(define-key evil-command-line-map "\d" #'evil-ex-delete-backward-char) +(define-key evil-command-line-map "\t" #'completion-at-point) +(define-key evil-command-line-map [tab] #'completion-at-point) +(define-key evil-command-line-map "\C-a" #'completion-at-point) +(define-key evil-command-line-map "\C-b" #'move-beginning-of-line) +(define-key evil-command-line-map "\C-c" #'abort-recursive-edit) +(define-key evil-command-line-map "\C-d" #'completion-at-point) +(define-key evil-command-line-map "\C-f" 'evil-command-window) +(define-key evil-command-line-map "\C-g" #'abort-minibuffers) +(define-key evil-command-line-map "\C-k" 'evil-insert-digraph) +(define-key evil-command-line-map "\C-l" #'completion-at-point) +(define-key evil-command-line-map "\C-n" #'next-history-element) +(define-key evil-command-line-map "\C-p" #'previous-history-element) +(define-key evil-command-line-map "\C-r" 'evil-paste-from-register) +(define-key evil-command-line-map "\C-u" 'evil-delete-whole-line) +(define-key evil-command-line-map "\C-v" #'quoted-insert) +(when evil-want-C-w-delete + (define-key evil-command-line-map "\C-w" #'evil-delete-backward-word)) +(define-key evil-command-line-map [escape] #'abort-recursive-edit) +(define-key evil-command-line-map [S-left] #'backward-word) +(define-key evil-command-line-map [S-right] #'forward-word) +(define-key evil-command-line-map [up] #'previous-complete-history-element) +(define-key evil-command-line-map [down] #'next-complete-history-element) +(define-key evil-command-line-map [prior] #'previous-history-element) +(define-key evil-command-line-map [next] #'next-history-element) +(define-key evil-command-line-map [return] #'exit-minibuffer) +(define-key evil-command-line-map (kbd "RET") #'exit-minibuffer) + +;; search command line +(set-keymap-parent evil-ex-search-keymap evil-command-line-map) +(unless evil-want-C-w-delete + (define-key evil-ex-search-keymap "\C-w" 'evil-search-yank-word)) + +;; Ex command line +(set-keymap-parent evil-ex-completion-map evil-command-line-map) + +;; eval prompt (the `=' register) +(define-key evil-eval-map "\C-b" 'move-beginning-of-line) +(define-key evil-eval-map "\C-c" 'abort-recursive-edit) +(define-key evil-eval-map "\C-g" 'abort-recursive-edit) +(define-key evil-eval-map "\C-k" 'evil-insert-digraph) +(define-key evil-eval-map "\C-p" #'previous-complete-history-element) +(define-key evil-eval-map "\C-r" 'evil-paste-from-register) +(define-key evil-eval-map "\C-n" #'next-complete-history-element) +(define-key evil-eval-map "\C-u" 'evil-delete-whole-line) +(define-key evil-eval-map "\C-v" #'quoted-insert) +(define-key evil-eval-map "\C-w" 'evil-delete-backward-word) +(define-key evil-eval-map [escape] 'abort-recursive-edit) +(define-key evil-eval-map [S-left] 'backward-word) +(define-key evil-eval-map [S-right] 'forward-word) +(define-key evil-eval-map [up] 'previous-complete-history-element) +(define-key evil-eval-map [down] 'next-complete-history-element) +(define-key evil-eval-map [prior] 'previous-history-element) +(define-key evil-eval-map [next] 'next-history-element) +(define-key evil-eval-map [return] 'exit-minibuffer) +(define-key evil-eval-map (kbd "RET") 'exit-minibuffer) + +;; evil-read-key +(define-key evil-read-key-map (kbd "ESC") #'keyboard-quit) +(define-key evil-read-key-map (kbd "C-]") #'keyboard-quit) +(define-key evil-read-key-map (kbd "C-g") #'keyboard-quit) +(define-key evil-read-key-map (kbd "C-q") #'evil-read-quoted-char) +(define-key evil-read-key-map (kbd "C-v") #'evil-read-quoted-char) +(define-key evil-read-key-map (kbd "C-k") #'evil-read-digraph-char) +(define-key evil-read-key-map "\r" "\n") + +;; command line window +(evil-define-key* '(normal insert) evil-command-window-mode-map + (kbd "RET") 'evil-command-window-execute) + +(provide 'evil-maps) + +;;; evil-maps.el ends here diff --git a/.config/emacs/lisp/evil/evil-repeat.el b/.config/emacs/lisp/evil/evil-repeat.el new file mode 100644 index 0000000..75727ef --- /dev/null +++ b/.config/emacs/lisp/evil/evil-repeat.el @@ -0,0 +1,641 @@ +;;; evil-repeat.el --- Repeat system -*- lexical-binding: t -*- + +;; Author: Frank Fischer +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; A repeat begins when leaving Normal state; it ends when re-entering +;; Normal state. The diagram below shows possible routes between +;; Normal state (N), Insert state (I), Visual state (V), +;; Operator-Pending state (O) and Replace state (R). (Emacs state +;; is an exception: nothing is repeated in that state.) +;; ___ +;; / \ +;; | R | +;; \___/ +;; ^ | +;; | | +;; ___ |___V ___ +;; / \ <------- / \ -------> / \ +;; | V | | N | | O | +;; \___/ -------> \___/ <------- \___/ +;; | | ^ | +;; | | | | +;; | V___| | +;; | / \ | +;; +--------> | I | <--------+ +;; \___/ +;; +;; The recording of a repeat is started in one of two cases: Either a +;; command is about to be executed (in pre-command-hook) or normal +;; state is exited. The recording is stopped whenever a command has +;; been completed and evil is in normal state afterwards. Therefore, +;; a non-inserting command in normal-state is recorded as a single +;; repeat unit. In contrast, if the command leaves normal state and +;; starts insert-state, all commands that are executed until +;; insert-state is left and normal state is reactivated are recorded +;; together in one repeat unit. In other words, a repeat unit consists +;; of all commands that are executed starting and ending in normal +;; state. +;; +;; Not all commands are recorded. There are several commands that are +;; completely ignored and other commands that even abort the currently +;; active recording, e.g., commands that switch buffer. +;; +;; During recording the repeat information is appended to the variable +;; `evil-repeat-info', which is cleared when the recording +;; starts. This accumulated repeat information is put into the +;; `evil-repeat-ring' when the recording is finished. The dot command, +;; `\[evil-repeat]' (`evil-repeat') replays the most recent entry in +;; the ring, preceeding repeats can be replayed using +;; `\[evil-repeat-pop]' (`evil-repeat-pop'). +;; +;; Repeat information can be stored in almost arbitrary form. How the +;; repeat information for each single command is recorded is +;; determined by the :repeat property of the command. This property +;; has the following interpretation: +;; +;; t record commands by storing the key-sequence that invoked it +;; nil ignore this command completely +;; ignore synonym to nil +;; motion command is recorded by storing the key-sequence but only in +;; insert state, otherwise it is ignored. +;; abort stop recording of repeat information immediately +;; change record commands by storing buffer changes +;; SYMBOL if SYMBOL is contained as key in `evil-repeat-types' call +;; the corresponding (function-)value, otherwise call the +;; function associated with SYMBOL. In both cases the +;; function should take exactly one argument which is either +;; `pre', `pre-read-key-sequence' or `post' specifying on +;; whether the function is called before or after the +;; execution of the command. +;; +;; Therefore, using a certain SYMBOL one can write specific repeation +;; functions for each command. +;; +;; Each value of ring `evil-repeat-info', i.e., each single repeat +;; information must be one of the following two possibilities: +;; If element is a sequence, it is regarded as a key-sequence to +;; be repeated. Otherwise the element must be a list +;; (FUNCTION PARAMS ...) which will be called using +;; (apply FUNCTION PARAMS) whenever this repeat is being executed. +;; +;; A user supplied repeat function can use the functions +;; `evil-record-repeat' to append further repeat-information of the +;; form described above to `evil-repeat-info'. See the implementation +;; of `evil-repeat-keystrokes' and `evil-repeat-changes' for examples. +;; +;; The repeat information is executed with `evil-execute-repeat-info', +;; which passes key-sequence elements to `execute-kbd-macro' and +;; executes other elements as defined above. A special version is +;; `evil-execute-repeat-info-with-count'. This function works as +;; `evil-execute-repeat-info', but replaces the count of the first +;; command. This is done by parsing the key-sequence, ignoring all +;; calls to `digit-prefix-argument' and `negative-argument', and +;; prepending the count as a string to the vector of the remaining +;; key-sequence. + +;;; Code: + +(require 'evil-states) + +(defmacro evil-without-repeat (&rest body) + (declare (indent defun) + (debug t)) + `(let ((pre-command-hook (remq 'evil-repeat-pre-hook pre-command-hook)) + (post-command-hook (remq 'evil-repeat-post-hook post-command-hook))) + ,@body + (evil-repeat-abort))) + +(defsubst evil-repeat-recording-p () + "Return non-nil if a recording is in progress." + (eq evil-recording-repeat t)) + +(defun evil-repeat-start () + "Start recording a new repeat into `evil-repeat-info'." + (evil-repeat-reset t) + (evil-repeat-record-buffer) + (when (evil-visual-state-p) + (let* ((range (evil-visual-range)) + (beg (evil-range-beginning range)) + (end (max 1 (1- (evil-range-end range)))) + (nfwdlines (evil-count-lines beg end))) + (evil-repeat-record + (cond + ((eq evil-visual-selection 'char) + (list #'evil-repeat-visual-char + nfwdlines + (- end + (if (zerop nfwdlines) + beg + (save-excursion + (goto-char end) + (line-beginning-position)))))) + ((eq evil-visual-selection 'line) + (list #'evil-repeat-visual-line nfwdlines)) + ((eq evil-visual-selection 'block) + (list #'evil-repeat-visual-block + nfwdlines + (abs (- (evil-column beg) (evil-column end)))))))))) + +(defun evil-repeat-stop () + "Stop recording a repeat. +Update `evil-repeat-ring' with the accumulated changes +in `evil-repeat-info' and clear variables." + (unwind-protect + (when (evil-repeat-recording-p) + (setq evil-repeat-info (evil-normalize-repeat-info evil-repeat-info)) + (when (and evil-repeat-info evil-repeat-ring) + (ring-insert evil-repeat-ring evil-repeat-info))) + (evil-repeat-reset nil))) + +(defun evil-repeat-abort () + "Abort current repeation." + (evil-repeat-reset 'abort)) + +(defun evil-repeat-reset (flag) + "Clear all repeat recording variables. +Set `evil-recording-repeat' to FLAG." + (setq evil-recording-repeat flag + evil-repeat-info nil + evil-repeat-buffer nil)) + +(defsubst evil-repeat-record-position (&optional pos) + "Set `evil-repeat-pos' to POS or point." + (setq evil-repeat-pos (or pos (point)))) + +(defun evil-repeat-record-buffer () + "Set `evil-repeat-buffer' to the current buffer." + (unless (minibufferp) + (setq evil-repeat-buffer (current-buffer)))) + +(defmacro evil-save-repeat-info (&rest body) + "Execute BODY, protecting the values of repeat variables." + (declare (indent defun) + (debug t)) + `(let (evil-repeat-ring + evil-recording-repeat + evil-recording-current-command + evil-repeat-info + evil-repeat-changes + evil-repeat-pos + evil-repeat-keys + evil-repeat-buffer + this-command + last-command) + ,@body)) + +(defun evil-repeat-different-buffer-p (&optional strict) + "Whether the buffer has changed in a repeat. +If STRICT is non-nil, returns t if the previous buffer +is unknown; otherwise returns t only if the previous +buffer is known and different from the current buffer." + (and (or (buffer-live-p evil-repeat-buffer) strict) + (not (minibufferp)) + (not (eq (current-buffer) evil-repeat-buffer)))) + +(defvar evil-repeat-types + '((t . evil-repeat-keystrokes) + (change . evil-repeat-changes) + (motion . evil-repeat-motion) + (insert-at-point . evil-repeat-insert-at-point) + (ignore . nil)) + "Alist of defined repeat-types.") + +(defun evil--repeat-type (command) + "Return the :repeat property of COMMAND." + (when (functionp command) ; ignore keyboard macros + (let* ((type (evil-get-command-property command :repeat t)) + (repeat-type (assq type evil-repeat-types))) + (if repeat-type (cdr repeat-type) type)))) + +(defun evil-repeat-type (command &optional default) + "Return the :repeat property of COMMAND. +If COMMAND doesn't have this property, return DEFAULT." + (declare (obsolete evil--repeat-type "1.15.0")) + (when (functionp command) ; ignore keyboard macros + (let* ((type (evil-get-command-property command :repeat default)) + (repeat-type (assq type evil-repeat-types))) + (if repeat-type (cdr repeat-type) type)))) + +(defun evil-repeat-force-abort-p (repeat-type) + "Whether the current command should abort the recording of repeat information. +Return non-nil if so." + (or (evil-repeat-different-buffer-p) ; ... buffer changed + (eq repeat-type 'abort) ; ... explicitly forced + (eq evil-recording-repeat 'abort) ; ... already aborted + (evil-emacs-state-p) ; ... in Emacs state + (and (evil-mouse-events-p ; ... mouse events + (this-command-keys-vector)) + (null repeat-type)) + (minibufferp))) ; ... minibuffer activated + +(defun evil-repeat-record (info) + "Append INFO to `evil-repeat-info'." + (when (evil-repeat-recording-p) + (setq evil-repeat-info (nconc evil-repeat-info (list info))))) + +;; called from `pre-command-hook' +(defun evil-repeat-pre-hook () + "Prepare the current command for recording the repeation." + (when evil-local-mode + (let ((repeat-type (evil--repeat-type this-command))) + (cond + ;; abort the repeat + ((evil-repeat-force-abort-p repeat-type) + ;; We mark the current record as being aborted, because there + ;; may be further pre-hooks following before the post-hook is + ;; called. + (evil-repeat-abort)) + ;; ignore those commands completely + ((or (null repeat-type) + (evil-mouse-events-p (this-command-keys-vector)))) + ;; record command + (t + ;; In normal-state or visual state, each command is a single + ;; repeation, therefore start a new repeation. + (when (or (evil-normal-state-p) + (evil-visual-state-p)) + (evil-repeat-start)) + (setq evil-recording-current-command t) + (funcall repeat-type 'pre)))))) +(put 'evil-repeat-pre-hook 'permanent-local-hook t) + +;; called from `post-command-hook' +(defun evil-repeat-post-hook () + "Finish recording of repeat-information for the current-command." + (when (and evil-local-mode evil-recording-repeat) + (let ((repeat-type (evil--repeat-type this-command))) + (cond + ;; abort the repeat + ((evil-repeat-force-abort-p repeat-type) + ;; The command has been aborted but is complete, so just reset + ;; the recording state. + (evil-repeat-reset nil)) + ;; ignore if command should not be recorded or the current + ;; command is not being recorded + ((or (null repeat-type) + (not evil-recording-current-command))) + ;; record command + (t + (funcall repeat-type 'post) + ;; In normal state, the repeat sequence is complete, so record it. + (when (evil-normal-state-p) + (evil-repeat-stop))))) + ;; done with recording the current command + (setq evil-recording-current-command nil))) +(put 'evil-repeat-post-hook 'permanent-local-hook t) + +(defun evil-clear-command-keys () + "Clear `this-command-keys' and all information about the current command keys. +Calling this function prevents further recording of the keys that +invoked the current command" + (clear-this-command-keys t) + (setq evil-repeat-keys "")) + +(defun evil-this-command-keys (&optional post-cmd) + "Version of `this-command-keys' with finer control over prefix args." + (vconcat + (let ((arg (if post-cmd current-prefix-arg prefix-arg))) + (and (numberp arg) + ;; Only add prefix if no repeat info recorded yet + (null evil-repeat-info) + (number-to-string arg))) + (this-single-command-keys))) + +(defun evil-repeat-keystrokes (flag) + "Repeation recording function for commands that are repeated by keystrokes." + (cond + ((eq flag 'pre) + (when evil-this-register + (evil-repeat-record + `(set evil-this-register ,evil-this-register))) + (setq evil-repeat-keys (evil-this-command-keys))) + ((memq flag '(post pre-read-key-sequence)) + (evil-repeat-record (if (zerop (length (evil-this-command-keys t))) + evil-repeat-keys + (evil-this-command-keys t))) + ;; erase commands keys to prevent double recording + (evil-clear-command-keys)))) + +(defun evil-repeat-motion (flag) + "Repetition for motions. +Motions are recorded by keystroke but only in Insert state." + (when (memq evil-state '(insert replace)) + (evil-repeat-keystrokes flag))) + +(defun evil-repeat-changes (flag) + "Repeation recording function for commands that are repeated by buffer changes." + (cond + ((eq flag 'pre) + (add-hook 'after-change-functions #'evil-repeat-change-hook nil t) + (evil-repeat-start-record-changes)) + ((eq flag 'post) + (remove-hook 'after-change-functions #'evil-repeat-change-hook t) + (evil-repeat-finish-record-changes)))) + +;; called from the `after-change-functions' hook +(defun evil-repeat-change-hook (beg end length) + "Record change information for current command." + (let ((repeat-type (evil--repeat-type this-command))) + (when (and (evil-repeat-recording-p) + (eq repeat-type 'evil-repeat-changes) + (not (evil-emacs-state-p)) + (not (evil-repeat-different-buffer-p t)) + evil-state) + (unless (evil-repeat-recording-p) + (evil-repeat-start)) + (evil-repeat-record-change (- beg evil-repeat-pos) + (buffer-substring beg end) + length)))) +(put 'evil-repeat-change-hook 'permanent-local-hook t) + +(defun evil-repeat-record-change (relpos ins ndel) + "Record the current buffer changes during a repeat. +If CHANGE is specified, it is added to `evil-repeat-changes'." + (when (evil-repeat-recording-p) + (push (list relpos ins ndel) evil-repeat-changes))) + +(defun evil-repeat-start-record-changes () + "Start the recording of a new set of buffer changes." + (setq evil-repeat-changes nil) + (evil-repeat-record-position)) + +(defun evil-repeat-finish-record-changes () + "Finish the recording of buffer changes and record them as repeat." + (when (evil-repeat-recording-p) + (evil-repeat-record `(evil-execute-change + ,(nreverse evil-repeat-changes) + ,(- (point) evil-repeat-pos))) + (setq evil-repeat-changes nil))) + +(defun evil-repeat-insert-at-point (flag) + "Repeation recording function for commands that insert text in region. +For example `mouse-yank-primary'. This records text insertion when a command +inserts some text in a buffer between (point) and (mark)." + (cond + ((eq flag 'pre) + (add-hook 'after-change-functions #'evil-repeat-insert-at-point-hook nil t)) + ((eq flag 'post) + (remove-hook 'after-change-functions #'evil-repeat-insert-at-point-hook t)))) + +(defun evil-repeat-insert-at-point-hook (beg end _length) + (let ((repeat-type (evil--repeat-type this-command))) + (when (and (evil-repeat-recording-p) + (eq repeat-type 'evil-repeat-insert-at-point) + (not (evil-emacs-state-p)) + (not (evil-repeat-different-buffer-p t)) + evil-state) + (setq evil-repeat-pos beg) + (evil-repeat-record (list 'insert (buffer-substring beg end)))))) +(put 'evil-repeat-insert-at-point-hook 'permanent-local-hook t) + +(defun evil-normalize-repeat-info (repeat-info) + "Concatenate consecutive arrays in REPEAT-INFO. +Return a single array." + (let* ((result (cons nil nil)) + (result-last result) + cur cur-last) + (dolist (rep repeat-info) + (cond + ((null rep)) + ((arrayp rep) + (setq rep (listify-key-sequence rep)) + (cond + (cur + (setcdr cur-last (cons rep nil)) + (setq cur-last (cdr cur-last))) + (t (setq cur (cons rep nil) + cur-last cur)))) + (t + (when cur + (setcdr result-last (cons (apply #'vconcat cur) nil)) + (setq result-last (cdr result-last) + cur nil)) + (setcdr result-last (cons rep nil)) + (setq result-last (cdr result-last))))) + (when cur + (setcdr result-last (cons (apply #'vconcat cur) nil))) + (cdr result))) + +(defun evil-repeat-visual-char (nfwdlines nfwdchars) + "Restore a character visual selection. +If the selection is in a single line, the restored visual +selection covers the same number of characters. If the selection +covers several lines, the restored selection covers the same +number of lines and the same number of characters in the last +line as the original selection." + (evil-visual-char) + (when (> nfwdlines 0) + (forward-line nfwdlines)) + (forward-char nfwdchars)) + +(defun evil-repeat-visual-line (nfwdlines) + "Restore a character visual selection. +If the selection is in a single line, the restored visual +selection covers the same number of characters. If the selection +covers several lines, the restored selection covers the same +number of lines and the same number of characters in the last +line as the original selection." + (evil-visual-line) + (forward-line nfwdlines)) + +(defun evil-repeat-visual-block (nfwdlines nfwdchars) + "Restore a character visual selection. +If the selection is in a single line, the restored visual +selection covers the same number of characters. If the selection +covers several lines, the restored selection covers the same +number of lines and the same number of characters in the last +line as the original selection." + (evil-visual-block) + (let ((col (current-column))) + (forward-line nfwdlines) + (move-to-column (+ col nfwdchars) t))) + +(defun evil-execute-change (changes rel-point) + "Execute as list of changes. + +CHANGES is a list of triples (REL-BEG INSERT-TEXT NDEL). +REL-BEG is the relative position (to point) where the change +takes place. INSERT-TEXT is the text to be inserted at that +position and NDEL the number of characters to be deleted at that +position before insertion. + +REL-POINT is the relative position to point before the changed +where point should be placed after all changes." + (evil-save-repeat-info + (let ((point (point))) + (dolist (change changes) + (goto-char (+ point (nth 0 change))) + (delete-char (nth 2 change)) + (insert (nth 1 change))) + (goto-char (+ point rel-point))))) + +(defun evil-execute-repeat-info (repeat-info) + "Execute a repeat-information REPEAT-INFO." + (evil-save-repeat-info + (dolist (rep repeat-info) + (cond + ((or (arrayp rep) (stringp rep)) + (let ((input-method current-input-method) + (evil-input-method nil)) + (deactivate-input-method) + (unwind-protect + (execute-kbd-macro rep) + (activate-input-method input-method)))) + ((consp rep) + (when (and (= 3 (length rep)) + (eq (nth 0 rep) 'set) + (eq (nth 1 rep) 'evil-this-register) + (>= (nth 2 rep) ?0) + (< (nth 2 rep) ?9)) + (setcar (nthcdr 2 rep) (1+ (nth 2 rep)))) + (apply (car rep) (cdr rep))) + (t (error "Unexpected repeat-info: %S" rep)))))) + +;; TODO: currently we prepend the replacing count before the +;; key-sequence that calls the command. Can we use direct +;; modification of prefix-arg instead? Does it work in +;; conjunction with `execute-kbd-macro'? +(defun evil-execute-repeat-info-with-count (count repeat-info) + "Repeat the repeat-information REPEAT-INFO with the count of +the first command replaced by COUNT. The count is replaced if +and only if COUNT is non-nil." + (evil-save-repeat-info + (cond + ;; do nothing (zero repeating) + ((and count (zerop count))) + ;; replace count + (count + (let ((evil-repeat-count count) + done) + (while (and repeat-info + (arrayp (car repeat-info)) + (not done)) + (let* ((count-and-cmd (evil-extract-count (pop repeat-info)))) + (push (vconcat (number-to-string count) + (nth 2 count-and-cmd) + (nth 3 count-and-cmd)) + repeat-info) + (setq done t))) + (evil-execute-repeat-info repeat-info))) + ;; repeat with original count + (t (evil-execute-repeat-info repeat-info))))) + +;; Keep the compiler happy - this is a buffer local var +(defvar evil--execute-normal-return-state) + +(evil-define-command evil-repeat (count &optional save-point) + "Repeat the last editing command with count replaced by COUNT. +If SAVE-POINT is non-nil, do not move point." + :repeat ignore + :suppress-operator t + (interactive (list current-prefix-arg + (not evil-repeat-move-cursor))) + (cond + ((null evil-repeat-ring) + (error "Already executing repeat")) + (save-point + (save-excursion + (evil-repeat count))) + (t + (unwind-protect + (let ((evil-last-find-temp evil-last-find) + (confirm-kill-emacs t) + (kill-buffer-hook + (cons #'(lambda () + (user-error "Cannot delete buffer in repeat command")) + kill-buffer-hook)) + (undo-pointer buffer-undo-list)) + (evil-with-single-undo + (setq evil-last-repeat (list (point) count undo-pointer)) + (evil-execute-repeat-info-with-count + count (ring-ref evil-repeat-ring 0)) + (setq evil-last-find evil-last-find-temp))) + (if (eq 'evil-execute-in-normal-state last-command) + (evil-change-state evil--execute-normal-return-state) + (evil-normal-state)))))) + +;; TODO: the same issue concerning disabled undos as for `evil-paste-pop' +(evil-define-command evil-repeat-pop (count &optional save-point) + "Replace the just repeated command with a previously executed command. +Only allowed after `evil-repeat', `evil-repeat-pop' or +`evil-repeat-pop-next'. Uses the same repeat count that +was used for the first repeat. + +The COUNT argument inserts the COUNT-th previous kill. +If COUNT is negative, this is a more recent kill." + :repeat nil + :suppress-operator t + (interactive (list (prefix-numeric-value current-prefix-arg) + (not evil-repeat-move-cursor))) + (cond + ((not (and (eq last-command #'evil-repeat) + evil-last-repeat)) + (user-error "Previous command was not evil-repeat: %s" last-command)) + (save-point + (save-excursion + (evil-repeat-pop count))) + (t + (unless (eq buffer-undo-list (nth 2 evil-last-repeat)) + (evil-undo-pop)) + (goto-char (car evil-last-repeat)) + ;; rotate the repeat-ring + (while (> count 0) + (when evil-repeat-ring + (ring-insert-at-beginning evil-repeat-ring + (ring-remove evil-repeat-ring 0))) + (setq count (1- count))) + (while (< count 0) + (when evil-repeat-ring + (ring-insert evil-repeat-ring + (ring-remove evil-repeat-ring))) + (setq count (1+ count))) + (setq this-command #'evil-repeat) + (evil-repeat (cadr evil-last-repeat))))) + +(evil-define-command evil-repeat-pop-next (count &optional save-point) + "Same as `evil-repeat-pop', but with negative COUNT." + :repeat nil + :suppress-operator t + (interactive (list (prefix-numeric-value current-prefix-arg) + (not evil-repeat-move-cursor))) + (evil-repeat-pop (- count) save-point)) + +(defun evil--read-key-sequence-advice (&rest _) + "Record `this-command-keys' before it is overwritten." + (when (and (evil-repeat-recording-p) + evil-recording-current-command) + (let ((repeat-type (evil--repeat-type this-command))) + (when (functionp repeat-type) + (funcall repeat-type 'pre-read-key-sequence))))) + +(advice-add 'read-key-sequence :before #'evil--read-key-sequence-advice) +(advice-add 'read-key-sequence-vector :before #'evil--read-key-sequence-advice) + +(provide 'evil-repeat) + +;;; evil-repeat.el ends here diff --git a/.config/emacs/lisp/evil/evil-search.el b/.config/emacs/lisp/evil/evil-search.el new file mode 100644 index 0000000..c882a8e --- /dev/null +++ b/.config/emacs/lisp/evil/evil-search.el @@ -0,0 +1,1249 @@ +;;; evil-search.el --- Search and substitute -*- lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Code: + +(require 'evil-core) +(require 'evil-common) +(require 'evil-ex) + +(defun evil-select-search-module (option module) + "Change the search module according to MODULE. +If MODULE is `isearch', then Emacs' isearch module is used. +If MODULE is `evil-search', then Evil's own interactive +search module is used." + (let ((search-functions + '(forward + backward + word-forward + word-backward + unbounded-word-forward + unbounded-word-backward + next + previous))) + (dolist (fun search-functions) + (let ((isearch (intern (format "evil-search-%s" fun))) + (evil-search (intern (format "evil-ex-search-%s" fun)))) + (if (eq module 'isearch) + (substitute-key-definition + evil-search isearch evil-motion-state-map) + (substitute-key-definition + isearch evil-search evil-motion-state-map))))) + (set-default option module)) + +;; this customization is here because it requires +;; the knowledge of `evil-select-search-mode' +(defcustom evil-search-module 'isearch + "The search module to be used. May be either `isearch', for +Emacs' isearch module, or `evil-search', for Evil's own +interactive search module. N.b. changing this will not affect keybindings. +To swap out relevant keybindings, see `evil-select-search-module' function." + :type '(radio (const :tag "Emacs built-in isearch." :value isearch) + (const :tag "Evil interactive search." :value evil-search)) + :group 'evil + :set #'evil-select-search-module + :initialize #'evil-custom-initialize-pending-reset) + +(defun evil-push-search-history (string forward) + "Push STRING into the appropriate search history (determined by FORWARD)." + (let ((var (if forward + 'evil-search-forward-history + 'evil-search-backward-history))) + (unless (equal string (car (symbol-value var))) + (push string (symbol-value var))))) + +(defun evil-search-incrementally (forward regexp-p) + "Search incrementally for user-entered text." + (let ((evil-search-prompt (evil-search-prompt forward)) + (isearch-search-fun-function 'evil-isearch-function) + (point (point)) + search-nonincremental-instead) + (setq isearch-forward forward) + (evil-save-echo-area + (evil-without-input-method-hooks + ;; set the input method locally rather than globally to ensure that + ;; isearch clears the input method when it's finished + (setq current-input-method evil-input-method) + (if forward + (isearch-forward regexp-p) + (isearch-backward regexp-p)) + (evil-push-search-history isearch-string forward) + (setq current-input-method nil)) + (when (/= (point) point) + ;; position the point at beginning of the match only if the call to + ;; `isearch' has really moved the point. `isearch' doesn't move the + ;; point only if "C-g" is hit twice to exit the search, in which case we + ;; shouldn't move the point either. + (when (and forward isearch-other-end) + (goto-char isearch-other-end)) + (when (and (eq point (point)) + (not (string= isearch-string ""))) + (if forward + (isearch-repeat-forward) + (isearch-repeat-backward)) + (isearch-exit) + (when (and forward isearch-other-end) + (goto-char isearch-other-end))) + (evil-flash-search-pattern + (evil-search-message isearch-string forward)))))) + +(defun evil-flash-search-pattern (string &optional all) + "Flash last search matches for duration of `evil-flash-delay'. +If ALL is non-nil, flash all matches. STRING is a message +to display in the echo area." + (let ((lazy-highlight-initial-delay 0) + (isearch-search-fun-function 'evil-isearch-function) + (isearch-case-fold-search case-fold-search) + (disable #'(lambda (&optional _arg) (evil-flash-hook t)))) + (when evil-flash-timer + (cancel-timer evil-flash-timer)) + (unless (or (null string) + (string= string "")) + (evil-echo-area-save) + (evil-echo "%s" string) + (isearch-highlight (match-beginning 0) (match-end 0)) + (when all + (setq isearch-lazy-highlight-wrapped nil + isearch-lazy-highlight-start (point) + isearch-lazy-highlight-end (point)) + (isearch-lazy-highlight-new-loop) + (unless isearch-lazy-highlight-overlays + (isearch-lazy-highlight-update))) + (add-hook 'pre-command-hook #'evil-flash-hook nil t) + (add-hook 'evil-operator-state-exit-hook #'evil-flash-hook nil t) + (add-hook 'pre-command-hook #'evil-clean-isearch-overlays nil t) + (setq evil-flash-timer + (run-at-time evil-flash-delay nil disable))))) + +(defun evil-clean-isearch-overlays () + "Clean isearch overlays unless `this-command' is search." + (remove-hook 'pre-command-hook #'evil-clean-isearch-overlays t) + (unless (memq this-command + '(evil-search-backward + evil-search-forward + evil-search-next + evil-search-previous + evil-search-word-backward + evil-search-word-forward)) + (isearch-clean-overlays))) +(put 'evil-clean-isearch-overlays 'permanent-local-hook t) + +(defun evil-flash-hook (&optional force) + "Disable hightlighting if `this-command' is not search. +Disable anyway if FORCE is t." + (when (or force + ;; to avoid flicker, don't disable highlighting + ;; if the next command is also a search command + (not (memq this-command + '(evil-search-backward + evil-search-forward + evil-search-next + evil-search-previous + evil-search-word-backward + evil-search-word-forward)))) + (evil-echo-area-restore) + (isearch-dehighlight) + (setq isearch-lazy-highlight-last-string nil) + (lazy-highlight-cleanup t) + (when evil-flash-timer + (cancel-timer evil-flash-timer))) + (remove-hook 'pre-command-hook #'evil-flash-hook t) + (remove-hook 'evil-operator-state-exit-hook #'evil-flash-hook t)) +(put 'evil-flash-hook 'permanent-local-hook t) + +(defun evil-search-with-predicate (search-fun pred string bound noerror count) + "Execute a search with a predicate function. +SEARCH-FUN is a search function (e.g. `re-search-forward') and +PREDICATE is a two-argument function satisfying the interface of +`isearch-filter-predicate', or `nil'. STRING, BOUND, NOERROR and +COUNT are passed unchanged to SEARCH-FUN. The first match +satisfying the predicate (or `nil') is returned." + (catch 'done + (while t + (let ((result (funcall search-fun string bound noerror count))) + (cond + ((not result) (throw 'done nil)) + ((not pred) (throw 'done result)) + ((funcall pred (match-beginning 0) (match-end 0)) (throw 'done result))))))) + +(defun evil-search-function (&optional forward regexp-p wrap predicate) + "Return a search function. +If FORWARD is nil, search backward, otherwise forward. +If REGEXP-P is non-nil, the input is a regular expression. +If WRAP is non-nil, the search wraps around the top or bottom +of the buffer. +If PREDICATE is non-nil, it must be a function accepting two +arguments: the bounds of a match, returning non-nil if that match is +acceptable." + `(lambda (string &optional bound noerror count) + (let ((start (point)) + (search-fun ',(if regexp-p + (if forward + 're-search-forward + 're-search-backward) + (if forward + 'search-forward + 'search-backward))) + result) + (setq result (evil-search-with-predicate + search-fun ,predicate string + bound ,(if wrap t 'noerror) count)) + (when (and ,wrap (null result)) + (goto-char ,(if forward '(point-min) '(point-max))) + (unwind-protect + (setq result (evil-search-with-predicate + search-fun ,predicate string bound noerror count)) + (unless result + (goto-char start)))) + result))) + +(defun evil-isearch-function () + "Return a search function for use with isearch. +Based on `isearch-regexp' and `isearch-forward'." + (evil-search-function isearch-forward evil-regexp-search evil-search-wrap 'isearch-filter-predicate)) + +(defun evil-search (string forward &optional regexp-p start) + "Search for STRING and highlight matches. +If FORWARD is nil, search backward, otherwise forward. +If REGEXP-P is non-nil, STRING is taken to be a regular expression. +START is the position to search from; if unspecified, it is +one more than the current position." + (when (and (stringp string) + (not (string= string ""))) + (let* ((orig (point)) + (start (or start + (if forward + (min (point-max) (1+ orig)) + orig))) + (isearch-regexp regexp-p) + (isearch-forward forward) + (case-fold-search + (unless (and search-upper-case + (not (isearch-no-upper-case-p string nil))) + case-fold-search)) + (search-func (evil-search-function + forward regexp-p evil-search-wrap 'isearch-filter-predicate))) + ;; no text properties, thank you very much + (set-text-properties 0 (length string) nil string) + ;; position to search from + (goto-char start) + (setq isearch-string string) + (isearch-update-ring string regexp-p) + (condition-case nil + (funcall search-func string) + (search-failed + (goto-char orig) + (user-error "\"%s\": %s not found" + string (if regexp-p "pattern" "string")))) + ;; always position point at the beginning of the match + (goto-char (match-beginning 0)) + ;; determine message for echo area + (cond + ((and forward (< (point) start)) + (when evil-search-wrap-ring-bell (ding)) + (setq string "Search wrapped around BOTTOM of buffer")) + ((and (not forward) (> (point) start)) + (when evil-search-wrap-ring-bell (ding)) + (setq string "Search wrapped around TOP of buffer")) + (t + (setq string (evil-search-message string forward)))) + (evil-flash-search-pattern string t)))) + +(defun evil-search-word (forward unbounded symbol) + "Search for word near point. +If FORWARD is nil, search backward, otherwise forward. If SYMBOL +is non-nil then the functions searches for the symbol at point, +otherwise for the word at point." + (let ((string (car regexp-search-ring))) + (setq isearch-forward forward) + (cond + ((and (memq last-command + '(evil-search-word-forward + evil-search-word-backward)) + (stringp string) + (not (string= string ""))) + (evil-search string forward t)) + (t + (setq string (evil-find-thing forward (if symbol 'symbol 'evil-word))) + (cond + ((null string) + (user-error "No word under point")) + (unbounded + (setq string (regexp-quote string))) + (t + (setq string + (format (if symbol "\\_<%s\\_>" "\\<%s\\>") + (regexp-quote string))))) + (evil-push-search-history string forward) + (evil-search string forward t))))) + +(defun evil--find-thing (forward thing) + "Return a cons of THING near point as a string and its position. +THING should be a symbol understood by `thing-at-point', +e.g. `symbol' or `word'. If FORWARD is nil, search backward, +otherwise forward. Returns nil if nothing is found." + (let ((move (if forward #'forward-char #'backward-char)) + (end (if forward #'eobp #'bobp)) + string) + (save-excursion + (setq string (thing-at-point thing)) + ;; if there's nothing under point, go forwards + ;; (or backwards) to find it + (while (and (null string) (not (funcall end))) + (funcall move) + (setq string (thing-at-point thing))) + (when (stringp string) + (set-text-properties 0 (length string) nil string)) + (when (> (length string) 0) + (cons string (point)))))) + +(defun evil-find-thing (forward thing) + "Return a THING near point as a string. +THING should be a symbol understood by `thing-at-point', +e.g. `symbol' or `word'. If FORWARD is nil, search backward, +otherwise forward. Returns nil if nothing is found." + (car (evil--find-thing forward thing))) + +(defun evil-find-word (forward) + "Return word near point as a string. +If FORWARD is nil, search backward, otherwise forward. Returns +nil if nothing is found." + (evil-find-thing forward 'word)) + +(defun evil-find-symbol (forward) + "Return word near point as a string. +If FORWARD is nil, search backward, otherwise forward. Returns +nil if nothing is found." + (evil-find-thing forward 'symbol)) + +(defun evil-search-prompt (forward) + "Return the search prompt for the given direction." + (if forward "/" "?")) + +(defun evil-search-message (string forward) + "Prefix STRING with the search prompt." + (format "%s%s" (evil-search-prompt forward) string)) + +(advice-add 'isearch-message-prefix :around #'evil--use-search-prompt) +(defun evil--use-search-prompt (orig-fun &rest args) + "Use `evil-search-prompt'." + (or evil-search-prompt + (apply orig-fun args))) + +(advice-add 'isearch-delete-char :around #'evil--exit-search-when-empty) +(defun evil--exit-search-when-empty (orig-fun &rest args) + "Exit search if no search string." + (cond + ((and evil-search-prompt (string= isearch-string "")) + (let (search-nonincremental-instead) + (setq isearch-success nil) + (isearch-exit))) + (t (apply orig-fun args)))) + +(advice-add 'isearch-lazy-highlight-search :around #'evil--without-search-wrap) +(defun evil--without-search-wrap (orig-fun &rest args) + "Never wrap the search in this context." + (let (evil-search-wrap) + (apply orig-fun args))) + +;;; Ex search + +(defun evil-ex-regex-without-case (re) + "Return the regular expression without all occurrences of \\c and \\C." + (evil-transform-regexp re '((?c . "") (?C . "")))) + +(defun evil-ex-regex-case (re default-case) + "Return the case as implied by \\c or \\C in regular expression RE. +If \\c appears anywhere in the pattern, the pattern is case +insensitive. If \\C appears, the pattern is case sensitive. +Only the first occurrence of \\c or \\C is used, all others are +ignored. If neither \\c nor \\C appears in the pattern, the case +specified by DEFAULT-CASE is used. DEFAULT-CASE should be either +`sensitive', `insensitive' or `smart'. In the latter case, the pattern +will be case-sensitive if and only if it contains an upper-case +letter, otherwise it will be case-insensitive." + (cond + ((string-match "\\(?:^\\|[^\\\\]\\)\\(?:\\\\\\\\\\)*\\\\\\([cC]\\)" re) + (if (eq (aref (match-string 1 re) 0) ?c) 'insensitive 'sensitive)) + ((eq default-case 'smart) + (if (isearch-no-upper-case-p re t) 'insensitive 'sensitive)) + (t default-case))) + +;; a pattern +(defun evil-ex-make-substitute-pattern (regexp flags) + "Create a PATTERN for substitution with FLAGS. +This function respects the values of `evil-ex-substitute-case' +and `evil-ex-substitute-global'." + (evil-ex-make-pattern + regexp + (cond ((memq ?i flags) 'insensitive) + ((memq ?I flags) 'sensitive) + ((not evil-ex-substitute-case) evil-ex-search-case) + (t evil-ex-substitute-case)) + (if (memq ?g flags) + (not evil-ex-substitute-global) + evil-ex-substitute-global))) + +(defun evil-ex-make-search-pattern (regexp) + "Create a PATTERN for search. +This function respects the values of `evil-ex-search-case'." + (evil-ex-make-pattern regexp evil-ex-search-case t)) + +(defun evil-ex-make-pattern (regexp case whole-line) + "Create a new search pattern. +REGEXP is the regular expression to be searched for. CASE should +be either `sensitive', `insensitive' for case-sensitive and +case-insensitive search respectively, or `smart'. In the latter case +the pattern is smart-case, i.e. it is automatically sensitive of the +pattern contains an upper case letter, otherwise it is insensitive. +The input REGEXP is considered a Vim-style regular expression if +`evil-ex-search-vim-style-regexp' is non-nil, in which case it is +transformed to an Emacs style regular expression (i.e. certain +backslash-codes are transformed. Otherwise REGEXP must be an Emacs +style regular expression and is not transformed." + (let ((re (evil-ex-regex-without-case regexp)) + (ignore-case (eq (evil-ex-regex-case regexp case) 'insensitive))) + ;; possibly transform regular expression from vim-style to + ;; Emacs-style. + (if (and evil-ex-search-vim-style-regexp + (not (or (string-prefix-p "\\_<" regexp) + (string-suffix-p "\\_>" regexp)))) + (setq re (evil-transform-vim-style-regexp re)) + ;; Even for Emacs regular expressions we translate certain + ;; whitespace sequences + (setq re (evil-transform-regexp re '((?t . "\t") + (?n . "\n") + (?r . "\r"))))) + (list re ignore-case whole-line))) + +(defun evil-ex-pattern-regex (pattern) + "Return the regular expression of a search PATTERN." + (nth 0 pattern)) + +(defun evil-ex-pattern-ignore-case (pattern) + "Return t if and only if PATTERN should ignore case." + (nth 1 pattern)) + +(defun evil-ex-pattern-whole-line (pattern) + "Return t if and only if PATTERN should match all occurences of a line. +Otherwise PATTERN matches only the first occurence." + (nth 2 pattern)) + +;;; Highlight + +(cl-defstruct (evil-ex-hl (:type vector) (:constructor nil) + (:copier nil) (:predicate nil)) + (name nil :read-only t) pattern + (face nil :read-only t) (window nil :read-only t) + (min nil :documentation "The minimal buffer position of the highlight.") + (max nil :documentation "The maximal buffer position of the highlight.") + (match-hook nil :read-only t) (update-hook nil :read-only t) + (overlays nil :documentation "The active overlays of the highlight.")) + +(cl-defun evil-ex-make-hl + (name &key (face 'evil-ex-lazy-highlight) (win (selected-window)) + min max match-hook update-hook) + "Create a new highlight object with name NAME and properties ARGS. +The following properties are supported: +:face The face to be used for the highlighting overlays. +:win The window in which the highlighting should be shown. + Note that the highlight will be visible in all windows showing + the corresponding buffer, but only the matches visible in the + specified window will actually be highlighted. If :win is nil, + the matches in all windows will be highlighted. +:min The minimal buffer position for highlighted matches. +:max The maximal buffer position for highlighted matches. +:match-hook A hook to be called once for each highlight. + The hook must take two arguments, the highlight and + the overlay for that highlight. +:update-hook A hook called once after updating the highlighting + with two arguments, the highlight and a message string + describing the current match status." + (unless (symbolp name) + (user-error "Expected symbol as name of highlight")) + (when (assq name evil-ex-active-highlights-alist) + (evil-ex-delete-hl name)) + (unless evil-ex-active-highlights-alist + (add-hook 'window-scroll-functions + #'evil-ex-hl-update-highlights-scroll nil t) + (add-hook 'window-size-change-functions + #'evil-ex-hl-update-highlights-resize nil)) + (push (cons name (vector name + nil + face + win + min max + match-hook update-hook + nil)) + evil-ex-active-highlights-alist)) + +(defun evil-ex-delete-hl (name) + "Remove the highlighting object with a certain NAME." + (let ((hl (cdr (assq name evil-ex-active-highlights-alist)))) + (when hl + (mapc #'delete-overlay (evil-ex-hl-overlays hl)) + (setq evil-ex-active-highlights-alist + (assq-delete-all name evil-ex-active-highlights-alist)) + (evil-ex-hl-update-highlights)) + (unless evil-ex-active-highlights-alist + (remove-hook 'window-scroll-functions + #'evil-ex-hl-update-highlights-scroll t) + (remove-hook 'window-size-change-functions + #'evil-ex-hl-update-highlights-resize)))) + +(defun evil-ex-hl-active-p (name) + "Whether the highlight with a certain NAME is active." + (and (assq name evil-ex-active-highlights-alist) t)) + +(defun evil-ex-hl-change (name pattern) + "Set the regular expression of highlight NAME to PATTERN." + (let ((hl (cdr (assq name evil-ex-active-highlights-alist)))) + (when hl + (setf (evil-ex-hl-pattern hl) pattern) + (evil-ex-hl-idle-update)))) + +(defun evil-ex-hl-set-region (name beg end &optional _type) + "Set minimal and maximal position of highlight NAME to BEG and END." + (let ((hl (cdr (assq name evil-ex-active-highlights-alist)))) + (when hl + (setf (evil-ex-hl-min hl) beg + (evil-ex-hl-max hl) end) + (evil-ex-hl-idle-update)))) + +(defun evil-ex-hl-get-max (name) + "Return the maximal position of the highlight with name NAME." + (let ((hl (cdr (assq name evil-ex-active-highlights-alist)))) + (and hl (evil-ex-hl-max hl)))) + +(defun evil-ex-hl-update-highlights () + "Update the overlays of all active highlights." + (dolist (hl (mapcar #'cdr evil-ex-active-highlights-alist)) + (let* ((old-ovs (evil-ex-hl-overlays hl)) + new-ovs + (pattern (evil-ex-hl-pattern hl)) + (case-fold-search (evil-ex-pattern-ignore-case pattern)) + (case-replace case-fold-search) + (face (evil-ex-hl-face hl)) + (match-hook (evil-ex-hl-match-hook hl)) + result) + (if pattern + ;; collect all visible ranges + (let (ranges sranges) + (dolist (win (if (eq evil-ex-interactive-search-highlight + 'all-windows) + (get-buffer-window-list (current-buffer) nil t) + (list (evil-ex-hl-window hl)))) + (when (window-live-p win) + (let ((beg (max (window-start win) + (or (evil-ex-hl-min hl) (point-min)))) + (end (min (window-end win) + (or (evil-ex-hl-max hl) (point-max))))) + (when (< beg end) + (push (cons beg end) ranges))))) + (setq ranges (sort ranges #'car-less-than-car)) + (while ranges + (let ((r1 (pop ranges)) + (r2 (pop ranges))) + (cond + ;; last range + ((null r2) (push r1 sranges)) + ;; ranges overlap, union + ((>= (cdr r1) (car r2)) + (push (cons (car r1) + (max (cdr r1) (cdr r2))) + ranges)) + ;; ranges distinct + (t (push r1 sranges) + (push r2 ranges))))) + + ;; run through all ranges + (condition-case lossage + (save-match-data + (dolist (r sranges) + (let ((beg (car r)) + (end (cdr r))) + (save-excursion + (goto-char beg) + ;; set the overlays for the current highlight, + ;; reusing old overlays (if possible) + (while (and (not (eobp)) + (evil-ex-search-find-next-pattern pattern) + (<= (match-end 0) end) + (not (and (= (match-beginning 0) end) + (save-excursion + (goto-char (match-beginning 0)) + (bolp))))) + (let ((ov (or (pop old-ovs) (make-overlay 0 0)))) + (move-overlay ov (match-beginning 0) (match-end 0)) + (overlay-put ov 'face face) + (overlay-put ov 'evil-ex-hl (evil-ex-hl-name hl)) + (overlay-put ov 'priority 1000) + (push ov new-ovs) + (when match-hook (funcall match-hook hl ov))) + (cond + ((not (or (evil-ex-pattern-whole-line pattern) + (save-excursion + (goto-char (match-beginning 0)) + (search-forward "\n" (match-end 0) t)))) + (forward-line)) + ((= (match-beginning 0) (match-end 0)) + (forward-char)) + (t (goto-char (match-end 0)))))))) + (mapc #'delete-overlay old-ovs) + (setf (evil-ex-hl-overlays hl) new-ovs) + (if (or (null pattern) new-ovs) + (setq result t) + ;; Maybe the match could just not be found somewhere else? + (save-excursion + (goto-char (or (evil-ex-hl-min hl) (point-min))) + (if (and (evil-ex-search-find-next-pattern pattern) + (< (match-end 0) (or (evil-ex-hl-max hl) + (point-max)))) + (setq result (format "Match in line %d" + (line-number-at-pos + (match-beginning 0)))) + (setq result "No match"))))) + + (invalid-regexp (setq result (cadr lossage))) + (search-failed (setq result (nth 2 lossage))) + (error (setq result (format "%s" (cadr lossage)))))) + ;; no pattern, remove all highlights + (mapc #'delete-overlay old-ovs) + (setf (evil-ex-hl-overlays hl) new-ovs)) + (when (evil-ex-hl-update-hook hl) + (funcall (evil-ex-hl-update-hook hl) hl result))))) + +(defun evil-ex-search-find-next-pattern (pattern &optional direction) + "Look for the next occurrence of PATTERN in a certain DIRECTION. +Note that this function ignores the whole-line property of PATTERN." + (setq direction (or direction 'forward)) + (let ((case-fold-search (evil-ex-pattern-ignore-case pattern))) + (cond + ((eq direction 'forward) + (re-search-forward (evil-ex-pattern-regex pattern) nil t)) + ((eq direction 'backward) + (let* ((pnt (point)) + (ret (re-search-backward (evil-ex-pattern-regex pattern) nil t)) + (m (and ret (match-data)))) + (if ret + (forward-char) + (goto-char (point-min))) + (let ((fwdret + (re-search-forward (evil-ex-pattern-regex pattern) nil t))) + (cond + ((and fwdret (< (match-beginning 0) pnt)) + (setq ret fwdret) + (goto-char (match-beginning 0))) + (ret + (set-match-data m) + (goto-char (match-beginning 0))) + (t + (goto-char pnt) + ret))))) + (t (user-error "Unknown search direction `%s'" direction))))) + +(defun evil-ex-hl-idle-update () + "Trigger the timer to update the highlights in the current buffer." + (when (and evil-ex-interactive-search-highlight + evil-ex-active-highlights-alist) + (when evil-ex-hl-update-timer + (cancel-timer evil-ex-hl-update-timer)) + (setq evil-ex-hl-update-timer + (run-at-time evil-ex-hl-update-delay nil + #'evil-ex-hl-do-update-highlight + (current-buffer))))) + +(defun evil-ex-hl-do-update-highlight (&optional buffer) + "Timer function for updating the highlights." + (when (buffer-live-p buffer) + (with-current-buffer buffer + (evil-ex-hl-update-highlights))) + (setq evil-ex-hl-update-timer nil)) + +(defun evil-ex-hl-update-highlights-scroll (win _beg) + "Update highlights after scrolling in some window." + (with-current-buffer (window-buffer win) + (evil-ex-hl-idle-update))) +(put 'evil-ex-hl-update-highlights-scroll 'permanent-local-hook t) + +(defun evil-ex-hl-update-highlights-resize (frame) + "Update highlights after resizing a window." + (let ((buffers (delete-dups (mapcar #'window-buffer (window-list frame))))) + (dolist (buf buffers) + (with-current-buffer buf + (evil-ex-hl-idle-update))))) +(put 'evil-ex-hl-update-highlights-resize 'permanent-local-hook t) + +;; interactive search +(defun evil-ex-search-activate-highlight (pattern) + "Activate highlighting of the search pattern set to PATTERN. +This function does nothing if `evil-ex-search-interactive' or +`evil-ex-search-highlight-all' is nil. " + (when (and evil-ex-search-interactive evil-ex-search-highlight-all) + (with-current-buffer (or evil-ex-original-buffer (current-buffer)) + (unless (evil-ex-hl-active-p 'evil-ex-search) + (evil-ex-make-hl 'evil-ex-search + :win (or (minibuffer-selected-window) (selected-window)))) + (when pattern + (evil-ex-hl-change 'evil-ex-search pattern))))) + +(defun evil-ex-search (&optional count) + "Search forward or backward COUNT times for the current Ex search pattern. +The search pattern is determined by `evil-ex-search-pattern', and the +direction by `evil-ex-search-direction'." + (setq evil-ex-search-start-point (point) + evil-ex-last-was-search t + count (or count 1)) + (let ((orig (point)) + wrapped) + (dotimes (_ (or count 1)) + (when (and (eq evil-ex-search-direction 'forward) (not (eobp))) + (forward-char) + ;; maybe skip end-of-line + (and (not evil-move-beyond-eol) (eolp) (not (eobp)) + (forward-char))) + (let ((res (evil-ex-find-next nil nil (not evil-search-wrap)))) + (cond + ((not res) + (goto-char orig) + (signal 'search-failed + (list (evil-ex-pattern-regex evil-ex-search-pattern)))) + ((eq res 'wrapped) (setq wrapped t))))) + (if wrapped + (let (message-log-max) + (when evil-search-wrap-ring-bell (ding)) + (message "Search wrapped"))) + (goto-char (match-beginning 0)) + (setq evil-ex-search-match-beg (match-beginning 0) + evil-ex-search-match-end (match-end 0)) + (evil-ex-search-goto-offset evil-ex-search-offset) + (evil-ex-search-activate-highlight evil-ex-search-pattern))) + +(defun evil-ex-find-next (&optional pattern direction nowrap) + "Search for the next occurrence of the PATTERN in DIRECTION. +PATTERN must be created using `evil-ex-make-pattern', DIRECTION is +either `forward' or `backward'. If NOWRAP is non-nil, the search does +not wrap at buffer boundaries. Furthermore this function only searches +invisible text if `search-invisible' is t. If PATTERN is not specified +the current global pattern `evil-ex-search-pattern' and if DIRECTION +is not specified the current global direction +`evil-ex-search-direction' is used. This function returns t if the +search was successful, nil if it was unsuccessful and `wrapped' if the +search was successful but wrapped around at buffer boundaries." + (setq pattern (or pattern evil-ex-search-pattern) + direction (or direction evil-ex-search-direction)) + (unless (and pattern (evil-ex-pattern-regex pattern)) + (signal 'search-failed (list "No search pattern"))) + (catch 'done + (let (wrapped) + (while t + (if (evil-ex-search-find-next-pattern pattern direction) + (when (or (eq search-invisible t) + (not (isearch-range-invisible + (match-beginning 0) (match-end 0)))) + ;; Successful search and not invisible + (throw 'done (if wrapped 'wrapped t))) + ;; Unsuccessful search + (if nowrap + (throw 'done nil) + (setq nowrap t + wrapped t) + (goto-char (if (eq direction 'forward) + (point-min) + (point-max))))))))) + +(defun evil-ex-search-update (pattern offset beg end) + "Update the highlighting for the search pattern. +PATTERN is the search pattern and OFFSET the associated offset. +BEG and END specify the current match." + (cond + ((and beg end) + ;; update overlay + (if evil-ex-search-overlay + (move-overlay evil-ex-search-overlay beg end) + (setq evil-ex-search-overlay (make-overlay beg end)) + (overlay-put evil-ex-search-overlay 'priority 1001) + (overlay-put evil-ex-search-overlay 'face 'evil-ex-search)) + ;; move point + (goto-char beg) + (evil-ex-search-goto-offset offset) + ;; update highlights + (when evil-ex-search-highlight-all + (evil-ex-hl-change 'evil-ex-search pattern))) + (t + ;; no match + (when evil-ex-search-overlay + ;; remove overlay + (delete-overlay evil-ex-search-overlay) + (setq evil-ex-search-overlay nil)) + ;; no highlights + (when evil-ex-search-highlight-all + (evil-ex-hl-change 'evil-ex-search nil)) + ;; and go to initial position + (goto-char evil-ex-search-start-point)))) + +(defvar evil--ex-search-update-timer nil + "Timer to coalesce `evil-ex-search-update-pattern' calls after pattern changes.") + +(defun evil-ex-search-after-change-function (_beg _end _old-len) + (when evil--ex-search-update-timer + (cancel-timer evil--ex-search-update-timer)) + (setq evil--ex-search-update-timer + (run-with-idle-timer 0 nil #'evil-ex-search-update-pattern))) + +(defun evil-ex-search-start-session () + "Initialize Ex for interactive search." + (add-hook 'pre-command-hook #'evil-ex-remove-default nil t) + (and evil-ex-search-interactive evil-ex-search-incremental + (add-hook 'after-change-functions + #'evil-ex-search-after-change-function nil t)) + (add-hook 'minibuffer-exit-hook #'evil-ex-search-stop-session nil t) + (add-hook 'mouse-leave-buffer-hook #'evil-ex-search-exit) + (evil-ex-search-activate-highlight nil)) + +(defvar evil-ex-search-yank-point nil) + +(defun evil-ex-search-stop-session () + "Stop interactive search." + (with-current-buffer evil-ex-original-buffer + ;; TODO: This is a bad fix to remove duplicates. The duplicates + ;; exist because `isearch-range-invisible' may add a single + ;; overlay multiple times if we are in an unlucky situation + ;; of overlapping overlays. This happens in our case because + ;; of the overlays that are used for (lazy) highlighting. + ;; Perhaps it would be better to disable those overlays + ;; temporarily before calling `isearch-range-invisible'. + (setq isearch-opened-overlays (delete-dups isearch-opened-overlays)) + (isearch-clean-overlays)) + (remove-hook 'mouse-leave-buffer-hook #'evil-ex-search-exit) + (when evil--ex-search-update-timer + (cancel-timer evil--ex-search-update-timer) + (setq evil--ex-search-update-timer nil)) + (when evil-ex-search-overlay + (delete-overlay evil-ex-search-overlay) + (setq evil-ex-search-overlay nil)) + (setq evil-ex-search-yank-point nil)) + +(defun evil-ex-split-search-pattern (pattern direction) + "Split PATTERN in regexp, offset and next-pattern parts. +Return a triplet (REGEXP OFFSET NEXT-SEARCH)." + (if (string-match + (if (eq direction 'forward) + "\\(?:^\\|[^\\\\]\\)\\(?:\\\\\\\\\\)*\\(/\\([^;]*\\)\\(?:;\\([/?].*\\)?\\)?\\)?$" + "\\(?:^\\|[^\\\\]\\)\\(?:\\\\\\\\\\)*\\(\\?\\([^;]*\\)\\(?:;\\([/?].*\\)?\\)?\\)?$") + pattern) + (list (substring pattern 0 (match-beginning 1)) + (match-string 2 pattern) + (match-string 3 pattern)) + (list pattern nil nil))) + +(defun evil-ex-search-full-pattern (string count direction) + "Search for a full search pattern STRING in DIRECTION. +This function splits STRING into \"pattern/offset/;next-pattern\" +parts and performs the search in DIRECTION which must be either +`forward' or `backward'. The first search is repeated COUNT times. If +the pattern part of STRING is empty, the last global pattern stored in +`evil-ex-search-pattern' is used instead if in addition the offset +part is nil (i.e. no pattern/offset separator), the last global offset +stored in `evil-ex-search-offset' is used as offset. The current match +data will correspond to the last successful match. This function +returns a triplet (RESULT PATTERN OFFSET) where RESULT is one of + + t the search was successful without wrapping + `wrap' the search was successful with wrapping + `empty-pattern' the last pattern was empty + nil the search was unsuccessful + +and PATTERN and OFFSET are the last pattern and offset this +function searched for. Note that this function does not handle +any error conditions." + (setq count (or count 1)) + (catch 'done + (while t + (let* ((res (evil-ex-split-search-pattern string direction)) + (pat (pop res)) + (offset (pop res)) + (next-pat (pop res)) + (orig-pat pat) + new-dir repeat-last wrapped) + (if (> (length pat) 0) + (setq pat (evil-ex-make-search-pattern pat)) + ;; use last pattern if no new pattern has been specified + (setq pat (or evil-ex-search-pattern + (throw 'done (list 'empty-pattern pat offset))) + offset (or offset evil-ex-search-offset))) + (while (> count 0) + (when (eq (or (evil-ex-find-next pat direction + (not evil-search-wrap)) + (throw 'done (list nil pat offset))) + 'wrapped) + (setq wrapped t)) + (setq count (1- count))) + (cond + ;; no next pattern, search complete + ((zerop (length next-pat)) + (evil-ex-search-goto-offset offset) + (throw 'done (list (if wrapped 'wrap t) pat offset))) + ;; single \"?\" or \"/\" means repeat last pattern and finish + ((= 1 (length next-pat)) + (evil-ex-search-goto-offset offset) + (setq new-dir (if (string= "/" next-pat) 'forward 'backward) + count (if (eq direction new-dir) 1 2) + string orig-pat + direction new-dir)) + ;; next non-empty pattern, next search iteration + (t (evil-ex-search-goto-offset offset) + (setq new-dir (if (= (aref next-pat 0) ?/) 'forward 'backward) + repeat-last (when (<= 2 (length next-pat)) + (member (substring next-pat 0 2) '("//" "??"))) + count (if (or (eq direction new-dir) (not repeat-last)) 1 2) + string (if repeat-last + (concat orig-pat (substring next-pat 1)) + (substring next-pat 1)) + direction new-dir))))))) + +(defun evil-ex-search-update-pattern () + "Update the current search pattern." + (let ((pattern-string (minibuffer-contents-no-properties)) + message) + (with-selected-window (minibuffer-selected-window) + (goto-char (1+ evil-ex-search-start-point)) + (condition-case err + (cl-destructuring-bind (success pattern offset) + (evil-ex-search-full-pattern pattern-string + (or evil-ex-search-count 1) + evil-ex-search-direction) + (cond + ((eq success 'wrap) + (evil-ex-search-update pattern offset + (match-beginning 0) (match-end 0)) + (setq message "Wrapped")) + ((eq success 'empty-pattern) + (evil-ex-search-update nil nil nil nil)) + (success + (evil-ex-search-update pattern offset + (match-beginning 0) (match-end 0))) + (t (evil-ex-search-update nil nil nil nil) + (setq message "Search failed")))) + (invalid-regexp + (evil-ex-search-update nil nil nil nil) + (setq message (cadr err))) + (error + (evil-ex-search-update nil nil nil nil) + (setq message (error-message-string err))))) + (when message (evil-ex-echo "%s" message)))) + +(defun evil-ex-search-exit () + "Exit interactive search, keeping lazy highlighting active." + (interactive) + (exit-minibuffer)) + +(defun evil-ex-search-abort () + "Abort interactive search, disabling lazy highlighting." + (interactive) + (evil-ex-delete-hl 'evil-ex-search) + (abort-recursive-edit)) + +(defun evil-ex-search-goto-offset (offset) + "Move point according to search OFFSET and set `evil-this-type' accordingly. +This function assumes that the current match data represents the +current search result." + (unless (zerop (length offset)) + (let ((beg (match-beginning 0)) + (end (match-end 0))) + (save-match-data + (unless (string-match + "^\\([esb]\\)?\\(\\([-+]\\)?\\([0-9]*\\)\\)$" + offset) + (user-error "Invalid search offset: %s" offset)) + (let ((count (if (= (match-beginning 4) (match-end 4)) + (cond + ((not (match-beginning 3)) 0) + ((= (aref offset (match-beginning 3)) ?+) +1) + (t -1)) + (string-to-number (match-string 2 offset))))) + (cond + ((not (match-beginning 1)) + (setq evil-this-type 'line) + (forward-line count)) + ((= (aref offset (match-beginning 1)) ?e) + (goto-char (+ end count -1)) + (setq evil-this-type 'inclusive)) + ((memq (aref offset (match-beginning 1)) '(?s ?b)) + (goto-char (+ beg count)) + (setq evil-this-type 'inclusive)))))))) + +(defun evil-ex-start-search (direction count) + "Start a new search in a certain DIRECTION." + (setq evil-ex-search-count count + evil-ex-search-direction direction + evil-ex-search-start-point (point) + evil-ex-last-was-search t) + ;; store buffer and window where the search started + (let* ((evil-ex-original-buffer (current-buffer)) + ;; read the search string + (minibuffer-local-map evil-ex-search-keymap) + (search-string + (condition-case err + (minibuffer-with-setup-hook #'evil-ex-search-start-session + (read-string + (if (eq evil-ex-search-direction 'forward) "/" "?") + (when evil-ex-search-history + (propertize (car evil-ex-search-history) 'face 'shadow)) + 'evil-ex-search-history)) + (quit + (evil-ex-delete-hl 'evil-ex-search) + (goto-char evil-ex-search-start-point) + (signal (car err) (cdr err)))))) + ;; pattern entered successful + (goto-char (if (eq evil-ex-search-direction 'forward) + (1+ evil-ex-search-start-point) + (1- evil-ex-search-start-point))) + (cl-destructuring-bind (success pattern offset) + (evil-ex-search-full-pattern search-string + evil-ex-search-count + evil-ex-search-direction) + (setq evil-ex-search-pattern pattern + evil-ex-search-offset offset) + (cond + ((memq success '(t wrap)) + (goto-char (match-beginning 0)) + (setq evil-ex-search-match-beg (match-beginning 0) + evil-ex-search-match-end (match-end 0)) + (evil-ex-search-goto-offset offset) + (evil-push-search-history search-string (eq direction 'forward)) + (if evil-ex-search-incremental + (unless evil-ex-search-persistent-highlight + (evil-ex-delete-hl 'evil-ex-search)) + (when evil-ex-search-highlight-all + (evil-ex-search-activate-highlight pattern)))) + (t + (goto-char evil-ex-search-start-point) + (evil-ex-delete-hl 'evil-ex-search) + (signal 'search-failed (list search-string))))))) + +(declare-function evil-ex-search-next "evil-commands") +(defun evil-ex-start-word-search (unbounded direction count &optional symbol) + "Search for the symbol under point. +The search matches the COUNT-th occurrence of the word. If the +UNBOUNDED argument is nil, the search matches only at symbol +boundaries, otherwise it matches anywhere. The DIRECTION +argument should be either `forward' or `backward', determining +the search direction. If SYMBOL is non-nil then the functions +searches for the symbol at point, otherwise for the word at +point." + (let* ((string (or (evil-find-thing (eq direction 'forward) + (if symbol 'symbol 'word)) + (user-error "No word under point"))) + (regex (if unbounded + (regexp-quote string) + (format (if symbol "\\_<%s\\_>" "\\<%s\\>") + (regexp-quote string))))) + (setq evil-ex-search-count count + evil-ex-search-direction direction + evil-ex-search-pattern + (let (evil-ex-search-vim-style-regexp) + (evil-ex-make-search-pattern regex)) + evil-ex-search-offset nil + evil-ex-last-was-search t) + ;; update search history unless this pattern equals the + ;; previous pattern + (unless (equal regex (car evil-ex-search-history)) + (push regex evil-ex-search-history)) + (evil-push-search-history regex (eq direction 'forward))) + (evil-ex-delete-hl 'evil-ex-search) + (evil-ex-search-next count)) + +;; substitute +(evil-ex-define-argument-type substitution + "A substitution pattern argument /pattern/replacement/flags. +This handler highlights the pattern of the current substitution." + :runner + (lambda (flag &optional arg) + (with-current-buffer evil-ex-original-buffer + (cond + ((eq flag 'start) + (evil-ex-make-hl + 'evil-ex-substitute + :face 'evil-ex-substitute-matches + :win (minibuffer-selected-window) + :update-hook #'evil-ex-pattern-update-ex-info + :match-hook (and evil-ex-substitute-interactive-replace + #'evil-ex-pattern-update-replacement)) + (setq flag 'update)) + ((eq flag 'stop) + (evil-ex-delete-hl 'evil-ex-substitute))) + + (when (and (eq flag 'update) + evil-ex-substitute-highlight-all + (> (length arg) 0)) + (condition-case err + (cl-destructuring-bind (pattern replacement _flags) + (evil-ex-get-substitute-info (or arg "") t) + (evil-ex-hl-change 'evil-ex-substitute pattern) + (setq evil-ex-substitute-current-replacement replacement) + (cl-destructuring-bind (beg end &rest) + (if evil-ex-range + (evil-expand-range evil-ex-range t) + (evil-range (line-beginning-position) (line-end-position) + 'line :expanded t)) + (evil-ex-hl-set-region 'evil-ex-substitute beg end))) + (end-of-file (evil-ex-echo "Incomplete replacement")) + (user-error (evil-ex-echo (error-message-string err)))))))) + +(defun evil-ex-pattern-update-ex-info (_hl result) + "Update the Ex info string." + (when (stringp result) + (evil-ex-echo "%s" result))) + +(defun evil-ex-pattern-update-replacement (_hl overlay) + "Update the replacement display." + (let ((repl (if evil-ex-substitute-current-replacement + (evil-match-substitute-replacement + evil-ex-substitute-current-replacement + (not case-replace)) + ""))) + (put-text-property 0 (length repl) + 'face 'evil-ex-substitute-replacement + repl) + (overlay-put overlay 'after-string repl))) + +(defun evil-ex-parse-global (string) + "Parse STRING as a global argument." + (let* ((pattern (nth 0 (evil-delimited-arguments string 2))) + (command (and pattern + (>= (- (length string) (length pattern)) 2) + (substring string (+ (length pattern) 2))))) + ;; use last pattern if none given + (when (zerop (length pattern)) + (setq pattern + (cond + ((and (eq evil-search-module 'evil-search) evil-ex-search-pattern) + (evil-ex-pattern-regex evil-ex-search-pattern)) + ((and (eq evil-search-module 'isearch) (not (zerop (length isearch-string)))) + isearch-string) + (t (user-error "No previous pattern"))))) + (list pattern command))) + +(defun evil-ex-get-substitute-info (string &optional implicit-r) + "Return the substitution info of :substitute argument STRING. +The result is a triplet \(PATTERN REPLACEMENT FLAGS). PATTERN is an +Ex-pattern (see `evil-ex-make-pattern') and REPLACEMENT is a compiled +replacement expression (see `evil-compile-replacement'). The +information returned is the actual substitution information w.r.t. to +special situations like empty patterns or repetition of previous +substitution commands. If IMPLICIT-R is non-nil, then the \"r\" flag +is assumed, i.e. in case of an empty pattern the last search pattern +is used. It is meant for :substitute commands with arguments." + (let (pattern replacement flags using-prev-pattern) + (cond + ((or (string= string "") (string-match-p "\\`[a-zA-Z]" string)) + ;; No pattern, since it starts with a letter which cannot be a + ;; separator: Repeat last substitute. + (setq replacement evil-ex-substitute-replacement) + ;; flags are everything that is not whitespace + (when (string-match "[^[:space:]]+" string) + (setq flags (match-string 0 string)))) + (t (let ((args (evil-delimited-arguments string 3))) + (setq pattern (pop args) + replacement (pop args) + flags (pop args)) + ;; if replacment equals "~" use previous replacement + (setq replacement (if (equal replacement "~") + evil-ex-substitute-replacement + (evil-compile-replacement replacement))) + ;; append implicit "r" flag if required + (when (and implicit-r (not (memq ?r (append flags nil)))) + (setq flags (concat flags "r")))))) + ;; if flags equals "&" add previous flags + (setq flags + (if (and (> (length flags) 0) (= (aref flags 0) ?&)) + (append (substring flags 1) evil-ex-substitute-flags) + (append flags nil))) + ;; if no pattern, use previous pattern, either search or + ;; substitute pattern depending on `evil-ex-last-was-search' and + ;; the r flag + (when (zerop (length pattern)) + (setq pattern + (if (eq evil-search-module 'evil-search) + (if (and evil-ex-last-was-search (memq ?r flags)) + (and evil-ex-search-pattern + (setq using-prev-pattern t) + (evil-ex-pattern-regex evil-ex-search-pattern)) + (and evil-ex-substitute-pattern + (setq using-prev-pattern t) + (evil-ex-pattern-regex evil-ex-substitute-pattern))) + (if (eq case-fold-search t) + isearch-string + (concat isearch-string "\\C"))) + flags (remq ?r flags))) + (when (and using-prev-pattern + (not (evil-ex-pattern-ignore-case evil-ex-search-pattern))) + (setq pattern (concat pattern "\\C"))) + ;; generate pattern + (when pattern + ;; Disable vim-style regexp conversion if using a previous pattern, because + ;; this conversion will already have been done before storing it + (let ((evil-ex-search-vim-style-regexp (and evil-ex-search-vim-style-regexp + (not using-prev-pattern)))) + (setq pattern (evil-ex-make-substitute-pattern pattern flags)))) + (list pattern replacement flags))) + +(defun evil-ex-nohighlight () + "Disable the active search highlightings." + (interactive) + (evil-ex-delete-hl 'evil-ex-substitute) + (evil-ex-delete-hl 'evil-ex-search)) + +(defun evil-search-yank-word () + "Pull next word from buffer into search string." + (interactive) + (let ((minibuf-content (minibuffer-contents-no-properties)) + (word "") start) + (with-current-buffer evil-ex-original-buffer + ;; Start to initial point if C-w have never been hit. + (unless evil-ex-search-yank-point + (if (string= "" minibuf-content) + (setq evil-ex-search-yank-point evil-ex-search-start-point) + (setq evil-ex-search-yank-point (point)))) + (save-excursion + (goto-char evil-ex-search-yank-point) + (setq start (point)) + (when (looking-at-p (regexp-quote minibuf-content)) + (forward-char (length minibuf-content)) + (setq start (point)) + (forward-word)) + (setq word (buffer-substring-no-properties start (point))))) + (and (eq evil-ex-search-case 'smart) + (let (case-fold-search) + (not (string-match-p "[A-Z]" minibuf-content))) + (setq word (downcase word))) + (insert word))) + +(provide 'evil-search) + +;;; evil-search.el ends here diff --git a/.config/emacs/lisp/evil/evil-states.el b/.config/emacs/lisp/evil/evil-states.el new file mode 100644 index 0000000..499154e --- /dev/null +++ b/.config/emacs/lisp/evil/evil-states.el @@ -0,0 +1,930 @@ +;;; evil-states.el --- States -*- lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Code: + +(require 'evil-core) + +;;; Normal state + +(evil-define-state normal + "Normal state. +AKA \"Command\" state." + :tag " " + :enable (motion) + (cond + ((evil-normal-state-p) + (overwrite-mode -1) + (add-hook 'post-command-hook #'evil-normal-post-command nil t)) + (t + (remove-hook 'post-command-hook #'evil-normal-post-command t)))) + +(defun evil-normal-post-command (&optional command) + "Reset command loop variables in Normal state. +Also prevent point from reaching the end of the line. +If the region is activated, enter Visual state." + (unless (null this-command) + (setq command (or command this-command)) + (when (evil-normal-state-p) + (setq evil-this-type nil + evil-this-operator nil + evil-this-motion nil + evil-this-motion-count nil + evil-inhibit-operator nil + evil-inhibit-operator-value nil) + (unless (memq command '(evil-use-register + digit-argument + negative-argument + universal-argument + universal-argument-minus + universal-argument-more + universal-argument-other-key)) + (setq evil-this-register nil)) + (evil-adjust-cursor)))) +(put 'evil-normal-post-command 'permanent-local-hook t) + +;;; Insert state + +(defun evil-maybe-remove-spaces (&optional do-remove) + "Remove space from newly opened empty line. +This function removes (indentation) spaces that have been +inserted by opening a new empty line. The behavior depends on the +variable `evil-maybe-remove-spaces'. If this variable is nil the +function does nothing. Otherwise the behavior depends on +DO-REMOVE. If DO-REMOVE is non-nil the spaces are +removed. Otherwise `evil-maybe-remove-spaces' is set to nil +unless the last command opened yet another new line. + +This function should be added as a post-command-hook to track +commands opening a new line." + (cond + ((not evil-maybe-remove-spaces) + (remove-hook 'post-command-hook #'evil-maybe-remove-spaces)) + (do-remove + (when (save-excursion + (beginning-of-line) + (looking-at "^\\s-*$")) + (delete-region (line-beginning-position) + (line-end-position))) + (setq evil-maybe-remove-spaces nil) + (remove-hook 'post-command-hook #'evil-maybe-remove-spaces)) + ((not (memq this-command + '(evil-open-above + evil-open-below + evil-append + evil-append-line + evil-change-whole-line + newline + newline-and-indent + indent-and-newline))) + (setq evil-maybe-remove-spaces nil) + (remove-hook 'post-command-hook #'evil-maybe-remove-spaces)))) + +(evil-define-state insert + "Insert state." + :tag " " + :cursor (bar . 2) + :message "-- INSERT --" + :entry-hook (evil-start-track-last-insertion) + :exit-hook (evil-cleanup-insert-state evil-stop-track-last-insertion) + :input-method t + (cond + ((evil-insert-state-p) + (add-hook 'post-command-hook #'evil-maybe-remove-spaces) + (add-hook 'pre-command-hook #'evil-insert-repeat-hook) + (setq evil-maybe-remove-spaces t) + (unless (eq evil-want-fine-undo t) + (evil-start-undo-step))) + (t + (remove-hook 'post-command-hook #'evil-maybe-remove-spaces) + (remove-hook 'pre-command-hook #'evil-insert-repeat-hook) + (evil-maybe-remove-spaces t) + (setq evil-insert-repeat-info evil-repeat-info) + (evil-set-marker ?^ nil t) + (unless (eq evil-want-fine-undo t) + (evil-end-undo-step)) + (when (or (evil-normal-state-p evil-next-state) + (evil-motion-state-p evil-next-state)) + (evil-move-cursor-back + (and (eolp) (not evil-move-beyond-eol))))))) + +(defun evil-insert-repeat-hook () + "Record insertion keys in `evil-insert-repeat-info'." + (setq evil-insert-repeat-info (last evil-repeat-info)) + (remove-hook 'pre-command-hook #'evil-insert-repeat-hook)) +(put 'evil-insert-repeat-hook 'permanent-local-hook t) + +(eval-when-compile + ;; TODO remove this once support for emacs 26 is dropped + (unless (fboundp 'combine-change-calls) + (defmacro combine-change-calls (_beg _end &rest body) `(progn ,@body)))) +(declare-function evil-execute-repeat-info "evil-repeat") +(defun evil-cleanup-insert-state () + "Called when Insert or Replace state is about to be exited. +Handles the repeat-count of the insertion command." + (when evil-insert-count + (let (pre-command-hook post-command-hook) + (dotimes (_ (1- evil-insert-count)) + (when evil-insert-lines + (evil-insert-newline-below) + (when evil-auto-indent + (indent-according-to-mode))) + (evil-execute-repeat-info (cdr evil-insert-repeat-info))))) + (when evil-insert-vcount + (let ((buffer-invisibility-spec + (if (listp buffer-invisibility-spec) + ;; make all lines hidden by hideshow temporarily visible + (cl-remove-if (lambda (x) (eq (or (car-safe x) x) 'hs)) + buffer-invisibility-spec) + buffer-invisibility-spec))) + (cl-destructuring-bind (line col vcount) evil-insert-vcount + (save-excursion + (combine-change-calls ; For performance + (progn (goto-char (point-min)) + (line-beginning-position line)) + (line-end-position (+ line vcount -1)) + (let (pre-command-hook post-command-hook) ; For performance + (dotimes (v (1- vcount)) + (goto-char (point-min)) + (forward-line (+ line v)) + (when (or (not evil-insert-skip-empty-lines) + (not (integerp col)) + (save-excursion + (evil-move-end-of-line) + (>= (current-column) col))) + (if (integerp col) + (move-to-column col t) + (funcall col)) + (dotimes (_ (or evil-insert-count 1)) + (evil-execute-repeat-info (cdr evil-insert-repeat-info))))))))))) + (and evil-want-fine-undo (evil-end-undo-step))) + +;;; Visual state + +;; Visual selections are implemented in terms of types, and are +;; compatible with the Emacs region. This is achieved by "translating" +;; the region to the selected text right before a command is executed. +;; If the command is a motion, the translation is postponed until a +;; non-motion command is invoked (distinguished by the :keep-visual +;; command property). +;; +;; Visual state activates the region, enabling Transient Mark mode if +;; not already enabled. This is only temporay: if Transient Mark mode +;; was disabled before entering Visual state, it is disabled when +;; exiting Visual state. This allows Visual state to harness the +;; "transient" behavior of many commands without overriding the user's +;; preferences in other states. + +(defmacro evil-define-visual-selection (selection doc &rest body) + "Define a Visual selection SELECTION. +Creates a command evil-visual-SELECTION for enabling the selection. +DOC is the function's documentation string. The following keywords +may be specified in BODY: + +:message STRING Status message when enabling the selection. +:type TYPE Type to use (defaults to SELECTION). + +Following the keywords is optional code which is executed each time +the selection is enabled. + +\(fn SELECTION DOC [[KEY VAL]...] BODY...)" + (declare (indent defun) + (doc-string 2) + (debug (&define name stringp + [&rest keywordp sexp] + def-body))) + (let* ((name (intern (format "evil-visual-%s" selection))) + (message (intern (format "%s-message" name))) + (tagvar (intern (format "%s-tag" name))) + (type selection) + (tag " ") + arg key string) + ;; collect keywords + (while (keywordp (car-safe body)) + (setq key (pop body) + arg (pop body)) + (cond + ((eq key :message) + (setq string arg)) + ((eq key :type) + (setq type arg)) + ((eq key :tag) + (setq tag arg)))) + ;; macro expansion + `(progn + (add-to-list 'evil-visual-alist (cons ',selection ',name)) + (defvar ,name ',type ,(format "*%s" doc)) + (defvar ,message ,string ,doc) + (defvar ,tagvar ,tag ,doc) + (evil-define-command ,name (&optional mark point type message) + ,@(when doc `(,doc)) + :keep-visual t + :repeat nil + (interactive + (list nil nil + (if (and (evil-visual-state-p) + (eq evil-visual-selection ',selection)) + 'exit ,name) t)) + (setq evil--region-from-mouse nil) + (if (eq type 'exit) + (evil-exit-visual-state) + (setq type (or type ,name) + evil-visual-selection ',selection) + (evil-visual-make-region mark point type message) + ,@body)) + ',selection))) + +(evil-define-visual-selection char + "Characterwise selection." + :type inclusive + :message "-- VISUAL --" + :tag " ") + +(evil-define-visual-selection line + "Linewise selection." + :message "-- VISUAL LINE --" + :tag " ") + +(evil-define-visual-selection screen-line + "Linewise selection in `visual-line-mode'." + :message "-- SCREEN LINE --" + :tag " ") + +(evil-define-visual-selection block + "Blockwise selection." + :message "-- VISUAL BLOCK --" + :tag " " + (evil-transient-mark -1) + ;; refresh the :corner property + (setq evil-visual-properties + (plist-put evil-visual-properties :corner + (evil-visual-block-corner)))) + +(evil-define-state visual + "Visual state." + :tag 'evil-visual-tag + :enable (motion normal) + :message 'evil-visual-message + (cond + ((evil-visual-state-p) + (evil-save-transient-mark-mode) + (setq select-active-regions nil) + (cond + ((region-active-p) + (if (< (evil-visual-direction) 0) + (evil-visual-select (region-beginning) (region-end) + evil-visual-char + (evil-visual-direction)) + (evil-visual-make-selection (mark t) (point) + evil-visual-char)) + (evil-visual-highlight)) + (t + (evil-visual-make-region (point) (point) evil-visual-char))) + (add-hook 'pre-command-hook #'evil-visual-pre-command nil t) + (add-hook 'post-command-hook #'evil-visual-post-command nil t) + (add-hook 'deactivate-mark-hook #'evil-visual-deactivate-hook nil t)) + (t + ;; Postpone deactivation of region if next state is Insert. + ;; This gives certain insertion commands (auto-pairing characters, + ;; for example) an opportunity to access the region. + (if (and (eq evil-next-state 'insert) + (eq evil-visual-selection 'char)) + (add-hook 'evil-normal-state-entry-hook + #'evil-visual-deactivate-hook nil t) + (evil-visual-deactivate-hook)) + (setq evil-visual-region-expanded nil) + (remove-hook 'pre-command-hook #'evil-visual-pre-command t) + (remove-hook 'post-command-hook #'evil-visual-post-command t) + (remove-hook 'deactivate-mark-hook #'evil-visual-deactivate-hook t) + (evil-visual-highlight -1)))) + +(defun evil-visual-pre-command (&optional command) + "Run before each COMMAND in Visual state. +Expand the region to the selection unless COMMAND is a motion." + (when (evil-visual-state-p) + (setq command (or command this-command)) + (if (and evil-transient-mouse-selection + evil--region-from-mouse + (not (rassq command evil-visual-alist))) + (progn (setq evil--region-from-mouse nil) + (evil-exit-visual-state)) + (when (and evil-transient-mouse-selection + evil--region-from-mouse + (rassq command evil-visual-alist)) + (setq evil-visual-selection nil ;; maintain visual state + evil--region-from-mouse nil)) + (when evil-visual-x-select-timer + (cancel-timer evil-visual-x-select-timer)) + (unless (evil-get-command-property command :keep-visual) + (evil-visual-update-x-selection) + (evil-visual-expand-region + ;; exclude final newline from linewise selection + ;; unless the command has real need of it + (and (eq (evil-visual-type) 'line) + (evil-get-command-property command :exclude-newline))))))) + +(put 'evil-visual-pre-command 'permanent-local-hook t) + +(defun evil-visual-post-command (&optional command) + "Run after each COMMAND in Visual state. +If COMMAND is a motion, refresh the selection; +otherwise exit Visual state." + (when (evil-visual-state-p) + (setq command (or command this-command)) + (if (or quit-flag + (memq command '(keyboard-quit keyboard-escape-quit)) + ;; Is `mark-active' nil for an unexpanded region? + deactivate-mark + (and (not evil-visual-region-expanded) + (not (region-active-p)) + (not (eq evil-visual-selection 'block)) + transient-mark-mode)) + (progn + (evil-exit-visual-state) + (evil-adjust-cursor)) + (if evil-visual-region-expanded + (evil-visual-contract-region) + (evil-visual-refresh)) + (setq evil-visual-x-select-timer + (run-with-idle-timer evil-visual-x-select-timeout nil + #'evil-visual-update-x-selection + (current-buffer))) + (evil-visual-highlight)))) +(put 'evil-visual-post-command 'permanent-local-hook t) + +(defun evil-visual-update-x-selection (&optional buffer) + "Update the X selection with the current visual region of BUFFER." + (let ((buf (or buffer (current-buffer)))) + (when (and evil-visual-update-x-selection-p + (buffer-live-p buf) + (evil-visual-state-p) + (display-selections-p) + (not (eq evil-visual-selection 'block))) + (with-current-buffer buf + (evil-set-selection 'PRIMARY (buffer-substring-no-properties + evil-visual-beginning + evil-visual-end)))))) + +(defun evil-visual-activate-hook (&optional _command) + "Enable Visual state if the region is activated." + (unless (evil-visual-state-p) + (evil-with-delay nil + (post-command-hook nil t "evil-activate-visual-state") + ;; the activation may only be momentary, so re-check + ;; in `post-command-hook' before entering Visual state + (unless (or (evil-visual-state-p) + (evil-insert-state-p) + (evil-emacs-state-p)) + (when (and (region-active-p) + (not deactivate-mark)) + (evil-visual-state)))))) +(put 'evil-visual-activate-hook 'permanent-local-hook t) + +(defun evil-visual-deactivate-hook (&optional command) + "Deactivate the region and restore Transient Mark mode." + (setq command (or command this-command)) + (remove-hook 'deactivate-mark-hook + #'evil-visual-deactivate-hook t) + (remove-hook 'evil-normal-state-entry-hook + #'evil-visual-deactivate-hook t) + (cond + ((and (evil-visual-state-p) command + (not (evil-get-command-property command :keep-visual))) + (setq evil-visual-region-expanded nil) + (evil-exit-visual-state)) + ((not (evil-visual-state-p)) + (evil-active-region -1) + (evil-restore-transient-mark-mode)))) +(put 'evil-visual-deactivate-hook 'permanent-local-hook t) + +(evil-define-command evil-exit-visual-state (&optional later buffer) + "Exit from Visual state to the previous state. +If LATER is non-nil, exit after the current command." + :keep-visual t + :repeat abort + (with-current-buffer (or buffer (current-buffer)) + (when (evil-visual-state-p) + (if later + (setq deactivate-mark t) + (when evil-visual-region-expanded + (evil-visual-contract-region)) + (setq evil-this-register nil + evil-prev-visual-selection evil-visual-selection + evil-prev-visual-mark (copy-marker evil-visual-mark) + evil-prev-visual-point (copy-marker evil-visual-point)) + (evil-change-to-previous-state))))) + +(defun evil-visual-tag (&optional selection) + "Return a mode-line tag for SELECTION. +SELECTION is a kind of selection as defined by +`evil-define-visual-selection', such as `char', `line' +or `block'." + (setq selection (or selection evil-visual-selection)) + (when selection + (symbol-value (intern (format "evil-visual-%s-tag" selection))))) + +(defun evil-visual-message (&optional selection) + "Create an echo area message for SELECTION. +SELECTION is a kind of selection as defined by +`evil-define-visual-selection', such as `char', `line' +or `block'." + (unless selection (setq selection evil-visual-selection)) + (when selection + (let ((message (symbol-value (intern (format "evil-visual-%s-message" + selection))))) + (cond + ((functionp message) (funcall message)) + ((stringp message) (evil-echo "%s" message)))))) + +(defun evil-visual-select (beg end &optional type dir message) + "Create a Visual selection of type TYPE from BEG to END. +Point and mark are positioned so that the resulting selection +has the specified boundaries. If DIR is negative, point precedes mark, +otherwise it succedes it. To specify point and mark directly, +use `evil-visual-make-selection'." + (let* ((range (evil-contract beg end type)) + (mark (evil-range-beginning range)) + (point (evil-range-end range)) + (dir (or dir 1))) + (when (< dir 0) + (evil-swap mark point)) + (evil-visual-make-selection mark point type message))) + +(defun evil-visual-make-selection (mark point &optional type message) + "Create a Visual selection with point at POINT and mark at MARK. +The boundaries of the selection are inferred from these +and the current TYPE. To specify the boundaries and infer +mark and point, use `evil-visual-select' instead." + (let* ((selection (evil-visual-selection-for-type type)) + (func (evil-visual-selection-function selection)) + (prev (and (evil-visual-state-p) evil-visual-selection)) + (mark (evil-normalize-position mark)) + (point (evil-normalize-position point)) + (state evil-state)) + (unless (evil-visual-state-p) + (evil-visual-state)) + (setq evil-visual-selection selection) + (funcall func mark point type + ;; signal a message when changing the selection + (when (or (not (evil-visual-state-p state)) + (not (eq selection prev))) + message)))) + +(defun evil-visual-make-region (mark point &optional type message) + "Create an active region from MARK to POINT. +If TYPE is given, also set the Visual type. +If MESSAGE is given, display it in the echo area." + (interactive) + (let* ((point (or point (point))) + (mark (or mark + (when (or (evil-visual-state-p) + (region-active-p)) + (mark t)) + point))) + (unless (evil-visual-state-p) + (evil-visual-state)) + (evil-active-region 1) + (setq evil-visual-region-expanded nil) + (evil-visual-refresh mark point type) + (cond + ((null evil-echo-state)) + ((stringp message) + (evil-echo "%s" message)) + (message + (cond + ((stringp evil-visual-state-message) + (evil-echo "%s" evil-visual-state-message)) + ((functionp evil-visual-state-message) + (funcall evil-visual-state-message))))))) + +(defun evil-visual-expand-region (&optional exclude-newline) + "Expand the region to the Visual selection. +If EXCLUDE-NEWLINE is non-nil and the selection ends with a newline, +exclude that newline from the region." + (when (and (evil-visual-state-p) + (not evil-visual-region-expanded)) + (let ((mark evil-visual-beginning) + (point evil-visual-end)) + (when (< evil-visual-direction 0) + (evil-swap mark point)) + (setq evil-visual-region-expanded t) + (evil-visual-refresh mark point) + (when (and exclude-newline + (save-excursion + (goto-char evil-visual-end) + (and (bolp) (not (bobp))))) + (if (< evil-visual-direction 0) + (evil-move-mark (max point (1- (mark)))) + (goto-char (max mark (1- (point))))))))) + +(defun evil-visual-contract-region () + "The inverse of `evil-visual-expand-region'. +Create a Visual selection that expands to the current region." + (evil-visual-refresh) + (setq evil-visual-region-expanded nil) + (evil-visual-refresh evil-visual-mark evil-visual-point)) + +(defun evil-visual-refresh (&optional mark point type &rest properties) + "Refresh point, mark and Visual variables. +Refreshes `evil-visual-beginning', `evil-visual-end', +`evil-visual-mark', `evil-visual-point', `evil-visual-selection', +`evil-visual-direction', `evil-visual-properties' and `evil-this-type'." + (let* ((point (or point (point))) + (mark (or mark (mark t) point)) + (dir (evil-visual-direction)) + (type (or type (evil-visual-type evil-visual-selection) + (evil-visual-type))) + range) + (evil-move-mark mark) + (goto-char point) + (setq evil-visual-beginning + (or evil-visual-beginning + (let ((marker (make-marker))) + (move-marker marker (min point mark)))) + evil-visual-end + (or evil-visual-end + (let ((marker (make-marker))) + (set-marker-insertion-type marker t) + (move-marker marker (max point mark)))) + evil-visual-mark + (or evil-visual-mark + (let ((marker (make-marker))) + (move-marker marker mark))) + evil-visual-point + (or evil-visual-point + (let ((marker (make-marker))) + (move-marker marker point)))) + (setq evil-visual-properties + (evil-concat-plists evil-visual-properties properties)) + (cond + (evil-visual-region-expanded + (setq type (or (evil-visual-type) type)) + (move-marker evil-visual-beginning (min point mark)) + (move-marker evil-visual-end (max point mark)) + ;; if the type is one-to-one, we can safely refresh + ;; the unexpanded positions as well + (when (evil-type-property type :one-to-one) + (setq range (apply #'evil-contract point mark type + evil-visual-properties) + mark (evil-range-beginning range) + point (evil-range-end range)) + (when (< dir 0) + (evil-swap mark point)) + (move-marker evil-visual-mark mark) + (move-marker evil-visual-point point))) + (t + (setq range (apply #'evil-expand point mark type + evil-visual-properties) + type (evil-type range type)) + (move-marker evil-visual-beginning (evil-range-beginning range)) + (move-marker evil-visual-end (evil-range-end range)) + (move-marker evil-visual-mark mark) + (move-marker evil-visual-point point))) + (setq evil-visual-direction dir + evil-this-type type))) + +(defun evil-visual-highlight (&optional arg) + "Highlight Visual selection, depending on the Visual type. +With negative ARG, disable highlighting." + (cond + ((and (numberp arg) (< arg 1)) + (when evil-visual-overlay + (delete-overlay evil-visual-overlay) + (setq evil-visual-overlay nil)) + (when evil-visual-block-overlays + (mapc #'delete-overlay evil-visual-block-overlays) + (setq evil-visual-block-overlays nil))) + ((eq evil-visual-selection 'block) + (when evil-visual-overlay + (evil-visual-highlight -1)) + (evil-visual-highlight-block + evil-visual-beginning + evil-visual-end)) + (t + (when evil-visual-block-overlays + (evil-visual-highlight -1)) + (if evil-visual-overlay + (move-overlay evil-visual-overlay + evil-visual-beginning evil-visual-end) + (setq evil-visual-overlay + (make-overlay evil-visual-beginning evil-visual-end))) + (overlay-put evil-visual-overlay 'face 'region) + (overlay-put evil-visual-overlay 'priority 99)))) + +(defun evil-visual-highlight-block (beg end &optional overlays) + "Highlight rectangular region from BEG to END. +Do this by putting an overlay on each line within the rectangle. +Each overlay extends across all the columns of the rectangle. +Reuse overlays where possible to prevent flicker." + (let* ((point (point)) + (overlays (or overlays 'evil-visual-block-overlays)) + (old (symbol-value overlays)) + beg-col end-col new nlines overlay window-beg window-end) + (save-excursion + ;; calculate the rectangular region represented by BEG and END, + ;; but put BEG in the upper-left corner and END in the + ;; lower-right if not already there + (setq beg-col (evil-column beg) + end-col (evil-column end)) + (when (>= beg-col end-col) + (if (= beg-col end-col) + (setq end-col (1+ end-col)) + (evil-swap beg-col end-col)) + (setq beg (progn (goto-char beg) (evil-move-to-column beg-col)) + end (progn (goto-char end) (evil-move-to-column end-col 1)))) + ;; maybe extend end column to EOL + (and (memq this-command '(next-line previous-line evil-use-register)) + (eq temporary-goal-column most-positive-fixnum) + (setq end-col most-positive-fixnum)) + ;; force a redisplay so we can do reliable window + ;; BEG/END calculations + (sit-for 0) + (setq window-beg (max (window-start) beg) + window-end (min (window-end) (1+ end)) + nlines (count-lines window-beg + (min window-end (point-max)))) + ;; iterate over those lines of the rectangle which are + ;; visible in the currently selected window + (goto-char window-beg) + (dotimes (_ nlines) + (let (before after row-beg row-end) + ;; beginning of row + (evil-move-to-column beg-col) + (when (< (current-column) beg-col) + ;; prepend overlay with virtual spaces if unable to + ;; move directly to the first column + (setq before + (propertize + (make-string + (- beg-col (current-column)) ?\s) + 'face + (or (get-text-property (1- (point)) 'face) + 'default)))) + (setq row-beg (point)) + ;; end of row + (evil-move-to-column end-col) + (when (and (not (eolp)) + (< (current-column) end-col)) + ;; append overlay with virtual spaces if unable to + ;; move directly to the last column + (setq after + (propertize + (make-string + (if (= (point) row-beg) + (- end-col beg-col) + (- end-col (current-column))) + ?\s) 'face 'region)) + ;; place cursor on one of the virtual spaces + (if (= point row-beg) + (put-text-property + 0 (min (length after) 1) + 'cursor t after) + (put-text-property + (max 0 (1- (length after))) (length after) + 'cursor t after))) + (setq row-end (min (point) (line-end-position))) + ;; trim old leading overlays + (while (and old + (setq overlay (car old)) + (< (overlay-start overlay) row-beg) + (/= (overlay-end overlay) row-end)) + (delete-overlay overlay) + (setq old (cdr old))) + ;; reuse an overlay if possible, otherwise create one + (cond + ((and old (setq overlay (car old)) + (or (= (overlay-start overlay) row-beg) + (= (overlay-end overlay) row-end))) + (move-overlay overlay row-beg row-end) + (overlay-put overlay 'before-string before) + (overlay-put overlay 'after-string after) + (setq new (cons overlay new) + old (cdr old))) + (t + (setq overlay (make-overlay row-beg row-end)) + (overlay-put overlay 'before-string before) + (overlay-put overlay 'after-string after) + (setq new (cons overlay new))))) + (forward-line 1)) + ;; display overlays + (dolist (overlay new) + (overlay-put overlay 'face 'region) + (overlay-put overlay 'priority 99)) + ;; trim old overlays + (dolist (overlay old) + (delete-overlay overlay)) + (set overlays (nreverse new))))) + +(defun evil-visual-range () + "Return the Visual selection as a range. +This is a list (BEG END TYPE PROPERTIES...), where BEG is the +beginning of the selection, END is the end of the selection, +TYPE is the selection's type, and PROPERTIES is a property list +of miscellaneous selection attributes." + (apply #'evil-range + evil-visual-beginning evil-visual-end + (evil-visual-type) + :expanded t + evil-visual-properties)) + +(defun evil-visual-direction () + "Return direction of Visual selection. +The direction is -1 if point precedes mark and 1 otherwise. +See also the variable `evil-visual-direction', which holds +the direction of the last selection." + (let* ((point (point)) + (mark (or (mark t) point))) + (if (< point mark) -1 1))) + +(defun evil-visual-type (&optional selection) + "Return the type of the Visual selection. +If SELECTION is specified, return the type of that instead." + (or (and (null selection) (evil-visual-state-p) evil-this-type) + (symbol-value (cdr (assq (or selection evil-visual-selection) + evil-visual-alist))))) + +(defun evil-visual-goto-end () + "Go to the last line of the Visual selection. +This position may differ from `evil-visual-end' depending on +the selection type, and is contained in the selection." + (let ((range (evil-contract-range (evil-visual-range)))) + (goto-char (evil-range-end range)))) + +(defun evil-visual-alist () + "Return an association list from types to selection symbols." + (mapcar #'(lambda (e) + (cons (symbol-value (cdr-safe e)) (cdr-safe e))) + evil-visual-alist)) + +(defun evil-visual-selection-function (selection) + "Return a selection function for TYPE. +Default to `evil-visual-make-region'." + (or (cdr-safe (assq selection evil-visual-alist)) + ;; generic selection function + 'evil-visual-make-region)) + +(defun evil-visual-selection-for-type (type) + "Return a Visual selection for TYPE." + (catch 'done + (dolist (selection evil-visual-alist) + (when (memq (symbol-value (cdr selection)) + (list type (evil-visual-type type))) + (throw 'done (car selection)))))) + +(defun evil-visual-block-corner (&optional corner point mark) + "Block corner corresponding to POINT, with MARK in opposite corner. +Depending on POINT and MARK, the return value is `upper-left', +`upper-right', `lower-left' or `lower-right': + + upper-left +---+ upper-right + | | + lower-left +---+ lower-right + +One-column or one-row blocks are ambiguous. In such cases, +the horizontal or vertical component of CORNER is used. +CORNER defaults to `upper-left'." + (let* ((point (or point (point))) + (mark (or mark (mark t))) + (corner (or corner + (when evil-visual-overlay + (overlay-get evil-visual-overlay :corner)) + 'upper-left)) + (point-col (evil-column point)) + (mark-col (evil-column mark)) + (upperp (if (save-excursion (goto-char (min point mark)) + (search-forward "\n" (max point mark) t)) + (< point mark) + (memq corner '(upper-left upper-right)))) + (leftp (if (= point-col mark-col) + (memq corner '(upper-left lower-left)) + (< point-col mark-col)))) + (if upperp + (if leftp 'upper-left 'upper-right) + (if leftp 'lower-left 'lower-right)))) + +;;; Operator-Pending state + +(evil-define-state operator + "Operator-Pending state." + :tag " " + :cursor evil-half-cursor + :enable (evil-operator-shortcut-map operator motion normal)) + +(evil-define-keymap evil-operator-shortcut-map + "Keymap for Operator-Pending shortcuts like \"dd\" and \"gqq\"." + :local t + (setq evil-operator-shortcut-map (make-sparse-keymap)) + (evil-initialize-local-keymaps)) + +;; the half-height "Operator-Pending cursor" cannot be specified +;; as a static `cursor-type' value, since its height depends on +;; the current font size +(defun evil-half-cursor () + "Change cursor to a half-height box. +\(This is really just a thick horizontal bar.)" + (let ((height (/ (window-pixel-height) (* (window-height) 2)))) + (setq cursor-type (cons 'hbar height)))) + +;;; Replace state + +(evil-define-state replace + "Replace state." + :tag " " + :cursor hbar + :message "-- REPLACE --" + :entry-hook (evil-start-track-last-insertion) + :exit-hook (evil-cleanup-insert-state evil-stop-track-last-insertion) + :input-method t + (cond + ((evil-replace-state-p) + (overwrite-mode 1) + (add-hook 'pre-command-hook #'evil-replace-pre-command nil t) + (add-hook 'pre-command-hook #'evil-insert-repeat-hook) + (unless (eq evil-want-fine-undo t) + (evil-start-undo-step))) + (t + (overwrite-mode -1) + (remove-hook 'pre-command-hook #'evil-replace-pre-command t) + (remove-hook 'pre-command-hook #'evil-insert-repeat-hook) + (setq evil-insert-repeat-info evil-repeat-info) + (evil-set-marker ?^ nil t) + (unless (eq evil-want-fine-undo t) + (evil-end-undo-step)) + (evil-move-cursor-back))) + (setq evil-replace-alist nil)) + +(defun evil-replace-pre-command () + "Remember the character under point." + (when (evil-replace-state-p) + (let ((char (unless (eolp) (char-after))) + (prev (assq (point) evil-replace-alist))) + (if prev + (setcdr prev char) + (push (cons (point) char) evil-replace-alist))))) +(put 'evil-replace-pre-command 'permanent-local-hook t) + +(defun evil-replace-backspace () + "Restore character under cursor." + (interactive) + (backward-char) + (let* ((prev (assq (point) evil-replace-alist)) + (char (cdr prev)) + (this-command #'evil-replace-backspace)) + (when prev + (delete-char 1) + (when char (save-excursion (insert char)))))) + +(defun evil-update-replace-alist (opoint count chars-to-delete &optional offset) + "Add CHARS-TO-DELETE chars to evil-replace-alist, starting at OPOINT. +If COUNT is greater than CHARS-TO-DELETE, pad the alist with nils. +Decrement recorded position by optional offset, or 0." + (when (evil-replace-state-p) + (dotimes (c count) + (let ((pos (+ c opoint))) + (add-to-list 'evil-replace-alist + (cons (- pos (or offset 0)) (when (< c chars-to-delete) + (char-after pos)))))))) + +;;; Motion state + +(evil-define-state motion + "Motion state." + :tag " " + :suppress-keymap t) + +;;; Emacs state + +(evil-define-state emacs + "Emacs state." + :tag " " + :message "-- EMACS --" + :input-method t + :intercept-esc nil) + +(provide 'evil-states) + +;;; evil-states.el ends here diff --git a/.config/emacs/lisp/evil/evil-test-helpers.el b/.config/emacs/lisp/evil/evil-test-helpers.el new file mode 100644 index 0000000..c7eeb71 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-test-helpers.el @@ -0,0 +1,416 @@ +;;; evil-test-helpers.el --- unit test helpers for Evil -*- coding: utf-8; lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye +;; Package-Requires: ((evil "1.15.0")) +;; Version: 1.15.0 + +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; This file contains helper functions for writing tests for +;; evil-mode. These helpers can also be used by other packages which +;; extend evil-mode. + +;; To write a test use `evil-test-buffer': + +;; (require 'evil-test-helpers) +;; +;; (ert-deftest evil-test () +;; :tags '(evil) +;; (evil-test-buffer +;; "[T]his creates a test buffer." ; cursor on "T" +;; ("w") ; key sequence +;; "This [c]reates a test buffer."))) ; cursor moved to "c" +;; +;; The initial state, the cursor syntax, etc., can be changed +;; with keyword arguments. See the documentation string of +;; `evil-test-buffer' for more details. +;; +;; This file is NOT part of Evil itself. + +(require 'elp) +(require 'ert) +(require 'evil) + +;;; Code: + +(when (fboundp 'font-lock-add-keywords) + (font-lock-add-keywords 'emacs-lisp-mode + '(("(\\(evil-test-buffer\\)\\>" + 1 font-lock-keyword-face)))) + +(defvar evil-test-point nil + "Marker for point.") +(make-variable-buffer-local 'evil-test-point) +(defvar evil-test-visual-start nil + "Marker for Visual beginning.") +(make-variable-buffer-local 'evil-test-visual-start) +(defvar evil-test-visual-end nil + "Marker for Visual end.") +(make-variable-buffer-local 'evil-test-visual-end) + +(defvaralias 'evil-test-select-enable-clipboard + (if (boundp 'select-enable-clipboard) + 'select-enable-clipboard + 'x-select-enable-clipboard)) + +(defmacro evil-test-buffer (&rest body) + "Execute FORMS in a temporary buffer. +The following optional keywords specify the buffer's properties: + +:state STATE The initial state, defaults to `normal'. +:visual SELECTION The Visual selection, defaults to `char'. +:point-start STRING String for matching beginning of point, + defaults to \"[\". +:point-end STRING String for matching end of point, + defaults to \"]\". +:visual-start STRING String for matching beginning of + Visual selection, defaults to \"<\". +:visual-end STRING String for matching end of + Visual selection, defaults to \">\". + +Then follows one or more forms. If the first form is a string, +it is taken to be a buffer description as passed to +`evil-test-buffer-from-string', and initializes the buffer. +Subsequent string forms validate the buffer. + +If a form is a list of strings or vectors, it is taken to be a +key sequence and is passed to `execute-kbd-macro'. If the form +is \(file FILENAME CONTENTS), then the test fails unless the +contents of FILENAME equal CONTENTS. If the form is \(error +SYMBOL ...) then the test fails unless an error of type SYMBOL is +raised. Remaining forms are evaluated as-is. + +\(fn [[KEY VALUE]...] FORMS...)" + (declare (indent defun)) + (let ((state 'normal) + arg key point-start point-end string + visual visual-start visual-end) + ;; collect keywords + (while (keywordp (car-safe body)) + (setq key (pop body) + arg (pop body)) + (cond + ((eq key :point-start) + (setq point-start (or arg ""))) + ((eq key :point-end) + (setq point-end (or arg ""))) + ((eq key :state) + (setq state arg)) + ((eq key :visual) + (setq visual arg)) + ((eq key :visual-start) + (setq visual-start (or arg ""))) + ((eq key :visual-end) + (setq visual-end (or arg ""))))) + ;; collect buffer initialization + (when (stringp (car-safe body)) + (setq string (pop body))) + ;; macro expansion + `(let ((buffer (evil-test-buffer-from-string + ,string ',state + ,point-start ,point-end + ',visual ,visual-start ,visual-end)) + (kill-ring kill-ring) + (kill-ring-yank-pointer kill-ring-yank-pointer) + evil-test-select-enable-clipboard + message-log-max) + (unwind-protect + (save-window-excursion + (with-current-buffer buffer + ;; necessary for keyboard macros to work + (switch-to-buffer-other-window (current-buffer)) + (buffer-enable-undo) + ;; parse remaining forms + ,@(mapcar + #'(lambda (form) + (let (error-symbol) + (when (and (listp form) + (eq (car-safe form) 'error)) + (setq error-symbol (car-safe (cdr-safe form)) + form (cdr-safe (cdr-safe form)))) + (let ((result + (cond + ((stringp form) + `(evil-test-buffer-string + ,form + ',point-start ',point-end + ',visual-start ',visual-end)) + ((eq (car-safe form) 'file) + `(evil-test-file-contents ,(cadr form) + ,(car (cddr form)))) + ((or (stringp (car-safe form)) + (vectorp (car-safe form)) + (memq (car-safe (car-safe form)) + '(kbd vconcat))) + ;; we need to execute everything as a single + ;; sequence for command loop hooks to work + `(execute-kbd-macro + (apply #'vconcat + (mapcar #'listify-key-sequence + (list ,@form))))) + ((memq (car-safe form) '(kbd vconcat)) + `(execute-kbd-macro ,form)) + (t form)))) + (if error-symbol + `(should-error ,result :type ',error-symbol) + result)))) + body))) + (when (buffer-name buffer) (kill-buffer buffer)))))) + +(defmacro evil-test-selection (string &optional end-string + before-predicate after-predicate) + "Verify that the Visual selection contains STRING." + (declare (indent defun)) + `(progn + (save-excursion + (goto-char (or evil-visual-beginning (region-beginning))) + (evil-test-text nil (or ,string ,end-string) ,before-predicate)) + (save-excursion + (goto-char (or evil-visual-end (region-end))) + (evil-test-text (or ,end-string ,string) nil nil ,after-predicate)))) + +(defun evil-test-buffer-string (string &optional + point-start point-end + visual-start visual-end) + "Validate the current buffer according to STRING. +If STRING contains an occurrence of POINT-START immediately +followed by POINT-END, that position is compared against point. +If STRING contains an occurrence of VISUAL-START followed by +VISUAL-END, those positions are compared against the Visual selection. +POINT-START and POINT-END default to [ and ]. +VISUAL-START and VISUAL-END default to < and >." + (let ((actual-buffer (current-buffer)) + (marker-buffer (evil-test-marker-buffer-from-string + string + point-start point-end + visual-start visual-end)) + before-point after-point string selection) + (unwind-protect + (with-current-buffer marker-buffer + (setq string (buffer-string)) + (when evil-test-point + (setq before-point (buffer-substring (point-min) evil-test-point) + after-point (buffer-substring evil-test-point (point-max)))) + (when (and evil-test-visual-start evil-test-visual-end) + (setq selection (buffer-substring + evil-test-visual-start evil-test-visual-end))) + (with-current-buffer actual-buffer + (if (or before-point after-point) + (evil-test-text before-point after-point) + ;; if the cursor isn't specified, just test the whole buffer + (save-excursion + (goto-char (point-min)) + (evil-test-text nil string #'bobp #'eobp))) + (when selection + (evil-test-selection selection)))) + (kill-buffer marker-buffer)))) + +(defun evil-test-buffer-from-string (string &optional + state + point-start point-end + visual visual-start visual-end) + "Create a new buffer according to STRING. +If STRING contains an occurrence of POINT-START immediately +followed by POINT-END, then point is moved to that position. +If STRING contains an occurrence of VISUAL-START followed by +VISUAL-END, then a Visual selection is created with those boundaries. +POINT-START and POINT-END default to [ and ]. +VISUAL-START and VISUAL-END default to < and >. +STATE is the initial state; it defaults to `normal'. +VISUAL is the Visual selection: it defaults to `char'." + (let ((type (evil-visual-type (or visual 'char))) + (buffer (evil-test-marker-buffer-from-string + string point-start point-end + visual-start visual-end))) + (with-current-buffer buffer + (prog1 buffer + (evil-change-state state) + ;; let the buffer change its major mode without disabling Evil + (add-hook 'after-change-major-mode-hook #'evil-initialize) + (when (and (markerp evil-test-visual-start) + (markerp evil-test-visual-end)) + (evil-visual-select + evil-test-visual-start evil-test-visual-end type) + (when evil-test-point + (goto-char evil-test-point) + (evil-visual-refresh) + (unless (and (= evil-visual-beginning + evil-test-visual-start) + (= evil-visual-end + evil-test-visual-end)) + (evil-visual-select + evil-test-visual-start evil-test-visual-end type -1) + (goto-char evil-test-point) + (evil-visual-refresh)))) + (when (markerp evil-test-point) + (goto-char evil-test-point)))))) + +(defun evil-test-marker-buffer-from-string (string &optional + point-start point-end + visual-start visual-end) + "Create a new marker buffer according to STRING. +If STRING contains an occurrence of POINT-START immediately +followed by POINT-END, that position is stored in the +buffer-local variable `evil-test-point'. Similarly, +if STRING contains an occurrence of VISUAL-START followed by +VISUAL-END, those positions are stored in the variables +`evil-test-visual-beginning' and `evil-test-visual-end'. +POINT-START and POINT-END default to [ and ]. +VISUAL-START and VISUAL-END default to < and >." + (let ((string (or string "")) + (point-start (regexp-quote + (if (characterp point-start) + (string point-start) + (or point-start "[")))) + (point-end (regexp-quote + (if (characterp point-end) + (string point-end) + (or point-end "]")))) + (visual-start (regexp-quote + (if (characterp visual-start) + (string visual-start) + (or visual-start "<")))) + (visual-end (regexp-quote + (if (characterp visual-end) + (string visual-end) + (or visual-end ">"))))) + (with-current-buffer (generate-new-buffer " *test*") + (prog1 (current-buffer) + (save-excursion + (insert string)) + (save-excursion + (when (> (length point-start) 0) + (if (> (length point-end) 0) + (when (re-search-forward + (format "\\(%s\\)[^%s]?\\(%s\\)" + point-start point-end point-end) nil t) + (goto-char (match-beginning 0)) + (delete-region (match-beginning 2) (match-end 2)) + (delete-region (match-beginning 1) (match-end 1)) + (setq evil-test-point + (move-marker (make-marker) (point)))) + (when (re-search-forward point-start nil t) + (goto-char (match-beginning 0)) + (delete-region (match-beginning 0) (match-end 0)) + (setq evil-test-point + (move-marker (make-marker) (point))))))) + (save-excursion + (when (and (> (length visual-start) 0) + (> (length visual-end) 0)) + (when (re-search-forward visual-start nil t) + (goto-char (match-beginning 0)) + (delete-region (match-beginning 0) (match-end 0)) + (setq evil-test-visual-start + (move-marker (make-marker) (point)))) + (when (re-search-forward visual-end nil t) + (goto-char (match-beginning 0)) + (delete-region (match-beginning 0) (match-end 0)) + (setq evil-test-visual-end + (move-marker (make-marker) (point)))))))))) + +(defun evil-test-text (before after &optional before-predicate after-predicate) + "Verify the text around point. +BEFORE is the expected text before point, and AFTER is +the text after point. BEFORE-PREDICATE is a predicate function +to execute at the beginning of the text, and AFTER-PREDICATE +is executed at the end." + (when before + (if (functionp before) + (setq before-predicate before + before nil) + (should (string= (buffer-substring + (max (point-min) (- (point) (length before))) + (point)) + before)))) + (when after + (if (functionp after) + (setq after-predicate after + after nil) + (should (string= (buffer-substring + (point) + (min (point-max) (+ (point) (length after)))) + after)))) + (when before-predicate + (ert-info ((format "Expect `%s' at the beginning" before-predicate)) + (save-excursion + (backward-char (length before)) + (should (funcall before-predicate))))) + (when after-predicate + (ert-info ((format "Expect `%s' at the end" after-predicate)) + (save-excursion + (forward-char (length after)) + (should (funcall after-predicate)))))) + +(defmacro evil-test-region (string &optional end-string + before-predicate after-predicate) + "Verify that the region contains STRING." + (declare (indent defun)) + `(progn + (save-excursion + (goto-char (region-beginning)) + (evil-test-text nil (or ,string ,end-string) ,before-predicate)) + (save-excursion + (goto-char (region-end)) + (evil-test-text (or ,end-string ,string) nil nil ,after-predicate)))) + +(defmacro evil-test-overlay (overlay string &optional end-string + before-predicate after-predicate) + "Verify that OVERLAY contains STRING." + (declare (indent defun)) + `(progn + (save-excursion + (goto-char (overlay-start ,overlay)) + (evil-test-text nil (or ,string ,end-string) ,before-predicate)) + (save-excursion + (goto-char (overlay-end ,overlay)) + (evil-test-text (or ,end-string ,string) nil nil ,after-predicate)))) + +(defmacro evil-with-temp-file (file-var content &rest body) + "Create a temp file with CONTENT and bind its name to FILE-VAR within BODY. +FILE-VAR must be a symbol which contains the name of the +temporary file within the macro body. CONTENT is either a string +to be used as the content of the temporary file or a form to be +executed with the temporary file's buffer as \(current-buffer), +see `with-temp-file'. BODY contains the forms to be executed +while the temporary file exists. The temporary file is deleted at +the end of the execution of BODY." + (declare (indent 2) + (debug (symbolp form body))) + `(let ((,file-var (make-temp-file "evil-test"))) + (with-temp-file ,file-var + ,(if (stringp content) + `(insert ,content) + content)) + ,@body + (delete-file ,file-var))) + +(defun evil-test-file-contents (name contents) + "Ensure that the contents of file with NAME equal CONTENTS." + (with-temp-buffer + (insert-file-contents name) + (should (string= (buffer-string) + contents)))) + +(provide 'evil-test-helpers) + +;;; evil-test-helpers.el ends here diff --git a/.config/emacs/lisp/evil/evil-tests.el b/.config/emacs/lisp/evil/evil-tests.el new file mode 100644 index 0000000..01af626 --- /dev/null +++ b/.config/emacs/lisp/evil/evil-tests.el @@ -0,0 +1,10130 @@ +;; evil-tests.el --- unit tests for Evil -*- coding: utf-8; lexical-binding: t; -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; This file is for developers. It runs some tests on Evil. +;; To load it, run the Makefile target "make test" or add +;; the following lines to .emacs: +;; +;; (setq evil-tests-run nil) ; set to t to run tests immediately +;; (global-set-key [f12] 'evil-tests-run) ; hotkey +;; (require 'evil-tests) +;; +;; Loading this file enables profiling on Evil. The current numbers +;; can be displayed with `elp-results'. The Makefile target +;; "make profiler" shows profiling results in the terminal on the +;; basis of running all tests. +;; +;; To write a test, use `ert-deftest' and specify a :tags value of at +;; least '(evil). The test may inspect the output of functions given +;; certain input, or it may execute a key sequence in a temporary +;; buffer and investigate the results. For the latter approach, the +;; macro `evil-test-buffer' creates a temporary buffer in Normal +;; state. String descriptors initialize and match the contents of +;; the buffer: +;; +;; (ert-deftest evil-test () +;; :tags '(evil) +;; (evil-test-buffer +;; "[T]his creates a test buffer." ; cursor on "T" +;; ("w") ; key sequence +;; "This [c]reates a test buffer."))) ; cursor moved to "c" +;; +;; The initial state, the cursor syntax, etc., can be changed +;; with keyword arguments. See the documentation string of +;; `evil-test-buffer' for more details. +;; +;; This file is NOT part of Evil itself. + +;; FIXME: Merely loading an ELisp file should not change Emacs's config! +(setq load-prefer-newer t) + +(require 'cl-lib) +(require 'elp) +(require 'ert) +;; Load non-compiled `evil-ex'. (It defines `evil-parser' - which is +;; needed by `evil-test-parser' - only inside an `eval-when-compile'.) +(require 'evil-ex "evil-ex.el") +(require 'evil) +(require 'evil-digraphs) +(require 'evil-test-helpers) +(require 'ispell) + +;;; Code: + +(defvar evil-tests-run nil + "*Run Evil tests.") + +(defvar evil-tests-profiler nil + "*Profile Evil tests.") + +(defun evil-tests-initialize (&optional tests profiler interactive) + (setq profiler (or profiler evil-tests-profiler)) + (when (consp profiler) + (setq profiler (car profiler))) + (when profiler + (setq evil-tests-profiler t) + (setq profiler + (cdr (assq profiler + '((call . elp-sort-by-call-count) + (average . elp-sort-by-average-time) + (total . elp-sort-by-total-time))))) + (setq elp-sort-by-function (or profiler 'elp-sort-by-call-count)) + (elp-instrument-package "evil")) + (if interactive + (if (y-or-n-p-with-timeout "Run tests? " 2 t) + (evil-tests-run tests interactive) + (message "You can run the tests at any time with `M-x evil-tests-run'")) + (evil-tests-run tests))) + +(defun evil-tests-run (&optional tests interactive) + "Run Evil tests." + (interactive '(nil t)) + (let ((elp-use-standard-output (not interactive))) + (setq tests + (or (null tests) + `(or ,@(mapcar #'(lambda (test) + (or (null test) + (and (memq test '(evil t)) t) + `(or (tag ,test) + ,(format "^%s$" test)))) + tests)))) + (cond + (interactive + (ert-run-tests-interactively tests) + (when evil-tests-profiler + (elp-results))) + (evil-tests-profiler + (ert-run-tests-batch tests) + (elp-results)) + (t + ;; We would like to use `ert-run-tests-batch-and-exit' + ;; Unfortunately it doesn't work outside of batch mode, and we + ;; can't use batch mode because we have tests that need windows. + ;; Instead, run the tests interactively, copy the results to a + ;; text file, and then exit with an appropriate code. + (setq attempt-stack-overflow-recovery nil + attempt-orderly-shutdown-on-fatal-signal nil) + (unwind-protect + (progn + (ert-run-tests-interactively tests) + (with-current-buffer "*ert*" + (append-to-file (point-min) (point-max) "test-results.txt") + (kill-emacs (if (zerop (ert-stats-completed-unexpected ert--results-stats)) 0 1)))) + (unwind-protect + (progn + (append-to-file "Error running tests\n" nil "test-results.txt") + (append-to-file (backtrace-to-string (backtrace-get-frames 'backtrace)) nil "test-results.txt")) + (kill-emacs 2))))))) + +(defun evil-tests-profiler (&optional force) + "Profile Evil tests." + (when (or evil-tests-profiler force) + (setq evil-tests-profiler t) + (elp-instrument-package "evil"))) + +;;; States + +(defun evil-test-local-mode-enabled () + "Verify that `evil-local-mode' is enabled properly" + (ert-info ("Set the mode variable to t") + (should (eq evil-local-mode t))) + (ert-info ("Refresh `emulation-mode-map-alist'") + (should (memq 'evil-mode-map-alist emulation-mode-map-alists))) + (ert-info ("Create a buffer-local value for `evil-mode-map-alist'") + (should (assq 'evil-mode-map-alist (buffer-local-variables)))) + (ert-info ("Initialize buffer-local keymaps") + (should (assq 'evil-normal-state-local-map (buffer-local-variables))) + (should (keymapp evil-normal-state-local-map)) + (should (assq 'evil-emacs-state-local-map (buffer-local-variables))) + (should (keymapp evil-emacs-state-local-map))) + (ert-info ("Don't add buffer-local entries to the default value") + (should-not (rassq evil-normal-state-local-map + (default-value 'evil-mode-map-alist))) + (should-not (rassq evil-emacs-state-local-map + (default-value 'evil-mode-map-alist))))) + +(defun evil-test-local-mode-disabled () + "Verify that `evil-local-mode' is disabled properly" + (ert-info ("Set the mode variable to nil") + (should-not evil-local-mode)) + (ert-info ("Disable all states") + (evil-test-no-states))) + +(defun evil-test-no-states () + "Verify that all states are disabled" + (ert-info ("Set `evil-state' to nil") + (should-not evil-state)) + (ert-info ("Disable all state keymaps") + (dolist (state (mapcar #'car evil-state-properties) t) + (should-not (evil-state-property state :mode t)) + (should-not (memq (evil-state-property state :keymap t) + (current-active-maps))) + (should-not (evil-state-property state :local t)) + (should-not (memq (evil-state-property state :local-keymap t) + (current-active-maps))) + (dolist (map (evil-state-auxiliary-keymaps state)) + (should-not (memq map (current-active-maps))))))) + +(ert-deftest evil-test-toggle-local-mode () + "Toggle `evil-local-mode'" + :tags '(evil state) + (with-temp-buffer + (ert-info ("Enable `evil-local-mode'") + (evil-local-mode 1) + (evil-test-local-mode-enabled)) + (ert-info ("Disable `evil-local-mode'") + (evil-local-mode -1) + (evil-test-local-mode-disabled)))) + +(defun evil-test-change-state (state) + "Change state to STATE and check keymaps" + (evil-change-state state) + (let ((mode (evil-state-property state :mode)) + ;; (keymap (evil-state-property state :keymap t)) + (local-mode (evil-state-property state :local)) + ;; (local-keymap (evil-state-property state :local-keymap t)) + (tag (evil-state-property state :tag t))) + (when (functionp tag) + (setq tag (funcall tag))) + (ert-info ("Update `evil-state'") + (should (eq evil-state state))) + (ert-info ("Ensure `evil-local-mode' is enabled") + (evil-test-local-mode-enabled)) + (ert-info ("Enable state modes") + (should (symbol-value mode)) + (should (symbol-value local-mode))) + (ert-info ("Push state keymaps to the top") + (evil-test-state-keymaps state)) + (ert-info ("Refresh mode line tag") + (should (equal evil-mode-line-tag tag))))) + +(defun evil-test-state-keymaps (state) + "Verify that STATE's keymaps are pushed to the top" + (let ((actual (evil-state-keymaps state)) + (expected `((,(evil-state-property state :local) + . , (evil-state-property state :local-keymap t)) + (,(evil-state-property state :mode) + . ,(evil-state-property state :keymap t))))) + ;; additional keymaps inherited with :enable + (cond + ((eq state 'operator) + (setq expected + `((evil-operator-shortcut-mode + . ,evil-operator-shortcut-map) + (evil-operator-state-local-minor-mode + . ,evil-operator-state-local-map) + (evil-operator-state-minor-mode + . ,evil-operator-state-map) + (evil-motion-state-local-minor-mode + . ,evil-motion-state-local-map) + (evil-motion-state-minor-mode + . ,evil-motion-state-map) + (evil-normal-state-local-minor-mode + . ,evil-normal-state-local-map) + (evil-normal-state-minor-mode + . ,evil-normal-state-map))))) + (let ((actual (butlast actual (- (length actual) + (length expected))))) + (should (equal actual expected)) + (dolist (map actual) + (setq map (cdr-safe map)) + (should (keymapp map)))))) + +(ert-deftest evil-test-exit-normal-state () + "Enter Normal state and then disable all states" + :tags '(evil state) + (with-temp-buffer + (evil-test-change-state 'normal) + (evil-normal-state -1) + (evil-test-no-states))) + +(ert-deftest evil-test-change-states () + "Change between Normal state, Emacs state and Operator-Pending state" + :tags '(evil state) + (with-temp-buffer + (evil-test-change-state 'normal) + (evil-test-change-state 'emacs) + (evil-test-change-state 'normal) + (evil-test-change-state 'operator) + (evil-test-change-state 'normal) + (evil-test-change-state 'emacs) + (evil-test-change-state 'replace) + (evil-test-change-state 'normal))) + +(ert-deftest evil-test-change-to-previous-state () + "Change to some state and back." + :tags '(evil state) + (with-temp-buffer + (evil-test-change-state 'normal) + (evil-test-change-state 'visual) + (evil-test-change-state 'emacs) + (evil-change-to-previous-state) + (should (eq evil-state 'visual)) + (evil-change-to-previous-state) + (should (eq evil-state 'normal)))) + +(ert-deftest evil-test-enter-normal-state-disabled () + "Enter Normal state even if `evil-local-mode' is disabled" + :tags '(evil state) + (with-temp-buffer + (evil-local-mode -1) + (evil-test-local-mode-disabled) + (evil-test-change-state 'normal))) + +(ert-deftest evil-test-execute-in-normal-state () + "Execute Normal state command in Insert state (`evil-execute-in-normal-state')." + :tags '(evil) + (evil-test-buffer "[a]bcdef\n" + ("I") + (should (evil-insert-state-p)) + ("\C-ox") + (ert-info ("Should return to Insert state") (should (evil-insert-state-p))) + "[b]cdef\n" + ("\C-oA") + (ert-info ("Should return to Insert state after Insert state command") + (should (evil-insert-state-p))) + ("bcdef[]\n")) + (ert-info ("Cursor is placed correctly afterwards") + (evil-test-buffer + :state insert + "abcdefg[]" + ("\C-o~") + "abcdefG[]") + (evil-test-buffer + :state insert + "abcdefg[]" + ("\C-ozz") + "abcdefg[]") + (evil-test-buffer + :state insert + "abc[]defg" + ("\C-o$") + "abcdefg[]") + (evil-test-buffer + :state insert + "abcdefg[]" + ("\C-o^") + "[]abcdefg") + (evil-test-buffer + :state insert + "abcdefg[]" + ("\C-oi") + "abcdef[]g") + (evil-test-buffer + :state insert + "\n[]" + ("\C-ok") + "[]\n") + (evil-test-buffer + "line1\nli[n]e2" + ("ma" "kA" "\C-o`a") + "line1\nli[]ne2")) + (ert-info ("Can enter replace state and stay in it") + (evil-test-buffer + :state insert + "abc[]defg" + ("\C-oRfoo") + "abcfoog")) + (ert-info ("Insert count is ignored") + (evil-test-buffer "[]" + ("2i" "abcdef" "\C-o~" "g" [escape]) + "abcdeF[g]")) + (ert-info ("Can execute evil-repeat in normal state") + (evil-test-buffer + ;; Although this is the same in Vim, text inserted after the temporary + ;; normal command is not recorded for repetition, which is a subtle + ;; (but arguably more useful) difference. + :state insert + "ab[]cfg" + ("\C-o~de\C-o.") + "abCdeF[]g"))) + +(defun evil-test-suppress-keymap (state) + "Verify that `self-insert-command' is suppressed in STATE" + (evil-test-buffer + ";; This buffer is for notes." + (evil-test-change-state state) + ;; TODO: this should be done better + (ert-info ("Disable the state's own keymaps so that the +suppression keymap comes first") + (setq evil-operator-state-minor-mode nil + evil-operator-state-local-minor-mode nil)) + (should (eq (key-binding "£") #'undefined)) + (ert-info ("Don't insert text") + ;; may or may not signal an error, depending on batch mode + (condition-case nil + (execute-kbd-macro "£££") + (error nil)) + (should (string= (buffer-substring 1 4) ";; "))))) + +(ert-deftest evil-test-emacs-state-suppress-keymap () + "`self-insert-command' works in Emacs state" + :tags '(evil state) + (should-error (evil-test-suppress-keymap 'emacs))) + +(ert-deftest evil-test-normal-state-suppress-keymap () + "No `self-insert-command' in Normal state" + :tags '(evil state) + (evil-test-suppress-keymap 'normal)) + +(ert-deftest evil-test-operator-state-suppress-keymap () + "Operator-Pending state should inherit suppression +of `self-insert-command' from Normal state" + :tags '(evil state) + (evil-test-suppress-keymap 'operator)) + +(ert-deftest evil-test-operator-state-shortcut-keymap () + "Enable shortcut keymap in Operator-Pending state" + :tags '(evil state) + (evil-test-buffer + (ert-info ("Activate `evil-operator-shortcut-map' in \ +Operator-Pending state") + (evil-test-change-state 'operator) + (should (rassq evil-operator-shortcut-map + (evil-state-keymaps 'operator))) + (should (keymapp evil-operator-shortcut-map)) + (should evil-operator-shortcut-mode) + (should (memq evil-operator-shortcut-map + (current-active-maps)))) + (ert-info ("Deactivate `evil-operator-shortcut-map' \ +outside Operator-Pending state") + (evil-test-change-state 'emacs) + (should-not evil-operator-shortcut-mode) + (should-not (memq evil-operator-shortcut-map + (current-active-maps)))) + (ert-info ("Reset `evil-operator-shortcut-map' \ +when entering Operator-Pending state") + (define-key evil-operator-shortcut-map "f" 'foo) + (should (eq (lookup-key evil-operator-shortcut-map "f") + 'foo)) + (evil-test-change-state 'operator) + (should-not (eq (lookup-key evil-operator-shortcut-map "f") + 'foo))) + (ert-info ("Reset `evil-operator-shortcut-map' \ +when exiting Operator-Pending state") + (define-key evil-operator-shortcut-map "b" 'bar) + (should (eq (lookup-key evil-operator-shortcut-map "b") + 'bar)) + (evil-test-change-state 'emacs) + (should-not (eq (lookup-key evil-operator-shortcut-map "b") + 'bar)))) + (ert-info ("Compute shortcuts with original keymaps") + (evil-test-buffer "foo" + (define-key evil-normal-state-local-map (kbd "SPC x") #'evil-delete) + ;; evil-forward-char in motion map should not shadow the executed + ;; evil-delete binding above when populating shortcut map. + ((kbd "SPC x x")) + ""))) + +(ert-deftest evil-test-auxiliary-maps () + "Test auxiliary keymaps" + :tags '(evil state) + ;; `evil-define-key' can't be used on a lexically-scoped keymap var. + (defvar evil--map) + (let ((evil--map (make-sparse-keymap)) aux) + (ert-info ("Create a new auxiliary keymap") + (evil-define-key 'normal evil--map "f" 'foo) + (setq aux (evil-get-auxiliary-keymap evil--map 'normal)) + (should (evil-auxiliary-keymap-p aux)) + (should (eq (lookup-key aux "f") 'foo))) + (ert-info ("Add to auxiliary keymap") + (evil-define-key 'normal evil--map "b" 'bar) + (should (eq (lookup-key aux "f") 'foo)) + (should (eq (lookup-key aux "b") 'bar))))) + +(ert-deftest evil-test-global-local-map-binding () + "Test use of `evil-define-key' for binding in global maps." + :tags '(evil state) + (let ((evil-normal-state-map (copy-keymap evil-normal-state-map)) + (evil-normal-state-local-map + (when (keymapp evil-normal-state-local-map) + (copy-keymap evil-normal-state-local-map))) + (global-map (copy-keymap global-map)) + (orig-local-map + (when (keymapp (current-local-map)) + (copy-keymap (current-local-map)))) + (map (or (current-local-map) (make-sparse-keymap)))) + (use-local-map map) + (ert-info ("Bind in a global state map") + (evil-define-key 'normal 'global "f" 'foo) + (should (eq (lookup-key evil-normal-state-map "f") 'foo))) + (ert-info ("Bind in a local state map") + (evil-define-key 'normal 'local "f" 'foo) + (should (eq (lookup-key evil-normal-state-local-map "f") 'foo))) + (ert-info ("Bind in the global map") + (evil-define-key nil 'global "b" 'bar) + (should (eq (lookup-key global-map "b") 'bar))) + (ert-info ("Bind in the local map") + (evil-define-key nil 'local "b" 'bar) + (should (eq (lookup-key (current-local-map) "b") 'bar))) + (use-local-map orig-local-map))) + +;;; Type system + +(ert-deftest evil-test-exclusive-type () + "Expand and contract the `line' type" + :tags '(evil type) + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (let* ((first-line 1) + (second-line (progn + (forward-line) + (point)))) + (forward-line) + (ert-info ("Return the beginning and end unchanged \ +if they are the same") + (should (equal (evil-normalize 1 1 'exclusive) + (list 1 1 'exclusive)))) + (ert-info ("expand to `inclusive' if the end position \ +is at the beginning of a line") + (should (equal (evil-normalize (1+ first-line) second-line 'exclusive) + (list (1+ first-line) (1- second-line) 'inclusive + :expanded t)))) + (ert-info ("expand to `line' if both the beginning and end \ +are at the beginning of a line") + (should (equal (evil-normalize first-line second-line 'exclusive) + (list first-line second-line 'line + :expanded t)))) + (ert-info ("Measure as the strict difference between the end \ +and the beginning") + (should (string= (evil-describe 1 1 'exclusive) + "0 characters")) + (should (string= (evil-describe 1 2 'exclusive) + "1 character")) + (should (string= (evil-describe 5 2 'exclusive) + "3 characters")))))) + +(ert-deftest evil-test-inclusive-type () + "Expand and contract the `inclusive' type" + :tags '(evil type) + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (ert-info ("Include the ending character") + (should (equal (evil-expand 1 1 'inclusive) + '(1 2 inclusive :expanded t)))) + (ert-info ("Don't mind if positions are in wrong order") + (should (equal (evil-expand 5 2 'inclusive) + '(2 6 inclusive :expanded t)))) + (ert-info ("Exclude the ending character when contracting") + (should (equal (evil-contract 1 2 'inclusive) + '(1 1 inclusive :expanded nil)))) + (ert-info ("Don't mind positions' order when contracting") + (should (equal (evil-contract 6 2 'inclusive) + '(2 5 inclusive :expanded nil)))) + (ert-info ("Measure as one more than the difference") + (should (string= (evil-describe 1 1 'inclusive) + "1 character")) + (should (string= (evil-describe 5 2 'inclusive) + "4 characters"))))) + +(ert-deftest evil-test-line-type () + "Expand the `line' type" + :tags '(evil type) + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (let* ((first-line 1) + (second-line (progn + (forward-line) + (point))) + (third-line (progn + (forward-line) + (point)))) + (ert-info ("Expand to the whole first line") + (should (equal (evil-expand first-line first-line 'line) + (list first-line second-line 'line :expanded t))) + (should (string= (evil-describe first-line first-line 'line) + "1 line"))) + (ert-info ("Expand to the two first lines") + (should (equal (evil-expand first-line second-line 'line) + (list first-line third-line 'line :expanded t))) + (should (string= (evil-describe first-line second-line 'line) + "2 lines")))))) + +(ert-deftest evil-test-block-type () + "Expand and contract the `block' type" + :tags '(evil type) + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (let* ((first-line 1) + (second-line (progn + (forward-line) + (point))) + (third-line (progn + (forward-line) + (point)))) + (ert-info ("Expand to a 1x1 block") + (should (equal (evil-expand 1 1 'block) + (list 1 2 'block :expanded t))) + (should (string= (evil-describe 1 1 'block) + "1 row and 1 column"))) + (ert-info ("Expand to a 2x1 block") + (should (equal (evil-expand first-line second-line 'block) + (list first-line (1+ second-line) 'block :expanded t))) + (should (string= (evil-describe first-line second-line 'block) + "2 rows and 1 column"))) + (ert-info ("Expand to a 3x2 block") + (should (equal (evil-expand first-line (1+ third-line) 'block) + (list first-line (1+ (1+ third-line)) + 'block :expanded t))) + (should (string= (evil-describe first-line (1+ third-line) 'block) + "3 rows and 2 columns"))) + (ert-info ("Contract to a 0x0 rectangle") + (should (equal (evil-contract 1 2 'block) + (list 1 1 'block :expanded nil)))) + (ert-info ("Contract to a 2x0 rectangle") + (should (equal (evil-contract first-line (1+ second-line) 'block) + (list first-line second-line 'block :expanded nil)))) + (ert-info ("Contract to a 3x1 rectangle") + (should (equal (evil-contract first-line (1+ (1+ third-line)) 'block) + (list first-line (1+ third-line) + 'block :expanded nil))))))) + +(ert-deftest evil-test-type-transform () + "Test `evil-transform'" + :tags '(evil type) + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (ert-info ("Return positions unchanged when passed nil \ +for TYPE or TRANSFORM") + (should (equal (evil-transform nil 1 2 'block) + '(1 2 block))) + (should (equal (evil-transform :expand 1 2 nil) + '(1 2))) + (should (equal (evil-transform nil 1 2 nil) + '(1 2)))) + (ert-info ("Accept markers, but return positions") + (should (equal (evil-transform :expand + (move-marker (make-marker) 1) 1 + 'inclusive) + '(1 2 inclusive :expanded t))) + (should (equal (evil-transform nil (move-marker (make-marker) 1) 2 + nil) + '(1 2)))))) + +(ert-deftest evil-test-type-modifiers () + "Test type modifiers like \"dv}\"" + :tags '(evil type) + (ert-info ("Change `inclusive' motions to `exclusive'") + (evil-test-buffer + "[A]bove some line" + ("dve") + "[e] some line")) + (ert-info ("Change `exclusive' motions to `inclusive'") + (evil-test-buffer + "Above [s]ome line + +Below some empty line" + ("dv}") + "Above[ ] +Below some empty line")) + (ert-info ("Change type to `line'") + (evil-test-buffer + "Above [s]ome line + +Below some empty line" + ("dV}") + "Below [s]ome empty line"))) + +;;; Insertion + +(ert-deftest evil-test-insert () + "Test `evil-insert'" + :tags '(evil insert) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save" + ("ievil rulz " [escape]) + ";; evil rulz[ ]This buffer is for notes you don't want to save")) + +(ert-deftest evil-test-append () + "Test `evil-append'" + :tags '(evil insert) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save" + ("aevil rulz " [escape]) + ";; Tevil rulz[ ]his buffer is for notes you don't want to save")) + +(ert-deftest evil-test-visual-insert () + "Test `evil-insert' in Visual state." + :tags '(evil insert) + (ert-info ("Repeat insert over empty lines") + (evil-test-buffer :visual line "<\n\n[]>" + ("IX" [escape]) + "X\nX\nX"))) + +(ert-deftest evil-test-visual-append () + "Test `evil-append' in Visual state." + :tags '(evil insert) + (evil-test-buffer " bar" + ("A_evil rulz " [escape]) + "foo_evil rulz[ ] bar")) + +(ert-deftest evil-test-open-above () + "Test `evil-open-above'" + :tags '(evil insert) + (evil-test-buffer + ";; This buffer is for notes you don't want to save, +\[;]; and for Lisp evaluation." + ("Oabc\ndef" [escape]) + ";; This buffer is for notes you don't want to save, +abc +de[f] +;; and for Lisp evaluation.") + (ert-info ("Open empty line") + (evil-test-buffer + "(let (var)\n [t]est)\n" + (emacs-lisp-mode) + ("O" [escape]) + "(let (var)\n[\n] test)\n")) + (ert-info ("Open non-empty line") + (evil-test-buffer + "(let (var)\n [t]est)\n" + (emacs-lisp-mode) + ("Odo-it" [escape]) + "(let (var)\n do-i[t]\n test)\n"))) + +(ert-deftest evil-test-open-below () + "Test `evil-open-below'" + :tags '(evil insert) + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("oabc\ndef" [escape]) + ";; This buffer is for notes you don't want to save, +abc +de[f] +;; and for Lisp evaluation.") + (ert-info ("Open empty line") + (evil-test-buffer + "[(]let (var)\n test)\n" + (emacs-lisp-mode) + ("o" [escape]) + "(let (var)\n[\n] test)\n")) + (ert-info ("Open non-empty line") + (evil-test-buffer + "[(]let (var)\n test)\n" + (emacs-lisp-mode) + ("odo-it" [escape]) + "(let (var)\n do-i[t]\n test)\n")) + (let ((evil-auto-indent t)) + (ert-info ("With count") + (evil-test-buffer + "[(]and a\n c)\n" + (emacs-lisp-mode) + ("3ob" [escape]) + "(and a\n b\n b\n [b]\n c)\n")))) + +(ert-deftest evil-test-open-below-folded () + "Test `evil-open-below' on folded lines" + :tags '(evil insert) + (evil-test-buffer + "[l]ine1\n\n(let ()\n var)\n\nlast line\n" + (emacs-lisp-mode) + (hs-minor-mode 1) + ("zm2joABC" [escape]) + "line1\n\n(let ()\n var)\nAB[C]\n\nlast line\n")) + +(ert-deftest evil-test-insert-line () + "Test `evil-insert-line'" + :tags '(evil insert) + (evil-test-buffer "foo [b]ar" + ("Ievil rulz " [escape]) + "evil rulz[ ]foo bar") + (ert-info ("With count") + (evil-test-buffer "foo [b]ar" + ("2Ievil rulz " [escape]) + "evil rulz evil rulz[ ]foo bar"))) + +(ert-deftest evil-test-append-line () + "Test `evil-append-line'" + :tags '(evil insert) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save" + ("Aevil rulz " [escape]) + ";; This buffer is for notes you don't want to saveevil rulz[ ]")) + +(ert-deftest evil-test-insert-digraph () + "Test `evil-insert-digraph'" + :tags '(evil insert) + (ert-info ("Predefined digraph") + (evil-test-buffer + ("i\C-kae") + "æ[]")) + (ert-info ("Custom digraph") + (let ((evil-digraphs-table-user '(((?a ?o) . ?å)))) + (evil-test-buffer + ("i\C-kao") + "å[]"))) + (ert-info ("Digraph in replace state") + (evil-test-buffer + "ab[c]defgh" + ("R\C-kc,") + "abç[d]efgh") + (ert-info ("with count") + (evil-test-buffer + "abc[d]efgh" + ("R\C-u3\C-kd*") + "abcδδδ[g]h")))) + +(ert-deftest evil-test-track-insertion () + "Test that the text of the previous insertion is tracked." + (ert-info ("Moving the cursor starts a new insertion") + (evil-test-buffer + ("iabc" [left] "xxx" [escape] "o\C-a") + "abxxxc\nxxx")) + (ert-info ("Autoindentation should be excluded") + (evil-test-buffer "\(setq x" + (emacs-lisp-mode) + ("o0\)" [escape] "o\C-a") + "(setq x + 0) +0\)"))) + +;;; Repeat system + +(ert-deftest evil-test-normalize-repeat-info () + "Test `evil-normalize-repeat-info'" + :tags '(evil repeat) + (ert-info ("Single array") + (should (equal (evil-normalize-repeat-info + '("abc")) + '([?a ?b ?c]))) + (should (equal (evil-normalize-repeat-info + '("\M-f")) + (list (kbd "M-f"))))) + (ert-info ("Single symbol") + (should (equal (evil-normalize-repeat-info + '(SYM)) + '(SYM)))) + (ert-info ("Arrays only") + (should (equal (evil-normalize-repeat-info + '("abc" [XX YY] "def")) + '([?a ?b ?c XX YY ?d ?e ?f])))) + (ert-info ("Several symbols") + (should (equal (evil-normalize-repeat-info + '(BEG MID END)) + '(BEG MID END)))) + (ert-info ("Arrays with symbol at the beginning") + (should (equal (evil-normalize-repeat-info + '(BEG "abc" [XX YY] "def")) + '(BEG [?a ?b ?c XX YY ?d ?e ?f])))) + (ert-info ("Arrays with symbol at the end") + (should (equal (evil-normalize-repeat-info + '("abc" [XX YY] "def" END)) + '([?a ?b ?c XX YY ?d ?e ?f] END)))) + (ert-info ("Arrays with symbol in the middle") + (should (equal (evil-normalize-repeat-info + '("abc" [XX YY] MID "def" )) + '([?a ?b ?c XX YY] MID [?d ?e ?f])))) + (ert-info ("Concatenate arrays with several symbols") + (should (equal (evil-normalize-repeat-info + '(BEG "abc" [XX YY] MID "def" END)) + '(BEG [?a ?b ?c XX YY] MID [?d ?e ?f] END))))) + +(defun evil-test-repeat-info (keys &optional recorded) + "Execute a sequence of keys and verify that `evil-repeat-ring' +records them correctly. KEYS is the sequence of keys to execute. +RECORDED is the expected sequence of recorded events. +If nil, KEYS is used." + (execute-kbd-macro keys) + (should (equal (evil-normalize-repeat-info (ring-ref evil-repeat-ring 0)) + (list (vconcat (or recorded keys)))))) + +(ert-deftest evil-test-normal-repeat-info-simple-command () + "Save key-sequence after simple editing command in Normal state" + :tags '(evil repeat) + (evil-test-buffer + "[T]his is a test buffer" + (ert-info ("Call simple command without count") + (evil-test-repeat-info "x")) + (ert-info ("Call simple command with count 3") + (evil-test-repeat-info "3x")))) + +(ert-deftest evil-test-normal-repeat-info-char-command () + "Save key-sequence after editing command with character in Normal state" + :tags '(evil repeat) + (evil-test-buffer + "[T]his is a test buffer" + (ert-info ("Call command with character argument without count") + (evil-test-repeat-info "r5")) + (ert-info ("Call command with character argument with count 12") + (evil-test-repeat-info "12rX")))) + +(ert-deftest evil-test-insert-repeat-info () + "Save key-sequence after Insert state" + :tags '(evil repeat) + (evil-test-buffer + (ert-info ("Insert text without count") + (evil-test-repeat-info (vconcat "iABC" [escape]))) + (ert-info ("Insert text with count 42") + (evil-test-repeat-info (vconcat "42iABC" [escape]))))) + +(ert-deftest evil-test-repeat () + "Repeat several editing commands" + :tags '(evil repeat) + (ert-info ("Repeat replace") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save" + ("rX") + "[X]; This buffer is for notes you don't want to save" + ([right right] ".") + "X;[X]This buffer is for notes you don't want to save")) + (ert-info ("Repeat replace with count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save" + ("2rX") + "X[X] This buffer is for notes you don't want to save" + ([right right] ".") + "XX X[X]is buffer is for notes you don't want to save")) + (ert-info ("Repeat replace without count with a new count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save" + ("rX") + "[X]; This buffer is for notes you don't want to save" + ([right right] "13.") + "X;XXXXXXXXXXXX[X]is for notes you don't want to save")) + (ert-info ("Repeat replace with count replacing original count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save" + ("10rX") + "XXXXXXXXX[X]ffer is for notes you don't want to save" + ([right right] "20.") + "XXXXXXXXXXfXXXXXXXXXXXXXXXXXXX[X] don't want to save")) + (ert-info ("Repeat movement in Insert state") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save" + ("i(\M-f)" [escape]) + ";; (This[)] buffer is for notes you don't want to save" + ("w.") + ";; (This) (buffer[)] is for notes you don't want to save")) + (ert-info ("Repeat search motion with offset") + (evil-select-search-module 'evil-search-module 'evil-search) + (evil-test-buffer + "[f]irst, second, third, fourth" + ("d/, /e" [return]) + "[s]econd, third, fourth" + ("2.") + "[f]ourth"))) + +(ert-deftest evil-test-repeat-register () + "Test repeating a register command." + :tags '(evil repeat) + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4\n" + ("\"addyy\"aP") + "[l]ine 1\nline 2\nline 3\nline 4\n" + (".") + "[l]ine 1\nline 1\nline 2\nline 3\nline 4\n")) + +(ert-deftest evil-test-repeat-numeric-register () + "Test repeating a command with a numeric register." + :tags '(evil repeat) + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4\nline 5\n" + ("dd...") + "[l]ine 5\n" + ("\"1P") + "[l]ine 4\nline 5\n" + (".") + "[l]ine 3\nline 4\nline 5\n" + (".") + "[l]ine 2\nline 3\nline 4\nline 5\n" + (".") + "[l]ine 1\nline 2\nline 3\nline 4\nline 5\n")) + +(ert-deftest evil-test-cmd-replace-char () + "Calling `evil-replace-char' should replace characters" + :tags '(evil repeat) + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save" + ("r5") + "[5]; This buffer is for notes you don't want to save" + ("3rX") + "XX[X]This buffer is for notes you don't want to save") + (ert-info ("Replace digraph") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save" + ("r e'") + "[é]; This buffer is for notes you don't want to save" + ("3r c*") + "ξξ[ξ]This buffer is for notes you don't want to save") + (ert-info ("Repeat replace digraph") + (evil-test-buffer + "ab[c]defghijkl" + ("r W*") + "ab[Ω]defghijkl" + ("fg" "2.") + "abΩdefΩ[Ω]ijkl"))) + (ert-info ("Replacing \\n should insert only one newline") + (evil-test-buffer + "(setq var xxx [y]yy zzz)\n" + (emacs-lisp-mode) + (setq indent-tabs-mode nil) + ("2r\n") + "(setq var xxx \n [y] zzz)\n"))) + +(ert-deftest evil-test-insert-with-count () + "Test `evil-insert' with repeat count" + :tags '(evil repeat) + (evil-test-buffer + ";; [T]his buffer is for notes" + ("2ievil rulz " [escape]) + ";; evil rulz evil rulz[ ]This buffer is for notes")) + +(ert-deftest evil-test-repeat-insert () + "Test repeating of `evil-insert'" + :tags '(evil repeat) + (ert-info ("Repeat insert") + (evil-test-buffer + "[;]; This buffer is for notes" + ("iABC" [escape]) + "AB[C];; This buffer is for notes" + ("..") + "ABABAB[C]CC;; This buffer is for notes")) + (ert-info ("Repeat insert with count") + (evil-test-buffer + "[;]; This buffer is for notes" + ("2iABC" [escape]) + "ABCAB[C];; This buffer is for notes" + ("..") + "ABCABABCABABCAB[C]CC;; This buffer is for notes")) + (ert-info ("Repeat insert with repeat count") + (evil-test-buffer + "[;]; This buffer is for notes" + ("iABC" [escape]) + "AB[C];; This buffer is for notes" + ("11.") + "ABABCABCABCABCABCABCABCABCABCABCAB[C]C;; This buffer is for notes")) + (ert-info ("Repeat insert with count with repeat with count") + (evil-test-buffer + "[;]; This buffer is for notes" + ("10iABC" [escape]) + "ABCABCABCABCABCABCABCABCABCAB[C];; This buffer is for notes" + ("11.") + "ABCABCABCABCABCABCABCABCABCABABCABCABCABCABCABCABCABCABCABCAB[C]C;; \ +This buffer is for notes"))) + +(ert-deftest evil-test-repeat-error () + "Test whether repeat returns to normal state in case of an error." + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + ("ixxx" [down] [down] [left] [left] [left] "yyy" [escape]) + "xxxline 1\nline 2\nyy[y]line 3\nline 4" + (should-error (execute-kbd-macro "j^.")) + (should (evil-normal-state-p)) + ("^") + "xxxline 1\nline 2\nyyyline 3\n[x]xxline 4")) + +(ert-deftest evil-test-quoted-insert () + "Test evil-quoted-insert in replace state." + (ert-info ("Simple replace C-v") + (evil-test-buffer + "ab[c]defg" + ("R\C-vx") + "abx[d]efg")) + (ert-info ("Control char replace C-v") + (evil-test-buffer + "ab[c]defg" + ("R\C-v\C-g") + "ab[d]efg")) + (ert-info ("C-v with count") + (evil-test-buffer + "ab[c]defg" + ("R\C-u3\C-vx") + "abxxx[f]g")) + (ert-info ("C-v with count near eol") + (evil-test-buffer + "abcde[f]g" + ("R\C-u3\C-vx") + "abcdexxx[]")) + (ert-info ("C-v in replace can be backspaced") + (evil-test-buffer + "ab[c]defg" + ("R\C-u3\C-vx" [backspace]) + "abxx[e]fg"))) + +(ert-deftest evil-test-repeat-quoted-insert () + "Test whether `quoted-insert' can be repeated." + (ert-info ("Insert C-v") + (evil-test-buffer + "lin[e] 1\nline 2\nline 3\n" + ("i\C-v\C-v" [escape]) + "lin[]e 1\nline 2\nline 3\n")) + (ert-info ("Insert ESC") + (evil-test-buffer + "lin[e] 1\nline 2\nline 3\n" + ("i\C-v" [escape escape]) + "lin[]e 1\nline 2\nline 3\n")) + (ert-info ("Block insert C-v") + (evil-test-buffer + "lin[e] 1\nline 2\nline 3\n" + ("gg\C-vGI\C-v\C-v" [escape]) + "[]line 1\nline 2\nline 3\n"))) + +(ert-deftest evil-test-insert-vcount () + "Test `evil-insert' with vertical repeating" + :tags '(evil repeat) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. + +;; Below the empty line." + (define-key evil-normal-state-local-map "i" + #'(lambda (count) + (interactive "p") + (evil-insert count 5))) + ("2iABC" [escape]) + "\ +;; ABCAB[C]This buffer is for notes you don't want to save. +;; ABCABCIf you want to create a file, visit that file with C-x C-f, +;; ABCABCthen enter the text in that file's own buffer. + ABCABC +;; ABCABCBelow the empty line.")) + +(ert-deftest evil-test-append-with-count () + "Test `evil-append' with repeat count" + :tags '(evil repeat) + (evil-test-buffer + ";; [T]his buffer is for notes" + ("2aevil rulz " [escape]) + ";; Tevil rulz evil rulz[ ]his buffer is for notes")) + +(ert-deftest evil-test-repeat-append () + "Test repeating of `evil-append'" + :tags '(evil repeat) + (ert-info ("Repeat insert") + (evil-test-buffer + "[;]; This buffer is for notes" + ("aABC" [escape]) + ";AB[C]; This buffer is for notes" + ("..") + ";ABCABCAB[C]; This buffer is for notes")) + (ert-info ("Repeat insert with count") + (evil-test-buffer + "[;]; This buffer is for notes" + ("2aABC" [escape]) + ";ABCAB[C]; This buffer is for notes" + ("..") + ";ABCABCABCABCABCAB[C]; This buffer is for notes")) + (ert-info ("Repeat insert with repeat count") + (evil-test-buffer + "[;]; This buffer is for notes" + ("aABC" [escape]) + ";AB[C]; This buffer is for notes" + ("11.") + ";ABCABCABCABCABCABCABCABCABCABCABCAB[C]; This buffer is for notes")) + (ert-info ("Repeat insert with count with repeat with count") + (evil-test-buffer + "[;]; This buffer is for notes" + ("10aABC" [escape]) + ";ABCABCABCABCABCABCABCABCABCAB[C]; This buffer is for notes" + ("11.") + ";ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCAB[C]; \ +This buffer is for notes"))) + +(ert-deftest evil-test-append-vcount () + "Test `evil-append' with vertical repeating" + :tags '(evil repeat) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. + +;; Below the empty line." + (define-key evil-normal-state-local-map "a" + #'(lambda (count) + (interactive "p") + (evil-append count 5))) + ("2aABC" [escape]) + "\ +;; TABCAB[C]his buffer is for notes you don't want to save. +;; IABCABCf you want to create a file, visit that file with C-x C-f, +;; tABCABChen enter the text in that file's own buffer. + ABCABC +;; BABCABCelow the empty line.")) + +(ert-deftest evil-test-open-above-with-count () + "Test `evil-open-above' with repeat count" + :tags '(evil repeat) + (evil-test-buffer + ";; This buffer is for notes you don't want to save, +\[;]; and for Lisp evaluation." + ("2Oevil\nrulz" [escape]) + ";; This buffer is for notes you don't want to save, +evil\nrulz\nevil\nrul[z] +;; and for Lisp evaluation.")) + +(ert-deftest evil-test-repeat-open-above () + "Test repeating of `evil-open-above'" + :tags '(evil repeat) + (ert-info ("Repeat insert") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save." + ("Oevil\nrulz" [escape]) + "evil\nrul[z] +;; This buffer is for notes you don't want to save." + ("..") + "evil\nevil\nevil\nrul[z]\nrulz\nrulz +;; This buffer is for notes you don't want to save.")) + (ert-info ("Repeat insert with count") + (evil-test-buffer + ";; This buffer is for notes you don't want to save." + ("2Oevil\nrulz" [escape]) + "evil\nrulz\nevil\nrul[z] +;; This buffer is for notes you don't want to save." + ("..") + "evil\nrulz\nevil\nevil\nrulz\nevil\nevil\nrulz\nevil\nrul[z]\nrulz\nrulz +;; This buffer is for notes you don't want to save.")) + (ert-info ("Repeat insert with repeat count") + (evil-test-buffer + ";; This buffer is for notes you don't want to save." + ("Oevil\nrulz" [escape]) + "evil\nrul[z]\n;; This buffer is for notes you don't want to save." + ("2.") + "evil\nevil\nrulz\nevil\nrul[z]\nrulz +;; This buffer is for notes you don't want to save.")) + (ert-info ("Repeat insert with count with repeat with count") + (evil-test-buffer + ";; This buffer is for notes you don't want to save." + ("2Oevil\nrulz" [escape]) + "evil\nrulz\nevil\nrul[z] +;; This buffer is for notes you don't want to save." + ("3.") + "evil\nrulz\nevil\nevil\nrulz\nevil\nrulz\nevil\nrul[z]\nrulz +;; This buffer is for notes you don't want to save."))) + +(ert-deftest evil-test-open-below-with-count () + "Test insertion of `evil-open-below' with repeat count" + :tags '(evil repeat) + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("2oevil\nrulz" [escape]) + ";; This buffer is for notes you don't want to save, +evil\nrulz\nevil\nrul[z] +;; and for Lisp evaluation.")) + +(ert-deftest evil-test-repeat-open-below () + "Test repeating `evil-open-below'" + :tags '(evil repeat) + (ert-info ("Repeat insert") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("oevil\nrulz" [escape]) + ";; This buffer is for notes you don't want to save, +evil\nrul[z]\n;; and for Lisp evaluation." + ("..") + ";; This buffer is for notes you don't want to save, +evil\nrulz\nevil\nrulz\nevil\nrul[z] +;; and for Lisp evaluation.")) + (ert-info ("Repeat insert with count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("2oevil\nrulz" [escape]) + ";; This buffer is for notes you don't want to save, +evil\nrulz\nevil\nrul[z] +;; and for Lisp evaluation." + ("..") + ";; This buffer is for notes you don't want to save, +evil\nrulz\nevil\nrulz\nevil\nrulz\nevil\nrulz\nevil\nrulz\nevil\nrul[z] +;; and for Lisp evaluation.")) + (ert-info ("Repeat insert with repeat count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("oevil\nrulz" [escape]) + ";; This buffer is for notes you don't want to save, +evil\nrul[z]\n;; and for Lisp evaluation." + ("2.") + ";; This buffer is for notes you don't want to save, +evil\nrulz\nevil\nrulz\nevil\nrul[z] +;; and for Lisp evaluation.")) + (ert-info ("Repeat insert with count with repeat with count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("2oevil\nrulz" [escape]) + ";; This buffer is for notes you don't want to save, +evil\nrulz\nevil\nrul[z] +;; and for Lisp evaluation." + ("3.") + ";; This buffer is for notes you don't want to save, +evil\nrulz\nevil\nrulz\nevil\nrulz\nevil\nrulz\nevil\nrul[z] +;; and for Lisp evaluation."))) + +(ert-deftest evil-test-repeat-insert-line () + "Test repeating of `evil-insert-line'" + :tags '(evil repeat) + (ert-info ("Repeat insert") + (evil-test-buffer + ";; This buffer is for note[s]" + ("IABC" [escape]) + "AB[C];; This buffer is for notes" + ("..") + "AB[C]ABCABC;; This buffer is for notes")) + (ert-info ("Repeat insert with count") + (evil-test-buffer + ";; This buffer is for note[s]" + ("2IABC" [escape]) + "ABCAB[C];; This buffer is for notes" + ("..") + "ABCAB[C]ABCABCABCABC;; This buffer is for notes")) + (ert-info ("Repeat insert with repeat count") + (evil-test-buffer + ";; This buffer is for note[s]" + ("IABC" [escape]) + "AB[C];; This buffer is for notes" + ("11.") + "ABCABCABCABCABCABCABCABCABCABCAB[C]ABC;; This buffer is for notes")) + (ert-info ("Repeat insert with count with repeat with count") + (evil-test-buffer + ";; This buffer is for note[s]" + ("10IABC" [escape]) + "ABCABCABCABCABCABCABCABCABCAB[C];; This buffer is for notes" + ("11.") + "ABCABCABCABCABCABCABCABCABCABCAB[C]ABCABCABCABCABCABCABCABCABCABC;; This buffer is for notes"))) + +(ert-deftest evil-test-insert-line-vcount () + "Test `evil-insert-line' with vertical repeating" + :tags '(evil repeat) + (evil-test-buffer + "int[ ]main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + (define-key evil-normal-state-local-map "I" + #'(lambda (count) + (interactive "p") + (evil-insert-line count 4))) + ("2IABC" [escape]) + "ABCABCint main(int argc, char** argv) +ABCABC{ + ABCABCprintf(\"Hello world\\n\"); + ABCABCreturn EXIT_SUCCESS; +}")) + +(ert-deftest evil-test-append-line-with-count () + "Test `evil-append-line' with repeat count" + :tags '(evil repeat) + (evil-test-buffer + ";; [T]his buffer is for notes." + ("2Aevil rulz " [escape]) + ";; This buffer is for notes.evil rulz evil rulz[ ]")) + +(ert-deftest evil-test-repeat-append-line () + "Test repeating of `evil-append-line'" + :tags '(evil repeat) + (ert-info ("Repeat insert") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("AABC" [escape]) + ";; This buffer is for notes.AB[C]" + ("..") + ";; This buffer is for notes.ABCABCAB[C]")) + (ert-info ("Repeat insert with count") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("2AABC" [escape]) + ";; This buffer is for notes.ABCAB[C]" + ("..") + ";; This buffer is for notes.ABCABCABCABCABCAB[C]")) + (ert-info ("Repeat insert with repeat count") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("AABC" [escape]) + ";; This buffer is for notes.ABC" + ("11.") + ";; This buffer is for notes.ABCABCABCABCABCABCABCABCABCABCABCAB[C]")) + (ert-info ("Repeat insert with count with repeat with count") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("10AABC" [escape]) + ";; This buffer is for notes.ABCABCABCABCABCABCABCABCABCAB[C]" + ("11.") + ";; This buffer is for notes.ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCAB[C]")) + (ert-info ("Repeat append over empty lines") + (evil-test-buffer + "" + ("i" [return] [return] [return] [return] [return] [return] [escape] "gg\C-vGAX" [escape]) + "X\nX\nX\nX\nX\nX\n"))) + +(ert-deftest evil-test-append-line-vcount () + "Test `evil-append-line' with vertical repeating" + :tags '(evil repeat) + (evil-test-buffer + "int[ ]main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + (define-key evil-normal-state-local-map "A" + #'(lambda (count) + (interactive "p") + (evil-append-line count 4))) + ("2AABC" [escape]) + "int main(int argc, char** argv)ABCAB[C] +{ABCABC + printf(\"Hello world\\n\");ABCABC + return EXIT_SUCCESS;ABCABC +}")) + +(ert-deftest evil-test-repeat-by-change () + "Test repeating by tracking changes for completion commands" + :tags '(evil repeat) + (let ((line-move-visual nil) + (change (evil-define-command nil () + :repeat change + (interactive) + (delete-char 5) + (insert "BEGIN\n") + (save-excursion + (insert "\nEND\n"))))) + (evil-test-buffer + ";; [T]his buffer is for notes." + (define-key evil-insert-state-local-map (kbd "C-c C-p") change) + ("iABC " (kbd "C-c C-p") "BODY" [escape]) + ";; ABC BEGIN +BOD[Y] +END +buffer is for notes." + (".") + ";; ABC BEGIN +BODABC BEGIN +BOD[Y] +END + +buffer is for notes."))) + +(ert-deftest evil-test-repeat-kill-buffer () + "Test safe-guard preventing buffers from being deleted +when repeating a command" + :tags '(evil repeat) + (ert-info ("Test killing works for direct calls \ +to `evil-execute-repeat-info'") + (evil-test-buffer + "[;]; This buffer is for notes." + (setq evil-repeat-ring (make-ring 10)) + (ring-insert evil-repeat-ring '((kill-buffer nil))) + (evil-execute-repeat-info (ring-ref evil-repeat-ring 0)) + (should-not (looking-at ";; This")))) + (ert-info ("Verify an error is raised when using \ +the `evil-repeat' command") + (evil-test-buffer + "[;]; This buffer is for notes." + (setq evil-repeat-ring (make-ring 10)) + (ring-insert evil-repeat-ring '((kill-buffer nil))) + (evil-execute-repeat-info (ring-ref evil-repeat-ring 0)) + (should-error (call-interactively #'evil-repeat))))) + +(ert-deftest evil-test-repeat-pop () + "Test `repeat-pop'." + :tags '(evil repeat) + (ert-info ("Test repeat-pop") + (evil-test-buffer + ";; [T]his buffer is for notes." + (setq evil-repeat-ring (make-ring 10)) + ("iABC" [escape] "aXYZ" [escape]) + ";; ABCXY[Z]This buffer is for notes." + (".") + ";; ABCXYZXY[Z]This buffer is for notes.")) + (ert-info ("Test repeat-pop") + (evil-test-buffer + ";; [T]his buffer is for notes." + (setq evil-repeat-ring (make-ring 10)) + ("iABC" [escape] "aXYZ" [escape]) + ";; ABCXY[Z]This buffer is for notes." + ("." (kbd "C-.")) + ";; ABCXYAB[C]ZThis buffer is for notes.")) + (ert-info ("Test repeat-pop-next") + (evil-test-buffer + ";; [T]his buffer is for notes." + (setq evil-repeat-ring (make-ring 10)) + ("iABC" [escape] "aXYZ" [escape]) + ";; ABCXY[Z]This buffer is for notes." + ("." (kbd "C-.") (kbd "M-.")) + ";; ABCXYZXY[Z]This buffer is for notes.")) + (ert-info ("Test repeat-pop after non-change") + (evil-test-buffer + ";; [T]his buffer is for notes." + (setq evil-repeat-ring (make-ring 10)) + ("iABC" [escape] "a" [escape] "aXYZ" [escape]) + ";; ABCXY[Z]This buffer is for notes." + ("." (kbd "C-.") (kbd "C-.")) + ";; ABCXYAB[C]ZThis buffer is for notes."))) + +(ert-deftest evil-test-ESC-repeat-normal-state () + "Test if ESC is not been recorded in normal state." + :tags '(evil repeat) + (ert-info ("Test normal ESC") + (evil-test-buffer + ";;[ ]This buffer is for notes." + (setq evil-repeat-ring (make-ring 10)) + (should (= (ring-length evil-repeat-ring) 0)) + ("aABC" [escape]) + ";; AB[C]This buffer is for notes." + (should (= (ring-length evil-repeat-ring) 1)) + (".") + ";; ABCAB[C]This buffer is for notes." + ([escape]) + (should (= (ring-length evil-repeat-ring) 1)) + (".") + ";; ABCABCAB[C]This buffer is for notes."))) + +(ert-deftest evil-test-abort-operator-repeat () + "Test if ESC in operator-state cancels recording of repeation." + :tags '(evil repeat) + (let ((inhibit-quit t)) + (ert-info ("Test ESC") + (evil-test-buffer + ";;[ ]This buffer is for notes." + (setq evil-repeat-ring (make-ring 10)) + (should (= (ring-length evil-repeat-ring) 0)) + ("aABC" [escape]) + ";; AB[C]This buffer is for notes." + (should (= (ring-length evil-repeat-ring) 1)) + (".") + ";; ABCAB[C]This buffer is for notes." + ("d" [escape]) + (should (= (ring-length evil-repeat-ring) 1)) + (".") + ";; ABCABCAB[C]This buffer is for notes.")))) + +(ert-deftest evil-test-repeat-with-find-char () + "Ensure that repeating find-char commands doesn't change `evil-last-find'" + :tags '(evil repeat) + (evil-test-buffer + "[b]ar baz bat" + ("dfa" "fb") + "r [b]az bat" + (".") + "r [z] bat" + (";") + "r z [b]at")) + +(ert-deftest evil-test-repeat-visual-char () + "Test repeat of character visual mode command." + :tags '(evil repeat) + (ert-info ("Test repeat on same line") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("v3lcABC" [escape]) + ";; AB[C] buffer is for notes." + ("ww.") + ";; ABC buffer AB[C]or notes.")) + (ert-info ("Test repeat on several lines") + (evil-test-buffer + ";; This [b]uffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. +" + ("vj^eerX") + ";; This XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXX[X] you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. +" + ("2gg^3w.") + ";; This XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXX you want XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXX[X]en enter the text in that file's own buffer. +"))) + +(ert-deftest evil-test-repeat-visual-line () + "Test repeat of linewise visual mode command." + :tags '(evil repeat) + (ert-info ("Test repeat on several lines") + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter th[e] text in that file's own buffer. + +;; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. +" + ("VkcNew Text" [escape]) + ";; This buffer is for notes you don't want to save. +New Tex[t] + +;; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. +" + ("jj.") + ";; This buffer is for notes you don't want to save. +New Text + +New Tex[t] +;; then enter the text in that file's own buffer. +"))) + +(ert-deftest evil-test-repeat-visual-block () + "Test repeat of block visual mode command." + :tags '(evil repeat) + (ert-info ("Test repeat on several lines") + (evil-test-buffer + ";; This [b]uffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. +;; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. +" + ((kbd "C-v") "3j2lrQ") + ";; This [Q]QQfer is for notes you don't want to save. +;; If yoQQQant to create a file, visit that file with C-x C-f, +;; then QQQer the text in that file's own buffer. +;; This QQQfer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. +" + ("2j3w.") + ";; This QQQfer is for notes you don't want to save. +;; If yoQQQant to create a file, visit that file with C-x C-f, +;; then QQQer the text [Q]QQthat file's own buffer. +;; This QQQfer is for nQQQs you don't want to save. +;; If you want to creatQQQ file, visit that file with C-x C-f, +;; then enter the text QQQthat file's own buffer. +"))) + +(ert-deftest evil-visual-block-append () + "Test appending in visual block." + :tags '(evil visual insert) + (ert-info ("Simple append") + (evil-test-buffer + "l[i]ne 1\nline 2\nline 3\n" + ((kbd "C-v") "jjllAXXX" [escape]) + "lineXX[X] 1\nlineXXX 2\nlineXXX 3\n")) + (ert-info ("Append after empty lines") + (evil-test-buffer + "line 1l[i]ne 1\nline 2\nline 3line 3\n" + (setq indent-tabs-mode nil) + ((kbd "C-v") "jjllAXXX" [escape]) + "line 1lineXX[X] 1\nline 2 XXX\nline 3lineXXX 3\n")) + (ert-info ("Append after empty first line") + (evil-test-buffer + "l[i]ne 1line 1\nline 2\nline 3line 3line 3\n" + (setq indent-tabs-mode nil) + ((kbd "C-v") "jj3feAXXX" [escape]) + "line 1line 1 XX[X]\nline 2 XXX\nline 3line 3lineXXX 3\n")) + (ert-info ("Append after end of lines") + (evil-test-buffer + "line 1l[i]ne 1line 1\nline 2\nline 3line 3\n" + (setq indent-tabs-mode nil) + ((kbd "C-v") "jj$AXXX" [escape]) + "line 1line 1line 1XX[X]\nline 2XXX\nline 3line 3XXX\n"))) + +(ert-deftest evil-visual-block-insert () + "Test inserting (prepending) in visual block." + :tags '(evil visual insert) + (ert-info ("Prepend and repeat") + (evil-test-buffer + "[a]lpha\nbravo\ncharlie\ndelta\necho\nfoxtrot\ngolf" + ("\C-v" "jj" "I" "zulu" [escape]) + "zul[u]alpha\nzulubravo\nzulucharlie\ndelta\necho\nfoxtrot\ngolf" + ("/delta" [return] ".") + "zulualpha\nzulubravo\nzulucharlie\nzul[u]delta\nzuluecho\nzulufoxtrot\ngolf"))) + +(ert-deftest evil-test-repeat-digraph () + "Test repeat of insertion of a digraph." + :tags '(evil digraph repeat) + (evil-test-buffer + "Line with ['] several apostrophes ', yeah." + ("s" (kbd "C-k") "'9" [escape]) + "Line with [’] several apostrophes ', yeah." + ("f'.") + "Line with ’ several apostrophes [’], yeah.")) + +;;; Operators + +(ert-deftest evil-test-indent () + "Test `evil-indent'" + :tags '(evil visual operator) + (evil-test-buffer + :state visual + "< Line with too much indentation.>" + ("=") + "Line with too much indentation.") + (ert-info ("Can repeat evil-indent without errors") + (evil-test-buffer + "[ ]x\n x" + ("==j.") + "x\nx"))) + +(ert-deftest evil-test-keypress-parser () + "Test `evil-keypress-parser'" + :tags '(evil operator) + (evil-test-buffer + :state operator + (ert-info ("Read from the keyboard unless INPUT is given") + (evil-test-buffer + :state operator + (let ((unread-command-events '(?d))) + (should (equal (evil-keypress-parser) + '(evil-delete nil))) + (should (equal (evil-keypress-parser '(?d)) + '(evil-delete nil)))))) + (ert-info ("Read remainder from the keyboard if INPUT is incomplete") + (let ((unread-command-events '(?d))) + (should (equal (evil-keypress-parser '(?2)) + '(evil-delete 2))))) + (ert-info ("Handle counts not starting with zero") + (should (equal (evil-keypress-parser '(?2 ?d)) + '(evil-delete 2))) + (should (equal (evil-keypress-parser '(?2 ?0 ?d)) + '(evil-delete 20))) + (should (equal (evil-keypress-parser '(?2 ?0 ?2 ?d)) + '(evil-delete 202))) + (should (equal (evil-keypress-parser '(?4 ?0 ?4 ?g ??)) + '(evil-rot13 404)))) + (ert-info ("Treat 0 as a motion") + (should (equal + (evil-keypress-parser '(?0)) + '(evil-beginning-of-line nil)))) + (ert-info ("Handle keyboard macros") + (evil-test-buffer + (define-key evil-motion-state-local-map (kbd "W") (kbd "w")) + (should (equal (evil-keypress-parser '(?W)) + '(evil-forward-word-begin nil))))))) + +(ert-deftest evil-test-invert-char () + "Test `evil-invert-char'" + :tags '(evil operator) + (evil-test-buffer + ";; [T]his buffer is for notes." + ("~") + ";; t[h]is buffer is for notes.") + (evil-test-buffer + ";; <[T]his> buffer is for notes." + ("~") + ";; [t]HIS buffer is for notes.") + (evil-test-buffer + :visual block + ";; <[T]his buffer is for notes, +;; and >for Lisp evaluation." + ("~") + ";; [t]HIS buffer is for notes, +;; AND for Lisp evaluation.")) + +(ert-deftest evil-test-rot13 () + "Test `evil-rot13'" + :tags '(evil operator) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("g?" [M-right]) + ";; [G]uvf buffer is for notes you don't want to save.")) + +(ert-deftest evil-test-rot13-with-count () + "Test `evil-rot13' with count argument" + :tags '(evil operator) + (ert-info ("Count before operator") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("2g?" [M-right]) + ";; [G]uvf ohssre is for notes you don't want to save.")) + (ert-info ("Count before motion") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("g?2" [M-right]) + ";; [G]uvf ohssre is for notes you don't want to save.")) + (ert-info ("Count before operator and motion") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("3g?2" [M-right]) + ";; [G]uvf ohssre vf sbe abgrf lbh don't want to save.")) + (ert-info ("Count exceeding buffer boundaries") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("g?200" [right]) + ";; [G]uvf ohssre vf sbe abgrf lbh qba'g jnag gb fnir."))) + +(ert-deftest evil-test-rot13-repeat () + "Test repeating of `evil-rot13'" + :tags '(evil operator) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("g?" [M-right] [M-right]) + ";; Guvf[ ]buffer is for notes you don't want to save." + (".") + ";; Guvf[ ]ohssre is for notes you don't want to save.")) + +(ert-deftest evil-test-rot13-repeat-with-count () + "Test repeating of `evil-rot13' with new count" + :tags '(evil operator) + (ert-info ("Count before operator") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("2g?" [M-right]) + ";; [G]uvf ohssre is for notes you don't want to save." + ("3.") + ";; [T]his buffer vf for notes you don't want to save.")) + (ert-info ("Count before motion") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("g?2" [M-right]) + ";; [G]uvf ohssre is for notes you don't want to save." + ("3.") + ";; [T]his buffer vf for notes you don't want to save.")) + (ert-info ("Count before operator and motion") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("3g?2" [M-right]) + ";; [G]uvf ohssre vf sbe abgrf lbh don't want to save." + ("4.") + ";; [T]his buffer is for abgrf lbh don't want to save."))) + +(ert-deftest evil-test-operator-delete () + "Test deleting text" + :tags '(evil operator) + (ert-info ("Delete characters") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("dl") + ";; [h]is buffer is for notes." + ("d1l") + ";; [i]s buffer is for notes." + ("1dl") + ";; [s] buffer is for notes." + ("1d1l") + ";; [ ]buffer is for notes." + ("d2l") + ";; [u]ffer is for notes." + ("2dl") + ";; [f]er is for notes." + ("d4l") + ";; [i]s for notes." + ("4dl") + ";; [o]r notes." + ("2d2l") + ";; [o]tes.")) + (ert-info ("Delete current line") + (ert-info ("With `evil-start-of-line' `nil'") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("dd") + ";; [a]nd for Lisp evaluation.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("d1d") + ";; [a]nd for Lisp evaluation.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("1dd") + ";; [a]nd for Lisp evaluation.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("1d1d") + ";; [a]nd for Lisp evaluation.")) + (ert-info ("With `evil-start-of-line' `t'") + (let ((evil-start-of-line t)) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("dd") + "[;]; and for Lisp evaluation.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("d1d") + "[;]; and for Lisp evaluation.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("1dd") + "[;]; and for Lisp evaluation.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("1d1d") + "[;]; and for Lisp evaluation.")))) + (ert-info ("Delete two lines") + (ert-info ("With `evil-start-of-line' `nil'") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("d2d") + ";; [t]hen enter the text in that file's own buffer.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("2dd") + ";; [t]hen enter the text in that file's own buffer.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("dj") + ";; [t]hen enter the text in that file's own buffer.") + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; [I]f you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("dk") + ";; [t]hen enter the text in that file's own buffer.")) + (ert-info ("With `evil-start-of-line' `t'") + (let ((evil-start-of-line t)) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("d2d") + "[;]; then enter the text in that file's own buffer.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("2dd") + "[;]; then enter the text in that file's own buffer.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("dj") + "[;]; then enter the text in that file's own buffer.") + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; [I]f you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("dk") + "[;]; then enter the text in that file's own buffer."))))) + +(evil-define-motion evil-test-square-motion (count) + "Test motion for selecting a square." + :type block + (let ((column (current-column))) + (forward-line (1- count)) + (move-to-column (+ column count -1)))) + +(ert-deftest evil-test-yank () + "Test `evil-yank'" + :tags '(evil operator yank) + (ert-info ("Yank characters") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("y2e") + (should (string= (current-kill 0) "This buffer")))) + (ert-info ("Yank lines") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("yj") + (should (string= (current-kill 0) + (buffer-substring (point-min) + (1+ (line-end-position 2))))) + (should (eq (car-safe (get-text-property 0 'yank-handler + (current-kill 0))) + 'evil-yank-line-handler))) + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +\[;]; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("y5j") + (should + (string= (current-kill 0) + (concat (buffer-substring (line-beginning-position 1) + (point-max)) + "\n"))) + (should (eq (car-safe (get-text-property 0 'yank-handler + (current-kill 0))) + 'evil-yank-line-handler)))) + (ert-info ("Yank rectangle") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y3s") + (should (string= (current-kill 0) "Thi\nIf \nthe")) + (should (eq (car-safe (get-text-property 0 'yank-handler + (current-kill 0))) + 'evil-yank-block-handler)))) + (ert-info (":yank, then paste") + (evil-test-buffer + "a\n[b]\nc\nd\n" + (":yank" [return] "p") + "a\nb\nb\nc\nd\n")) + (ert-info (":yank with COUNT") + (evil-test-buffer + "a\n[b]\nc\nd\n" + (":yank 2" [return] "p") + "a\nb\nb\nc\nc\nd\n")) + (ert-info (":yank with COUNT in visual state") + (evil-test-buffer + "a\n\nd\ne\nf\n" + (":yank 3" [return] "p") + "a\nb\nc\nd\ne\nc\nd\ne\nf\n")) + (ert-info (":yank with REGISTER") + (evil-test-buffer + "a\n[b]\nc\nd\n" + (":yank r") ;; yank into the 'r' register + "a\nb\nc\nd\n" + ;; check the 'r' register contains the yanked text + (should (string= (substring-no-properties (evil-get-register ?r)) "b\n")))) + (ert-info (":yank with REGISTER and COUNT") + (evil-test-buffer + "a\n[b]\nc\nd\ne\nf\n" + (":yank r 3") + "a\nb\nc\nd\ne\nf\n" + (should (string= (substring-no-properties (evil-get-register ?r)) "b\nc\nd\n")))) + (ert-info (":yank with range yanks without moving point") + (evil-test-buffer + "[a]\nb\nc\nd\ne\n" + (":4y" [return] "p") + "a\n[d]\nb\nc\nd\ne\n") + (evil-test-buffer + "[a]\nb\nc\nd\ne\n" + (":+4y" [return] "p") + "a\n[e]\nb\nc\nd\ne\n") + (evil-test-buffer + "1\n2\n3\n4\n5\n6\n7\n8\n9\n1[0]" + (":-5,.y") + "1\n2\n3\n4\n5\n6\n7\n8\n9\n1[0]") + (evil-test-buffer + "1\n2\n3\n4\n[5]\n6\n7\n8\n9\n10" + ("my" "G" ":'y,.y") + "1\n2\n3\n4\n5\n6\n7\n8\n9\n[1]0"))) + +(ert-deftest evil-test-yank-line () + "Test `evil-yank-line'" + :tags '(evil operator) + (ert-info ("Yank line with eof being part of visual selection") + (evil-test-buffer + ";; This is line one +;; This is lin[e] two +;; This is line three" + ("v$Yjp") + ";; This is line one +;; This is line two +;; This is line three +[;]; This is line two"))) + +(ert-deftest evil-test-delete () + "Test `evil-delete'" + :tags '(evil operator delete) + (ert-info ("Delete characters") + (evil-test-buffer + ";; This buffer is for notes you don't want to save[.]" + ("x") + ";; This buffer is for notes you don't want to sav[e]" + (goto-char 4) + ";; [T]his buffer is for notes you don't want to save" + ("d2e") + ";; [ ]is for notes you don't want to save" + (should (string= (current-kill 0) "This buffer")) + ("P") + ";; This buffe[r] is for notes you don't want to save")) + (ert-info ("Delete lines") + (ert-info ("With `evil-start-of-line' `nil'") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("2dd") + ";; [t]hen enter the text in that file's own buffer." + ("P") + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("With `evil-start-of-line' `t'") + (let ((evil-start-of-line t)) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("2dd") + "[;]; then enter the text in that file's own buffer." + ("P") + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")))) + (ert-info ("Delete last line") + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; [I]f you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("2dd") + ";; [T]his buffer is for notes you don't want to save.")) + (ert-info ("Delete last empty line") + (evil-test-buffer + "line 1\nline 2\n\n[]" + ("dd") + "line 1\nline 2\n[]")) + (ert-info ("Delete rectangle") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("d3s") + "[T]his buffer is for notes you don't want to save. +If you want to create a file, visit that file with C-x C-f, +then enter the text in that file's own buffer.")) + (ert-info (":delete") + (evil-test-buffer + "a\n[b]\nc\nd\n" + (":delete") + "a\nc\nd\n")) + (ert-info (":delete with COUNT") + (evil-test-buffer + "a\n[b]\nc\nd\n" + (":delete 2") + "a\nd\n")) + (ert-info (":delete with COUNT in visual state") + (evil-test-buffer + "a\n\nd\ne\nf\n" + (":delete 3") + "a\nb\nf\n")) + (ert-info (":delete with REGISTER") + (evil-test-buffer + "a\n[b]\nc\nd\n" + (":delete r") ;; delete into the 'r' register + "a\nc\nd\n" + ;; check the 'r' register contains the deleted text + (should (string= (substring-no-properties (evil-get-register ?r)) "b\n")))) + (ert-info (":delete with REGISTER and COUNT") + (evil-test-buffer + "a\n[b]\nc\nd\ne\nf\n" + (":delete r 3") + "a\ne\nf\n" + (should (string= (substring-no-properties (evil-get-register ?r)) "b\nc\nd\n")))) + (ert-info ("Charwise multiple whole line delete becomes linewise") + (evil-test-buffer + "1\n[2]\n3\n4" + ("d2w") + "1\n[4]"))) + +(ert-deftest evil-test-delete-line () + "Test `evil-delete-line'" + :tags '(evil operator) + (ert-info ("Delete to end of line") + (evil-test-buffer + ";; This buffer is for notes[ ]you don't want to save." + ("D") + ";; This buffer is for note[s]")) + (ert-info ("Act linewise on character selection") + (evil-test-buffer + ";; This is for notes, +and for Lisp evaluation." + ("D") + "and for Lisp [e]valuation.")) + (ert-info ("Act on each line of block selection") + (evil-test-buffer + :visual block + ";; This buffer is for ." + ("D") + ";; This buffer is for[ ] +;; and for Lisp evalua")) + (ert-info ("Yank full block with block selection") + (evil-test-buffer + :visual block + "line1 le3 line3\n" + ("D") + "line1 [l]\nline2 l\nline3 l\n" + ("0P") + "ine1 line1 line1line1 l +ine2 line2 l +ine3 line3 line3 l\n")) + (ert-info ("Delete additional whole lines with count") + (evil-test-buffer + "a[a]a\nbbb\nc\n" + ("2D") + "a\nc\n")) + (ert-info ("Delete line with eof being part of visual selection") + (evil-test-buffer + ";; This is line one +;; This is lin[e] two +;; This is line three" + ("v$D") + ";; This is line one +;; This is line three")) + (ert-info ("Deletion in normal state leaves point in the right place") + (evil-test-buffer + "alpha b[r]avo charlie\ndelta echo foxtrot\ngolf hotel india" + (should-not evil-start-of-line) + ("D") + "alpha [b]\ndelta echo foxtrot\ngolf hotel india" + ("/echo" [return] "C" "newtext" [escape]) + "alpha b\ndelta newtex[t]\ngolf hotel india") + (let ((evil-start-of-line t)) + (evil-test-buffer + "alpha b[r]avo charlie\ndelta echo foxtrot\ngolf hotel india" + ("D") + "alpha [b]\ndelta echo foxtrot\ngolf hotel india" + ("/echo" [return] "C" "newtext" [escape]) + "alpha b\ndelta newtex[t]\ngolf hotel india"))) + (ert-info ("Line deletion in visual state leaves point in the right place") + (evil-test-buffer + "alpha [b]ravo charlie\ndelta echo foxtrot\ngolf hotel india" + (should-not evil-start-of-line) + ("vD") + "delta [e]cho foxtrot\ngolf hotel india" + ("vX") + "golf h[o]tel india") + (let ((evil-start-of-line t)) + (evil-test-buffer + "alpha [b]ravo charlie\ndelta echo foxtrot\ngolf hotel india" + ("vD") + "[d]elta echo foxtrot\ngolf hotel india" + ("vX") + "[g]olf hotel india")))) + +(ert-deftest evil-test-delete-folded () + "Test `evil-delete' on folded lines." + :tags '(evil operator) + (ert-info ("Delete folded lines") + (evil-test-buffer + "[l]ine1\n\n(let ()\n var)\n\n(let ()\n var2)\n" + (emacs-lisp-mode) + (hs-minor-mode 1) + ("zm2jdd") + "line1\n\n[\n](let ()\n var2)\n")) + (ert-info ("Delete folded lines with count") + (evil-test-buffer + "[l]ine1\n\n(let ()\n var)\n\n(let ()\n var2)\n\nlast line\n" + (emacs-lisp-mode) + (hs-minor-mode 1) + ("zm2j3dd") + "line1\n\n[\n]last line\n"))) + +(ert-deftest evil-test-delete-backward-word () + "Test `evil-delete-backward-word' in insert & replace states, and ex command-line." + :tags '(evil) + (ert-info ("evil-delete-backward-word in insert state") + (let ((evil-backspace-join-lines t)) + (evil-test-buffer + "abc def\n ghi j[k]l\n" + ("i" (kbd "C-w")) + "abc def\n ghi [k]l\n" + ((kbd "C-w")) + "abc def\n [k]l\n" + ((kbd "C-w")) + "abc def\n[k]l\n" + ((kbd "C-w")) + "abc def[k]l\n")) + (let (evil-backspace-join-lines) + (evil-test-buffer + "abc def\n[k]l\n" + (should-error (execute-kbd-macro (concat "i" (kbd "C-w")))) + "abc def\n[k]l\n"))) + (ert-info ("evil-delete-backward-word in replace state") + (evil-test-buffer + "alpha bravo [c]harlie delta" + ("R" "one two") + "alpha bravo one two[ ]delta" + ("\C-w") + "alpha bravo one [l]ie delta" + ("\C-w") + "alpha bravo [c]harlie delta" + ("\C-w") + "alpha [b]ravo charlie delta")) + (ert-info ("evil-delete-backward-word in ex command-line") + (evil-test-buffer + "[ ]" + (":normal i" "one-two" (kbd "C-w") (kbd "C-w") [return]) + "on[e]"))) + +(ert-deftest evil-test-visual-X () + "Test `X' in visual state." + :tags '(evil) + (evil-test-buffer + "This is line one\nThis is lin[e] two\nThis is line three" + ("v$oX") + "This is line one\nThis is lin[e] three")) + +(ert-deftest evil-test-delete-back-to-indentation () + "Test `evil-delete-back-to-indentation' in insert & replace states." + :tags '(evil) + (let ((evil-backspace-join-lines t)) + (evil-test-buffer + "abc def\n ghi j[k]l\n" + ("i" (call-interactively #'evil-delete-back-to-indentation)) + "abc def\n [k]l\n" + (left-char 2) + "abc def\n [ ] kl\n" + (call-interactively #'evil-delete-back-to-indentation) + "abc def\n[ ] kl\n" + (call-interactively #'evil-delete-back-to-indentation) + "abc def[ ] kl\n")) + (let (evil-backspace-join-lines) + (evil-test-buffer + "abc def\n[k]l\n" + (should-error + (progn + (execute-kbd-macro "i") + (call-interactively #'evil-delete-back-to-indentation))) + "abc def\n[k]l\n")) + (ert-info ("Delete back to indentation in replace state") + (evil-test-buffer + " alpha [b]ravo charlie" + ("R" "delta") + " alpha delta[ ]charlie" + (evil-delete-back-to-indentation) + " [a]lpha bravo charlie"))) + +(ert-deftest evil-test-change () + "Test `evil-change'" + :tags '(evil operator) + (ert-info ("Change characters") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("c2eABC" [escape]) + ";; AB[C] is for notes you don't want to save." + (should (string= (current-kill 0) "This buffer")) + ("p") + ";; ABCThis buffe[r] is for notes you don't want to save.")) + (ert-info ("Change lines") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("2ccABCLINE\nDEFLINE" [escape]) + "ABCLINE +DEFLIN[E] +;; then enter the text in that file's own buffer." + ("p") + "ABCLINE +DEFLINE +\[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Change last line") + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; [I]f you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("2ccABC" [escape]) + ";; This buffer is for notes you don't want to save. +AB[C]")) + (ert-info ("C changes whole line in visual characterwise and linewise states") + (evil-test-buffer + "Two lines [s]hould suffice +for this test." + ("veC" "all gone!") + "all gone![] +for this test.") + (evil-test-buffer + "Two lines [w]ill be fine +for this test too." + ("VjC" "all gone!") + "all gone![]")) + (ert-info ("C clears the visual blockwise selection, and all text to the right") + (evil-test-buffer + "Two [l]ines will be fine for +the tests here as well." + ("\C-vjeC") + "Two [] +the ")) + (ert-info ("S clears the whole line in normal mode, and all lines touched by visual selection") + (evil-test-buffer + "Two lines [s]hould suffice +for this test." + ("S" "all gone!") + "all gone![] +for this test.") + (evil-test-buffer + "Two lines [s]hould suffice +for this test." + ("vS" "all gone!") + "all gone![] +for this test.") + (evil-test-buffer + "Two lines [s]hould suffice +for this test." + ("VjS" "all gone!") + "all gone![]") + (evil-test-buffer + "Two lines [s]hould suffice +for this test." + ("\C-VjS" "all gone!") + "all gone![]")) + (ert-info ("R behaves the same as S in visual modes") + (evil-test-buffer + "Two lines [s]hould suffice +for this test." + ("vR" "all gone!") + "all gone![] +for this test.")) + (ert-info ("Change rectangle") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("c3sABC" [escape]) + "AB[C]This buffer is for notes you don't want to save. +ABCIf you want to create a file, visit that file with C-x C-f, +ABCthen enter the text in that file's own buffer."))) + +(ert-deftest evil-maybe-remove-spaces-test () + "Test maybe removing (indentation) spaces after some commands when making a clear line." + :tags '(evil operator) + (ert-info ("changing the line and returning to normal mode removes spaces") + (evil-test-buffer + (emacs-lisp-mode) + ("i(one two" [return] "three" [return] "four" [return] "five" [escape] "?three" [return]) + "(one two + [t]hree + four + five" + ("cc" "new line" [escape] "+") + "(one two + new line + [f]our + five" + ("cc" [escape]) + "(one two + new line +[] + five"))) + +(ert-deftest evil-test-change-line () + "Test `evil-change-line'" + :tags '(evil operator) + (ert-info ("Change line with eof being part of visual selection") + (evil-test-buffer + "This is line one +This is lin[e] two +This is line three" + ("v$C") + "This is line one +[] +This is line three"))) + +(ert-deftest evil-test-change-word () + "Test changing words" + :tags '(evil operator) + (ert-info ("Non-word") + (evil-test-buffer + "[;]; This buffer is for notes." + ("cwABC" [escape]) + "AB[C] This buffer is for notes.")) + (ert-info ("Word") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("cwABC" [escape]) + ";; AB[C] buffer is for notes.")) + (ert-info ("Single character") + (evil-test-buffer + "[;] This buffer is for notes." + ("cwABC" [escape]) + "AB[C] This buffer is for notes.")) + (ert-info ("Whitespace") + (evil-test-buffer + "This[ ]is a test\n" + ("cwABC" [escape]) + "ThisAB[C]is a test\n"))) + +(ert-deftest evil-test-join () + "Test `evil-join'" + :tags '(evil join operator) + (ert-info ("Simple") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f." + ("J") + ";; This buffer is for notes you don't want to save.[ ]\ +;; If you want to create a file, visit that file with C-x C-f.")) + (ert-info ("Visual") + (evil-test-buffer + :visual line + "<;; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f.>" + ("J") + ";; This buffer is for notes you don't want to save.[ ]\ +;; If you want to create a file, visit that file with C-x C-f.")) + (ert-info ("Join with count") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (":join 3") + "line 1 line 2 line 3\nline 4")) + (ert-info ("Join with bang and count") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (":join! 3") + "line 1line 2line 3\nline 4")) + (ert-info ("Join with bang and count, exceeding end-of-buffer") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (":join! 10") + "line 1line 2line 3line 4")) + (ert-info ("Join with count 1 should be the same as without count") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (":join 1") + "line 1 line 2\nline 3\nline 4")) + (ert-info ("Join with count 2 should be the same as with count 1") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (":join 2") + "line 1 line 2\nline 3\nline 4")) + (ert-info ("Join with count and single line range") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (":2join 3") + "line 1\nline 2 line 3 line 4")) + (ert-info ("Join with count and range") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (":1,2join 3") + "line 1\nline 2 line 3 line 4")) + (ert-info ("Join with count, range and bang") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (":1,2join! 3") + "line 1\nline 2line 3line 4")) + (ert-info ("Join with range") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (":1,3join") + "line 1 line 2 line 3\nline 4")) + (ert-info ("Join blank line") + (evil-test-buffer + "(when foo\n[]\n bar)" + (emacs-lisp-mode) + ("J") + "(when foo\n [b]ar)"))) + +(ert-deftest evil-test-substitute () + "Test `evil-substitute'" + :tags '(evil operator) + (ert-info ("Simple") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("5sABC" [escape]) + ";; AB[C]buffer is for notes.")) + (ert-info ("On empty line") + (evil-test-buffer + "Above some line +\[] +Below some empty line" + ("5sABC" [escape]) + "Above some line +AB[C] +Below some empty line"))) + +(ert-deftest evil-test-shift () + "Test `evil-shift-right' and `evil-shift-left'." + :tags '(evil operator) + (let ((evil-shift-width 4) + indent-tabs-mode) + (ert-info ("Shift linewise") + (ert-info ("With `evil-start-of-line' `nil'") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\n" + ("Vj>") + "[ ] line 1\n line 2\nline 3\n")) + (ert-info ("With `evil-start-of-line' `t'") + (let ((evil-start-of-line t)) + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\n" + ("Vj>") + " [l]ine 1\n line 2\nline 3\n")))) + (ert-info ("Shift char selection on whole line") + (ert-info ("With `evil-start-of-line' `nil'") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\n" + ("v$>") + " line [1]\nline 2\nline 3\n")) + (ert-info ("With `evil-start-of-line' `t'") + (let ((evil-start-of-line t)) + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\n" + ("v$>") + " [l]ine 1\nline 2\nline 3\n")))) + (ert-info ("Shift visual with count") + (ert-info ("With `evil-start-of-line' `nil'") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\n" + ("Vj3>") + "[ ] line 1\n line 2\nline 3\n" + ("Vj2<") + "[ ] line 1\n line 2\nline 3\n")) + (ert-info ("With `evil-start-of-line' `t'") + (let ((evil-start-of-line t)) + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\n" + ("Vj3>") + " [l]ine 1\n line 2\nline 3\n" + ("Vj2<") + " [l]ine 1\n line 2\nline 3\n")))) + (ert-info ("Shift in insert state") + (evil-test-buffer + "line 1\nl[i]ne 2\nline 3\n" + ("i\C-t\C-t") + "line 1\n l[i]ne 2\nline 3\n" + ("\C-d") + "line 1\n l[i]ne 2\nline 3\n")) + (ert-info ("Delete all indentation in insert state") + (evil-test-buffer + "line1\n sometext[ ]" + ("a" "somemore" "0\C-d") + "line1\nsometext somemore[]")))) + +(ert-deftest evil-test-operator-set-marks () + "Test `[ and `] are set by operators." + :tags '(evil operator) + (ert-info ("Can go to end of last operated text") + (evil-test-buffer + "alpha br[a]vo charlie" + ("gUiw") + "alpha [B]RAVO charlie" + ("`]") + "alpha BRAV[O] charlie")) + (ert-info ("Can go to end of last operated text") + (evil-test-buffer + "alpha br[a]vo charlie" + ("g?iw$") + "alpha oenib charli[e]" + ("`[") + "alpha [o]enib charlie")) + (ert-info ("Marks are correct after deletion") + (evil-test-buffer + "alpha br[a]vo charlie" + ("daw^") + "[a]lpha charlie" + ("`]") + "alpha [c]harlie" + ("^`[") + "alpha [c]harlie") + (evil-test-buffer + "alpha br[a]vo charlie" + ("D^") + "[a]lpha br" + ("`]") + "alpha b[r]" + ("^`[") + "alpha b[r]"))) + +(ert-deftest evil-test-operator-save-state-for-motion () + "Test that `evil-operator-range' restores state after executing motion." + (evil-test-buffer "abc" + ("dfbix" [escape]) + "xc")) + +;;; Paste + +(ert-deftest evil-test-paste-before () + "Test `evil-paste-before'" + :tags '(evil paste) + (ert-info ("Paste characters") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("y2ej0") + ";; This buffer is for notes you don't want to save, +\[;]; and for Lisp evaluation." + ("P") + ";; This buffer is for notes you don't want to save, +This buffe[r];; and for Lisp evaluation.")) + (ert-info ("Paste characters with count") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("y2ej0") + ";; This buffer is for notes you don't want to save, +\[;]; and for Lisp evaluation." + ("3P") + ";; This buffer is for notes you don't want to save, +This bufferThis bufferThis buffe[r];; and for Lisp evaluation.")) + (ert-info ("Paste characters at end-of-buffer") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("y2eG$") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation[.]" + ("2P") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluationThis bufferThis buffe[r].")) + (ert-info ("Paste lines") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("2yyP") + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +;; This buffer is for notes you don't want to save, +;; and for Lisp evaluation.")) + (ert-info ("Paste lines with count") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("2yy2P") + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +;; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +;; This buffer is for notes you don't want to save, +;; and for Lisp evaluation.")) + (ert-info ("Paste lines at end-of-buffer") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("2yyG$") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation[.]" + ("2P") + ";; This buffer is for notes you don't want to save, +\[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +;; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +;; and for Lisp evaluation.")) + (ert-info ("Paste block") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ysP") + "[;]; ;; This buffer is for notes you don't want to save. +;; ;; If you want to create a file, visit that file with C-x C-f, +;; ;; then enter the text in that file's own buffer.")) + (ert-info ("Paste block with count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ys2P") + "[;]; ;; ;; This buffer is for notes you don't want to save. +;; ;; ;; If you want to create a file, visit that file with C-x C-f, +;; ;; ;; then enter the text in that file's own buffer.")) + (ert-info ("Paste block with empty line") + (evil-test-buffer + "[;]; Above some line + +;; Below some empty line" + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ys2P") + "[;]; ;; ;; Above some line + \n\ +;; ;; ;; Below some empty line")) + (ert-info ("Paste block crossing end of buffer") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ysj") + ";; This buffer is for notes you don't want to save. +\[;]; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("P") + ";; This buffer is for notes you don't want to save. +\[;]; ;; If you want to create a file, visit that file with C-x C-f, +;; ;; then enter the text in that file's own buffer. +;;")) + (ert-info ("Paste block at end-of-line") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ys$") + ";; This buffer is for notes you don't want to save[.] +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("p") + ";; This buffer is for notes you don't want to save.[;]; +;; If you want to create a file, visit that file wi;; th C-x C-f, +;; then enter the text in that file's own buffer. ;;")) + (ert-info ("Don't mutate register when stripping test props") + (let (indent-tabs-mode) + (evil-test-buffer + "[a]aaaaaaaaaaaa +bbbbbbbb +cccc" + ("\C-vG$yA " "\C-r\"" [escape] "up") + "aaaaaaaaaaaaa[a]aaaaaaaaaaaa +bbbbbbbb bbbbbbbb +cccc cccc")))) + +(ert-deftest evil-test-paste-after () + "Test `evil-paste-after'" + :tags '(evil paste) + (ert-info ("Paste characters") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("y2ej0") + ";; This buffer is for notes you don't want to save, +\[;]; and for Lisp evaluation." + ("p") + ";; This buffer is for notes you don't want to save, +;This buffe[r]; and for Lisp evaluation.")) + (ert-info ("Paste characters with count") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("y2ej0") + ";; This buffer is for notes you don't want to save, +\[;]; and for Lisp evaluation." + ("3p") + ";; This buffer is for notes you don't want to save, +;This bufferThis bufferThis buffe[r]; and for Lisp evaluation.")) + (ert-info ("Paste characters at end-of-buffer") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("y2eG$") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation[.]" + ("2p") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation.This bufferThis buffe[r]")) + (ert-info ("Paste lines") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("2yyp") + ";; This buffer is for notes you don't want to save, +\[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +;; and for Lisp evaluation.")) + (ert-info ("Paste lines with count") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("2yy2p") + ";; This buffer is for notes you don't want to save, +\[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +;; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +;; and for Lisp evaluation.")) + (ert-info ("Paste lines at end-of-buffer") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("2yyG$") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation[.]" + ("2p") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +\[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. +;; This buffer is for notes you don't want to save, +;; and for Lisp evaluation.")) + (ert-info ("Paste block") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ysp") + ";[;]; ; This buffer is for notes you don't want to save. +;;; ; If you want to create a file, visit that file with C-x C-f, +;;; ; then enter the text in that file's own buffer.")) + (ert-info ("Paste block with count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ys2p") + ";[;]; ;; ; This buffer is for notes you don't want to save. +;;; ;; ; If you want to create a file, visit that file with C-x C-f, +;;; ;; ; then enter the text in that file's own buffer.")) + (ert-info ("Paste block with empty line") + (evil-test-buffer + "[;]; Above some line + +;; Below some empty line" + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ys2p") + ";;; ;; ; Above some line + +;;; ;; ; Below some empty line")) + (ert-info ("Paste block crossing end of buffer") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ysj") + ";; This buffer is for notes you don't want to save. +\[;]; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("p") + ";; This buffer is for notes you don't want to save. +;;; ; If you want to create a file, visit that file with C-x C-f, +;;; ; then enter the text in that file's own buffer. + ;;")) + (ert-info ("Paste block at end-of-line") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("3ys$") + ";; This buffer is for notes you don't want to save[.] +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("p") + ";; This buffer is for notes you don't want to save.;; +;; If you want to create a file, visit that file wi;; th C-x C-f, +;; then enter the text in that file's own buffer. ;;")) + (ert-info ("Paste preserves preceding text properties") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (put-text-property (point) (line-end-position) 'font-lock-face 'warning) + ("yyp") + ";; This buffer is for notes you don't want to save. +[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (should (equal (get-text-property (point-min) 'font-lock-face) 'warning))))) + +(ert-deftest evil-test-paste-pop-before () + "Test `evil-paste-pop' after `evil-paste-before'" + :tags '(evil paste) + (ert-info ("Paste") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sj") + ";; This buffer is for notes you don't want to save. +\[;]; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("P") + ";; This buffer is for notes you don't want to save. +\[;]; ;; If you want to create a file, visit that file with C-x C-f, +;; ;; then enter the text in that file's own buffer. +;;")) + (ert-info ("Single pop") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjP\C-p") + ";; This buffer is for notes you don't want to save. +\[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Two pops") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjP\C-p\C-p") + ";; This buffer is for notes you don't want to save. +;; Thi[s];; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Pop with count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjP2\C-p") + ";; This buffer is for notes you don't want to save. +;; Thi[s];; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Single pop-next") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjP2\C-p\C-n") + ";; This buffer is for notes you don't want to save. +\[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Pop-next with count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjP\C-p\C-p2\C-n") + ";; This buffer is for notes you don't want to save. +\[;]; ;; If you want to create a file, visit that file with C-x C-f, +;; ;; then enter the text in that file's own buffer. +;;"))) + +(ert-deftest evil-test-paste-pop-after () + "Test `evil-paste-pop' after `evil-paste-after'" + :tags '(evil paste) + (ert-info ("Paste") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sj") + ";; This buffer is for notes you don't want to save. +\[;]; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("p") + ";; This buffer is for notes you don't want to save. +;[;]; ; If you want to create a file, visit that file with C-x C-f, +;;; ; then enter the text in that file's own buffer. + ;;")) + (ert-info ("Single pop") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjp\C-p") + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +\[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Two pops") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjp\C-p\C-p") + ";; This buffer is for notes you don't want to save. +;;; Thi[s]; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Pop with count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjp2\C-p") + ";; This buffer is for notes you don't want to save. +;;; Thi[s]; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Single pop-next") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjp2\C-p\C-n") + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +\[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Pop-next with count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjp\C-p\C-p2\C-n") + ";; This buffer is for notes you don't want to save. +;[;]; ; If you want to create a file, visit that file with C-x C-f, +;;; ; then enter the text in that file's own buffer. + ;;"))) + +(ert-deftest evil-test-paste-pop-without-undo () + "Test `evil-paste-pop' with undo disabled" + :tags '(evil paste) + (ert-info ("Pop-next with count without undo") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (setq buffer-undo-list t) + (define-key evil-operator-state-local-map "s" #'evil-test-square-motion) + ("y2e2yyy3sjP\C-p\C-p2\C-n") + ";; This buffer is for notes you don't want to save. +\[;]; ;; If you want to create a file, visit that file with C-x C-f, +;; ;; then enter the text in that file's own buffer. +;;"))) + +(ert-deftest evil-test-visual-paste () + "Test `evil-paste-before' and `evil-paste-after' in Visual state" + :tags '(evil paste) + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; [I]f you want to create a file, visit that file with C-x C-f." + ("yyk") + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f." + ("VP") + "[;]; If you want to create a file, visit that file with C-x C-f. +;; If you want to create a file, visit that file with C-x C-f.") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f." + ("yyj") + ";; This buffer is for notes you don't want to save. +;; [I]f you want to create a file, visit that file with C-x C-f." + ("Vp") + ";; This buffer is for notes you don't want to save. +\[;]; This buffer is for notes you don't want to save.") + (ert-info ("Visual-paste from register 3") + ;; This behaviour deviates from vim, which populates registers 1-9 with + ;; deleted text only, not yanked text. This is an aspect of `evil-yank's + ;; use of the emacs kill-ring, so is consistent with non-visual paste. + (evil-test-buffer + "[w]ord1a word1b word1c word1d +word2a word2b word2c word2d" + ("yiwwyiwwyiw") + "word1a word1b [w]ord1c word1d +word2a word2b word2c word2d" + ("+viw\"3p") + "word1a word1b word1c word1d +word1[a] word2b word2c word2d")) + (ert-info ("Visual-paste respects `evil-kill-on-visual-paste'") + (evil-test-buffer + "[w]ord1 word2 word3" + (setq evil-kill-on-visual-paste nil) + ("yewyew") + "word1 word2 [w]ord3" + ("ve\"2p") + "word1 word2 word[1]" + ("o\C-r\"") + "word1 word2 word1 +word2[]") + (evil-test-buffer + "[w]ord1 word2 word3" + (setq evil-kill-on-visual-paste t) + ("yewyew") + "word1 word2 [w]ord3" + ("ve\"2p") + "word1 word2 word[1]" + ("o\C-r\"") + "word1 word2 word1 +word3[]")) + (ert-info ("Visual-paste from `=' register") + (evil-test-buffer + "foo" + ("viw" "\"=p(* 6 7)" [return]) + "4[2]")) + (ert-info ("Blockwise visual paste (of charwise text) with count") + (evil-test-buffer + "[a]bc\n123\n123\n123" + ("ye" "jl" "\C-vG" "2p") + "abc\n1[a]bcabc3\n1abcabc3\n1abcabc3" + ("gv") ;; Test point & mark are stored correctly + "abc\n13")) + (ert-info ("Blockwise visual paste of linewise text") + (evil-test-buffer "[a]bc\n123\n123" + ("yy" "jl" "\C-vj" "p") + "abc\n13\n13\nabc")) + (ert-info ("Blockwise visual paste of blockwise text") + (evil-test-buffer "xy\n[z]\n123\n123" + ("\C-vkly2jl\C-vjp") + "xy\nz\n1xy3\n1z 3"))) + +(ert-deftest evil-test-visual-paste-pop () + "Test `evil-paste-pop' after visual paste." + :tags '(evil paste) + (ert-info ("Visual-char paste, char paste") + (evil-test-buffer + "[w]ord1a word1b word1c\nword2a word2b\nword3a word3b word3c word3d\n" + ("yiwyywyiw^jw") + "word1a word1b word1c\nword2a [w]ord2b\nword3a word3b word3c word3d\n" + ("viwp") + "word1a word1b word1c\nword2a word1[b]\nword3a word3b word3c word3d\n")) + (ert-info ("Visual-char paste, char paste, line pop") + (evil-test-buffer + "[w]ord1a word1b word1c\nword2a word2b\nword3a word3b word3c word3d\n" + ("yiwyywyiw^jw") + "word1a word1b word1c\nword2a [w]ord2b\nword3a word3b word3c word3d\n" + ("viwp\C-p") + "word1a word1b word1c\nword2a \n[w]ord1a word1b word1c\n\nword3a word3b word3c word3d\n")) + (ert-info ("Visual-char paste, char paste, line pop, char pop") + (evil-test-buffer + "[w]ord1a word1b word1c\nword2a word2b\nword3a word3b word3c word3d\n" + ("yiwyywyiw^jw") + "word1a word1b word1c\nword2a [w]ord2b\nword3a word3b word3c word3d\n" + ("viwp\C-p\C-p") + "word1a word1b word1c\nword2a word1[a]\nword3a word3b word3c word3d\n")) + (ert-info ("Visual-line paste, char paste") + (evil-test-buffer + "[w]ord1a word1b word1c\nword2a word2b\nword3a word3b word3c word3d\n" + ("yiwyywyiw^j") + "word1a word1b word1c\n[w]ord2a word2b\nword3a word3b word3c word3d\n" + ("Vp") + "word1a word1b word1c\nword1[b]\nword3a word3b word3c word3d\n")) + (ert-info ("Visual-line paste, char paste, line pop") + (evil-test-buffer + "[w]ord1a word1b word1c\nword2a word2b\nword3a word3b word3c word3d\n" + ("yiwyywyiw^j") + "word1a word1b word1c\n[w]ord2a word2b\nword3a word3b word3c word3d\n" + ("Vp\C-p") + "word1a word1b word1c\n[w]ord1a word1b word1c\nword3a word3b word3c word3d\n")) + (ert-info ("Visual-line paste, char paste, line pop, char pop") + (evil-test-buffer + "[w]ord1a word1b word1c\nword2a word2b\nword3a word3b word3c word3d\n" + ("yiwyywyiw^j") + "word1a word1b word1c\n[w]ord2a word2b\nword3a word3b word3c word3d\n" + ("Vp\C-p\C-p") + "word1a word1b word1c\nword1[a]\nword3a word3b word3c word3d\n"))) + +(ert-deftest evil-test-register () + "Test yanking and pasting to and from register." + :tags '(evil yank paste) + (ert-info ("simple lower case register") + (evil-test-buffer + "[f]oo\n" + ("\"ayw\"aP") + "fo[o]foo\n" + ("\"ayy\"aP") + "[f]oofoo\nfoofoo\n")) + (ert-info ("upper case register") + (evil-test-buffer + "[f]oo\n" + ("\"ayw\"Ayw\"aP") + "foofo[o]foo\n" + ("\"ayy\"Ayy\"aP") + "[f]oofoofoo\nfoofoofoo\nfoofoofoo\n")) + (ert-info ("upper case register and lines") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4\n" + ("\"a2Yjj\"A2Y\"aP") + "line 1\nline 2\n[l]ine 1\nline 2\nline 3\nline 4\nline 3\nline 4\n" + ("8G\"ap") + "line 1\nline 2\nline 1\nline 2\nline 3\nline 4\nline 3\nline 4\n[l]ine 1\nline 2\nline 3\nline 4\n")) + (ert-info ("yank with count") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\n" + ("\"a2yw\"aP") + "line [1]line 1\nline 2\nline 3\n" + ("\"a2yy\"aP") + "[l]ine 1line 1\nline 2\nline 1line 1\nline 2\nline 3\n")) + (dolist (module '(evil-search isearch)) + (evil-select-search-module 'evil-search-module module) + (ert-info ((format "special register / (module: %s)" module)) + (evil-test-buffer + "[f]oo bar\n" + ("/bar" [return] "0i\C-r/") + "bar[f]oo bar\n"))) + (ert-info ("special register :") + (evil-test-buffer + "[f]oo bar\n" + (":noh\ni\C-r:"))) + (ert-info ("Paste from register during change to register") + (evil-test-buffer + "[a]lpha beta" + ("\"ayiw" "w" "\"bciw" "\C-ra") + "alpha alpha[]")) + (ert-info ("Paste from register in replace state") + (evil-test-buffer + "[a]lpha bravo charlie" + ("yiw" "w" "R" "\C-r0") + "alpha alpha[ ]charlie" + ([backspace] [backspace] [backspace]) + "alpha al[a]vo charlie")) + (ert-info ("Normal delete after visual delete doesn't clobber register") + (evil-test-buffer + "[a]lpha bravo charlie delta" + ("vf \"xd" "dw" ";\"xp") + "charlie alpha delta")) + (ert-info ("Choosing register keeps eol anchoring") + (evil-test-buffer + "[a]aaaaa\nbbbbb\ncccc" + ("\C-v" "2j" "$" "\"xy" "G" "o" [escape] "\"xp") + "aaaaaa\nbbbbb\ncccc\n[a]aaaaa\nbbbbb\ncccc"))) + +(ert-deftest evil-test-last-insert-register () + "Test last insertion register." + (evil-test-buffer + "[l]ine 1\n" + ("GiABC" [escape]) + "line 1\nAB[C]" + ("go\".P") + "AB[C]line 1\nABC")) + +(ert-deftest evil-test-zero-register () + "\"0 contains the last text that was yanked without specificying a register." + (evil-test-buffer + "[l]ine 1\nline 2\n" + ("yy\"0p") + "line 1\n[l]ine 1\nline 2\n" + ("j\"ayy\"0p") + "line 1\nline 1\nline 2\n[l]ine 1\n" ; yanked line 2 to "a, so "0 is still line 1 + ("kdd\"0p") + "line 1\nline 1\nline 1\n[l]ine 1\n")) + +(ert-deftest evil-test-=-register () + "\"= is not really a register . It inserts the result of evaluating some elisp" + (ert-info ("Can eval elisp, and can fetch default (last) result") + (evil-test-buffer + :state insert + "8x8= []" + ("\C-r=(* 8 8)" [return]) + "8x8= 64" + ([return] "16x4= \C-r=" [return]) + "8x8= 64 +16x4= 64")) + + (ert-info ("Can eval infix math, and can use register at prompt") + (evil-test-buffer + "[5]0/10 * 100 = " + ("\"nyt=" "A\C-r=" "\C-rn" [return]) + "50/10 * 100 = 500"))) + +(ert-deftest evil-test-gp-gP () + (ert-info ("gp and gP are just like p and P but move the cursor forward") + (evil-test-buffer + "[a]lpha bravo charlie" + ("ye" "2f " "gp") + "alpha bravo alpha[c]harlie" + ("2F " "gP") + "alphaalpha[ ]bravo alphacharlie" + ("l" "ve" "gp") + "alphaalpha alpha[ ]alphacharlie")) + (ert-info ("gp linewise") + (evil-test-buffer + "[a]lpha line\nbravo line\ncharlie line\ndelta line\necho line" + ("2yy" "2j") + "alpha line\nbravo line\n[c]harlie line\ndelta line\necho line" + ("gp") + "alpha line\nbravo line\ncharlie line\nalpha line\nbravo line\n[d]elta line\necho line" + (".") + "alpha line\nbravo line\ncharlie line\nalpha line\nbravo line\ndelta line\nalpha line\nbravo line\necho line"))) + +(ert-deftest evil-test-ex-put () + "evil-ex-put inserts text linewise, regardless of yank-handler" + (ert-info ("Can put linewise text from default register, by line number") + (evil-test-buffer + "[L]orem ipsum dolor sit amet +consectetur adipiscing elit +sed do eiusmod tempor incididunt" + ("yy:2put" [return]) + "Lorem ipsum dolor sit amet +consectetur adipiscing elit +[L]orem ipsum dolor sit amet +sed do eiusmod tempor incididunt")) + + (ert-info ("Can put blockwise text from letter register, backwards") + (evil-test-buffer + "Lorem ipsum [d]olor sit amet +consectetur adipiscing elit +sed do eiusmod tempor incididunt" + ("\C-vje\"xy" "bye" "Vj" ":put! x" [return]) + "Lorem ipsum dolor sit amet +dolor sit +[a]dipiscing +consectetur adipiscing elit +sed do eiusmod tempor incididunt")) + + (ert-info ("Can supply args and put from = register") + (evil-test-buffer + "[L]ine one." + (":put = (* 6 7)" [return]) + "Line one. +[4]2"))) + +(ert-deftest evil-test-align () + "Test `evil-align-left', `evil-align-right' and `evil-align-center'." + :tags '(evil operator) + (evil-without-display + (let ((fill-column 70) + indent-tabs-mode) + (evil-test-buffer + "before\n[l]ine 1\nthis is line number 2\nline number 3\nafter\n" + (":.,+2ri" [return] (kbd "M-x") "untabify" [return]) + "before\n [l]ine 1\n this is line number 2\n line number 3\nafter\n" + (":.,+2ri 60" [return] (kbd "M-x") "untabify" [return]) + "before\n [l]ine 1\n this is line number 2\n line number 3\nafter\n" + (":.,+2le" [return] (kbd "M-x") "untabify" [return]) + "before\n[l]ine 1\nthis is line number 2\nline number 3\nafter\n" + (":.,+2le 10" [return]) + "before\n [l]ine 1\n this is line number 2\n line number 3\nafter\n" + (":.,+2ce" [return] (kbd "M-x") "untabify" [return]) + "before\n [l]ine 1\n this is line number 2\n line number 3\nafter\n" + (":.,+2ce 40" [return] (kbd "M-x") "untabify" [return]) + "before\n [l]ine 1\n this is line number 2\n line number 3\nafter\n")))) + +;;; Motions + +(ert-deftest evil-test-forward-char () + "Test `evil-forward-char' motion" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + "[;]; This buffer is for notes." + ("l") + ";[;] This buffer is for notes.")) + (ert-info ("End of line") + (let ((evil-cross-lines t) + (evil-move-beyond-eol nil)) + (evil-test-buffer + ";; This buffer is for notes[,] +;; and for Lisp evaluation." + ("l") + ";; This buffer is for notes, +\[;]; and for Lisp evaluation."))) + (ert-info ("With count") + (evil-test-buffer + "[;]; This buffer is for notes." + ("12l") + ";; This buff[e]r is for notes.")) + (ert-info ("End of line") + (evil-test-buffer + ";; This buffer is for notes[.]" + (error end-of-line "l") + (error end-of-line "10l"))) + (ert-info ("Until end-of-line") + (evil-test-buffer + "[;]; This buffer is for notes." + (error end-of-line "100l") + ";; This buffer is for notes[.]")) + (ert-info ("On empty line") + (evil-test-buffer + "Above some line +\[] +Below some empty line" + (should-error (execute-kbd-macro "l")) + (should-error (execute-kbd-macro "42l"))))) + +(ert-deftest evil-test-backward-char () + "Test `evil-backward-char' motion" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + ";; This[ ]buffer is for notes." + ("h") + ";; Thi[s] buffer is for notes.")) + (ert-info ("With count") + (evil-test-buffer + ";; This[ ]buffer is for notes." + ("3h") + ";; T[h]is buffer is for notes.")) + (ert-info ("Beginning of line") + (evil-test-buffer + "[;]; This buffer is for notes." + (should-error (execute-kbd-macro "h")) + (should-error (execute-kbd-macro "10h")))) + (ert-info ("Until beginning-of-line") + (evil-test-buffer + ";; This[ ]buffer is for notes." + (error beginning-of-line "100h") + "[;]; This buffer is for notes.")) + (ert-info ("On empty line") + (evil-test-buffer + "Above some line +\[] +Below some empty line" + (should-error (execute-kbd-macro "h")) + (should-error (execute-kbd-macro "42h"))))) + +(ert-deftest evil-test-previous-line () + "Test `evil-previous-line' motion" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + ";; This buffer is for notes you don't want to save, +;; [a]nd for Lisp evaluation." + ("k") + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation.")) + (ert-info ("With count") + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; [t]hen enter the text in that file's own buffer." + ("2k") + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("Until beginning of buffer") + (evil-test-buffer + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; [t]hen enter the text in that file's own buffer." + ("100k") + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer.")) + (ert-info ("At beginning of buffer") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + (should-error (execute-kbd-macro "k")) + (should-error (execute-kbd-macro "42k"))))) + +(ert-deftest evil-test-next-line () + "Test `evil-next-line' motion" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("j") + ";; This buffer is for notes you don't want to save, +;; [a]nd for Lisp evaluation.")) + (ert-info ("With count") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("2j") + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; [t]hen enter the text in that file's own buffer.")) + (ert-info ("Until end of buffer") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("100j") + ";; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; [t]hen enter the text in that file's own buffer.")) + (ert-info ("At end of buffer") + (evil-test-buffer + ";; This buffer is for notes you don't want to [s]ave." + (should-error (execute-kbd-macro "j")) + (should-error (execute-kbd-macro "42j"))))) + +(ert-deftest evil-test-next+previous-preserve-column () + "Test `evil-previous-line' and `evil-next-line' preserve the column." + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + "ab[c]\nabcdef\n\nabcd\n" + ("j") + "abc\nab[c]def\n\nabcd\n") + (evil-test-buffer + "ab[c]\nabcdef\n\nabcd\n" + ("jj") + "abc\nabcdef\n[\n]abcd\n") + (evil-test-buffer + "ab[c]\nabcdef\n\nabcd\n" + ("jjj") + "abc\nabcdef\n\nab[c]d\n") + (evil-test-buffer + "ab[c]\nabcdef\n\nabcd\n" + ("jjjk") + "abc\nabcdef\n[\n]abcd\n") + (evil-test-buffer + "ab[c]\nabcdef\n\nabcd\n" + ("jjjkk") + "abc\nab[c]def\n\nabcd\n"))) + +(ert-deftest evil-test-logical-visual-next-line () + "Test alternating between logical and visual next/previous line motions." + :tags '(evil motion) + (skip-unless (and (not noninteractive) (> (window-width) 2))) + (evil-test-buffer "[x]\nyy\n" + (insert (make-string (window-width) ?x)) ; Make first line soft-wrap + (goto-char (point-min)) + ("gjj") + (should (= (current-column) 1)) + ("Gkgk") + (should-not (bolp)))) + +(ert-deftest evil-test-eol-anchoring-with-visual-line-movement () + "Test gj and gk once the cursor is anchored at eol with $." + :tags '(evil motion) + (skip-unless (and (not noninteractive) (> (window-width) 13))) + (evil-test-buffer + "Short [l]ine\nA longer line\nThird line" + ("$gj") + "Short line\nA longer lin[e]\nThird line")) + +(ert-deftest evil-test-other-commands-preserve-column () + "Test other comamnds preserve the column, when appropriate." + :tags '(evil motion) + (ert-info ("evil-goto-line can preserve column") + (let ((evil-start-of-line nil)) + (evil-test-buffer + "Shor[t] line +Average line +The longest line" + ("2G") + "Short line +Aver[a]ge line +The longest line" + ("$G") + "Short line +Average line +The longest lin[e]" + ("hgg") + "Short lin[e] +Average line +The longest line"))) + + (ert-info ("evil-goto-line respects evil-start-of-line") + (let ((evil-start-of-line t)) + (evil-test-buffer + "foo\n[b]ar" + ("$ggj") + "foo\n[b]ar"))) + + (ert-info ("N% (`evil-jump-item' with count) can preserve column") + (let ((evil-start-of-line nil)) + (evil-test-buffer + "Short line +Average line +The lo[n]gest line" + ("5%") + "Short [l]ine +Average line +The longest line" + ("$90%") + "Short line +Average line +The longest lin[e]")))) + +(ert-deftest evil-test-beginning-of-line () + "Test `evil-beginning-of-line' motion" + :tags '(evil motion) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("0") + "[;]; This buffer is for notes you don't want to save." + ("0") + "[;]; This buffer is for notes you don't want to save.")) + +(ert-deftest evil-test-end-of-line () + "Test `evil-end-of-line' motion" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save." + ("$") + ";; This buffer is for notes you don't want to save[.]" + ("$") + ";; This buffer is for notes you don't want to save[.]")) + (ert-info ("Don't delete blank lines") + (evil-test-buffer + "Above some line +\[] +Below some empty line" + ("d$") + "Above some line +\[] +Below some empty line")) + (ert-info ("Visual state") + (evil-test-buffer + "f[o]o\nbar" + ("v$") + "fbar") + (let ((evil-v$-excludes-newline t)) + (evil-test-buffer + "f[o]o\nbar" + ("v$") + "f\nbar")))) + +(ert-deftest evil-test-percentage-of-line () + "Test `evil-percentage-of-line' motion" + :tags '(evil motion) + (evil-test-buffer + "[0]123456789" + ("gM") + "01234[5]6789" + ("10gM") + "0[1]23456789" + ("85gM") + "01234567[8]9")) + +(ert-deftest evil-test-first-non-blank () + "Test `evil-first-non-blank' motion" + :tags '(evil motion) + (evil-test-buffer + "\ + printf(\"Hello world\\n\")[;] + return EXIT_SUCCESS;" + ("^") + "\ + [p]rintf(\"Hello world\\n\"); + return EXIT_SUCCESS;" + ("j^") + "\ + printf(\"Hello world\\n\"); + [r]eturn EXIT_SUCCESS;")) + +(ert-deftest evil-test-last-non-blank () + "Test `evil-last-non-blank' motion" + :tags '(evil motion) + (evil-test-buffer + "[i]nt main(int argc, char** argv) \n\ +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("g_") + "int main(int argc, char** argv[)] \n\ +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("jjg_") + "int main(int argc, char** argv) \n\ +{ + printf(\"Hello world\\n\")[;] + return EXIT_SUCCESS; +}")) + +(ert-deftest evil-test-goto-first-line () + "Test `evil-goto-first-line' motion" + :tags '(evil motion) + (ert-info ("With `evil-start-of-line' `nil'") + (evil-test-buffer + "[i]nt main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("3gg") + "int main(int argc, char** argv) +{ +[ ] printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("gg") + "[i]nt main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("100gg") + "int main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +\[}]")) + (ert-info ("With `evil-start-of-line' `nil'") + (let ((evil-start-of-line t)) + (evil-test-buffer + "[i]nt main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("3gg") + "int main(int argc, char** argv) +{ + [p]rintf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("gg") + "[i]nt main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("100gg") + "int main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +\[}]")))) + +(ert-deftest evil-test-goto-line () + "Test `evil-goto-line' motion" + :tags '(evil motion) + (ert-info ("With `evil-start-of-line' `t'") + (let ((evil-start-of-line t)) + (evil-test-buffer + "[i]nt main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("G") + "int main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +\[}]" + ("3G") + "int main(int argc, char** argv) +{ + [p]rintf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("100G") + "int main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +\[}]"))) + (ert-info ("With `evil-start-of-line' `nil'") + (evil-test-buffer + "[i]nt main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("G") + "int main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +\[}]" + ("3G") + "int main(int argc, char** argv) +{ +[ ] printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("100G") + "int main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +\[}]"))) + +(ert-deftest evil-test-goto-char () + "Test `evil-goto-char' motion and ex command." + :tags '(evil motion ex) + (evil-test-buffer + "[W]e only need a short buffer for this test" + (":goto 9") + "We only [n]eed a short buffer for this test" + (":goto") + "[W]e only need a short buffer for this test" + ("16go") + "We only need a [s]hort buffer for this test" + ("go") + "[W]e only need a short buffer for this test" + (evil-goto-char 24) + "We only need a short bu[f]fer for this test" + ("v29go") + "We only need a short buor this test")) + +(ert-deftest evil-test-operator-0 () + "Test motion \"0\" with an operator." + :tags '(evil motion) + (evil-test-buffer + ";; [T]his buffer is for notes." + ("d0") + "[T]his buffer is for notes.")) + +(ert-deftest evil-test-forward-not-word () + "Test `evil-forward-not-thing'" + :tags '(evil motion) + (evil-test-buffer + "[ ] aa,," + (evil-forward-not-thing 'evil-word) + " [a]a,,")) + +;; TODO: test Visual motions and window motions +(ert-deftest evil-test-forward-word-begin () + "Test `evil-forward-word-begin'" + :tags '(evil motion) + (ert-info ("Non-word") + (evil-test-buffer + "[;]; This buffer is for notes." + ("w") + ";; [T]his buffer is for notes.")) + (ert-info ("Simple") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("w") + ";; This [b]uffer is for notes.")) + (ert-info ("With count") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("3w") + ";; This buffer is [f]or notes.")) + (ert-info ("With count on whitespace") + (evil-test-buffer + ";;[ ]This buffer is for notes." + ("3w") + ";; This buffer [i]s for notes.")) + (ert-info ("Empty line") + (evil-test-buffer + "Above some line +\[] +Below some empty line" + ("w") + "Above some line + +\[B]elow some empty line") + (evil-test-buffer + "[A]bove + +Below some empty line" + ("dw") + "[] + +Below some empty line" + ("dw") + "[] +Below some empty line" + ("dw") + "[B]elow some empty line") + (evil-test-buffer + "[A]bove + + Below some empty line with leading whitespace" + ("dw") + "[] + + Below some empty line with leading whitespace" + ("dw") + "[] + Below some empty line with leading whitespace" + ("dw") + " [B]elow some empty line") + (evil-test-buffer + "Some line with trailing whitespace [ ] \n next line\n" + ("dw") + "Some line with trailing whitespace [ ]\n next line\n") + (evil-test-buffer + "[A]\n" + ("dw") + "[]\n")) + (ert-info ("End of buffer") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("100w") + ";; This buffer is for notes[.]" + (should-error (execute-kbd-macro "w")) + (should-error (execute-kbd-macro "10w")))) + (ert-info ("Before last character in buffer") + (evil-test-buffer + "fo[o]." + ("w") + "foo[.]") + (evil-test-buffer + "fo[o] " + ("w") + "foo[ ]") + (evil-test-buffer + "[ ]e" + ("w") + " [e]"))) + +(ert-deftest evil-test-forward-word-end () + "Test `evil-forward-word-end'" + :tags '(evil motion) + (ert-info ("Non-word") + (evil-test-buffer + "[;]; This buffer is for notes." + ("e") + ";[;] This buffer is for notes.")) + (ert-info ("Simple") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("e") + ";; Thi[s] buffer is for notes.")) + (ert-info ("With count") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("3e") + ";; This buffer i[s] for notes.")) + (ert-info ("With count on whitespace") + (evil-test-buffer + ";;[ ]This buffer is for notes." + ("3e") + ";; This buffer i[s] for notes.")) + (ert-info ("Delete") + (evil-test-buffer + ";; This[-]buffer-is-for-notes." + ("de") + ";; This[-]is-for-notes.")) + (ert-info ("Empty line") + (evil-test-buffer + "Above some line +\[] +Below some empty line" + ("e") + "Above some line + +Belo[w] some empty line")) + (ert-info ("End of buffer") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("100e") + ";; This buffer is for notes[.]" + (should-error (execute-kbd-macro "e")) + (should-error (execute-kbd-macro "10e")))) + ;; In Vim, "de" may delete two words rather than one + ;; if the first word is only one letter. In Evil, + ;; "de" always deletes one word. + (ert-info ("Delete a single-letter word") + (evil-test-buffer + "a [b] c" + ("de") + "a [ ]c"))) + +(ert-deftest evil-test-backward-word-begin () + "Test `evil-backward-word-begin'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("b") + ";; This buffer is for [n]otes.")) + (ert-info ("With count") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("2b") + ";; This buffer is [f]or notes.")) + (ert-info ("Empty line") + (evil-test-buffer + "Above some line +\[] +Below some empty line" + ("b") + "Above some [l]ine + +Below some empty line")) + (ert-info ("With count on whitespace") + (evil-test-buffer + ";; This buffer is for[ ]notes." + ("2b") + ";; This buffer [i]s for notes.")) + (ert-info ("Beginning of buffer") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("100b") + "[;]; This buffer is for notes." + (should-error (execute-kbd-macro "b")) + (should-error (execute-kbd-macro "10b"))))) + +(ert-deftest evil-test-backward-word-end () + "Test `evil-backward-word-end'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("ge") + ";; This buffer is for note[s].")) + (ert-info ("With count") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("2ge") + ";; This buffer is fo[r] notes.")) + (ert-info ("Empty line") + (evil-test-buffer + "Above some line +\[] +Below some empty line" + ("ge") + "Above some lin[e] + +Below some empty line")) + (ert-info ("With count on whitespace") + (evil-test-buffer + ";; This buffer is for[ ]notes." + ("2ge") + ";; This buffer i[s] for notes.")) + (ert-info ("Beginning of buffer") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("100ge") + "[;]; This buffer is for notes." + (should-error (execute-kbd-macro "ge")) + (should-error (execute-kbd-macro "10ge"))))) + +(ert-deftest evil-test-forward-word-begin-cjk () + "Test `evil-forward-word-begin' on CJK words" + :tags '(evil motion cjk) + (ert-info ("Latin / numeric") + (evil-test-buffer + "[a]bcd1234" + ("w") + "abcd123[4]")) + (ert-info ("Latin / Kanji") + (evil-test-buffer + "[a]bcd漢字" + ("w") + "abcd[漢]字")) + (ert-info ("Latin / Hiragana") + (evil-test-buffer + "[a]bcdひらがな" + ("w") + "abcd[ひ]らがな")) + (ert-info ("Latin / Katakana") + (evil-test-buffer + "[a]bcdカタカナ" + ("w") + "abcd[カ]タカナ")) + (ert-info ("Latin / half-width Katakana") + (evil-test-buffer + "[a]bcdカタカナ" + ("w") + "abcdカタカ[ナ]")) + (ert-info ("Latin / full-width alphabet") + (evil-test-buffer + "[a]bcdABC" + ("w") + "abcdAB[C]")) + (ert-info ("Latin / full-width numeric") + (evil-test-buffer + "[a]bcd123" + ("w") + "abcd12[3]")) + (ert-info ("Latin / Hangul") + (evil-test-buffer + "[a]bcd한글" + ("w") + "abcd[한]글")) + (ert-info ("numeric / Latin") + (evil-test-buffer + "[1]234abcd" + ("w") + "1234abc[d]")) + (ert-info ("numeric / Kanji") + (evil-test-buffer + "[1]234漢字" + ("w") + "1234[漢]字")) + (ert-info ("numeric / Hiragana") + (evil-test-buffer + "[1]234ひらがな" + ("w") + "1234[ひ]らがな")) + (ert-info ("numeric / Katakana") + (evil-test-buffer + "[1]234カタカナ" + ("w") + "1234[カ]タカナ")) + (ert-info ("numeric / half-width Katakana") + (evil-test-buffer + "[1]234カタカナ" + ("w") + "1234カタカ[ナ]")) + (ert-info ("numeric / full-width alphabet") + (evil-test-buffer + "[1]234ABC" + ("w") + "1234AB[C]")) + (ert-info ("numeric / full-width numeric") + (evil-test-buffer + "[1]234123" + ("w") + "123412[3]")) + (ert-info ("numeric / Hangul") + (evil-test-buffer + "[1]234한글" + ("w") + "1234[한]글")) + (ert-info ("Kanji / Latin") + (evil-test-buffer + "[漢]字abcd" + ("w") + "漢字[a]bcd")) + (ert-info ("Kanji / numeric") + (evil-test-buffer + "[漢]字1234" + ("w") + "漢字[1]234")) + (ert-info ("Kanji / Hiragana") + (evil-test-buffer + "[漢]字ひらがな" + ("w") + "漢字[ひ]らがな")) + (ert-info ("Kanji / Katakana") + (evil-test-buffer + "[漢]字カタカナ" + ("w") + "漢字[カ]タカナ")) + (ert-info ("Kanji / half-width Katakana") + (evil-test-buffer + "[漢]字カタカナ" + ("w") + "漢字[カ]タカナ")) + (ert-info ("Kanji / full-width alphabet") + (evil-test-buffer + "[漢]字ABC" + ("w") + "漢字[A]BC")) + (ert-info ("Kanji / full-width numeric") + (evil-test-buffer + "[漢]字123" + ("w") + "漢字[1]23")) + (ert-info ("Kanji / Hangul") + (evil-test-buffer + "[漢]字한글" + ("w") + "漢字[한]글")) + (ert-info ("Hiragana / Latin") + (evil-test-buffer + "[ひ]らがなabcd" + ("w") + "ひらがな[a]bcd")) + (ert-info ("Hiragana / numeric") + (evil-test-buffer + "[ひ]らがな1234" + ("w") + "ひらがな[1]234")) + (ert-info ("Hiragana / Kanji") + (evil-test-buffer + "[ひ]らがな漢字" + ("w") + "ひらがな[漢]字")) + (ert-info ("Hiragana / Katakana") + (evil-test-buffer + "[ひ]らがなカタカナ" + ("w") + "ひらがな[カ]タカナ")) + (ert-info ("Hiragana / half-width Katakana") + (evil-test-buffer + "[ひ]らがなカタカナ" + ("w") + "ひらがな[カ]タカナ")) + (ert-info ("Hiragana / full-width alphabet") + (evil-test-buffer + "[ひ]らがなABC" + ("w") + "ひらがな[A]BC")) + (ert-info ("Hiragana / full-width numeric") + (evil-test-buffer + "[ひ]らがな123" + ("w") + "ひらがな[1]23")) + (ert-info ("Hiragana / Hangul") + (evil-test-buffer + "[ひ]らがな한글" + ("w") + "ひらがな[한]글")) + (ert-info ("Katakana / Latin") + (evil-test-buffer + "[カ]タカナabcd" + ("w") + "カタカナ[a]bcd")) + (ert-info ("Katakana / numeric") + (evil-test-buffer + "[カ]タカナ1234" + ("w") + "カタカナ[1]234")) + (ert-info ("Katakana / Kanji") + (evil-test-buffer + "[カ]タカナ漢字" + ("w") + "カタカナ[漢]字")) + (ert-info ("Katakana / Hiragana") + (evil-test-buffer + "[カ]タカナひらがな" + ("w") + "カタカナ[ひ]らがな")) + (ert-info ("Katakana / half-width Katakana") + (evil-test-buffer + "[カ]タカナカタカナ" + ("w") + "カタカナ[カ]タカナ")) + (ert-info ("Katakana / full-width alphabet") + (evil-test-buffer + "[カ]タカナABC" + ("w") + "カタカナ[A]BC")) + (ert-info ("Katakana / full-width numeric") + (evil-test-buffer + "[カ]タカナ123" + ("w") + "カタカナ[1]23")) + (ert-info ("Katakana / Hangul") + (evil-test-buffer + "[カ]タカナ한글" + ("w") + "カタカナ[한]글")) + (ert-info ("half-width Katakana / Latin") + (evil-test-buffer + "[カ]タカナabcd" + ("w") + "カタカナabc[d]")) + (ert-info ("half-width Katakana / numeric") + (evil-test-buffer + "[カ]タカナ1234" + ("w") + "カタカナ123[4]")) + (ert-info ("half-width Katakana / Kanji") + (evil-test-buffer + "[カ]タカナ漢字" + ("w") + "カタカナ[漢]字")) + (ert-info ("half-width Katakana / Hiragana") + (evil-test-buffer + "[カ]タカナひらがな" + ("w") + "カタカナ[ひ]らがな")) + (ert-info ("half-width Katakana / Katakana") + (evil-test-buffer + "[カ]タカナカタカナ" + ("w") + "カタカナ[カ]タカナ")) + (ert-info ("half-width Katakana / full-width alphabet") + (evil-test-buffer + "[カ]タカナABC" + ("w") + "カタカナAB[C]")) + (ert-info ("half-width Katakana / full-width numeric") + (evil-test-buffer + "[カ]タカナ123" + ("w") + "カタカナ12[3]")) + (ert-info ("half-width Katakana / Hangul") + (evil-test-buffer + "[カ]タカナ한글" + ("w") + "カタカナ[한]글")) + (ert-info ("full-width alphabet / Latin") + (evil-test-buffer + "[A]BCabcd" + ("w") + "ABCabc[d]")) + (ert-info ("full-width alphabet / numeric") + (evil-test-buffer + "[A]BC1234" + ("w") + "ABC123[4]")) + (ert-info ("full-width alphabet / Kanji") + (evil-test-buffer + "[A]BC漢字" + ("w") + "ABC[漢]字")) + (ert-info ("full-width alphabet / Hiragana") + (evil-test-buffer + "[A]BCひらがな" + ("w") + "ABC[ひ]らがな")) + (ert-info ("full-width alphabet / Katakana") + (evil-test-buffer + "[A]BCカタカナ" + ("w") + "ABC[カ]タカナ")) + (ert-info ("full-width alphabet / half-width Katakana") + (evil-test-buffer + "[A]BCカタカナ" + ("w") + "ABCカタカ[ナ]")) + (ert-info ("full-width alphabet / full-width numeric") + (evil-test-buffer + "[A]BC123" + ("w") + "ABC12[3]")) + (ert-info ("full-width alphabet / Hangul") + (evil-test-buffer + "[A]BC한글" + ("w") + "ABC[한]글")) + (ert-info ("full-width numeric / Latin") + (evil-test-buffer + "[1]23abcd" + ("w") + "123abc[d]")) + (ert-info ("full-width numeric / numeric") + (evil-test-buffer + "[1]231234" + ("w") + "123123[4]")) + (ert-info ("full-width numeric / Kanji") + (evil-test-buffer + "[1]23漢字" + ("w") + "123[漢]字")) + (ert-info ("full-width numeric / Hiragana") + (evil-test-buffer + "[1]23ひらがな" + ("w") + "123[ひ]らがな")) + (ert-info ("full-width numeric / Katakana") + (evil-test-buffer + "[1]23カタカナ" + ("w") + "123[カ]タカナ")) + (ert-info ("full-width numeric / half-width Katakana") + (evil-test-buffer + "[1]23カタカナ" + ("w") + "123カタカ[ナ]")) + (ert-info ("full-width numeric / full-width alphabet") + (evil-test-buffer + "[1]23ABC" + ("w") + "123AB[C]")) + (ert-info ("full-width numeric / Hangul") + (evil-test-buffer + "[1]23한글" + ("w") + "123[한]글")) + (ert-info ("Hangul / Latin") + (evil-test-buffer + "[한]글abcd" + ("w") + "한글[a]bcd")) + (ert-info ("Hangul / numeric") + (evil-test-buffer + "[한]글1234" + ("w") + "한글[1]234")) + (ert-info ("Hangul / Kanji") + (evil-test-buffer + "[한]글漢字" + ("w") + "한글[漢]字")) + (ert-info ("Hangul / Hiragana") + (evil-test-buffer + "[한]글ひらがな" + ("w") + "한글[ひ]らがな")) + (ert-info ("Hangul / Katakana") + (evil-test-buffer + "[한]글カタカナ" + ("w") + "한글[カ]タカナ")) + (ert-info ("Hangul / half-width Katakana") + (evil-test-buffer + "[한]글カタカナ" + ("w") + "한글[カ]タカナ")) + (ert-info ("Hangul / full-width alphabet") + (evil-test-buffer + "[한]글ABC" + ("w") + "한글[A]BC")) + (ert-info ("Hangul / full-width numeric") + (evil-test-buffer + "[한]글123" + ("w") + "한글[1]23"))) + +(ert-deftest evil-test-forward-word-end-cjk () + "Test `evil-forward-word-end' on CJK words" + :tags '(evil motion cjk) + (ert-info ("Latin / numeric") + (evil-test-buffer + "[a]bcd1234" + ("e") + "abcd123[4]")) + (ert-info ("Latin / Kanji") + (evil-test-buffer + "[a]bcd漢字" + ("e") + "abc[d]漢字")) + (ert-info ("Latin / Hiragana") + (evil-test-buffer + "[a]bcdひらがな" + ("e") + "abc[d]ひらがな")) + (ert-info ("Latin / Katakana") + (evil-test-buffer + "[a]bcdカタカナ" + ("e") + "abc[d]カタカナ")) + (ert-info ("Latin / half-width Katakana") + (evil-test-buffer + "[a]bcdカタカナ" + ("e") + "abcdカタカ[ナ]")) + (ert-info ("Latin / full-width alphabet") + (evil-test-buffer + "[a]bcdABC" + ("e") + "abcdAB[C]")) + (ert-info ("Latin / full-width numeric") + (evil-test-buffer + "[a]bcd123" + ("e") + "abcd12[3]")) + (ert-info ("Latin / Hangul") + (evil-test-buffer + "[a]bcd한글" + ("e") + "abc[d]한글")) + (ert-info ("numeric / Latin") + (evil-test-buffer + "[1]234abcd" + ("e") + "1234abc[d]")) + (ert-info ("numeric / Kanji") + (evil-test-buffer + "[1]234漢字" + ("e") + "123[4]漢字")) + (ert-info ("numeric / Hiragana") + (evil-test-buffer + "[1]234ひらがな" + ("e") + "123[4]ひらがな")) + (ert-info ("numeric / Katakana") + (evil-test-buffer + "[1]234カタカナ" + ("e") + "123[4]カタカナ")) + (ert-info ("numeric / half-width Katakana") + (evil-test-buffer + "[1]234カタカナ" + ("e") + "1234カタカ[ナ]")) + (ert-info ("numeric / full-width alphabet") + (evil-test-buffer + "[1]234ABC" + ("e") + "1234AB[C]")) + (ert-info ("numeric / full-width numeric") + (evil-test-buffer + "[1]234123" + ("e") + "123412[3]")) + (ert-info ("numeric / Hangul") + (evil-test-buffer + "[1]234한글" + ("e") + "123[4]한글")) + (ert-info ("Kanji / Latin") + (evil-test-buffer + "[漢]字abcd" + ("e") + "漢[字]abcd")) + (ert-info ("Kanji / numeric") + (evil-test-buffer + "[漢]字1234" + ("e") + "漢[字]1234")) + (ert-info ("Kanji / Hiragana") + (evil-test-buffer + "[漢]字ひらがな" + ("e") + "漢[字]ひらがな")) + (ert-info ("Kanji / Katakana") + (evil-test-buffer + "[漢]字カタカナ" + ("e") + "漢[字]カタカナ")) + (ert-info ("Kanji / half-width Katakana") + (evil-test-buffer + "[漢]字カタカナ" + ("e") + "漢[字]カタカナ")) + (ert-info ("Kanji / full-width alphabet") + (evil-test-buffer + "[漢]字ABC" + ("e") + "漢[字]ABC")) + (ert-info ("Kanji / full-width numeric") + (evil-test-buffer + "[漢]字123" + ("e") + "漢[字]123")) + (ert-info ("Kanji / Hangul") + (evil-test-buffer + "[漢]字한글" + ("e") + "漢[字]한글")) + (ert-info ("Hiragana / Latin") + (evil-test-buffer + "[ひ]らがなabcd" + ("e") + "ひらが[な]abcd")) + (ert-info ("Hiragana / numeric") + (evil-test-buffer + "[ひ]らがな1234" + ("e") + "ひらが[な]1234")) + (ert-info ("Hiragana / Kanji") + (evil-test-buffer + "[ひ]らがな漢字" + ("e") + "ひらが[な]漢字")) + (ert-info ("Hiragana / Katakana") + (evil-test-buffer + "[ひ]らがなカタカナ" + ("e") + "ひらが[な]カタカナ")) + (ert-info ("Hiragana / half-width Katakana") + (evil-test-buffer + "[ひ]らがなカタカナ" + ("e") + "ひらが[な]カタカナ")) + (ert-info ("Hiragana / full-width alphabet") + (evil-test-buffer + "[ひ]らがなABC" + ("e") + "ひらが[な]ABC")) + (ert-info ("Hiragana / full-width numeric") + (evil-test-buffer + "[ひ]らがな123" + ("e") + "ひらが[な]123")) + (ert-info ("Hiragana / Hangul") + (evil-test-buffer + "[ひ]らがな한글" + ("e") + "ひらが[な]한글")) + (ert-info ("Katakana / Latin") + (evil-test-buffer + "[カ]タカナabcd" + ("e") + "カタカ[ナ]abcd")) + (ert-info ("Katakana / numeric") + (evil-test-buffer + "[カ]タカナ1234" + ("e") + "カタカ[ナ]1234")) + (ert-info ("Katakana / Kanji") + (evil-test-buffer + "[カ]タカナ漢字" + ("e") + "カタカ[ナ]漢字")) + (ert-info ("Katakana / Hiragana") + (evil-test-buffer + "[カ]タカナひらがな" + ("e") + "カタカ[ナ]ひらがな")) + (ert-info ("Katakana / half-width Katakana") + (evil-test-buffer + "[カ]タカナカタカナ" + ("e") + "カタカ[ナ]カタカナ")) + (ert-info ("Katakana / full-width alphabet") + (evil-test-buffer + "[カ]タカナABC" + ("e") + "カタカ[ナ]ABC")) + (ert-info ("Katakana / full-width numeric") + (evil-test-buffer + "[カ]タカナ123" + ("e") + "カタカ[ナ]123")) + (ert-info ("Katakana / Hangul") + (evil-test-buffer + "[カ]タカナ한글" + ("e") + "カタカ[ナ]한글")) + (ert-info ("half-width Katakana / Latin") + (evil-test-buffer + "[カ]タカナabcd" + ("e") + "カタカナabc[d]")) + (ert-info ("half-width Katakana / numeric") + (evil-test-buffer + "[カ]タカナ1234" + ("e") + "カタカナ123[4]")) + (ert-info ("half-width Katakana / Kanji") + (evil-test-buffer + "[カ]タカナ漢字" + ("e") + "カタカ[ナ]漢字")) + (ert-info ("half-width Katakana / Hiragana") + (evil-test-buffer + "[カ]タカナひらがな" + ("e") + "カタカ[ナ]ひらがな")) + (ert-info ("half-width Katakana / Katakana") + (evil-test-buffer + "[カ]タカナカタカナ" + ("e") + "カタカ[ナ]カタカナ")) + (ert-info ("half-width Katakana / full-width alphabet") + (evil-test-buffer + "[カ]タカナABC" + ("e") + "カタカナAB[C]")) + (ert-info ("half-width Katakana / full-width numeric") + (evil-test-buffer + "[カ]タカナ123" + ("e") + "カタカナ12[3]")) + (ert-info ("half-width Katakana / Hangul") + (evil-test-buffer + "[カ]タカナ한글" + ("e") + "カタカ[ナ]한글")) + (ert-info ("full-width alphabet / Latin") + (evil-test-buffer + "[A]BCabcd" + ("e") + "ABCabc[d]")) + (ert-info ("full-width alphabet / numeric") + (evil-test-buffer + "[A]BC1234" + ("e") + "ABC123[4]")) + (ert-info ("full-width alphabet / Kanji") + (evil-test-buffer + "[A]BC漢字" + ("e") + "AB[C]漢字")) + (ert-info ("full-width alphabet / Hiragana") + (evil-test-buffer + "[A]BCひらがな" + ("e") + "AB[C]ひらがな")) + (ert-info ("full-width alphabet / Katakana") + (evil-test-buffer + "[A]BCカタカナ" + ("e") + "AB[C]カタカナ")) + (ert-info ("full-width alphabet / half-width Katakana") + (evil-test-buffer + "[A]BCカタカナ" + ("e") + "ABCカタカ[ナ]")) + (ert-info ("full-width alphabet / full-width numeric") + (evil-test-buffer + "[A]BC123" + ("e") + "ABC12[3]")) + (ert-info ("full-width alphabet / Hangul") + (evil-test-buffer + "[A]BC한글" + ("e") + "AB[C]한글")) + (ert-info ("full-width numeric / Latin") + (evil-test-buffer + "[1]23abcd" + ("e") + "123abc[d]")) + (ert-info ("full-width numeric / numeric") + (evil-test-buffer + "[1]231234" + ("e") + "123123[4]")) + (ert-info ("full-width numeric / Kanji") + (evil-test-buffer + "[1]23漢字" + ("e") + "12[3]漢字")) + (ert-info ("full-width numeric / Hiragana") + (evil-test-buffer + "[1]23ひらがな" + ("e") + "12[3]ひらがな")) + (ert-info ("full-width numeric / Katakana") + (evil-test-buffer + "[1]23カタカナ" + ("e") + "12[3]カタカナ")) + (ert-info ("full-width numeric / half-width Katakana") + (evil-test-buffer + "[1]23カタカナ" + ("e") + "123カタカ[ナ]")) + (ert-info ("full-width numeric / full-width alphabet") + (evil-test-buffer + "[1]23ABC" + ("e") + "123AB[C]")) + (ert-info ("full-width numeric / Hangul") + (evil-test-buffer + "[1]23한글" + ("e") + "12[3]한글")) + (ert-info ("Hangul / Latin") + (evil-test-buffer + "[한]글abcd" + ("e") + "한[글]abcd")) + (ert-info ("Hangul / numeric") + (evil-test-buffer + "[한]글1234" + ("e") + "한[글]1234")) + (ert-info ("Hangul / Kanji") + (evil-test-buffer + "[한]글漢字" + ("e") + "한[글]漢字")) + (ert-info ("Hangul / Hiragana") + (evil-test-buffer + "[한]글ひらがな" + ("e") + "한[글]ひらがな")) + (ert-info ("Hangul / Katakana") + (evil-test-buffer + "[한]글カタカナ" + ("e") + "한[글]カタカナ")) + (ert-info ("Hangul / half-width Katakana") + (evil-test-buffer + "[한]글カタカナ" + ("e") + "한[글]カタカナ")) + (ert-info ("Hangul / full-width alphabet") + (evil-test-buffer + "[한]글ABC" + ("e") + "한[글]ABC")) + (ert-info ("Hangul / full-width numeric") + (evil-test-buffer + "[한]글123" + ("e") + "한[글]123"))) + +(ert-deftest evil-test-backword-word-begin-cjk () + "Test `evil-backward-word-begin' on CJK words" + :tags '(evil motion cjk) + (ert-info ("Latin / numeric") + (evil-test-buffer + "abcd123[4]" + ("b") + "[a]bcd1234")) + (ert-info ("Latin / Kanji") + (evil-test-buffer + "abcd漢[字]" + ("b") + "abcd[漢]字")) + (ert-info ("Latin / Hiragana") + (evil-test-buffer + "abcdひらが[な]" + ("b") + "abcd[ひ]らがな")) + (ert-info ("Latin / Katakana") + (evil-test-buffer + "abcdカタカ[ナ]" + ("b") + "abcd[カ]タカナ")) + (ert-info ("Latin / half-width Katakana") + (evil-test-buffer + "abcdカタカ[ナ]" + ("b") + "[a]bcdカタカナ")) + (ert-info ("Latin / full-width alphabet") + (evil-test-buffer + "abcdAB[C]" + ("b") + "[a]bcdABC")) + (ert-info ("Latin / full-width numeric") + (evil-test-buffer + "abcd12[3]" + ("b") + "[a]bcd123")) + (ert-info ("Latin / Hangul") + (evil-test-buffer + "abcd한[글]" + ("b") + "abcd[한]글")) + (ert-info ("numeric / Latin") + (evil-test-buffer + "1234abc[d]" + ("b") + "[1]234abcd")) + (ert-info ("numeric / Kanji") + (evil-test-buffer + "1234漢[字]" + ("b") + "1234[漢]字")) + (ert-info ("numeric / Hiragana") + (evil-test-buffer + "1234ひらが[な]" + ("b") + "1234[ひ]らがな")) + (ert-info ("numeric / Katakana") + (evil-test-buffer + "1234カタカ[ナ]" + ("b") + "1234[カ]タカナ")) + (ert-info ("numeric / half-width Katakana") + (evil-test-buffer + "1234カタカ[ナ]" + ("b") + "[1]234カタカナ")) + (ert-info ("numeric / full-width alphabet") + (evil-test-buffer + "1234AB[C]" + ("b") + "[1]234ABC")) + (ert-info ("numeric / full-width numeric") + (evil-test-buffer + "123412[3]" + ("b") + "[1]234123")) + (ert-info ("numeric / Hangul") + (evil-test-buffer + "1234한[글]" + ("b") + "1234[한]글")) + (ert-info ("Kanji / Latin") + (evil-test-buffer + "漢字abc[d]" + ("b") + "漢字[a]bcd")) + (ert-info ("Kanji / numeric") + (evil-test-buffer + "漢字123[4]" + ("b") + "漢字[1]234")) + (ert-info ("Kanji / Hiragana") + (evil-test-buffer + "漢字ひらが[な]" + ("b") + "漢字[ひ]らがな")) + (ert-info ("Kanji / Katakana") + (evil-test-buffer + "漢字カタカ[ナ]" + ("b") + "漢字[カ]タカナ")) + (ert-info ("Kanji / half-width Katakana") + (evil-test-buffer + "漢字カタカ[ナ]" + ("b") + "漢字[カ]タカナ")) + (ert-info ("Kanji / full-width alphabet") + (evil-test-buffer + "漢字AB[C]" + ("b") + "漢字[A]BC")) + (ert-info ("Kanji / full-width numeric") + (evil-test-buffer + "漢字12[3]" + ("b") + "漢字[1]23")) + (ert-info ("Kanji / Hangul") + (evil-test-buffer + "漢字한[글]" + ("b") + "漢字[한]글")) + (ert-info ("Hiragana / Latin") + (evil-test-buffer + "ひらがなabc[d]" + ("b") + "ひらがな[a]bcd")) + (ert-info ("Hiragana / numeric") + (evil-test-buffer + "ひらがな123[4]" + ("b") + "ひらがな[1]234")) + (ert-info ("Hiragana / Kanji") + (evil-test-buffer + "ひらがな漢[字]" + ("b") + "ひらがな[漢]字")) + (ert-info ("Hiragana / Katakana") + (evil-test-buffer + "ひらがなカタカ[ナ]" + ("b") + "ひらがな[カ]タカナ")) + (ert-info ("Hiragana / half-width Katakana") + (evil-test-buffer + "ひらがなカタカ[ナ]" + ("b") + "ひらがな[カ]タカナ")) + (ert-info ("Hiragana / full-width alphabet") + (evil-test-buffer + "ひらがなAB[C]" + ("b") + "ひらがな[A]BC")) + (ert-info ("Hiragana / full-width numeric") + (evil-test-buffer + "ひらがな12[3]" + ("b") + "ひらがな[1]23")) + (ert-info ("Hiragana / Hangul") + (evil-test-buffer + "ひらがな한[글]" + ("b") + "ひらがな[한]글")) + (ert-info ("Katakana / Latin") + (evil-test-buffer + "カタカナabc[d]" + ("b") + "カタカナ[a]bcd")) + (ert-info ("Katakana / numeric") + (evil-test-buffer + "カタカナ123[4]" + ("b") + "カタカナ[1]234")) + (ert-info ("Katakana / Kanji") + (evil-test-buffer + "カタカナ漢[字]" + ("b") + "カタカナ[漢]字")) + (ert-info ("Katakana / Hiragana") + (evil-test-buffer + "カタカナひらが[な]" + ("b") + "カタカナ[ひ]らがな")) + (ert-info ("Katakana / half-width Katakana") + (evil-test-buffer + "カタカナカタカ[ナ]" + ("b") + "カタカナ[カ]タカナ")) + (ert-info ("Katakana / full-width alphabet") + (evil-test-buffer + "カタカナAB[C]" + ("b") + "カタカナ[A]BC")) + (ert-info ("Katakana / full-width numeric") + (evil-test-buffer + "カタカナ12[3]" + ("b") + "カタカナ[1]23")) + (ert-info ("Katakana / Hangul") + (evil-test-buffer + "カタカナ한[글]" + ("b") + "カタカナ[한]글")) + (ert-info ("half-width Katakana / Latin") + (evil-test-buffer + "カタカナabc[d]" + ("b") + "[カ]タカナabcd")) + (ert-info ("half-width Katakana / numeric") + (evil-test-buffer + "カタカナ123[4]" + ("b") + "[カ]タカナ1234")) + (ert-info ("half-width Katakana / Kanji") + (evil-test-buffer + "カタカナ漢[字]" + ("b") + "カタカナ[漢]字")) + (ert-info ("half-width Katakana / Hiragana") + (evil-test-buffer + "カタカナひらが[な]" + ("b") + "カタカナ[ひ]らがな")) + (ert-info ("half-width Katakana / Katakana") + (evil-test-buffer + "カタカナカタカ[ナ]" + ("b") + "カタカナ[カ]タカナ")) + (ert-info ("half-width Katakana / full-width alphabet") + (evil-test-buffer + "カタカナAB[C]" + ("b") + "[カ]タカナABC")) + (ert-info ("half-width Katakana / full-width numeric") + (evil-test-buffer + "カタカナ12[3]" + ("b") + "[カ]タカナ123")) + (ert-info ("half-width Katakana / Hangul") + (evil-test-buffer + "カタカナ한[글]" + ("b") + "カタカナ[한]글")) + (ert-info ("full-width alphabet / Latin") + (evil-test-buffer + "ABCabc[d]" + ("b") + "[A]BCabcd")) + (ert-info ("full-width alphabet / numeric") + (evil-test-buffer + "ABC123[4]" + ("b") + "[A]BC1234")) + (ert-info ("full-width alphabet / Kanji") + (evil-test-buffer + "ABC漢[字]" + ("b") + "ABC[漢]字")) + (ert-info ("full-width alphabet / Hiragana") + (evil-test-buffer + "ABCひらが[な]" + ("b") + "ABC[ひ]らがな")) + (ert-info ("full-width alphabet / Katakana") + (evil-test-buffer + "ABCカタカ[ナ]" + ("b") + "ABC[カ]タカナ")) + (ert-info ("full-width alphabet / half-width Katakana") + (evil-test-buffer + "ABCカタカ[ナ]" + ("b") + "[A]BCカタカナ")) + (ert-info ("full-width alphabet / full-width numeric") + (evil-test-buffer + "ABC12[3]" + ("b") + "[A]BC123")) + (ert-info ("full-width alphabet / Hangul") + (evil-test-buffer + "ABC한[글]" + ("b") + "ABC[한]글")) + (ert-info ("full-width numeric / Latin") + (evil-test-buffer + "123abc[d]" + ("b") + "[1]23abcd")) + (ert-info ("full-width numeric / numeric") + (evil-test-buffer + "123123[4]" + ("b") + "[1]231234")) + (ert-info ("full-width numeric / Kanji") + (evil-test-buffer + "123漢[字]" + ("b") + "123[漢]字")) + (ert-info ("full-width numeric / Hiragana") + (evil-test-buffer + "123ひらが[な]" + ("b") + "123[ひ]らがな")) + (ert-info ("full-width numeric / Katakana") + (evil-test-buffer + "123カタカ[ナ]" + ("b") + "123[カ]タカナ")) + (ert-info ("full-width numeric / half-width Katakana") + (evil-test-buffer + "123カタカ[ナ]" + ("b") + "[1]23カタカナ")) + (ert-info ("full-width numeric / full-width alphabet") + (evil-test-buffer + "123AB[C]" + ("b") + "[1]23ABC")) + (ert-info ("full-width numeric / Hangul") + (evil-test-buffer + "123한[글]" + ("b") + "123[한]글")) + (ert-info ("Hangul / Latin") + (evil-test-buffer + "한글abc[d]" + ("b") + "한글[a]bcd")) + (ert-info ("Hangul / numeric") + (evil-test-buffer + "한글123[4]" + ("b") + "한글[1]234")) + (ert-info ("Hangul / Kanji") + (evil-test-buffer + "한글漢[字]" + ("b") + "한글[漢]字")) + (ert-info ("Hangul / Hiragana") + (evil-test-buffer + "한글ひらが[な]" + ("b") + "한글[ひ]らがな")) + (ert-info ("Hangul / Katakana") + (evil-test-buffer + "한글カタカ[ナ]" + ("b") + "한글[カ]タカナ")) + (ert-info ("Hangul / half-width Katakana") + (evil-test-buffer + "한글カタカ[ナ]" + ("b") + "한글[カ]タカナ")) + (ert-info ("Hangul / full-width alphabet") + (evil-test-buffer + "한글AB[C]" + ("b") + "한글[A]BC")) + (ert-info ("Hangul / full-width numeric") + (evil-test-buffer + "한글12[3]" + ("b") + "한글[1]23"))) + +(ert-deftest evil-test-backword-word-end-cjk () + "Test `evil-backward-word-end' on CJK words" + :tags '(evil motion cjk) + (ert-info ("Latin / numeric") + (evil-test-buffer + "abcd123[4]" + ("ge") + "[a]bcd1234")) + (ert-info ("Latin / Kanji") + (evil-test-buffer + "abcd漢[字]" + ("ge") + "abc[d]漢字")) + (ert-info ("Latin / Hiragana") + (evil-test-buffer + "abcdひらが[な]" + ("ge") + "abc[d]ひらがな")) + (ert-info ("Latin / Katakana") + (evil-test-buffer + "abcdカタカ[ナ]" + ("ge") + "abc[d]カタカナ")) + (ert-info ("Latin / half-width Katakana") + (evil-test-buffer + "abcdカタカ[ナ]" + ("ge") + "[a]bcdカタカナ")) + (ert-info ("Latin / full-width alphabet") + (evil-test-buffer + "abcdAB[C]" + ("ge") + "[a]bcdABC")) + (ert-info ("Latin / full-width numeric") + (evil-test-buffer + "abcd12[3]" + ("ge") + "[a]bcd123")) + (ert-info ("Latin / Hangul") + (evil-test-buffer + "abcd한[글]" + ("ge") + "abc[d]한글")) + (ert-info ("numeric / Latin") + (evil-test-buffer + "1234abc[d]" + ("ge") + "[1]234abcd")) + (ert-info ("numeric / Kanji") + (evil-test-buffer + "1234漢[字]" + ("ge") + "123[4]漢字")) + (ert-info ("numeric / Hiragana") + (evil-test-buffer + "1234ひらが[な]" + ("ge") + "123[4]ひらがな")) + (ert-info ("numeric / Katakana") + (evil-test-buffer + "1234カタカ[ナ]" + ("ge") + "123[4]カタカナ")) + (ert-info ("numeric / half-width Katakana") + (evil-test-buffer + "1234カタカ[ナ]" + ("ge") + "[1]234カタカナ")) + (ert-info ("numeric / full-width alphabet") + (evil-test-buffer + "1234AB[C]" + ("ge") + "[1]234ABC")) + (ert-info ("numeric / full-width numeric") + (evil-test-buffer + "123412[3]" + ("ge") + "[1]234123")) + (ert-info ("numeric / Hangul") + (evil-test-buffer + "1234한[글]" + ("ge") + "123[4]한글")) + (ert-info ("Kanji / Latin") + (evil-test-buffer + "漢字abc[d]" + ("ge") + "漢[字]abcd")) + (ert-info ("Kanji / numeric") + (evil-test-buffer + "漢字123[4]" + ("ge") + "漢[字]1234")) + (ert-info ("Kanji / Hiragana") + (evil-test-buffer + "漢字ひらが[な]" + ("ge") + "漢[字]ひらがな")) + (ert-info ("Kanji / Katakana") + (evil-test-buffer + "漢字カタカ[ナ]" + ("ge") + "漢[字]カタカナ")) + (ert-info ("Kanji / half-width Katakana") + (evil-test-buffer + "漢字カタカ[ナ]" + ("ge") + "漢[字]カタカナ")) + (ert-info ("Kanji / full-width alphabet") + (evil-test-buffer + "漢字AB[C]" + ("ge") + "漢[字]ABC")) + (ert-info ("Kanji / full-width numeric") + (evil-test-buffer + "漢字12[3]" + ("ge") + "漢[字]123")) + (ert-info ("Kanji / Hangul") + (evil-test-buffer + "漢字한[글]" + ("ge") + "漢[字]한글")) + (ert-info ("Hiragana / Latin") + (evil-test-buffer + "ひらがなabc[d]" + ("ge") + "ひらが[な]abcd")) + (ert-info ("Hiragana / numeric") + (evil-test-buffer + "ひらがな123[4]" + ("ge") + "ひらが[な]1234")) + (ert-info ("Hiragana / Kanji") + (evil-test-buffer + "ひらがな漢[字]" + ("ge") + "ひらが[な]漢字")) + (ert-info ("Hiragana / Katakana") + (evil-test-buffer + "ひらがなカタカ[ナ]" + ("ge") + "ひらが[な]カタカナ")) + (ert-info ("Hiragana / half-width Katakana") + (evil-test-buffer + "ひらがなカタカ[ナ]" + ("ge") + "ひらが[な]カタカナ")) + (ert-info ("Hiragana / full-width alphabet") + (evil-test-buffer + "ひらがなAB[C]" + ("ge") + "ひらが[な]ABC")) + (ert-info ("Hiragana / full-width numeric") + (evil-test-buffer + "ひらがな12[3]" + ("ge") + "ひらが[な]123")) + (ert-info ("Hiragana / Hangul") + (evil-test-buffer + "ひらがな한[글]" + ("ge") + "ひらが[な]한글")) + (ert-info ("Katakana / Latin") + (evil-test-buffer + "カタカナabc[d]" + ("ge") + "カタカ[ナ]abcd")) + (ert-info ("Katakana / numeric") + (evil-test-buffer + "カタカナ123[4]" + ("ge") + "カタカ[ナ]1234")) + (ert-info ("Katakana / Kanji") + (evil-test-buffer + "カタカナ漢[字]" + ("ge") + "カタカ[ナ]漢字")) + (ert-info ("Katakana / Hiragana") + (evil-test-buffer + "カタカナひらが[な]" + ("ge") + "カタカ[ナ]ひらがな")) + (ert-info ("Katakana / half-width Katakana") + (evil-test-buffer + "カタカナカタカ[ナ]" + ("ge") + "カタカ[ナ]カタカナ")) + (ert-info ("Katakana / full-width alphabet") + (evil-test-buffer + "カタカナAB[C]" + ("ge") + "カタカ[ナ]ABC")) + (ert-info ("Katakana / full-width numeric") + (evil-test-buffer + "カタカナ12[3]" + ("ge") + "カタカ[ナ]123")) + (ert-info ("Katakana / Hangul") + (evil-test-buffer + "カタカナ한[글]" + ("ge") + "カタカ[ナ]한글")) + (ert-info ("half-width Katakana / Latin") + (evil-test-buffer + "カタカナabc[d]" + ("ge") + "[カ]タカナabcd")) + (ert-info ("half-width Katakana / numeric") + (evil-test-buffer + "カタカナ123[4]" + ("ge") + "[カ]タカナ1234")) + (ert-info ("half-width Katakana / Kanji") + (evil-test-buffer + "カタカナ漢[字]" + ("ge") + "カタカ[ナ]漢字")) + (ert-info ("half-width Katakana / Hiragana") + (evil-test-buffer + "カタカナひらが[な]" + ("ge") + "カタカ[ナ]ひらがな")) + (ert-info ("half-width Katakana / Katakana") + (evil-test-buffer + "カタカナカタカ[ナ]" + ("ge") + "カタカ[ナ]カタカナ")) + (ert-info ("half-width Katakana / full-width alphabet") + (evil-test-buffer + "カタカナAB[C]" + ("ge") + "[カ]タカナABC")) + (ert-info ("half-width Katakana / full-width numeric") + (evil-test-buffer + "カタカナ12[3]" + ("ge") + "[カ]タカナ123")) + (ert-info ("half-width Katakana / Hangul") + (evil-test-buffer + "カタカナ한[글]" + ("ge") + "カタカ[ナ]한글")) + (ert-info ("full-width alphabet / Latin") + (evil-test-buffer + "ABCabc[d]" + ("ge") + "[A]BCabcd")) + (ert-info ("full-width alphabet / numeric") + (evil-test-buffer + "ABC123[4]" + ("ge") + "[A]BC1234")) + (ert-info ("full-width alphabet / Kanji") + (evil-test-buffer + "ABC漢[字]" + ("ge") + "AB[C]漢字")) + (ert-info ("full-width alphabet / Hiragana") + (evil-test-buffer + "ABCひらが[な]" + ("ge") + "AB[C]ひらがな")) + (ert-info ("full-width alphabet / Katakana") + (evil-test-buffer + "ABCカタカ[ナ]" + ("ge") + "AB[C]カタカナ")) + (ert-info ("full-width alphabet / half-width Katakana") + (evil-test-buffer + "ABCカタカ[ナ]" + ("ge") + "[A]BCカタカナ")) + (ert-info ("full-width alphabet / full-width numeric") + (evil-test-buffer + "ABC12[3]" + ("ge") + "[A]BC123")) + (ert-info ("full-width alphabet / Hangul") + (evil-test-buffer + "ABC한[글]" + ("ge") + "AB[C]한글")) + (ert-info ("full-width numeric / Latin") + (evil-test-buffer + "123abc[d]" + ("ge") + "[1]23abcd")) + (ert-info ("full-width numeric / numeric") + (evil-test-buffer + "123123[4]" + ("ge") + "[1]231234")) + (ert-info ("full-width numeric / Kanji") + (evil-test-buffer + "123漢[字]" + ("ge") + "12[3]漢字")) + (ert-info ("full-width numeric / Hiragana") + (evil-test-buffer + "123ひらが[な]" + ("ge") + "12[3]ひらがな")) + (ert-info ("full-width numeric / Katakana") + (evil-test-buffer + "123カタカ[ナ]" + ("ge") + "12[3]カタカナ")) + (ert-info ("full-width numeric / half-width Katakana") + (evil-test-buffer + "123カタカ[ナ]" + ("ge") + "[1]23カタカナ")) + (ert-info ("full-width numeric / full-width alphabet") + (evil-test-buffer + "123AB[C]" + ("ge") + "[1]23ABC")) + (ert-info ("full-width numeric / Hangul") + (evil-test-buffer + "123한[글]" + ("ge") + "12[3]한글")) + (ert-info ("Hangul / Latin") + (evil-test-buffer + "한글abc[d]" + ("ge") + "한[글]abcd")) + (ert-info ("Hangul / numeric") + (evil-test-buffer + "한글123[4]" + ("ge") + "한[글]1234")) + (ert-info ("Hangul / Kanji") + (evil-test-buffer + "한글漢[字]" + ("ge") + "한[글]漢字")) + (ert-info ("Hangul / Hiragana") + (evil-test-buffer + "한글ひらが[な]" + ("ge") + "한[글]ひらがな")) + (ert-info ("Hangul / Katakana") + (evil-test-buffer + "한글カタカ[ナ]" + ("ge") + "한[글]カタカナ")) + (ert-info ("Hangul / half-width Katakana") + (evil-test-buffer + "한글カタカ[ナ]" + ("ge") + "한[글]カタカナ")) + (ert-info ("Hangul / full-width alphabet") + (evil-test-buffer + "한글AB[C]" + ("ge") + "한[글]ABC")) + (ert-info ("Hangul / full-width numeric") + (evil-test-buffer + "한글12[3]" + ("ge") + "한[글]123"))) + +(ert-deftest evil-test-forward-paragraph () + "Test `evil-forward-paragraph'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + "[A]bove some line + +Below some empty line" + ("}") + "Above some line +\[] +Below some empty line")) + (ert-info ("With count") + (evil-test-buffer + "[A]bove some line + +Below some empty line" + ("2}") + "Above some line + +Below some empty lin[e]")) + (ert-info ("End of buffer") + (evil-test-buffer + "[B]elow some empty line" + ("100}") + "Below some empty lin[e]" + (should-error (execute-kbd-macro "}")) + (should-error (execute-kbd-macro "42}")))) + (ert-info ("End of buffer with newline") + (evil-test-buffer + "[B]elow some empty line\n\n" + ("100}") + "Below some empty line\n\n[]" + (should-error (execute-kbd-macro "}")) + (should-error (execute-kbd-macro "42}"))))) + +(ert-deftest evil-test-backward-paragraph () + "Test `evil-backward-paragraph'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + "Above some line + +Below some empty lin[e]" + ("{") + "Above some line +\[] +Below some empty line")) + (ert-info ("With count") + (evil-test-buffer + "Above some line + +Below some empty lin[e]" + ("2{") + "[A]bove some line + +Below some empty line")) + (ert-info ("Beginning of buffer") + (evil-test-buffer + "Above some line + +Below some empty lin[e]" + ("100{") + "[A]bove some line + +Below some empty line" + (should-error (execute-kbd-macro "{")) + (should-error (execute-kbd-macro "42{")))) + (ert-info ("Beginning of buffer with newlines") + (evil-test-buffer + "\n\nAbove some line + +Below some empty lin[e]" + ("100{") + "[]\n\nAbove some line + +Below some empty line" + (should-error (execute-kbd-macro "{")) + (should-error (execute-kbd-macro "42{"))))) + +(ert-deftest evil-test-forward-sentence () + "Test `evil-forward-sentence'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. + +Below some empty line." + (")") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. [I]f you want to create a file, +;; visit that file with C-x C-f. + +Below some empty line." + (")") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. +\[] +Below some empty line." + (")") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. + +\[B]elow some empty line.")) + (ert-info ("With count") + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. + +Below some empty line." + ("2)") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. +\[] +Below some empty line." + ("2)") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. + +Below some empty line[.]")) + (ert-info ("End of buffer") + (evil-test-buffer + "[B]elow some empty line." + ("100)") + "Below some empty line[.]" + (should-error (execute-kbd-macro ")")) + (should-error (execute-kbd-macro "42)")))) + (ert-info ("End of buffer with newline") + (evil-test-buffer + "[B]elow some empty line.\n\n" + (")") + "Below some empty line.\n[\n]" + (")") + "Below some empty line.\n\n[]" + (should-error (execute-kbd-macro ")")) + (should-error (execute-kbd-macro "42)"))))) + +(ert-deftest evil-test-backward-sentence () + "Test `evil-backward-sentence'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. + +Below some empty line[.]" + ("(") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. + +\[B]elow some empty line." + ("(") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. +\[] +Below some empty line." + ("(") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. [I]f you want to create a file, +;; visit that file with C-x C-f. + +Below some empty line." + ("(") + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. + +Below some empty line.")) + (ert-info ("With count") + (evil-test-buffer + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. + +Below some empty line[.]" + ("2(") + ";; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. +\[] +Below some empty line." + ("2(") + "[;]; This buffer is for notes you don't want to save, +;; and for Lisp evaluation. If you want to create a file, +;; visit that file with C-x C-f. + +Below some empty line.")) + (ert-info ("Beginning of buffer") + (evil-test-buffer + ";; This buffer is for notes you don't want to save[.]" + ("100(") + "[;]; This buffer is for notes you don't want to save." + (should-error (execute-kbd-macro "(")) + (should-error (execute-kbd-macro "42(")))) + (ert-info ("Beginning of buffer with newlines") + (evil-test-buffer + "\n\n;; This buffer is for notes you don't want to save[.]" + ("100(") + "[]\n\n;; This buffer is for notes you don't want to save." + (should-error (execute-kbd-macro "(")) + (should-error (execute-kbd-macro "42("))))) + +(ert-deftest evil-test-find-char () + "Test `evil-find-char'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + "[;]; This buffer is for notes." + ("fT") + ";; [T]his buffer is for notes.")) + (ert-info ("With count") + (evil-test-buffer + "[;]; This buffer is for notes." + ("2fe") + ";; This buffer is for not[e]s.")) + (ert-info ("Repeat") + (evil-test-buffer + "[;]; This buffer is for notes." + ("fe;") + ";; This buffer is for not[e]s.")) + (ert-info ("Repeat backward") + (evil-test-buffer + "[;]; This buffer is for notes." + ("2fe,") + ";; This buff[e]r is for notes.")) + (ert-info ("No match") + (evil-test-buffer + "[;]; This buffer is for notes." + (should-error (execute-kbd-macro "fL")))) + (ert-info ("End of line") + (let ((evil-cross-lines t)) + (evil-test-buffer + "[;]; This buffer is for notes, +;; and for Lisp evaluation." + ("fL") + ";; This buffer is for notes, +;; and for [L]isp evaluation."))) + (ert-info ("Empty line") + (evil-test-buffer + "[] +This buffer is for notes." + (should-error (execute-kbd-macro "fT"))))) + +(ert-deftest evil-test-find-char-backward () + "Test `evil-find-char-backward'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("FT") + ";; [T]his buffer is for notes.")) + (ert-info ("With count") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("2Fe") + ";; This buff[e]r is for notes.")) + (ert-info ("Repeat") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("Fe;") + ";; This buff[e]r is for notes.")) + (ert-info ("Repeat backward") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("2Fe,") + ";; This buffer is for not[e]s.")) + (ert-info ("No match") + (evil-test-buffer + ";; This buffer is for notes[.]" + (should-error (execute-kbd-macro "FL")))) + (ert-info ("End of line") + (let ((evil-cross-lines t)) + (evil-test-buffer + ";; This buffer is for notes, +;; and for Lisp evaluation[.]" + ("FT") + ";; [T]his buffer is for notes, +;; and for Lisp evaluation.")))) + +(ert-deftest evil-test-find-digraph-char () + "Test evil-read-digraph-char while finding char." + :tags '(evil motion) + (ert-info ("Find digraph char") + (evil-test-buffer + "a[b]cde∀g" + ("f\C-kFA") + "abcde[∀]g"))) + +(ert-deftest evil-test-find-char-to () + "Test `evil-find-char-to'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + "[;]; This buffer is for notes." + ("tT") + ";;[ ]This buffer is for notes.")) + (ert-info ("With count") + (evil-test-buffer + "[;]; This buffer is for notes." + ("2te") + ";; This buffer is for no[t]es.")) + (ert-info ("Repeat") + (evil-test-buffer + "[;]; This buffer is for notes." + ("tel;") + ";; This buffer is for no[t]es.")) + (ert-info ("Repeat backward") + (evil-test-buffer + "[;]; This buffer is for notes." + ("2te,") + ";; This buffe[r] is for notes.")) + (ert-info ("Repeat should skip adjacent character") + (let ((evil-repeat-find-to-skip-next t)) + (evil-test-buffer + "[a]aaxaaaxaaaxaaa" + ("tx;") + "aaaxaa[a]xaaaxaaa" + (";") + "aaaxaaaxaa[a]xaaa" + (",") + "aaaxaaax[a]aaxaaa" + (",") + "aaax[a]aaxaaaxaaa"))) + (ert-info ("Repeat should NOT skip adjacent character") + (let ((evil-repeat-find-to-skip-next nil)) + (evil-test-buffer + "[a]aaxaaaxaaaxaaa" + ("tx;") + "aa[a]xaaaxaaaxaaa"))) + (ert-info ("No match") + (evil-test-buffer + "[;]; This buffer is for notes." + (should-error (execute-kbd-macro "tL")))) + (ert-info ("End of line") + (let ((evil-cross-lines t)) + (evil-test-buffer + "[;]; This buffer is for notes, +;; and for Lisp evaluation." + ("tL") + ";; This buffer is for notes, +;; and for[ ]Lisp evaluation.")))) + +(ert-deftest evil-test-find-char-to-backward () + "Test `evil-find-char-to-backward'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("TT") + ";; T[h]is buffer is for notes.")) + (ert-info ("With count") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("2Te") + ";; This buffe[r] is for notes.")) + (ert-info ("Repeat") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("Teh;") + ";; This buffe[r] is for notes.")) + (ert-info ("Repeat backward") + (evil-test-buffer + ";; This buffer is for notes[.]" + ("2Te,") + ";; This buffer is for no[t]es.")) + (ert-info ("Repeat should skip adjacent character") + (let ((evil-repeat-find-to-skip-next t)) + (evil-test-buffer + "aaaxaaaxaaaxaa[a]" + ("Tx;") + "aaaxaaax[a]aaxaaa" + (";") + "aaax[a]aaxaaaxaaa" + (",") + "aaaxaa[a]xaaaxaaa" + (",") + "aaaxaaaxaa[a]xaaa"))) + (ert-info ("Repeat should NOT skip adjacent character") + (let ((evil-repeat-find-to-skip-next nil)) + (evil-test-buffer + "aaaxaaaxaaaxaa[a]" + ("Tx;") + "aaaxaaaxaaax[a]aa"))) + (ert-info ("No match") + (evil-test-buffer + ";; This buffer is for notes[.]" + (should-error (execute-kbd-macro "TL")))) + (ert-info ("End of line") + (let ((evil-cross-lines t)) + (evil-test-buffer + ";; This buffer is for notes, +;; and for Lisp evaluation[.]" + ("TT") + ";; T[h]is buffer is for notes, +;; and for Lisp evaluation.")))) + +(ert-deftest evil-test-jump-item () + "Test `evil-jump-item'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + "int main[(]int argc, char** argv)" + ("%") + "int main(int argc, char** argv[)]" + ("%") + "int main[(]int argc, char** argv)")) + (ert-info ("Before parenthesis") + (evil-test-buffer + "[i]nt main(int argc, char** argv)" + ("%") + "int main(int argc, char** argv[)]" + ("5h") + "int main(int argc, char**[ ]argv)" + ("%") + "int main[(]int argc, char** argv)")) + (ert-info ("Over several lines") + (evil-test-buffer + "int main(int argc, char** argv) +\[{] + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +}" + ("%") + "int main(int argc, char** argv) +{ + printf(\"Hello world\\n\"); + return EXIT_SUCCESS; +\[}]")) + (ert-info ("On line without parenthesis") + (evil-test-buffer + "[#]include " + (should-error (execute-kbd-macro "%")))) + (ert-info ("Before unmatched opening parenthesies") + (evil-test-buffer + "x[x]xx ( yyyyy () zzzz" + (should-error (execute-kbd-macro "%")) + "x[x]xx ( yyyyy () zzzz")) + (ert-info ("Before unmatched closing parenthesies") + (evil-test-buffer + "x[x]xx ) yyyyy () zzzz" + (should-error (execute-kbd-macro "%")) + "x[x]xx ) yyyyy () zzzz")) + + (ert-info ("At the end of the line") + (evil-test-buffer + "[p]ublic void foo(String bar) {\n blabla;\n}\n" + ("v$%") + "public void foo(String bar) {\n blabla;\n[}]\n"))) + +(ert-deftest evil-test-unmatched-paren () + "Test `evil-previous-open-paren' and `evil-next-close-paren'" + :tags '(evil motion) + (ert-info ("Simple") + (evil-test-buffer + "foo ( { ( [b]ar ) baz } )" + ("[(") + "foo ( { [(] bar ) baz } )" + ("])") + "foo ( { ( bar [)] baz } )" + ("[(") + "foo ( { [(] bar ) baz } )" + ("[(") + "foo [(] { ( bar ) baz } )" + ("f)])") + "foo ( { ( bar ) baz } [)]")) + (ert-info ("With count") + (evil-test-buffer + "foo ( { ( [b]ar ) baz } )" + ("2[(") + "foo [(] { ( bar ) baz } )") + (evil-test-buffer + "foo ( { ( [b]ar ) baz } )" + ("2])") + "foo ( { ( bar ) baz } [)]"))) + +(ert-deftest evil-test-paren-jump-from-string () + "Test jump to next/prev paren from a string" + :tags '(evil motion) + (evil-test-buffer + "{ + fun(a, \"T[]est\", test()); +} +" + ("[(") + "{ + fun[(]a, \"Test\", test()); +} +" + ("])") + "{ + fun(a, \"Test\", test()[)]; +} +") + (evil-test-buffer + "{ + fun(a, \"T[]est\", test()); +} +" + ("])") + "{ + fun(a, \"Test\", test()[)]; +} +" + ("[(") + "{ + fun[(]a, \"Test\", test()); +} +") + (evil-test-buffer + "{ + fun(a, \"T[]est\", test()); +} +" + ("[{") + "[{] + fun(a, \"Test\", test()); +} +") + (evil-test-buffer + "{ + fun(a, \"T[]est\", test()); +} +" + ("]}") + "{ + fun(a, \"Test\", test()); +[}] +")) + +(ert-deftest evil-test-paren-jump-inside-string-from-string () + "Test jump to next/prev paren inside string from a string" + :tags '(evil motion) + (evil-test-buffer + "{ (\"Test with paren (inside multi (l[e]vel))\", test()); } " + ("[(") + "{ (\"Test with paren (inside multi [(]level))\", test()); } " + ("[(") + "{ (\"Test with paren [(]inside multi (level))\", test()); } ") + (evil-test-buffer + "{ (\"Test with paren (inside multi (l[e]vel))\", test()); } " + ("])") + "{ (\"Test with paren (inside multi (level[)])\", test()); } " + ("])") + "{ (\"Test with paren (inside multi (level)[)]\", test()); } ") + (evil-test-buffer + "{ (\"Test with paren {inside multi {l[e]vel}}\", test()); } " + ("[{") + "{ (\"Test with paren {inside multi [{]level}}\", test()); } " + ("[{") + "{ (\"Test with paren [{]inside multi {level}}\", test()); } ") + (evil-test-buffer + "{ (\"Test with paren {inside multi {l[e]vel}}\", test()); } " + ("]}") + "{ (\"Test with paren {inside multi {level[}]}\", test()); } " + ("]}") + "{ (\"Test with paren {inside multi {level}[}]\", test()); } ")) + +(ert-deftest evil-test-insert-resume () + "Test `evil-insert-resume'" + :tags '(evil motion) + (ert-info ("Can move to last insert location and take a count") + (evil-test-buffer + "alpha bravo [ ]delta echo" + ("i" "charlie" [escape] "^") + "[a]lpha bravo charlie delta echo" + ("2gi" " zulu" [escape]) + "alpha bravo charlie zulu zul[u] delta echo")) + (ert-info ("Can move to `^' marker in visual line state") + (evil-test-buffer + "alpha [b]ravo\ncharlie delta\necho foxtrot" + ("i" [escape] "/foxtrot" [return]) + "alpha bravo\ncharlie delta\necho [f]oxtrot" + ("Vk") + "alpha bravo\ncharl[i]e delta\necho foxtrot" + ("gi") + "alpha [b]ravo\ncharlie delta\necho foxtrot" + ("vd") + "alpha [o]xtrot"))) + +(ert-deftest evil-test-next-mark () + "Test `evil-next-mark', `evil-previous-mark'" + :tags '(evil motion) + (ert-info ("Can move to next mark, next mark line, +previous mark and previous mark line") + (evil-test-buffer + "[a]lpha bravo +charlie delta echo +foxtrot golf hotel +india juliet" + ("ma" "w" "mb" "w" + "mc" "w" "md" "w" "me" "w" + "mf" "w" "mg" "w" "mh" "w" + "mi" "w" "mj") + "alpha bravo +charlie delta echo +foxtrot golf hotel +india [j]uliet" + ("3]`") + "alpha bravo +[c]harlie delta echo +foxtrot golf hotel +india juliet" + ("]'") + "alpha bravo +charlie delta echo +[f]oxtrot golf hotel +india juliet" + ("[`") + "alpha bravo +charlie delta [e]cho +foxtrot golf hotel +india juliet" + ("2['") + "alpha bravo +charlie delta echo +foxtrot golf hotel +[i]ndia juliet"))) + +(ert-deftest evil-set-col-0-mark-test () + "Test `evil-set-col-0-mark' ex command" + :tags '(evil ex) + (evil-test-buffer + "Lin[e] 1 +Line 2" + (":mark k" [return] "G" "`k") + "[L]ine 1 +Line 2")) + +(ert-deftest evil-delete-marks-test () + "Test `evil-delete-marks' ex command" + :tags '(evil ex) + (ert-info ("Can delete marks") + (evil-test-buffer + "[O]ne line is enough if we use backtick to navigate" + ("mO" "w" "ml" "w" "mi" "$b" "m4") + "One line is enough if we use backtick to [n]avigate" + ("`O") + "[O]ne line is enough if we use backtick to navigate" + (":delm O" [return] "`i") + "One line [i]s enough if we use backtick to navigate" + (error user-error "`O") + "One line [i]s enough if we use backtick to navigate" + ("`4") + "One line is enough if we use backtick to [n]avigate" + (":delm h-m" [return]) + (error user-error "`i") + "One line is enough if we use backtick to [n]avigate"))) + +(ert-deftest evil-automatic-marks-test () + "Test that certain commands automatically set mark(er)s" + :tags '(evil) + (ert-info ("Visual selection sets angle-bracket marks") + (evil-test-buffer + "alpha br[a]vo charlie" + ("viw" [escape] "$") + "alpha bravo charli[e]" + ("`>") + "alpha brav[o] charlie" + ("`<") + "alpha [b]ravo charlie"))) + +(ert-deftest evil-test-flyspell-motions () + "Test flyspell motions" + :tags '(evil motion) + (skip-unless (condition-case _ (progn (ispell-check-version) t) (error))) + (ert-info ("Simple") + (evil-test-buffer + "[I] cannt tpye for lyfe" + (flyspell-mode) + (flyspell-buffer) + ("]s") + "I [c]annt tpye for lyfe" + ("]s") + "I cannt [t]pye for lyfe" + ("]s") + "I cannt tpye for [l]yfe" + ("]s") + "I [c]annt tpye for lyfe" + ("[s") + "I cannt tpye for [l]yfe" + ("[s") + "I cannt [t]pye for lyfe") + (evil-test-buffer + "[o]en otw thre" + (flyspell-mode) + (flyspell-buffer) + ("]s") + "oen [o]tw thre" + ("]s") + "oen otw [t]hre" + ("]s") + "[o]en otw thre")) + (ert-info ("With count") + (evil-test-buffer + "[I] cannt tpye for lyfe" + (flyspell-mode) + (flyspell-buffer) + ("2]s") + "I cannt [t]pye for lyfe" + ("2]s") + "I [c]annt tpye for lyfe" + ("2[s") + "I cannt [t]pye for lyfe" + ("2[s") + "I cannt tpye for [l]yfe")) + (ert-info ("With evil-search-wrap disabled") + (let (evil-search-wrap) + (evil-test-buffer + "[I] cannt tpye for lyfe" + (flyspell-mode) + (flyspell-buffer) + ("]s") + "I [c]annt tpye for lyfe" + ("]s") + "I cannt [t]pye for lyfe" + ("]s") + "I cannt tpye for [l]yfe" + (error search-failed "]s")))) + (ert-info ("One mistake") + (evil-test-buffer + "[I]'m almst there..." + (flyspell-mode) + (flyspell-buffer) + ("]s") + "I'm [a]lmst there..." + ("]s") + "I'm [a]lmst there...")) + (ert-info ("No mistakes") + (evil-test-buffer + "[I]'ve learned to type!" + (flyspell-mode) + (flyspell-buffer) + (error search-failed "]s") + (error search-failed "[s")))) + +;;; Text objects + +(ert-deftest evil-test-text-object () + "Test `evil-define-text-object'" + :tags '(evil text-object) + (let ((object (evil-define-text-object nil (count &optional beg end _type) + (let ((sel (and beg end (evil-range beg end)))) + (when (and sel (> count 0)) (forward-char 1)) + (let ((range (if (< count 0) + (list (- (point) 3) (point)) + (list (point) (+ (point) 3))))) + (if sel + (evil-range-union range sel) + range)))))) + (ert-info ("Select three characters after point") + (evil-test-buffer + :state operator + ";; [T]his buffer is for notes." + (should (equal (funcall object 1) '(4 7 inclusive))))) + (ert-info ("Select three characters before point") + (evil-test-buffer + :state operator + ";; [T]his buffer is for notes." + (should (equal (funcall object -1) '(1 4 inclusive))))) + (ert-info ("Select three characters after selection") + (evil-test-buffer + ";; buffer is for notes." + (call-interactively object) + ";; ffer is for notes.")) + (ert-info ("Select three characters before selection") + (evil-test-buffer + ";; <[T]his> buffer is for notes." + (call-interactively object) + "<[;]; This> buffer is for notes.")) + (ert-info ("Delete three characters after point") + (evil-test-buffer + "[;]; This buffer is for notes." + (define-key evil-operator-state-local-map "io" object) + ("dio") + "[T]his buffer is for notes.")))) + +(ert-deftest evil-test-word-objects () + "Test `evil-inner-word' and `evil-a-word'" + :tags '(evil text-object) + (ert-info ("Select a word") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("viw") + ";; buffer is for notes.") + (evil-test-buffer + ";; [T]his buffer is for notes." + ("vaw") + ";; buffer is for notes.") + (evil-test-buffer + ";; Thi[s] buffer is for notes." + ("viw") + ";; buffer is for notes.") + (evil-test-buffer + ";; Thi[s] buffer is for notes." + ("vaw") + ";; buffer is for notes.")) + (ert-info ("Select two words") + (ert-info ("Include whitespace on this side") + (evil-test-buffer + ";;< Thi[s]> buffer is for notes." + ("aw") + ";;< This buffe[r]> is for notes.") + (evil-test-buffer + ";; This <[b]uffer >is for notes." + ("aw") + ";; <[T]his buffer >is for notes.")) + (ert-info ("Include whitespace on the other side") + (evil-test-buffer + ";; buffer is for notes." + ("aw") + ";; is for notes.") + (evil-test-buffer + ";; This<[ ]buffer> is for notes." + ("aw") + ";;<[ ]This buffer> is for notes."))) + (ert-info ("select first visual word") + (evil-test-buffer + "([a])" + ("viw") + "(<[a]>)")) + (ert-info ("Deleting whitespace is confined to the line") + (evil-test-buffer + "foo\n [ ] bar" + ("diw") + "foo\n[b]ar") + (evil-test-buffer + "foo\n [ ] bar" + ("diW") + "foo\n[b]ar") + (evil-test-buffer + "fo[o]\nbar" + ("diW") + "[\n]bar") + (evil-test-buffer + "foo\n [ ] bar" + ("daw") + "foo\n[]") + (evil-test-buffer + "foo\n [ ] bar" + ("daW") + "foo\n[]")) + (ert-info ("Deleting the only word on a line doesn't delete indentation") + (evil-test-buffer + " [b]ar" + ("daw") + " [ ]") + (evil-test-buffer + " [b]ar" + ("daW") + " [ ]") + (ert-info ("But deleting the final word of many behaves as normal") + (evil-test-buffer + " foo [b]ar" + ("daw") + " fo[o]"))) + (ert-info ("Deleting whitespace on whitepsace only line") + (evil-test-buffer + "foo\n[ ]\nbar" + ("diw") + "foo\n[]\nbar") + (evil-test-buffer + "foo\n[ ]\nbar" + ("daw") + "foo\n[]\nbar"))) + +(ert-deftest evil-test-word-objects-cjk () + "Test `evil-inner-word' and `evil-a-word' on CJK words" + :tags '(evil text-object cjk) + (ert-info ("Select a word") + (evil-test-buffer + "[a]bcd1234" + ("viw") + "") + (evil-test-buffer + "[a]bcd1234" + ("vaw") + "") + (evil-test-buffer + "[a]bcd漢字" + ("viw") + "漢字") + (evil-test-buffer + "[a]bcd漢字" + ("vaw") + "漢字") + (evil-test-buffer + "[a]bcdひらがな" + ("viw") + "ひらがな") + (evil-test-buffer + "[a]bcdひらがな" + ("vaw") + "ひらがな") + (evil-test-buffer + "[a]bcdカタカナ" + ("viw") + "カタカナ") + (evil-test-buffer + "[a]bcdカタカナ" + ("vaw") + "カタカナ") + (evil-test-buffer + "[a]bcdカタカナ" + ("viw") + "") + (evil-test-buffer + "[a]bcdカタカナ" + ("vaw") + "") + (evil-test-buffer + "[a]bcdABC" + ("viw") + "") + (evil-test-buffer + "[a]bcdABC" + ("vaw") + "") + (evil-test-buffer + "[a]bcd123" + ("viw") + "") + (evil-test-buffer + "[a]bcd123" + ("vaw") + "") + (evil-test-buffer + "[a]bcd한글" + ("viw") + "한글") + (evil-test-buffer + "[a]bcd한글" + ("vaw") + "한글") + (evil-test-buffer + "[1]234abcd" + ("viw") + "<1234abc[d]>") + (evil-test-buffer + "[1]234abcd" + ("vaw") + "<1234abc[d]>") + (evil-test-buffer + "[1]234漢字" + ("viw") + "<123[4]>漢字") + (evil-test-buffer + "[1]234漢字" + ("vaw") + "<123[4]>漢字") + (evil-test-buffer + "[1]234ひらがな" + ("viw") + "<123[4]>ひらがな") + (evil-test-buffer + "[1]234ひらがな" + ("vaw") + "<123[4]>ひらがな") + (evil-test-buffer + "[1]234カタカナ" + ("viw") + "<123[4]>カタカナ") + (evil-test-buffer + "[1]234カタカナ" + ("vaw") + "<123[4]>カタカナ") + (evil-test-buffer + "[1]234カタカナ" + ("viw") + "<1234カタカ[ナ]>") + (evil-test-buffer + "[1]234カタカナ" + ("vaw") + "<1234カタカ[ナ]>") + (evil-test-buffer + "[1]234ABC" + ("viw") + "<1234AB[C]>") + (evil-test-buffer + "[1]234ABC" + ("vaw") + "<1234AB[C]>") + (evil-test-buffer + "[1]234123" + ("viw") + "<123412[3]>") + (evil-test-buffer + "[1]234123" + ("vaw") + "<123412[3]>") + (evil-test-buffer + "[1]234한글" + ("viw") + "<123[4]>한글") + (evil-test-buffer + "[1]234한글" + ("vaw") + "<123[4]>한글") + (evil-test-buffer + "[漢]字abcd" + ("viw") + "<漢[字]>abcd") + (evil-test-buffer + "[漢]字abcd" + ("vaw") + "<漢[字]>abcd") + (evil-test-buffer + "[漢]字1234" + ("viw") + "<漢[字]>1234") + (evil-test-buffer + "[漢]字1234" + ("vaw") + "<漢[字]>1234") + (evil-test-buffer + "[漢]字ひらがな" + ("viw") + "<漢[字]>ひらがな") + (evil-test-buffer + "[漢]字ひらがな" + ("vaw") + "<漢[字]>ひらがな") + (evil-test-buffer + "[漢]字カタカナ" + ("viw") + "<漢[字]>カタカナ") + (evil-test-buffer + "[漢]字カタカナ" + ("vaw") + "<漢[字]>カタカナ") + (evil-test-buffer + "[漢]字カタカナ" + ("viw") + "<漢[字]>カタカナ") + (evil-test-buffer + "[漢]字カタカナ" + ("vaw") + "<漢[字]>カタカナ") + (evil-test-buffer + "[漢]字ABC" + ("viw") + "<漢[字]>ABC") + (evil-test-buffer + "[漢]字ABC" + ("vaw") + "<漢[字]>ABC") + (evil-test-buffer + "[漢]字123" + ("viw") + "<漢[字]>123") + (evil-test-buffer + "[漢]字123" + ("vaw") + "<漢[字]>123") + (evil-test-buffer + "[漢]字한글" + ("viw") + "<漢[字]>한글") + (evil-test-buffer + "[漢]字한글" + ("vaw") + "<漢[字]>한글") + (evil-test-buffer + "[ひ]らがなabcd" + ("viw") + "<ひらが[な]>abcd") + (evil-test-buffer + "[ひ]らがなabcd" + ("vaw") + "<ひらが[な]>abcd") + (evil-test-buffer + "[ひ]らがな1234" + ("viw") + "<ひらが[な]>1234") + (evil-test-buffer + "[ひ]らがな1234" + ("vaw") + "<ひらが[な]>1234") + (evil-test-buffer + "[ひ]らがな漢字" + ("viw") + "<ひらが[な]>漢字") + (evil-test-buffer + "[ひ]らがな漢字" + ("vaw") + "<ひらが[な]>漢字") + (evil-test-buffer + "[ひ]らがなカタカナ" + ("viw") + "<ひらが[な]>カタカナ") + (evil-test-buffer + "[ひ]らがなカタカナ" + ("vaw") + "<ひらが[な]>カタカナ") + (evil-test-buffer + "[ひ]らがなカタカナ" + ("viw") + "<ひらが[な]>カタカナ") + (evil-test-buffer + "[ひ]らがなカタカナ" + ("vaw") + "<ひらが[な]>カタカナ") + (evil-test-buffer + "[ひ]らがなABC" + ("viw") + "<ひらが[な]>ABC") + (evil-test-buffer + "[ひ]らがなABC" + ("vaw") + "<ひらが[な]>ABC") + (evil-test-buffer + "[ひ]らがな123" + ("viw") + "<ひらが[な]>123") + (evil-test-buffer + "[ひ]らがな123" + ("vaw") + "<ひらが[な]>123") + (evil-test-buffer + "[ひ]らがな한글" + ("viw") + "<ひらが[な]>한글") + (evil-test-buffer + "[ひ]らがな한글" + ("vaw") + "<ひらが[な]>한글") + (evil-test-buffer + "[カ]タカナabcd" + ("viw") + "<カタカ[ナ]>abcd") + (evil-test-buffer + "[カ]タカナabcd" + ("vaw") + "<カタカ[ナ]>abcd") + (evil-test-buffer + "[カ]タカナ1234" + ("viw") + "<カタカ[ナ]>1234") + (evil-test-buffer + "[カ]タカナ1234" + ("vaw") + "<カタカ[ナ]>1234") + (evil-test-buffer + "[カ]タカナ漢字" + ("viw") + "<カタカ[ナ]>漢字") + (evil-test-buffer + "[カ]タカナ漢字" + ("vaw") + "<カタカ[ナ]>漢字") + (evil-test-buffer + "[カ]タカナひらがな" + ("viw") + "<カタカ[ナ]>ひらがな") + (evil-test-buffer + "[カ]タカナひらがな" + ("vaw") + "<カタカ[ナ]>ひらがな") + (evil-test-buffer + "[カ]タカナカタカナ" + ("viw") + "<カタカ[ナ]>カタカナ") + (evil-test-buffer + "[カ]タカナカタカナ" + ("vaw") + "<カタカ[ナ]>カタカナ") + (evil-test-buffer + "[カ]タカナABC" + ("viw") + "<カタカ[ナ]>ABC") + (evil-test-buffer + "[カ]タカナABC" + ("vaw") + "<カタカ[ナ]>ABC") + (evil-test-buffer + "[カ]タカナ123" + ("viw") + "<カタカ[ナ]>123") + (evil-test-buffer + "[カ]タカナ123" + ("vaw") + "<カタカ[ナ]>123") + (evil-test-buffer + "[カ]タカナ한글" + ("viw") + "<カタカ[ナ]>한글") + (evil-test-buffer + "[カ]タカナ한글" + ("vaw") + "<カタカ[ナ]>한글") + (evil-test-buffer + "[カ]タカナabcd" + ("viw") + "<カタカナabc[d]>") + (evil-test-buffer + "[カ]タカナabcd" + ("vaw") + "<カタカナabc[d]>") + (evil-test-buffer + "[カ]タカナ1234" + ("viw") + "<カタカナ123[4]>") + (evil-test-buffer + "[カ]タカナ1234" + ("vaw") + "<カタカナ123[4]>") + (evil-test-buffer + "[カ]タカナ漢字" + ("viw") + "<カタカ[ナ]>漢字") + (evil-test-buffer + "[カ]タカナ漢字" + ("vaw") + "<カタカ[ナ]>漢字") + (evil-test-buffer + "[カ]タカナひらがな" + ("viw") + "<カタカ[ナ]>ひらがな") + (evil-test-buffer + "[カ]タカナひらがな" + ("vaw") + "<カタカ[ナ]>ひらがな") + (evil-test-buffer + "[カ]タカナカタカナ" + ("viw") + "<カタカ[ナ]>カタカナ") + (evil-test-buffer + "[カ]タカナカタカナ" + ("vaw") + "<カタカ[ナ]>カタカナ") + (evil-test-buffer + "[カ]タカナABC" + ("viw") + "<カタカナAB[C]>") + (evil-test-buffer + "[カ]タカナABC" + ("vaw") + "<カタカナAB[C]>") + (evil-test-buffer + "[カ]タカナ123" + ("viw") + "<カタカナ12[3]>") + (evil-test-buffer + "[カ]タカナ123" + ("vaw") + "<カタカナ12[3]>") + (evil-test-buffer + "[カ]タカナ한글" + ("viw") + "<カタカ[ナ]>한글") + (evil-test-buffer + "[カ]タカナ한글" + ("vaw") + "<カタカ[ナ]>한글") + (evil-test-buffer + "[A]BCabcd" + ("viw") + "<ABCabc[d]>") + (evil-test-buffer + "[A]BCabcd" + ("vaw") + "<ABCabc[d]>") + (evil-test-buffer + "[A]BC1234" + ("viw") + "<ABC123[4]>") + (evil-test-buffer + "[A]BC1234" + ("vaw") + "<ABC123[4]>") + (evil-test-buffer + "[A]BC漢字" + ("viw") + "<AB[C]>漢字") + (evil-test-buffer + "[A]BC漢字" + ("vaw") + "<AB[C]>漢字") + (evil-test-buffer + "[A]BCひらがな" + ("viw") + "<AB[C]>ひらがな") + (evil-test-buffer + "[A]BCひらがな" + ("vaw") + "<AB[C]>ひらがな") + (evil-test-buffer + "[A]BCカタカナ" + ("viw") + "<AB[C]>カタカナ") + (evil-test-buffer + "[A]BCカタカナ" + ("vaw") + "<AB[C]>カタカナ") + (evil-test-buffer + "[A]BCカタカナ" + ("viw") + "<ABCカタカ[ナ]>") + (evil-test-buffer + "[A]BCカタカナ" + ("vaw") + "<ABCカタカ[ナ]>") + (evil-test-buffer + "[A]BC123" + ("viw") + "<ABC12[3]>") + (evil-test-buffer + "[A]BC123" + ("vaw") + "<ABC12[3]>") + (evil-test-buffer + "[A]BC한글" + ("viw") + "<AB[C]>한글") + (evil-test-buffer + "[A]BC한글" + ("vaw") + "<AB[C]>한글") + (evil-test-buffer + "[1]23abcd" + ("viw") + "<123abc[d]>") + (evil-test-buffer + "[1]23abcd" + ("vaw") + "<123abc[d]>") + (evil-test-buffer + "[1]231234" + ("viw") + "<123123[4]>") + (evil-test-buffer + "[1]231234" + ("vaw") + "<123123[4]>") + (evil-test-buffer + "[1]23漢字" + ("viw") + "<12[3]>漢字") + (evil-test-buffer + "[1]23漢字" + ("vaw") + "<12[3]>漢字") + (evil-test-buffer + "[1]23ひらがな" + ("viw") + "<12[3]>ひらがな") + (evil-test-buffer + "[1]23ひらがな" + ("vaw") + "<12[3]>ひらがな") + (evil-test-buffer + "[1]23カタカナ" + ("viw") + "<12[3]>カタカナ") + (evil-test-buffer + "[1]23カタカナ" + ("vaw") + "<12[3]>カタカナ") + (evil-test-buffer + "[1]23カタカナ" + ("viw") + "<123カタカ[ナ]>") + (evil-test-buffer + "[1]23カタカナ" + ("vaw") + "<123カタカ[ナ]>") + (evil-test-buffer + "[1]23ABC" + ("viw") + "<123AB[C]>") + (evil-test-buffer + "[1]23ABC" + ("vaw") + "<123AB[C]>") + (evil-test-buffer + "[1]23한글" + ("viw") + "<12[3]>한글") + (evil-test-buffer + "[1]23한글" + ("vaw") + "<12[3]>한글") + (evil-test-buffer + "[한]글abcd" + ("viw") + "<한[글]>abcd") + (evil-test-buffer + "[한]글abcd" + ("vaw") + "<한[글]>abcd") + (evil-test-buffer + "[한]글1234" + ("viw") + "<한[글]>1234") + (evil-test-buffer + "[한]글1234" + ("vaw") + "<한[글]>1234") + (evil-test-buffer + "[한]글漢字" + ("viw") + "<한[글]>漢字") + (evil-test-buffer + "[한]글漢字" + ("vaw") + "<한[글]>漢字") + (evil-test-buffer + "[한]글ひらがな" + ("viw") + "<한[글]>ひらがな") + (evil-test-buffer + "[한]글ひらがな" + ("vaw") + "<한[글]>ひらがな") + (evil-test-buffer + "[한]글カタカナ" + ("viw") + "<한[글]>カタカナ") + (evil-test-buffer + "[한]글カタカナ" + ("vaw") + "<한[글]>カタカナ") + (evil-test-buffer + "[한]글カタカナ" + ("viw") + "<한[글]>カタカナ") + (evil-test-buffer + "[한]글カタカナ" + ("vaw") + "<한[글]>カタカナ") + (evil-test-buffer + "[한]글ABC" + ("viw") + "<한[글]>ABC") + (evil-test-buffer + "[한]글ABC" + ("vaw") + "<한[글]>ABC") + (evil-test-buffer + "[한]글123" + ("viw") + "<한[글]>123") + (evil-test-buffer + "[한]글123" + ("vaw") + "<한[글]>123"))) + +(ert-deftest evil-test-paragraph-objects () + "Test `evil-inner-paragraph' and `evil-a-paragraph'" + :tags '(evil text-object) + (ert-info ("Select a paragraph with point at beginning") + (evil-test-buffer + "[;]; This buffer is for notes, +;; and for Lisp evaluation. + +;; This buffer is for notes, +;; and for Lisp evaluation." + ("vap") + "<;; This buffer is for notes, +;; and for Lisp evaluation. +\[]\n>\ +;; This buffer is for notes, +;; and for Lisp evaluation.")) + (ert-info ("Select a paragraph with point at last line") + (evil-test-buffer + ";; This buffer is for notes, +\[;]; and for Lisp evaluation. + +;; This buffer is for notes, +;; and for Lisp evaluation." + ("vap") + "<;; This buffer is for notes, +;; and for Lisp evaluation. +\[]\n>\ +;; This buffer is for notes, +;; and for Lisp evaluation.")) + (ert-info ("Select a paragraph with point after paragraph") + (evil-test-buffer + ";; This buffer is for notes, +;; and for Lisp evaluation. +\[] +;; This buffer is for notes, +;; and for Lisp evaluation." + ("vap") + ";; This buffer is for notes, +;; and for Lisp evaluation. +< +;; This buffer is for notes, +;; and for Lisp evaluation[.]>")) + (ert-info ("Select inner paragraph") + (evil-test-buffer + "[;]; This buffer is for notes, +;; and for Lisp evaluation. + +;; This buffer is for notes, +;; and for Lisp evaluation." + ("vip") + "<;; This buffer is for notes, +;; and for Lisp evaluation.[] +> +;; This buffer is for notes, +;; and for Lisp evaluation.") + (evil-test-buffer + ";; This buffer is for notes, +\[;]; and for Lisp evaluation. + +;; This buffer is for notes, +;; and for Lisp evaluation." + ("vip") + "<;; This buffer is for notes, +;; and for Lisp evaluation.[] +> +;; This buffer is for notes, +;; and for Lisp evaluation.") + (evil-test-buffer + ";; This buffer is for notes, +;; and for Lisp evaluation. +\[] +;; This buffer is for notes, +;; and for Lisp evaluation." + ("vip") + ";; This buffer is for notes, +;; and for Lisp evaluation. +< +;; This buffer is for notes, +;; and for Lisp evaluation[.]>"))) + +(ert-deftest evil-test-quote-objects () + "Test `evil-inner-single-quote' and `evil-a-single-quote'" + :tags '(evil text-object) + (ert-info ("Select text inside of '...'") + (evil-test-buffer + "This is 'a [t]est' for quote objects." + ("vi'") + "This is '' for quote objects.") + (evil-test-buffer + "This is \"a '[t]est'\" for quote objects." + ("vi'") + "This is \"a ''\" for quote objects.")) + (ert-info ("Select text including enclosing quotes") + (evil-test-buffer + "This is 'a [t]est' for quote objects." + ("v2i'") + "This is <'a test[']> for quote objects.")) + (ert-info ("Select text including enclosing quotes and following space") + (evil-test-buffer + "This is 'a [t]est' for quote objects." + ("va'") + "This is <'a test'[ ]>for quote objects.")) + (ert-info ("Select text including enclosing quotes and previous space") + (evil-test-buffer + "This is 'a [t]est'. For quote objects." + ("va'") + "This is< 'a test[']>. For quote objects.")) + (ert-info ("Select text on opening quote") + (evil-test-buffer + "This is [\"]a test\". For \"quote\" objects." + (emacs-lisp-mode) + ("va\"") + "This is< \"a test[\"]>. For \"quote\" objects.")) + (ert-info ("Select text on closing quote") + (evil-test-buffer + "This is \"a test[\"]. For \"quote\" objects." + (emacs-lisp-mode) + ("va\"") + "This is< \"a test[\"]>. For \"quote\" objects.")) + (ert-info ("Delete text from outside") + (evil-test-buffer + "Th[i]s is \"a test\". For \"quote\" objects." + (emacs-lisp-mode) + ("da\"") + "This is[.] For \"quote\" objects.")) + (ert-info ("Operator on empty quotes") + (evil-test-buffer + "This is [a]n \"\" empty quote" + (emacs-lisp-mode) + ("ci\"XXX" [escape]) + "This is an \"XX[X]\" empty quote"))) + +(ert-deftest evil-test-paren-objects () + "Test `evil-inner-paren', etc." + :tags '(evil text-object) + (ert-info ("Select inner text") + (evil-test-buffer + "[(]aaa)" + (emacs-lisp-mode) ; syntax + ("vi(") + "()") + (evil-test-buffer + "(aaa[)]" + (emacs-lisp-mode) + ("vi(") + "()") + (ert-info ("Next to outer delimiter") + (evil-test-buffer + "([(]aaa))" + (emacs-lisp-mode) + ("vi(") + "(())") + (evil-test-buffer + "((aaa[)])" + (emacs-lisp-mode) + ("vi(") + "(())"))) + (ert-info ("Select double inner parentheses") + (evil-test-buffer + "([(]word))" + ("dib") + "(())") + (evil-test-buffer + "[(](word))" + ("dib") + "()") + (evil-test-buffer + "((word[)])" + ("dib") + "(())") + (evil-test-buffer + "((word)[)]" + ("dib") + "()")) + (ert-info ("Select double outer parentheses") + (evil-test-buffer + "a([(]word))b" + ("dab") + "a()b") + (evil-test-buffer + "a[(](word))b" + ("dab") + "ab") + (evil-test-buffer + "a((word[)])b" + ("dab") + "a()b") + (evil-test-buffer + "a((word)[)]b" + ("dab") + "ab")) + (ert-info ("Select parentheses inside strings") + (evil-test-buffer + "(aaa \"b(b[b]b)\" aa)" + (emacs-lisp-mode) + ("va(") + "(aaa \"b<(bbb[)]>\" aa)")) + (ert-info ("Break out of empty strings") + (evil-test-buffer + "(aaa \"bb[b]b\" aa)" + (emacs-lisp-mode) + ("va(") + "<(aaa \"bbbb\" aa[)]>")) + (ert-info ("Select inner parentheses around strings") + (evil-test-buffer + "((\"t[e]st\"))\n" + (emacs-lisp-mode) + ("vib") + "((<\"test[\"]>))\n" + ("ib") + "(<(\"test\"[)]>)\n") + (evil-test-buffer + "( ( \"t[e]st\" ) )\n" + (emacs-lisp-mode) + ("vib") + "( (< \"test\"[ ]>) )\n" + ("ib") + "(< ( \"test\" )[ ]>)\n") + (evil-test-buffer + "((\"t[e]st\"))\n" + (emacs-lisp-mode) + ("vhhib") + "((<[\"]test\">))\n" + ("ib") + "(<[(]\"test\")>)\n") + (evil-test-buffer + "( ( \"t[e]st\" ) )\n" + (emacs-lisp-mode) + ("vhhib") + "( (<[ ]\"test\" >) )\n" + ("ib") + "(<[ ]( \"test\" ) >)\n")) + (ert-info ("Select outer parentheses around strings") + (evil-test-buffer + "((\"t[e]st\"))\n" + (emacs-lisp-mode) + ("vab") + "(<(\"test\"[)]>)\n" + ("ab") + "<((\"test\")[)]>\n") + (evil-test-buffer + "( ( \"t[e]st\" ) )\n" + (emacs-lisp-mode) + ("vab") + "( <( \"test\" [)]> )\n" + ("ab") + "<( ( \"test\" ) [)]>\n") + (evil-test-buffer + "((\"t[e]st\"))\n" + (emacs-lisp-mode) + ("vhhab") + "(<[(]\"test\")>)\n" + ("ab") + "<[(](\"test\"))>\n") + (evil-test-buffer + "( ( \"t[e]st\" ) )\n" + (emacs-lisp-mode) + ("vhhab") + "( <[(] \"test\" )> )\n" + ("ab") + "<[(] ( \"test\" ) )>\n") + (evil-test-buffer + "(([\"]\"))\n" + ("dab") + "([)]\n")) + (ert-info ("Select inner paren on different lines") + (evil-test-buffer + "for (auto i : vector) { + if (cond) { + do_[s]omething(); + } +}" + ("vi}") + "for (auto i : vector) { + if (cond) { +< do_something();[\n]> }\n}" + ("i}") + "for (auto i : vector) { +< if (cond) { + do_something(); + }[\n]>}")) + (ert-info ("Enlarge to smallest complete surrounding") + (evil-test-buffer + "for (auto i : vector) { + if (comething(); + } +}" + ("i}") + "for (auto i : vector) { +< if (cond) { + do_something(); + }[\n]>}")) + (ert-info ("yank on blocks is turned linewise") + (evil-test-buffer + "{\n [f]oo();\n}\n" + ("yiBp") + "{\n foo();\n [f]oo();\n}\n")) + (ert-info ("exclusive like if ending at bol") + (evil-test-buffer + "(defun foo ()\n[ ] (insert \"bar\")\n )\n" + ("cibx" [escape]) + "([x]\n )\n")) + (ert-info ("Operator on empty parentheses") + (evil-test-buffer + "a([(]))b" + ("cibx" [escape]) + "a(([x]))b") + (evil-test-buffer + "a(([)])b" + ("cibx" [escape]) + "a(([x]))b"))) + +(ert-deftest evil-test-forces-linewise-text-objects () + "Test `evil-text-object-change-visual-type' option." + :tags '(evil text-object) + (let ((evil-text-object-change-visual-type t)) + (ert-info ("Change visual type") + (evil-test-buffer + " function(opts) { + this.var1 = something(); + [t]his.var2 = something_else(); + return something_nasty(); + } +" + ("Vi}") + " function(opts) { +< this.var1 = something(); + this.var2 = something_else(); + return something_nasty();[ +]> } +" + (should (eq (evil-visual-type) 'inclusive))))) + (let ((evil-text-object-change-visual-type nil)) + (ert-info ("Change visual type keeping linewise") + (evil-test-buffer + " function(opts) { + this.var1 = something(); + [t]his.var2 = something_else(); + return something_nasty(); + } +" + ("Vi}") + " function(opts) { +< this.var1 = something(); + this.var2 = something_else(); + return something_nasty();\n> } +" + (should (eq (evil-visual-type) 'line))))) + (let ((evil-text-object-change-visual-type nil)) + (ert-info ("Linewise outer block") + (evil-test-buffer + " function(opts) { + this.var1 = something(); + [t]his.var2 = something_else(); + return something_nasty(); + } +" + ("Va}") + "< function(opts) { + this.var1 = something(); + this.var2 = something_else(); + return something_nasty(); + } +>" + (should (eq (evil-visual-type) 'line))))) + (ert-info ("Forced motion type should change text object type") + (evil-test-buffer + "for (int i=0; i<10; i++) { + if ([c]ond) { + do_something(); + } +}" + ("dVi}") + "for (int i=0; i<10; i++) { +\[}]"))) + +(ert-deftest evil-test-tag-objects () + "Test `evil-inner-tag', etc." + :tags '(evil text-object) + (ert-info ("Handle nested tags") + (evil-test-buffer + :visual-start "{" + :visual-end "}" + "

f[o]o bar

" + ("vit") + "

{fo[o]} bar

")) + (ert-info ("Break out of tags") + (evil-test-buffer + :visual-start "{" + :visual-end "}" + "bbbb" + ("vit") + "{bbb[b]}") + (evil-test-buffer + :visual-start "{" + :visual-end "}" + "bbbb" + ("vat") + "{bbbb]}")) + (ert-info ("Handle quoted strings tags") + (evil-test-buffer + :visual-start "{" + :visual-end "}" + " + +
+\[ ] +

+UPDATE +

+

+test hello Creative Commons +

+
+ + +" + ("vit") + " + +
{\n \n

+UPDATE +

+

+test hello Creative Commons +

[\n]}
+ + +")) + (ert-info ("Handle js arrow fns") + (evil-test-buffer + :visual-start "«" + :visual-end "»" + "" + ("vit") + "")) + (ert-info ("Inner tag is selected, even when empty") + (evil-test-buffer + :visual-start "«" + :visual-end "»" + "

fooqux

" + ("cit" "bar") + "

foobarqux

"))) + +;;; Visual state + +(defun evil-test-visual-select (selection &optional mark point) + "Verify that TYPE is selected correctly" + (let ((type (evil-visual-type selection))) + (evil-visual-make-selection mark point type) + (ert-info ("Activate region unless SELECTION is `block'") + (cond + ((eq selection 'block) + (should (mark t)) + (should-not (region-active-p)) + (should-not transient-mark-mode)) + (t + (should (mark)) + (should (region-active-p))))) + (ert-info ("Refresh Visual markers") + (should (= (evil-range-beginning (evil-expand (point) (mark) type)) + evil-visual-beginning)) + (should (= (evil-range-end (evil-expand (point) (mark) type)) + evil-visual-end)) + (should (eq (evil-visual-type) type)) + (should (eq evil-visual-direction + (if (< (point) (mark)) -1 1)))))) + +(ert-deftest evil-test-visual-refresh () + "Test `evil-visual-refresh'" + :tags '(evil visual) + (evil-test-buffer + ";; [T]his buffer is for notes." + (evil-visual-refresh nil nil 'inclusive) + (should (= evil-visual-beginning 4)) + (should (= evil-visual-end 5))) + (evil-test-buffer + ";; [T]his buffer is for notes." + (let ((evil-visual-region-expanded t)) + (evil-visual-refresh nil nil 'inclusive) + (should (= evil-visual-beginning 4)) + (should (= evil-visual-end 4))))) + +(ert-deftest evil-test-non-transient-visual-selection () + "Ensure visual charwise selection can be made when transient mark mode is nil." + :tags '(evil visual) + (evil-test-buffer + "Alpha [b]ravo charlie" + (setq-local transient-mark-mode nil) + ("ve") + "Alpha charlie")) + +(ert-deftest evil-test-visual-exchange () + "Test `exchange-point-and-mark' in Visual character selection" + :tags '(evil visual) + (evil-test-buffer + ";; <[T]his> buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("o") + (should (region-active-p)) + ";; buffer is for notes you don't want to save, +;; and for Lisp evaluation.")) + +(ert-deftest evil-test-visual-char () + "Test Visual character selection" + :tags '(evil visual) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + (evil-test-visual-select 'char) + ";; <[T]>his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("e") + ";; buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("o") + ";; <[T]his> buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("d") + ";; [ ]buffer is for notes you don't want to save, +;; and for Lisp evaluation." + ("vV") + "<;; [ ]buffer is for notes you don't want to save,\n>\ +;; and for Lisp evaluation.")) + +(ert-deftest evil-test-visual-line () + "Test Visual line selection" + :tags '(evil visual) + (evil-test-buffer + ";; [T]his buffer is for notes you don't want to save, +;; and for Lisp evaluation." + (evil-test-visual-select 'line) + "<;; [T]his buffer is for notes you don't want to save,\n>\ +;; and for Lisp evaluation." + ("e") + "<;; Thi[s] buffer is for notes you don't want to save,\n>\ +;; and for Lisp evaluation." + ("o") + "<;; [T]his buffer is for notes you don't want to save,\n>\ +;; and for Lisp evaluation." + ("d") + ";; [a]nd for Lisp evaluation.")) + +(ert-deftest evil-test-visual-block () + "Test Visual block selection" + :tags '(evil visual) + (evil-test-buffer + "[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + (evil-test-visual-select 'block) + "<[;]>; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer." + ("jjll") + "<;; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;;[ ]>then enter the text in that file's own buffer." + ("O") + ";; [;]; then enter the text in that file's own buffer." + ("o") + ";;[ ];; then enter the text in that file's own buffer." + ("O") + "<[;]; This buffer is for notes you don't want to save. +;; If you want to create a file, visit that file with C-x C-f, +;; >then enter the text in that file's own buffer." + ("d") + "This buffer is for notes you don't want to save. +If you want to create a file, visit that file with C-x C-f, +then enter the text in that file's own buffer.") + (ert-info ("Visual block can select over lines of different length") + (evil-test-buffer + "Short [l]ine +A much longer line +A medium line +Tiny ln" + ("\C-v$jd") + "Short[ ] +A much +A medium line +Tiny ln" + ("jj\C-v" [end] "jd") + "Short +A much +A me[d] +Tiny ")) + (ert-info ("Visual block does not extend up to curswant unless tracking EOL") + (evil-test-buffer + "[a]bc +" + ("\C-vfcjd") + "[b]c +")) + (ert-info ("Pasting visual block") + (evil-test-buffer + "alpha [b]ravo charlie +delta echo foxtrot +golf hotel india +juliet kilo mike" + ("\C-v" "jje" "y" "$" "p") + "alpha bravo charlie[b]rav +delta echo foxtrot echo +golf hotel india otel +juliet kilo mike" + ("G" "o" [escape] "p") + "alpha bravo charliebrav +delta echo foxtrot echo +golf hotel india otel +juliet kilo mike +[b]rav +echo +otel"))) + +(ert-deftest evil-test-visual-restore () + "Test restoring a previous selection" + :tags '(evil visual) + (ert-info ("Restore characterwise selection") + (evil-test-buffer + ";; <[T]his> buffer is for notes." + ([escape] "gv") + ";; <[T]his> buffer is for notes.")) + (ert-info ("Restore linewise selection") + (evil-test-buffer + :visual line + "<;; [T]his buffer is for notes.>" + ([escape] "gv") + "<;; [T]his buffer is for notes.>")) + (ert-info ("Restore blockwise selection") + (evil-test-buffer + :visual block + "<;; This buffer is for notes, +;;[ ]>and for Lisp evaluation." + ([escape] "gv") + "<;; This buffer is for notes, +;;[ ]>and for Lisp evaluation.") + (ert-info ("After paste shifts initially selected text") + (evil-test-buffer + :visual block + "<1\n2\n[3]>" + ("yP") + "[1]1\n22\n33" + ("gvr*") + "[*]1\n*2\n*3"))) + (ert-info ("Restore linewise visually-pasted selection") + (evil-test-buffer + "[a]lpha bravo\ncharlie delta +echo foxtrot\ngolf hotel" + ("2yy" "++" "Vp" "gv") + "alpha bravo\ncharlie delta +golf hotel")) + ;; 4 repetitions appears necessary, from manual testing + (ert-info ("Restore previous linewise selection from linewise selection") + (evil-test-buffer + "alpha bravo\nch[a]rlie delta\necho foxtrot\ngolf hotel" + ("V" [escape] "jV") + "alpha bravo\ncharlie delta\ngolf hotel" + ("gv") + "alpha bravo\necho foxtrot\ngolf hotel" + ("gv") + "alpha bravo\ncharlie delta\ngolf hotel" + ("gv") + "alpha bravo\necho foxtrot\ngolf hotel" + ("gv") + "alpha bravo\ncharlie delta\ngolf hotel")) + (ert-info ("Restore between previous charwise selection and linewise selection") + (evil-test-buffer + "alpha bravo\nch[a]rlie delta\necho foxtrot\ngolf hotel" + ("viw" [escape] "jV") + "alpha bravo\ncharlie delta\ngolf hotel" + ("gv") + "alpha bravo\n delta\necho foxtrot\ngolf hotel" + ("gv") + "alpha bravo\ncharlie delta\ngolf hotel" + ("gv") + "alpha bravo\n delta\necho foxtrot\ngolf hotel" + ("gv") + "alpha bravo\ncharlie delta\ngolf hotel"))) + +(ert-deftest evil-test-visual-redefine () + "Test redefining a previous selection" + :tags '(evil visual) + (ert-info ("Redefine characterwise selection") + (evil-test-buffer + ";; <[T]his> buffer is for notes." + ([escape] "gv") + ";; <[T]his> buffer is for notes." + ([escape] "2lm<2wm>gv") + ";; Ths for notes.")) + (ert-info ("Redefine linewise selection") + (evil-test-buffer + :visual line + "<[a]lpha bravo +>charlie delta +echo foxtrot +golf hotel" + ([escape] "gv") + "<[a]lpha bravo +>charlie delta +echo foxtrot +golf hotel" + ([escape] "2jm>gv") + "golf hotel" + ([escape] "kmgolf hotel")) + (ert-info ("Redefine blockwise selection") + (evil-test-buffer + :visual block + " delta +echo foxtrot +golf hotel" + ([escape] "gv") + " delta +echo foxtrot +golf hotel" + ([escape] "2jm>gv") + "tel" + ([escape] "k0mtel"))) + +(ert-deftest evil-test-visual-separate-from-operator-marks () + "Test that visual selection is kept separate from the '[ and '] marks (#1744)." + (evil-test-buffer "x\ny" + ("ylvpjxgv") + "[x]\n")) + +;;; Replace state + +(ert-deftest evil-test-replacement () + "Test replacing consecutive characters" + :tags '(evil replace) + (ert-info ("Replace and restore consecutive characters") + (evil-test-buffer + ";; [T]his buffer is for notes" + ("Rfoo") + ";; foo[s] buffer is for notes" + ([backspace backspace backspace]) + ";; [T]his buffer is for notes")) + (ert-info ("Replace and restore consecutive characters beyond eol") + (evil-test-buffer + ";; [T]his buffer is for notes" + ("wwwwRxxxxxxx") + ";; This buffer is for xxxxxxx[]" + ([backspace backspace backspace backspace backspace backspace backspace]) + ";; This buffer is for [n]otes")) + (ert-info ("Replace from line below and restore") + (define-key evil-replace-state-map (kbd "C-e") #'evil-copy-from-below) + (evil-test-buffer + ";; [f]oo bar\n;; qux quux" + ("R\C-e\C-e\C-e") + ";; qux[ ]bar\n;; qux quux" + ([backspace backspace backspace]) + ";; [f]oo bar\n;; qux quux") + (define-key evil-replace-state-map (kbd "C-e") nil)) + (ert-info ("Replace from line above and restore") + (define-key evil-replace-state-map (kbd "C-y") #'evil-copy-from-above) + (evil-test-buffer + ";; foo bar\n;; [q]ux quux" + ("R\C-y\C-y\C-y") + ";; foo bar\n;; foo[ ]quux" + ([backspace backspace backspace]) + ";; foo bar\n;; [q]ux quux") + (define-key evil-replace-state-map (kbd "C-y") nil)) + (ert-info ("Replace character twice and restore") + (evil-test-buffer "[a]" + ("Rb" [left] "c" [backspace]) + "b")) + (ert-info ("Can give Replace-state a count repeat it") + (evil-test-buffer + "a[l]pha bravo" + ("3Rx" [escape]) + "axx[x]a bravo" + ("wi\C-a") + "axxxa x[]bravo")) + (ert-info ("Replace deletion keeps consistent . register") + (evil-test-buffer + "a[l]pha bravo" + ("Rfop" [backspace] "o" [escape]) + "afo[o]a bravo" + ("A \C-@") + "afooa bravo fo[o]")) + (ert-info ("Replace-state sets ^ mark") + (evil-test-buffer + "a[l]pha bravo" + ("Rxx" [escape] "$") + "axxha brav[o]" + ("`^") + "axx[h]a bravo"))) + +;;; Ex + +(ert-deftest evil-test-ex-parse () + "Test `evil-ex-parse'" + :tags '(evil ex) + (should (equal (evil-ex-parse "5cmd arg") + '(evil-ex-call-command (string-to-number "5") "cmd" "arg"))) + (should (equal (evil-ex-parse "5cmd !arg") + '(evil-ex-call-command (string-to-number "5") "cmd" "!arg"))) + (should (equal (evil-ex-parse "5 arg") + '(evil-ex-call-command (string-to-number "5") "arg" nil))) + (should (equal (evil-ex-parse "+1,+2t-1") + '(evil-ex-call-command + (let ((l1 (evil-ex-line + nil + (+ (evil-ex-signed-number + (intern "+") + (string-to-number "1")))))) + (save-excursion + (and l1 (string= "," ";") (goto-line l1)) + (evil-ex-range + l1 (evil-ex-line + nil + (+ (evil-ex-signed-number + (intern "+") + (string-to-number "2"))))))) + "t" + "-1")))) + +(ert-deftest evil-test-ex-parse-ranges () + "Test parsing of ranges" + :tags '(evil ex) + (cl-flet + ((mk-range + (a b &optional relative) + `(let ((l1 ,(when a `(evil-ex-line ,@a)))) + (save-excursion + (and l1 (string= ,(if relative ";" ",") ";") (goto-line l1)) + (evil-ex-range l1 ,(when b `(evil-ex-line ,@b))))))) + (should (equal (evil-ex-parse "%" nil 'range) + '(evil-ex-full-range))) + (should (equal (evil-ex-parse "*" nil 'range) + '(evil-ex-last-visual-range))) + (should (equal (evil-ex-parse "5,27" nil 'range) + (mk-range '((string-to-number "5") nil) + '((string-to-number "27") nil)))) + (should (equal (evil-ex-parse "5,$" nil 'range) + (mk-range '((string-to-number "5") nil) + '((evil-ex-last-line) nil)))) + (should (equal (evil-ex-parse "5,'x" nil 'range) + (mk-range '((string-to-number "5") nil) + '((evil-ex-marker "x") nil)))) + (should (equal (evil-ex-parse "`x,`y" nil 'range) + '(evil-ex-char-marker-range "x" "y"))) + (should (equal (evil-ex-parse "`[,`]" nil 'range) + '(evil-ex-char-marker-range "[" "]"))) + (should (equal (evil-ex-parse "5,+" nil 'range) + (mk-range '((string-to-number "5") nil) + '(nil (+ (evil-ex-signed-number (intern "+") nil)))))) + (should (equal (evil-ex-parse "5,-" nil 'range) + (mk-range '((string-to-number "5") nil) + '(nil (+ (evil-ex-signed-number (intern "-") nil)))))) + (should (equal (evil-ex-parse "5,4+2-7-3+10-" nil 'range) + (mk-range + '((string-to-number "5") nil) + '((string-to-number "4") + (+ (evil-ex-signed-number + (intern "+") (string-to-number "2")) + (evil-ex-signed-number + (intern "-") (string-to-number "7")) + (evil-ex-signed-number + (intern "-") (string-to-number "3")) + (evil-ex-signed-number + (intern "+") (string-to-number "10")) + (evil-ex-signed-number (intern "-") nil)))))) + (should (equal (evil-ex-parse ".-2,4+2-7-3+10-" nil 'range) + (mk-range + '((evil-ex-current-line) + (+ (evil-ex-signed-number + (intern "-") (string-to-number "2")))) + '((string-to-number "4") + (+ (evil-ex-signed-number + (intern "+") (string-to-number "2")) + (evil-ex-signed-number + (intern "-") (string-to-number "7")) + (evil-ex-signed-number + (intern "-") (string-to-number "3")) + (evil-ex-signed-number + (intern "+") (string-to-number "10")) + (evil-ex-signed-number + (intern "-") nil)))))) + (should (equal (evil-ex-parse "'a-2,$-10" nil 'range) + (mk-range + '((evil-ex-marker "a") + (+ (evil-ex-signed-number + (intern "-") (string-to-number "2")))) + '((evil-ex-last-line) + (+ (evil-ex-signed-number + (intern "-") (string-to-number "10"))))))) + (should (equal (evil-ex-parse "'[,']" nil 'range) + (mk-range '((evil-ex-marker "[") nil) + '((evil-ex-marker "]") nil)))) + (should (equal (evil-ex-parse "'{,'}" nil 'range) + (mk-range '((evil-ex-marker "{") nil) + '((evil-ex-marker "}") nil)))) + (should (equal (evil-ex-parse "'(,')" nil 'range) + (mk-range '((evil-ex-marker "(") nil) + '((evil-ex-marker ")") nil)))) + (should (equal (evil-ex-parse ",']" nil 'range) + (mk-range nil '((evil-ex-marker "]") nil)))) + (should (equal (evil-ex-parse ";']" nil 'range) + (mk-range nil '((evil-ex-marker "]") nil) t))) + (should (equal (evil-ex-parse ".+42" nil 'range) + '(evil-ex-range + (evil-ex-line + (evil-ex-current-line) + (+ (evil-ex-signed-number + (intern "+") (string-to-number "42"))))))))) + +(ert-deftest evil-test-ex-parse-emacs-commands () + "Test parsing of Emacs commands" + :tags '(evil ex) + (should (equal (evil-ex-parse "ido-mode") + '(evil-ex-call-command nil "ido-mode" nil))) + (should (equal (evil-ex-parse "yas/reload-all") + '(evil-ex-call-command nil "yas/reload-all" nil))) + (should (equal (evil-ex-parse "mu4e") + '(evil-ex-call-command nil "mu4e" nil))) + (should (equal (evil-ex-parse "make-frame") + '(evil-ex-call-command nil "make-frame" nil)))) + +(ert-deftest evil-text-ex-search-offset () + "Test for addresses like /base//pattern/" + :tags '(evil ex) + (ert-info ("without base") + (evil-test-buffer + "[l]ine 1\naaa\nbbb\naaa\nccc\nddd" + (":/aaa/d") + "line 1\nbbb\naaa\nccc\nddd")) + (ert-info ("with base") + (evil-test-buffer + "[l]ine 1\naaa\nbbb\naaa\nccc\nddd" + (":/bbb//aaa/d") + "line 1\naaa\nbbb\nccc\nddd")) + (ert-info ("range without base") + (evil-test-buffer + "[l]ine 1\naaa\nbbb\naaa\nccc\nddd\nccc\neee\n" + (":/aaa/;/ccc/d") + "line 1\nddd\nccc\neee\n")) + (ert-info ("range with base") + (evil-test-buffer + "[l]ine 1\naaa\nbbb\naaa\nccc\nddd\nccc\neee\n" + (":/bbb//aaa/;/ddd//ccc/d") + "line 1\naaa\nbbb\neee\n"))) + +(ert-deftest evil-test-ex-goto-line () + "Test if :number moves point to a certain line" + :tags '(evil ex) + (ert-info ("Move to line") + (let ((evil-start-of-line t)) + (evil-test-buffer + :visual line + "1\n 2\n [ ]3\n 4\n 5\n" + (":4" [return]) + "1\n 2\n 3\n [4]\n 5\n" + (":2" [return]) + "1\n [2]\n 3\n 4\n 5\n")))) + +(ert-deftest evil-test-ex-repeat () + "Test :@: command." + :tags '(evil ex) + (evil-without-display + (ert-info ("Repeat in current line") + (evil-test-buffer + "[a]bcdef\nabcdef\nabcdef" + (":s/[be]/X/g" [return]) + "[a]XcdXf\nabcdef\nabcdef" + ("jj:@:" [return]) + "aXcdXf\nabcdef\n[a]XcdXf")) + (ert-info ("Repeat in specified line") + (evil-test-buffer + "[a]bcdef\nabcdef\nabcdef" + (":s/[be]/X/g" [return]) + "[a]XcdXf\nabcdef\nabcdef" + (":3@:" [return]) + "aXcdXf\nabcdef\n[a]XcdXf")) + (ert-info ("Double repeat, first without then with specified line") + (evil-test-buffer + "[a]bcdef\nabcdef\nabcdef" + (":s/[be]/X/" [return]) + "[a]Xcdef\nabcdef\nabcdef" + ("jj:@:" [return] ":1@:" [return]) + "[a]XcdXf\nabcdef\naXcdef")))) + +(ert-deftest evil-test-ex-repeat2 () + "Test @: command." + :tags '(evil ex) + (evil-without-display + (ert-info ("Repeat in current line") + (evil-test-buffer + "[a]bcdef\nabcdef\nabcdef" + (":s/[be]/X" [return]) + "[a]Xcdef\nabcdef\nabcdef" + ("jj@:") + "aXcdef\nabcdef\n[a]Xcdef")) + (ert-info ("Repeat with count in current line") + (evil-test-buffer + "[a]bcdef\nabcdef\nabcdef" + (":s/[be]/X" [return]) + "[a]Xcdef\nabcdef\nabcdef" + ("jj2@:") + "aXcdef\nabcdef\n[a]XcdXf")) + (ert-info ("Do not record dot repeat") + (evil-test-buffer + "" + ("OAAAAAA" [escape] "^") + "[A]AAAAA\n" + (":s/A/X" [return]) + "[X]AAAAA\n" + ("@:") + "[X]XAAAA\n" + (".") + "AAAAAA\nXXAAAA\n")))) + +(ert-deftest evil-test-ex-visual-char-range () + "Test visual character ranges in ex state." + :tags '(evil ex visual) + (evil-without-display + (ert-info ("No character range, inclusive") + (let ((evil-visual-char 'inclusive) + evil-ex-visual-char-range) + (evil-test-buffer + "li[n]e 1\nline 2\nline 3\nline 4\n" + ("vjll:d" [return]) + "line 3\nline 4\n"))) + (ert-info ("No character range, exclusive") + (let ((evil-visual-char 'inclusive) + evil-ex-visual-char-range) + (evil-test-buffer + "li[n]e 1\nline 2\nline 3\nline 4\n" + ("vjll:d" [return]) + "line 3\nline 4\n"))) + (ert-info ("Character range, inclusive") + (let ((evil-visual-char 'inclusive) + (evil-ex-visual-char-range t)) + (evil-test-buffer + "li[n]e 1\nline 2\nline 3\nline 4\n" + ("vjll:d" [return]) + "li2\nline 3\nline 4\n"))) + (ert-info ("Character range, exclusive") + (let ((evil-visual-char 'exclusive) + (evil-ex-visual-char-range t)) + (evil-test-buffer + "li[n]e 1\nline 2\nline 3\nline 4\n" + ("vjll:d" [return]) + "li 2\nline 3\nline 4\n"))))) + +(ert-deftest evil-test-ex-substitute-replacement () + "Test `evil-ex-substitute' with special replacements." + :tags '(evil ex search) + (ert-info ("Substitute upper first on first match in line") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar" + (":s/\\(foo\\|bar\\)/\\u\\1" [return]) + "[x]xx Foo bar foo bar foo bar")) + (ert-info ("Substitute upper first on first match in line with confirm") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar" + (":s/\\(foo\\|bar\\)/\\u\\1/c" [return] "y") + "[x]xx Foo bar foo bar foo bar")) + (ert-info ("Substitute upper first on whole line") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar" + (":s/\\(foo\\|bar\\)/\\u\\1/g" [return]) + "[x]xx Foo Bar Foo Bar Foo Bar")) + (ert-info ("Substitute upper first on whole line") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar" + (":s/\\(foo\\|bar\\)/\\u\\1/gc" [return] "yynyyn") + "[x]xx Foo Bar foo Bar Foo bar")) + (ert-info ("Substitute upper/lower on first match in line") + (evil-test-buffer + "[x]xx foo BAR foo BAR foo BAR" + (":s/\\(f[[:alpha:]]*\\>\\)\\s-*\\(b[[:alpha:]]*\\>\\)/\\L\\2_\\e\\U\\1" [return]) + "[x]xx bar_FOO foo BAR foo BAR")) + (ert-info ("Substitute upper/lower on first match in line with confirm") + (evil-test-buffer + "[x]xx foo BAR foo BAR foo BAR" + (":s/\\(f[[:alpha:]]*\\>\\)\\s-*\\(b[[:alpha:]]*\\>\\)/\\L\\2_\\e\\U\\1/c" [return] "y") + "[x]xx bar_FOO foo BAR foo BAR")) + (ert-info ("Substitute upper/lower on whole line") + (evil-test-buffer + "[x]xx foo BAR foo BAR foo BAR" + (":s/\\(f[[:alpha:]]*\\>\\)\\s-*\\(b[[:alpha:]]*\\>\\)/\\L\\2_\\e\\U\\1/g" [return]) + "[x]xx bar_FOO bar_FOO bar_FOO")) + (ert-info ("Substitute upper/lower on whole line") + (evil-test-buffer + "[x]xx foo BAR foo BAR foo BAR" + (":s/\\(f[[:alpha:]]*\\>\\)\\s-*\\(b[[:alpha:]]*\\>\\)/\\L\\2_\\e\\U\\1/gc" [return] "yny") + "[x]xx bar_FOO foo BAR bar_FOO")) + (ert-info ("Substitute with escaped characters in replacement") + (evil-test-buffer + "[a]bcXdefXghiXjkl\n" + (":s/X/\\|\\/\\|/g" [return]) + "[a]bc|/|def|/|ghi|/|jkl\n")) + (ert-info ("Substitute with register") + (evil-test-buffer + "[a]bc\niiiXiiiXiiiXiii\n" + ("\"ayiwj:s/X/\\=@a/g" [return]) + "abc\n[i]iiabciiiabciiiabciii\n")) + (ert-info ("Substitute newlines") + (evil-test-buffer + "[a]bc\ndef\nghi\n" + (":%s/\n/z/" [return]) + "[a]bczdefzghiz")) + (ert-info ("Substitute newlines with g flag") + (evil-test-buffer + "[a]bc\ndef\nghi\n" + (":%s/\n/z/g" [return]) + "[a]bczdefzghiz")) + (ert-info ("Substitute newlines without newline in regexp") + (evil-test-buffer + "[A]BC\nDEF\nGHI\n" + (":%s/[^]]*/z/" [return]) + "Z")) + (ert-info ("Substitute n flag does not replace") + (evil-test-buffer + "[a]bc\naef\nahi\n" + (":%s/a//n" [return]) + "[a]bc\naef\nahi\n")) + (ert-info ("Substitute n flag does not replace with g flag") + (evil-test-buffer + "[a]bc\naef\nahi\n" + (":%s/a//gn" [return]) + "[a]bc\naef\nahi\n")) + (ert-info ("Substitute $ does not loop infinitely") + (evil-test-buffer + "[a]bc\ndef\nghi" + (":%s/$/ END/g" [return]) + "abc END\ndef END\n[g]hi END")) + (ert-info ("Substitute the zero-length beginning of line character") + (evil-test-buffer + "[a]bc\ndef\nghi" + (":s/^/ #/" [return]) + " [#]abc\ndef\nghi")) + (ert-info ("Substitute the zero-length beginning of line character with g flag") + (evil-test-buffer + "[a]bc\ndef\nghi" + (":s/^/ #/g" [return]) + " [#]abc\ndef\nghi")) + (ert-info ("Use Substitute to delete individual characters") + (evil-test-buffer + "[x]xyxxz" + (":%s/x//g" [return]) + "[y]z")) + (ert-info ("Substitute doesn't match final empty line") + (evil-test-buffer + "abc\n[d]ef\n\nghi" + (":s/$/4" [return]) + "abc\n[d]ef4\n\nghi") + (evil-test-buffer + "abc\n[d]ef\n\nghi" + (":s/f\\w*/4" [return]) + "abc\n[d]e4\n\nghi")) + (ert-info ("Substitute doesn't move point if 0 matches") + (evil-test-buffer + "abc\nd[e]f\nghi" + (":%s/nothere/foo" [return]) + "abc\nd[e]f\nghi")) + (ert-info ("Substitute up to EOB works") + (evil-test-buffer "[f]oo" + (":s/foo") + "")) + (ert-info ("Substitute with multi-line replacement") + (evil-test-buffer "x" + (":s/./foo\\nbar") + "foo\n[b]ar")) + (ert-info ("Use replace count in substitution") + (evil-test-buffer "xx" + (":s/./\\#/g") + "01")) + (ert-info ("Substitute with `calc' Lisp expression") + (evil-test-buffer "1 + 2" + (":s/.*/\\,(calc-eval \\0)") + "3"))) + +(ert-deftest evil-test-ex-repeat-substitute-replacement () + "Test `evil-ex-substitute' with repeating of previous substitutions." + :tags '(evil ex search) + (ert-info ("Repeat previous pattern") + (evil-select-search-module 'evil-search-module 'evil-search) + (evil-test-buffer + "[x]xx foo bar foo bar foo bar" + (":s/foo/AAA" [return]) + "[x]xx AAA bar foo bar foo bar" + (":s//BBB" [return]) + "[x]xx AAA bar BBB bar foo bar" + ("/bar" [return] ":s//CCC" [return]) + "[x]xx AAA CCC BBB bar foo bar" + (":s/ar/XX" [return]) + "[x]xx AAA CCC BBB bXX foo bar" + (":s//YY" [return]) + "[x]xx AAA CCC BBB bXX foo bYY")) + (ert-info ("Repeat previous replacement") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar" + (":s/foo/AAA" [return]) + "[x]xx AAA bar foo bar foo bar" + (":s/bar/~" [return]) + "[x]xx AAA AAA foo bar foo bar")) + (ert-info ("Repeat with previous flags") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar" + (":s/foo/AAA/g" [return]) + "[x]xx AAA bar AAA bar AAA bar" + (":s/bar/BBB/&" [return]) + "[x]xx AAA BBB AAA BBB AAA BBB")) + (ert-info ("Repeat previous substitute without flags") + (evil-select-search-module 'evil-search-module 'evil-search) + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA/g" [return]) + "[x]xx AAA bar AAA bar AAA bar\nxxx foo bar foo bar foo bar" + ("j:s" [return]) + "xxx AAA bar AAA bar AAA bar\n[x]xx AAA bar foo bar foo bar" + ("/bar" [return] ":s" [return]) + "xxx AAA bar AAA bar AAA bar\n[x]xx AAA bar AAA bar foo bar") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA/g" [return]) + "[x]xx AAA bar AAA bar AAA bar\nxxx foo bar foo bar foo bar" + ("j&") + "xxx AAA bar AAA bar AAA bar\n[x]xx AAA bar foo bar foo bar") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA/g" [return]) + "[x]xx AAA bar AAA bar AAA bar\nxxx foo bar foo bar foo bar" + ("j:&" [return]) + "xxx AAA bar AAA bar AAA bar\n[x]xx AAA bar foo bar foo bar")) + (ert-info ("Repeat previous substitute with the same flags") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA/g" [return]) + "[x]xx AAA bar AAA bar AAA bar\nxxx foo bar foo bar foo bar" + ("j:s//~/&" [return]) + "xxx AAA bar AAA bar AAA bar\n[x]xx AAA bar AAA bar AAA bar") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA/g" [return]) + "[x]xx AAA bar AAA bar AAA bar\nxxx foo bar foo bar foo bar" + ("j:&&" [return]) + "xxx AAA bar AAA bar AAA bar\n[x]xx AAA bar AAA bar AAA bar")) + (ert-info ("Repeat previous substitute with new flags") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA" [return]) + "[x]xx AAA bar foo bar foo bar\nxxx foo bar foo bar foo bar" + ("j:s g" [return]) + "xxx AAA bar foo bar foo bar\n[x]xx AAA bar AAA bar AAA bar") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA" [return]) + "[x]xx AAA bar foo bar foo bar\nxxx foo bar foo bar foo bar" + ("j:& g" [return]) + "xxx AAA bar foo bar foo bar\n[x]xx AAA bar AAA bar AAA bar")) + (ert-info ("Repeat with previous search pattern") + (evil-select-search-module 'evil-search-module 'evil-search) + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA" [return]) + "[x]xx AAA bar foo bar foo bar\nxxx foo bar foo bar foo bar" + ("/bar" [return]) + "xxx AAA [b]ar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":2s rg" [return]) + "xxx AAA bar foo bar foo bar\n[x]xx foo AAA foo AAA foo AAA") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA" [return]) + "[x]xx AAA bar foo bar foo bar\nxxx foo bar foo bar foo bar" + ("/bar" [return]) + "xxx AAA [b]ar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":2~ g" [return]) + "xxx AAA bar foo bar foo bar\n[x]xx foo AAA foo AAA foo AAA")) + (ert-info ("Repeat previous substitute globally") + (evil-test-buffer + "[x]xx foo bar foo bar foo bar\nxxx foo bar foo bar foo bar" + (":s/foo/AAA/g" [return]) + "[x]xx AAA bar AAA bar AAA bar\nxxx foo bar foo bar foo bar" + ("g&") + "xxx AAA bar AAA bar AAA bar\n[x]xx AAA bar AAA bar AAA bar")) + (ert-info ("Substitute with last search, maintaining case sensitivty") + (evil-select-search-module 'evil-search-module 'evil-search) + (evil-test-buffer + "[a]lpha\nbravo\nbravo\ncharlie\nBravo\ndelta" + ("/\\Cbravo" [return]) + "alpha\n[b]ravo\nbravo\ncharlie\nBravo\ndelta" + (":%s//zulu" [return]) + "alpha\nzulu\n[z]ulu\ncharlie\nBravo\ndelta")) + (ert-info ("Repeat magic multiple times") + (let ((evil-magic 'very-magic) + (evil-ex-search-vim-style-regexp t)) + (evil-test-buffer + "[b]ravo, alpha\ndelta, charlie\nfoxtrot, echo\nhotel, golf" + (":s/(.*), (.*)/\\2 \\1" [return]) + "alpha bravo\ndelta, charlie\nfoxtrot, echo\nhotel, golf" + ("j&") + "alpha bravo\ncharlie delta\nfoxtrot, echo\nhotel, golf" + ("j&") + "alpha bravo\ncharlie delta\necho foxtrot\nhotel, golf" + ("j&") + "alpha bravo\ncharlie delta\necho foxtrot\ngolf hotel")))) + +(ert-deftest evil-test-ex-regex-without-case () + "Test `evil-ex-regex-without-case'" + :tags '(evil ex search) + (should (equal (evil-ex-regex-without-case "cdeCDE") + "cdeCDE")) + (should (equal (evil-ex-regex-without-case "\\ccde\\CCDE") + "cdeCDE")) + (should (equal (evil-ex-regex-without-case "\\\\ccde\\\\CCDE") + "\\\\ccde\\\\CCDE")) + (should (equal (evil-ex-regex-without-case "\\\\\\ccde\\\\\\CCDE") + "\\\\cde\\\\CDE"))) + +(ert-deftest evil-test-ex-regex-case () + "Test `evil-ex-regex-case'" + :tags '(evil ex search) + (should (equal (evil-ex-regex-case "cde" 'smart) 'insensitive)) + (should (equal (evil-ex-regex-case "cDe" 'smart) 'sensitive)) + (should (equal (evil-ex-regex-case "cde" 'sensitive) 'sensitive)) + (should (equal (evil-ex-regex-case "cde" 'insensitive) 'insensitive)) + (should (equal (evil-ex-regex-case "\\ccde" 'smart) 'insensitive)) + (should (equal (evil-ex-regex-case "\\cCde" 'smart) 'insensitive)) + (should (equal (evil-ex-regex-case "\\Ccde" 'smart) 'sensitive)) + (should (equal (evil-ex-regex-case "\\CCde" 'smart) 'sensitive)) + (should (equal (evil-ex-regex-case "\\ccd\\Ce" 'smart) 'insensitive)) + (should (equal (evil-ex-regex-case "\\cCd\\Ce" 'smart) 'insensitive)) + (should (equal (evil-ex-regex-case "\\Ccd\\ce" 'smart) 'sensitive)) + (should (equal (evil-ex-regex-case "\\CCd\\ce" 'smart) 'sensitive))) + +(ert-deftest evil-test-ex-search () + "Test evil internal search." + :tags '(evil ex search) + (evil-without-display + (evil-select-search-module 'evil-search-module 'evil-search) + (ert-info ("Test smart case insensitive") + (evil-test-buffer + "[s]tart you YOU You you YOU You" + ("/you" [return]) + "start [y]ou YOU You you YOU You" + ("n") + "start you [Y]OU You you YOU You" + ("n") + "start you YOU [Y]ou you YOU You" + ("n") + "start you YOU You [y]ou YOU You")) + (ert-info ("Test smart case sensitive") + (evil-test-buffer + "[s]tart you YOU You you YOU You" + ("/You" [return]) + "start you YOU [Y]ou you YOU You" + ("n") + "start you YOU You you YOU [Y]ou")) + (ert-info ("Test insensitive") + (evil-test-buffer + "[s]tart you YOU You you YOU You" + ("/\\cyou" [return]) + "start [y]ou YOU You you YOU You" + ("n") + "start you [Y]OU You you YOU You" + ("n") + "start you YOU [Y]ou you YOU You" + ("n") + "start you YOU You [y]ou YOU You")) + (ert-info ("Test sensitive") + (evil-test-buffer + "[s]tart you YOU You you YOU You" + ("/\\Cyou" [return]) + "start [y]ou YOU You you YOU You" + ("n") + "start you YOU You [y]ou YOU You")) + (ert-info ("Test failing search does not move point") + (evil-test-buffer + "foo [f]oo foo\nbar bar2 bar\nbaz baz baz\n" + (error search-failed "/foofoo" [return]) + "foo [f]oo foo\nbar bar2 bar\nbaz baz baz\n" + ("/bar2" [return]) + "foo foo foo\nbar [b]ar2 bar\nbaz baz baz\n" + ("dw") + "foo foo foo\nbar [b]ar\nbaz baz baz\n" + (error search-failed "n") + "foo foo foo\nbar [b]ar\nbaz baz baz\n" + (error search-failed "N") + "foo foo foo\nbar [b]ar\nbaz baz baz\n")) + (ert-info ("Test search for newline") + (evil-test-buffer + "[s]tart\nline 2\nline 3\n\n" + ("/\\n" [return]) + "star[t]\nline 2\nline 3\n\n" + ("n") + "start\nline [2]\nline 3\n\n" + ("n") + "start\nline 2\nline [3]\n\n" + ("n") + "start\nline 2\nline 3\n[]\n")) + (ert-info ("Can search for start/end of symbol") + (let ((evil-magic 'very-magic)) + (evil-test-buffer + "[a]lpha bravo bra charlie" + ("/bra\\_>" [return]) + "alpha bravo [b]ra charlie"))) + (ert-info ("Can search for literal < and >") + (let ((evil-magic 'very-magic)) + (evil-test-buffer + :visual-start "«" + :visual-end "»" + "[a]lpha bravo charlie" + ("/" [return]) + "alpha bravo [<]bravo> charlie"))) + (ert-info ("Can paste from register in ex-search") + (evil-test-buffer + "Alpha [b]ravo charlie alpha bravo delta bravo delta" + ("\"bye" "w") + "Alpha bravo [c]harlie alpha bravo delta bravo delta" + ("/\C-rb" [return]) + "Alpha bravo charlie alpha [b]ravo delta bravo delta" + ("w/\C-r\C-o" [return]) + "Alpha bravo charlie alpha bravo delta bravo [d]elta")) + (ert-info ("Can use C-w yank-word functionality") + (let ((old-val evil-want-C-w-delete)) + (custom-set-variables '(evil-want-C-w-delete nil)) + (evil-test-buffer + "alpha [b]ravo charlie delta bravo echo" + ("/\C-w" [return]) + "alpha bravo charlie delta [b]ravo echo") + (custom-set-variables `(evil-want-C-w-delete ,old-val)))) + (ert-info ("Can use backreferences with vim-style regexp") + (let ((evil-ex-search-vim-style-regexp t)) + (evil-test-buffer + "[a]bacababcacabccadefghij" + ;; Very magic + ("/\\v(.)(.)(.)\\3\\1/e" [return]) + "abacababcacabcc[a]defghij") + (evil-test-buffer + "[a]bacababcacabccadefghij" + ;; Default magic + ("/\\(.\\)\\(.\\)\\(.\\)\\3\\1/e" [return]) + "abacababcacabcc[a]defghij") + (evil-test-buffer + "[a]bacababcac1abcca1defghij" + ;; With literal numbers + ("/\\(.\\)\\(.\\)\\(.\\)\\3\\11/e" [return]) + "abacababcac1abcca[1]defghij"))))) + +(ert-deftest evil-test-ex-search-offset () + "Test search offsets." + :tags '(evil ex search) + (evil-without-display + (evil-select-search-module 'evil-search-module 'evil-search) + (ert-info ("Test line offsets") + (evil-test-buffer + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/bar/2") + "foo foo\nbar bar\nbaz baz\n[A]nother line\nAnd yet another line" + ("?bar?-") + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/r bar/") + "foo foo\nba[r] bar\nbaz baz\nAnother line\nAnd yet another line")) + (ert-info ("Test end offsets") + (evil-test-buffer + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/bar/e") + "foo foo\nba[r] bar\nbaz baz\nAnother line\nAnd yet another line" + ("/baz/e+2") + "foo foo\nbar bar\nbaz [b]az\nAnother line\nAnd yet another line" + ("/line/e-1") + "foo foo\nbar bar\nbaz baz\nAnother li[n]e\nAnd yet another line")) + (ert-info ("Test begin offsets") + (evil-test-buffer + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/bar/b") + "foo foo\n[b]ar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/baz/b+2") + "foo foo\nbar bar\nba[z] baz\nAnother line\nAnd yet another line" + ("/line/b-") + "foo foo\nbar bar\nbaz baz\nAnother[ ]line\nAnd yet another line")) + (ert-info ("Test search-next with offset") + (evil-test-buffer + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/ ba/+1" [return]) + "foo foo\nbar bar\n[b]az baz\nAnother line\nAnd yet another line" + ("n") + "foo foo\nbar bar\nbaz baz\n[A]nother line\nAnd yet another line")) + (ert-info ("Test search next after /$") + (evil-test-buffer + "[l]ine 1\nline 2\n\n\line 4\n" + ("/$" [return]) + "line [1]\nline 2\n\n\line 4\n" + ("n") + "line 1\nline [2]\n\n\line 4\n" + ("n") + "line 1\nline 2\n[\n]\line 4\n" + ("n") + "line 1\nline 2\n\n\line [4]\n")))) + +(ert-deftest evil-test-ex-search-pattern-offset () + "Test pattern offsets." + :tags '(evil ex search) + (evil-without-display + (evil-select-search-module 'evil-search-module 'evil-search) + (ert-info ("Test simple pattern offsets") + (evil-test-buffer + "[f]oo foo\nbar bar\nfoo foo\nbaz baz\nAnother line\nAnd yet another line" + ("/bar/;/foo" [return]) + "foo foo\nbar bar\n[f]oo foo\nbaz baz\nAnother line\nAnd yet another line")) + (ert-info ("Test simple pattern offsets in backward direction") + (evil-test-buffer + "[f]oo foo\nbar bar\nfoo foo\nbaz baz\nAnother line\nAnd yet another line" + ("/bar/;?foo" [return]) + "foo [f]oo\nbar bar\nfoo foo\nbaz baz\nAnother line\nAnd yet another line")) + (ert-info ("Ensure second pattern is used for search repeat") + (evil-test-buffer + "[f]oo foo\nbar bar\nfoo foo\nbaz baz\nAnother line\nAnd yet another line" + ("/bar/;?foo" [return] "n") + "foo foo\nbar bar\n[f]oo foo\nbaz baz\nAnother line\nAnd yet another line")))) + +(ert-deftest evil-test-ex-search-repeat () + "Test repeat of search." + :tags '(evil ex search) + (evil-without-display + (evil-select-search-module 'evil-search-module 'evil-search) + (ert-info ("Test repeat of simple pattern") + (evil-test-buffer + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/bar" [return] "/" [return]) + "foo foo\nbar [b]ar\nbaz baz\nAnother line\nAnd yet another line")) + (ert-info ("Test repeat of simple pattern with new offset") + (evil-test-buffer + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/bar" [return] "//e" [return]) + "foo foo\nbar ba[r]\nbaz baz\nAnother line\nAnd yet another line")) + (ert-info ("Test repeat of pattern with offset") + (evil-test-buffer + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/bar/e" [return] "/" [return]) + "foo foo\nbar ba[r]\nbaz baz\nAnother line\nAnd yet another line")) + (ert-info ("Test repeat of pattern with offset without offset") + (evil-test-buffer + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/bar/e" [return] "//" [return]) + "foo foo\nbar [b]ar\nbaz baz\nAnother line\nAnd yet another line")) + (ert-info ("Test repeat of pattern with offset with new offset") + (evil-test-buffer + "[f]oo foo\nbar bar\nbaz baz\nAnother line\nAnd yet another line" + ("/bar/e" [return] "//b+1" [return]) + "foo foo\nbar b[a]r\nbaz baz\nAnother line\nAnd yet another line")) + (ert-info ("Test repeat of pattern in the same search") + (evil-test-buffer + "[a]lpha bravo charlie delta charlie alpha bravo alpha" + ("/charlie/;/") + "alpha bravo charlie delta [c]harlie alpha bravo alpha" + ("/alpha/;//") + "alpha bravo charlie delta charlie alpha bravo [a]lpha" + ("?charlie?;?") + "alpha bravo [c]harlie delta charlie alpha bravo alpha") + (ert-info ("including when switching direction") + (evil-test-buffer + "[a]lpha bravo charlie delta charlie alpha bravo alpha" + ("/bravo/;?") + "alpha bravo charlie delta charlie alpha [b]ravo alpha" + ("?alpha?;//") + "alpha bravo charlie delta charlie alpha bravo [a]lpha"))))) + +(ert-deftest evil-test-ex-search-word () + "Test search for word under point." + :tags '(evil ex search) + (evil-without-display + (evil-select-search-module 'evil-search-module 'evil-search) + (setq evil-ex-search-history nil) + (evil-test-buffer + "so[m]e text with a strange word +and here some other stuff +maybe we need one line more with some text\n" + (setq evil-symbol-word-search nil) + ("*") + "some text with a strange word +and here [s]ome other stuff +maybe we need one line more with some text\n" + ("n") + "some text with a strange word +and here some other stuff +maybe we need one line more with [s]ome text\n" + (ert-info ("Search history") + (should (equal evil-ex-search-history '("\\")))) + ("*") + "[s]ome text with a strange word +and here some other stuff +maybe we need one line more with some text\n" + (ert-info ("Search history with double pattern") + (should (equal evil-ex-search-history '("\\"))))) + (ert-info ("Test unbounded search") + (evil-select-search-module 'evil-search-module 'evil-search) + (setq evil-ex-search-history nil) + (evil-test-buffer + "[s]ymbol\n(defun my-symbolfunc ())\n(defvar my-symbolvar)\nanother symbol\n" + ("*") + (setq evil-symbol-word-search nil) + "symbol\n(defun my-symbolfunc ())\n(defvar my-symbolvar)\nanother [s]ymbol\n" + ("ggg*") + "symbol\n(defun my-[s]ymbolfunc ())\n(defvar my-symbolvar)\nanother symbol\n" + (should (equal evil-ex-search-history '("symbol" "\\"))) + ("n") + "symbol\n(defun my-symbolfunc ())\n(defvar my-[s]ymbolvar)\nanother symbol\n")) + (ert-info ("Test symbol search") + (evil-select-search-module 'evil-search-module 'evil-search) + (evil-test-buffer + "(defun my-s[y]mbol-func ())\n(defvar my-symbol-var)\n(my-symbol-func)\n(setq my-symbol-func2 (my-symbol-func))\n" + (setq evil-symbol-word-search t) + ("*") + "(defun my-symbol-func ())\n(defvar my-symbol-var)\n([m]y-symbol-func)\n(setq my-symbol-func2 (my-symbol-func))\n" + ("n") + "(defun my-symbol-func ())\n(defvar my-symbol-var)\n(my-symbol-func)\n(setq my-symbol-func2 ([m]y-symbol-func))\n")) + (ert-info ("Test with non-nil `evil-ex-search-vim-style-regexp'") + (evil-select-search-module 'evil-search-module 'evil-search) + (let ((evil-ex-search-vim-style-regexp t) + (evil-magic 'very-magic)) + (evil-test-buffer + "[a]lpha bravo alpha charlie alpha" + ("*") + "alpha bravo [a]lpha charlie alpha" + ("/" [return]) + "alpha bravo alpha charlie [a]lpha"))))) + +(ert-deftest evil-test-ex-search-motion () + "Test that Ex forward search, as a motion, can be repeated." + :tags '(evil ex search) + (evil-without-display + (evil-select-search-module 'evil-search-module 'evil-search) + (evil-test-buffer "alpha [b]ravo charlie delta golf hotel charlie india" + ("c/charlie" [return] "replacement " [escape] "4w.") + "alpha replacement charlie delta golf replacement[ ]charlie india"))) + +(ert-deftest evil-test-ex-search-next+previous-match () + :tags '(evil ex search) + (evil-without-display + (evil-select-search-module 'evil-search-module 'evil-search) + (ert-info ("evil-next-match in normal state") + (evil-test-buffer + "[b]ravo charlie delta charlie alpha charlie bravo" + ("/charlie" [return] "e") + "bravo charli[e] delta charlie alpha charlie bravo" + ("gn") + "bravo delta charlie alpha charlie bravo" + ([escape] "b") + "bravo [c]harlie delta charlie alpha charlie bravo" + ("gn") + "bravo delta charlie alpha charlie bravo" + ([escape] "w") + "bravo charlie [d]elta charlie alpha charlie bravo" + ("gn") + "bravo charlie delta alpha charlie bravo" + ([escape] "^") + "[b]ravo charlie delta charlie alpha charlie bravo" + ("3gn") + "bravo charlie delta charlie alpha bravo")) + (ert-info ("evil-previous-match in normal state") + (evil-test-buffer + "[b]ravo charlie delta charlie alpha charlie bravo" + ("/charlie" [return] "e") + "bravo charli[e] delta charlie alpha charlie bravo" + ("$gN") + "bravo charlie delta charlie alpha <[c]harlie> bravo" + ([escape] "gN") + "bravo charlie delta charlie alpha <[c]harlie> bravo" + ([escape] "e" "2gN") + "bravo charlie delta <[c]harlie> alpha charlie bravo")) + (ert-info ("evil-next-match in visual state") + (evil-test-buffer + "[b]ravo charlie delta charlie alpha charlie bravo" + ("/charlie" [return] "e") + "bravo charli[e] delta charlie alpha charlie bravo" + ("gn") + "bravo delta charlie alpha charlie bravo" + ("gn") + "bravo alpha charlie bravo" + ("o") + "bravo <[c]harlie delta charlie> alpha charlie bravo" + ("gn") + "bravo charli<[e] delta charlie> alpha charlie bravo" + ([escape] "^v2gn") + " alpha charlie bravo")) + (ert-info ("evil-previous-match in visual state") + (evil-test-buffer + "bravo charlie delta charlie alpha charlie brav[o]" + ("?charlie" [return]) + "bravo charlie delta charlie alpha [c]harlie bravo" + ("gN") + "bravo charlie delta charlie alpha <[c]harlie> bravo" + ("gN") + "bravo charlie delta <[c]harlie alpha charlie> bravo" + ("o") + "bravo charlie delta bravo" + ("gN") + "bravo charlie delta harlie bravo" + ([escape] "$v2gN") + "bravo charlie delta <[c]harlie alpha charlie bravo>")) + (ert-info ("evil-match in operator state") + (evil-test-buffer + "[b]ravo charlie delta charlie alpha charlie bravo" + ("/charlie" [return]) + "bravo [c]harlie delta charlie alpha charlie bravo" + ("cgn" "foo" [escape]) + "bravo fo[o] delta charlie alpha charlie bravo" + (".") + "bravo foo delta fo[o] alpha charlie bravo" + ("$cgN" "bar" [escape]) + "bravo foo delta foo alpha ba[r] bravo")) + (ert-info ("Unfound evil ex next match doesn't move cursor") + (evil-test-buffer + "[a]lpha bravo" + (should-error (execute-kbd-macro "/zulu")) + "[a]lpha bravo" + (should-error (execute-kbd-macro "gn")) + "[a]lpha bravo")))) + +(ert-deftest evil-test-isearch-word () + "Test isearch for word under point." + :tags '(evil isearch) + (evil-without-display + (evil-select-search-module 'evil-search-module 'isearch) + (evil-test-buffer + "so[m]e text with a strange word +and here some other stuff +maybe we need one line more with some text\n" + (setq evil-symbol-word-search nil) + ("*") + "some text with a strange word +and here [s]ome other stuff +maybe we need one line more with some text\n" + ("n") + "some text with a strange word +and here some other stuff +maybe we need one line more with [s]ome text\n" + ("*") + "[s]ome text with a strange word +and here some other stuff +maybe we need one line more with some text\n") + (ert-info ("Test unbounded search") + (evil-select-search-module 'evil-search-module 'isearch) + (evil-test-buffer + "[s]ymbol\n(defun my-symbolfunc ())\n(defvar my-symbolvar)\nanother symbol\n" + (setq evil-symbol-word-search nil) + ("*") + "symbol\n(defun my-symbolfunc ())\n(defvar my-symbolvar)\nanother [s]ymbol\n" + ("ggg*") + "symbol\n(defun my-[s]ymbolfunc ())\n(defvar my-symbolvar)\nanother symbol\n" + ("n") + "symbol\n(defun my-symbolfunc ())\n(defvar my-[s]ymbolvar)\nanother symbol\n")) + (ert-info ("Test symbol search") + (evil-select-search-module 'evil-search-module 'isearch) + (evil-test-buffer + "(defun my-s[y]mbol-func ())\n(defvar my-symbol-var)\n(my-symbol-func)\n(setq my-symbol-func2 (my-symbol-func))\n" + (setq evil-symbol-word-search t) + ("*") + "(defun my-symbol-func ())\n(defvar my-symbol-var)\n([m]y-symbol-func)\n(setq my-symbol-func2 (my-symbol-func))\n" + ("n") + "(defun my-symbol-func ())\n(defvar my-symbol-var)\n(my-symbol-func)\n(setq my-symbol-func2 ([m]y-symbol-func))\n")))) + +(ert-deftest evil-test-read () + "Test of `evil-read'" + :tags '(evil ex) + (evil-without-display + (ert-info ("Test insertion of file with trailing newline") + (evil-with-temp-file name + "temp file 1\ntemp file 2\n" + (ert-info ("At first line") + (evil-test-buffer + "[l]ine 1\nline 2" + ((vconcat ":read " name [return])) + "line 1\n[t]emp file 1\ntemp file 2\nline 2")) + (ert-info ("At last line") + (evil-test-buffer + "line 1\n[l]ine 2" + ((vconcat ":read " name [return])) + "line 1\nline 2\n[t]emp file 1\ntemp file 2\n")) + (ert-info ("After specified line number") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4\line 5" + ((vconcat ":3read " name [return])) + "line 1\nline 2\nline 3\n[t]emp file 1\ntemp file 2\nline 4\line 5")) + (ert-info ("After specified line 0") + (evil-test-buffer + "line 1\nline [2]\nline 3\nline 4\line 5" + ((vconcat ":0read " name [return])) + "[t]emp file 1\ntemp file 2\nline 1\nline 2\nline 3\nline 4\line 5")))) + (ert-info ("Test insertion of file without trailing newline") + (evil-with-temp-file name + "temp file 1\ntemp file 2" + (evil-test-buffer + "[l]ine 1\nline 2" + ((vconcat ":read " name [return])) + "line 1\n[t]emp file 1\ntemp file 2\nline 2"))) + (ert-info ("Test insertion of shell command") + (ert-info ("with space") + (evil-test-buffer + "[l]line 1\nline 2" + (":read !echo cmd line 1" [return]) + "line 1\n[c]md line 1\nline 2")) + (ert-info ("without space") + (evil-test-buffer + "[l]line 1\nline 2" + (":read!echo cmd line 1" [return]) + "line 1\n[c]md line 1\nline 2"))) + (ert-info ("Test substitution of % in shell commands") + (evil-with-temp-file name + "3\n2\n1\n" + (evil-test-buffer + (":e " name [return]) + "[3]\n2\n1\n" + (":read !echo %" [return] ":w" [return]) + (file name (concat "3\n" + (buffer-file-name) "\n" + "2\n" + "1\n"))))) + (ert-info ("Ensure that point ends up at the last line of shell output, if any") + (evil-with-temp-file name + "3\n2\n1\n" + (evil-test-buffer + "[l]ine 1\nline 2" + ((vconcat ":read !cat " name [return])) + "line 1\n3\n2\n[1]\nline 2"))) + (ert-info ("Test insertion of shell command without trailing newline") + (ert-info ("with space") + (evil-test-buffer + "[l]line 1\nline 2" + (":read !echo -n cmd line 1" [return]) + "line 1\n[c]md line 1\nline 2")) + (ert-info ("without space") + (evil-test-buffer + "[l]line 1\nline 2" + (":read!echo -n cmd line 1" [return]) + "line 1\n[c]md line 1\nline 2"))))) + +(ert-deftest evil-test-shell-command () + "Test `evil-shell-command'." + (ert-info ("ex shell command") + (evil-test-buffer + "[l]ine 5\nline 4\nline 3\nline 2\nline 1\n" + (":2,3!sort" [return]) + "line 5\n[l]ine 3\nline 4\nline 2\nline 1\n")) + (ert-info ("shell command operator with count") + (evil-test-buffer + "line 5\n[l]ine 4\nline 3\nline 2\nline 1\n" + ("2!!sort" [return]) + "line 5\n[l]ine 3\nline 4\nline 2\nline 1\n")) + (ert-info ("shell command operator with motion") + (evil-test-buffer + "line 5\n[l]ine 4\nline 3\nline 2\nline 1\n" + ("!jsort" [return]) + "line 5\n[l]ine 3\nline 4\nline 2\nline 1\n")) + (ert-info ("shell command operator with backward motion") + (evil-test-buffer + "line 5\nline 4\n[l]ine 3\nline 2\nline 1\n" + ("!ksort" [return]) + "line 5\n[l]ine 3\nline 4\nline 2\nline 1\n")) + (ert-info ("shell command operator with visual selection") + (evil-test-buffer + "line 5\n[l]ine 4\nline 3\nline 2\nline 1\n" + ("vj!sort" [return]) + "line 5\n[l]ine 3\nline 4\nline 2\nline 1\n"))) + +(defmacro evil-with-both-search-modules (&rest body) + `(mapc (lambda (search-module) + (setq evil-search-forward-history nil + evil-search-backward-history nil + evil-ex-search-history nil) + (evil-select-search-module 'evil-search-module search-module) + ,@body) + '(isearch evil-search))) + +(ert-deftest evil-test-global () + "Test `evil-ex-global'." + :tags '(evil ex global) + (ert-info ("global delete") + (evil-test-buffer + "[n]o 1\nno 2\nno 3\nyes 4\nno 5\nno 6\nno 7\n" + (":g/yes/d" [return]) + "no 1\nno 2\nno 3\n[n]o 5\nno 6\nno 7\n")) + (ert-info ("global delete, specifying case sensitivty") + (evil-test-buffer + "[a]lpha bravo charlie\nalpha Bravo charlie\nalpha BRAVO charlie\nalpha delta charlie" + (":g/\\CBravo/d" [return]) + "alpha bravo charlie\n[a]lpha BRAVO charlie\nalpha delta charlie" + (":g/\\cBravo/d" [return]) + "alpha delta charlie")) + (ert-info ("global delete with arg") + (evil-test-buffer + "[n]o 1\nno 2\nno 3\nyes 4\nno 5\nno 6\nno 7\n" + (":g/yes/d2" [return]) + "no 1\nno 2\nno 3\n[n]o 6\nno 7\n")) + (ert-info ("global delete with range") + (evil-test-buffer + "alpha\nbravo\ncharlie\ndelta\ncharlie\necho\ngolf\ncharlie\nhotel" + (":g/charlie/-1d") + "alpha\ncharlie\ncharlie\necho\ncharlie\nhotel")) + (ert-info ("global substitute") + (evil-test-buffer + "[n]o 1\nno 2\nno 3\nyes 4\nno 5\nno 6\nno 7\n" + (":g/no/s/[3-6]/x" [return]) + "no 1\nno 2\nno x\nyes 4\nno x\nno x\n[n]o 7\n" + ("u") + "no 1\nno 2\nno [3]\nyes 4\nno 5\nno 6\nno 7\n")) + (ert-info ("global substitute with trailing slash") + (evil-test-buffer + "[n]o 1\nno 2\nno 3\nyes 4\nno 5\nno 6\nno 7\n" + (":g/no/s/[3-6]/x/" [return]) + "no 1\nno 2\nno x\nyes 4\nno x\nno x\n[n]o 7\n" + ("u") + "no 1\nno 2\nno [3]\nyes 4\nno 5\nno 6\nno 7\n")) + (evil-select-search-module 'evil-search-module 'evil-search) + (ert-info ("global use last match if none given, with evil-search") + (evil-test-buffer + "[n]o 1\nno 2\nno 3\nyes 4\nno 5\nno 6\nno 7\n" + ("/yes" [return]) + "no 1\nno 2\nno 3\nyes 4\nno 5\nno 6\nno 7\n" + (":g//d" [return]) + "no 1\nno 2\nno 3\n[n]o 5\nno 6\nno 7\n" + (":v//d" [return]) + "")) + (evil-select-search-module 'evil-search-module 'isearch) + (ert-info ("global use last match if none given, with isearch") + (evil-test-buffer + "[n]o 1\nno 2\nno 3\nisearch 4\nno 5\nno 6\nno 7\n" + ("/isearch" [return]) + "no 1\nno 2\nno 3\nisearch 4\nno 5\nno 6\nno 7\n" + (":g//d" [return]) + "no 1\nno 2\nno 3\n[n]o 5\nno 6\nno 7\n" + (":v//d" [return]) + "")) + (ert-info (":global should take into account evil-ex-search-case") + (evil-with-both-search-modules + (let ((evil-ex-search-case 'sensitive)) + (evil-test-buffer + "this\nThis\n" + (":g/this/d" [return]) + "This\n")) + (let ((evil-ex-search-case 'insensitive)) + (evil-test-buffer + "this\nThis\n" + (":g/this/d" [return]) + "")) + (let ((evil-ex-search-case 'smart)) + (evil-test-buffer + "this\nThis\n" + (":g/this/d" [return]) + "") + (evil-test-buffer + "this\nThis\n" + (":g/This/d" [return]) + "this\n")))) + (ert-info (":global should transform vim-style regexp when appropriate") + (let ((evil-ex-search-vim-style-regexp t)) + (evil-test-buffer + "a\n1\nb\n2\nc\n3\n" + (":g/\\d/>") + "a\n 1\nb\n 2\nc\n 3\n")))) + +(ert-deftest evil-test-normal () + "Test `evil-ex-normal'." + :tags '(evil ex) + (let (evil-want-fine-undo) + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4\nline 5\n" + (":normal lxIABC" [escape] "AXYZ" [return]) + "ABClne 1XY[Z]\nline 2\nline 3\nline 4\nline 5\n" + (":3,4normal lxIABC" [escape] "AXYZ" [return]) + "ABClne 1XYZ\nline 2\nABClne 3XYZ\nABClne 4XY[Z]\nline 5\n" + ("u") + "ABClne 1XYZ\nline 2\nl[i]ne 3\nline 4\nline 5\n"))) + +(ert-deftest evil-test-copy () + :tags '(evil ex) + "Test `evil-copy'." + (ert-info ("Copy to last line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (":2,3copy$") + "line1\nline2\nline3\nline4\nline5\nline2\n[l]ine3\n")) + (ert-info ("Copy to last incomplete line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5" + (":2,3copy$") + "line1\nline2\nline3\nline4\nline5\nline2\n[l]ine3\n")) + (ert-info ("Copy incomplete line to last incomplete line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5" + (":4,5copy$") + "line1\nline2\nline3\nline4\nline5\nline4\n[l]ine5\n")) + (ert-info ("Copy to first line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (":2,3copy0") + "line2\n[l]ine3\nline1\nline2\nline3\nline4\nline5\n")) + (ert-info ("Copy to intermediate line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (":2,4copy2") + "line1\nline2\nline2\nline3\n[l]ine4\nline3\nline4\nline5\n")) + (ert-info ("Copy to current line") + (evil-test-buffer + "line1\nline2\nline3\nli[n]e4\nline5\n" + (":2,4copy.") + "line1\nline2\nline3\nline4\nline2\nline3\n[l]ine4\nline5\n"))) + +(ert-deftest evil-test-move () + :tags '(evil ex) + "Test `evil-move'." + (ert-info ("Move to last line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (":2,3move$") + "line1\nline4\nline5\nline2\n[l]ine3\n")) + (ert-info ("Move to last incomplete line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5" + (":2,3move$") + "line1\nline4\nline5\nline2\n[l]ine3\n")) + (ert-info ("Move incomplete line to last incomplete line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5" + (":4,5move$") + "line1\nline2\nline3\nline4\n[l]ine5\n")) + (ert-info ("Move to first line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (":2,3move0") + "line2\n[l]ine3\nline1\nline4\nline5\n")) + (ert-info ("Move to intermediate line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (":2,4move2") + "line1\nline2\nline3\n[l]ine4\nline5\n")) + (ert-info ("Move to other line") + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (":2,3move4") + "line1\nline4\nline2\n[l]ine3\nline5\n")) + (ert-info ("Move to current line") + (evil-test-buffer + "line1\nline2\nline3\nli[n]e4\nline5\n" + (":2,4move.") + "line1\nline2\nline3\n[l]ine4\nline5\n")) + (ert-info ("Move to backwards line, searching forwards (wrapping around)") + (evil-test-buffer + " +Target +Other line +[S]ource +" + (":move/Target/") + " +Target +[S]ource +Other line +")) + (ert-info ("Move to forwards line, searching backwards (wrapping around)") + (evil-test-buffer + " +Target +[O]ther line +Source +" + (":move?Source?") + " +Target +Source +[O]ther line +")) + (ert-info ("Move with global (classic line reversal)") + (evil-test-buffer + "5\n4\n3\n2\n1\n" + (":g/^/m0") + "1\n2\n3\n4\n5\n") + (ert-info ("... and visual selection") + (evil-test-buffer + "<5\n4\n3\n2\n[1]>\n" + (":g/^/m0") + "1\n2\n3\n4\n5\n")) + (ert-info ("... and no last blank line") + (evil-test-buffer + "5\n4\n3\n2\n1" + (":g/^/m0") + "1\n2\n3\n4\n5")))) + +(ert-deftest evil-test-write () + :tags '(evil ex) + "Test `evil-write'." + (ert-info ("Write open file") + (evil-with-temp-file filename "line1\nline2\nline3\n" + (evil-test-buffer + ((vconcat ":e " filename [return])) + "[l]ine1\nline2\nline3\n" + ("Galine4\nline5\n" [escape]) + "line1\nline2\nline3\nline4\nline5\n" + (":w") + (file filename "line1\nline2\nline3\nline4\nline5\n")))) + (ert-info ("Write current buffer to new file") + (let ((filename (make-temp-file "evil-test-write"))) + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (delete-file filename) + ((vconcat ":w " filename [return])) + (file filename "line1\nline2\nline3\nline4\nline5\n") + (delete-file filename)))) + (ert-info ("Write part of a buffer") + (let ((filename (make-temp-file "evil-test-write"))) + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (delete-file filename) + ((vconcat ":2,3w " filename [return])) + (file filename "line2\nline3\n") + (delete-file filename)))) + (ert-info ("Appending a file") + (let ((filename (make-temp-file "evil-test-write"))) + (evil-test-buffer + "[l]ine1\nline2\nline3\nline4\nline5\n" + (delete-file filename) + ((vconcat ":4w " filename [return])) + (file filename "line4\n") + ((vconcat ":1,2w >>" filename [return])) + (file filename "line4\nline1\nline2\n") + ((vconcat ":w >> " filename [return])) + (file filename + "line4\nline1\nline2\nline1\nline2\nline3\nline4\nline5\n") + (delete-file filename))))) + +(ert-deftest evil-test-ex-sort () + :tags '(evil ex) + "Text Ex command \":sort\" (`evil-ex-sort')." + (ert-info ("Plain sort") + (evil-test-buffer + "[z]zyy\ntest\ntEst\ntesT\nTEST\ntest\n" + (":sort") + "[T]EST\ntEst\ntesT\ntest\ntest\nzzyy\n")) + (ert-info ("Reverse sort") + (evil-test-buffer + "[z]zyy\ntest\ntEst\ntesT\nTEST\ntest\n" + (":sort!") + "[z]zyy\ntest\ntest\ntesT\ntEst\nTEST\n")) + (ert-info ("case insensitive") + (evil-test-buffer + "[z]zyy\ntest\ntEst\ntesT\nTEST\ntest\n" + (":sort i") + "[t]est\ntEst\ntesT\nTEST\ntest\nzzyy\n")) + (ert-info ("unique") + (evil-test-buffer + "[z]zyy\ntest\ntEst\ntesT\nTEST\ntest\n" + (":sort u") + "[T]EST\ntEst\ntesT\ntest\nzzyy\n")) + (ert-info ("case insensitive and unique") + (evil-test-buffer + "[z]zyy\ntest\ntEst\ntesT\nTEST\ntest\n" + (":sort iu") + "[t]est\nzzyy\n")) + (ert-info ("pattern sort") + (evil-test-buffer + "[t]e|z|t\nzz|a|y\n" + (":sort /|[a-z]|/ r") + ("zz|a|y\nte|z|t\n")) + (evil-test-buffer + "[a],b\nb,a\n" + (":sort /[^,]*,/") + "b,a\na,b\n") + (evil-test-buffer + "[a],b\nb,a\n" + ("/," [return] ":sort //") + "b,a\na,b\n")) + (ert-info ("numeric sort") + (ert-info ("decimal") + (evil-test-buffer + "27\n027\n2\na\n1\n" + (":sort n") + "a\n1\n2\n27\n027\n")) + (ert-info ("octal") + (evil-test-buffer + "9\n8\n" + (":sort o") + "9\n8\n") + (evil-test-buffer + "777\n776\n7239\n" + (":sort o") + "7239\n776\n777\n")) + (ert-info ("hexadecimal") + (evil-test-buffer + "0xae\n0xb\nae\nad\n" + (":sort x") + "0xb\nad\n0xae\nae\n")))) + +;;; Command line window + +(ert-deftest evil-test-command-window-ex () + "Test command line window for Ex commands." + (let ((evil-ex-history (list "s/foo/baz" "s/foo/bar"))) + (evil-test-buffer "[b]ar baz foo" + ("q:") + "s/foo/bar\ns/foo/baz\n[]\n" + ("kk:s/bar/quz" [return]) + "[s]/foo/quz\ns/foo/baz\n" + ("fzrx" [return]) + "[b]ar baz qux" + (should (equal (car evil-ex-history) "s/foo/qux"))))) + +(ert-deftest evil-test-command-window-noop () + "Test that executing a blank command does nothing." + (evil-test-buffer "[f]oo foo foo" + ("q:") + "[]\n" + ([return]) + "[f]oo foo foo")) + +(ert-deftest evil-test-command-window-search-history () + "Test command window with forward and backward search history." + (let ((evil-search-module 'isearch) + (evil-search-forward-history (list "four" "three" "qux")) + (evil-search-backward-history (list "baz" "bar"))) + (evil-test-buffer "foo bar [b]az qux one two three four" + ("q/") + "qux\nthree\nfour\n[]\n" + ("k" [return]) + "foo bar baz qux one two three [f]our" + ("0N") + "foo bar baz qux one two three [f]our" + ("q?") + "bar\nbaz\n[]\n" + ("k$rr" [return]) + "foo [b]ar baz qux one two three four" + (error 'user-error "q/iNOT THERE" [return]) + "foo [b]ar baz qux one two three four"))) + +(ert-deftest evil-test-command-window-search-word () + "Test command window history when searching for word under cursor." + (let ((evil-search-module 'isearch)) + (evil-test-buffer + "[f]oo bar foo bar foo" + ("**") + "foo bar foo bar [f]oo" + ("B#") + "foo [b]ar foo bar foo" + ("q/k" [return]) + "foo bar [f]oo bar foo" + ("q?k" [return]) + "foo [b]ar foo bar foo"))) + +;;; Window motions + +(ert-deftest evil-test-window-next-and-prev-respect-window-parameters () + "Test that `evil-window-next' and `evil-window-prev' respect window parameters. +E.g. they do not move to windows that have the `no-other-window' +parameter set." + (save-window-excursion + ;; Make sure there is a single original window and open a second + ;; other window with `no-other-window' set. + (delete-other-windows) + (let* ((this-window (selected-window)) + (other-window (split-window))) + (set-window-parameter other-window 'no-other-window t) + ;; `evil-window-next'/`evil-window-prev' should not switch to + ;; the window with `no-other-window' parameter set. + (evil-window-next nil) + (should (eq (selected-window) this-window)) + (evil-window-prev nil) + (should (eq (selected-window) this-window))))) + +;;; Utilities + +(ert-deftest evil-test-parser () + "Test `evil-parser'" + (with-temp-buffer + (cl-flet + ((parse + (s &rest args) + (erase-buffer) + (insert s) + (goto-char (point-min)) + (apply + (evil-parser + '((number "[0-9]+" #'string-to-number) + (plus "\\+" #'intern) + (minus "-" #'intern) + (operator + plus + minus) + (sign (\? operator)) + (signed-number (sign number)) + (expr + ("foo" (& "bar")) + ("xxx" (! "yyy")) + (number operator number)) + (epsilon nil)) + expr operator plus signed-number number epsilon) + args))) + (ert-info ("Nothing") + (should (equal (parse "1+2" 'epsilon) '(nil)))) + (ert-info ("Symbols") + (should (equal (parse "+" 'plus) '((intern "+")))) + (should (equal (parse "+" 'operator) '((intern "+"))))) + (ert-info ("Leading whitespace") + (should (equal (parse " 1" 'number) '((string-to-number "1"))))) + (ert-info ("Syntax tree") + (should (equal (parse "1" 'signed-number t) + '((signed-number (sign . 1) (number . 2)))))) + (ert-info ("Lookahead") + (should (equal (parse "foobar" 'expr) '((list "foo")))) + (should (null (parse "foobaz" 'expr))) + (should (null (parse "xxxyyy" 'expr))) + (should (equal (parse "xxxzzz" 'expr) '((list "xxx"))))) + (ert-info ("Semantic actions") + (should (equal (parse "1+1" 'expr) + '((list + (string-to-number "1") + (intern "+") + (string-to-number "1"))))))))) + +(ert-deftest evil-test-delimited-arguments () + "Test `evil-delimited-arguments'" + :tags '(evil util) + (ert-info ("Any number of arguments") + (should (equal (evil-delimited-arguments "/a/b/c/") + '("a" "b" "c"))) + (should (equal (evil-delimited-arguments "/a/b/c") + '("a" "b" "c"))) + (should (equal (evil-delimited-arguments "/a/b//") + '("a" "b" ""))) + (should (equal (evil-delimited-arguments "/a///") + '("a" "" ""))) + (should (equal (evil-delimited-arguments "/a/ ") + '("a" " "))) + (should (equal (evil-delimited-arguments "/a/") + '("a"))) + (should (equal (evil-delimited-arguments "//b//") + '("" "b" ""))) + (should (equal (evil-delimited-arguments "/a//c") + '("a" "" "c"))) + (should (equal (evil-delimited-arguments "////") + '("" "" ""))) + (should (equal (evil-delimited-arguments "/") + nil)) + (should (equal (evil-delimited-arguments " ") + nil)) + (should (equal (evil-delimited-arguments "") + nil))) + (ert-info ("Two arguments") + (should (equal (evil-delimited-arguments "/a/b/c" 2) + '("a" "b/c"))) + (should (equal (evil-delimited-arguments "/a/b/" 2) + '("a" "b"))) + (should (equal (evil-delimited-arguments "/a/b" 2) + '("a" "b"))) + (should (equal (evil-delimited-arguments "/a//" 2) + '("a" ""))) + (should (equal (evil-delimited-arguments "/a/ " 2) + '("a" " "))) + (should (equal (evil-delimited-arguments "/a/" 2) + '("a" nil))) + (should (equal (evil-delimited-arguments "/a" 2) + '("a" nil))) + (should (equal (evil-delimited-arguments " " 2) + '(nil nil))) + (should (equal (evil-delimited-arguments "" 2) + '(nil nil)))) + (ert-info ("One argument") + (should (equal (evil-delimited-arguments "/a/b/c" 1) + '("a/b/c"))) + (should (equal (evil-delimited-arguments "/a/ " 1) + '("a"))) + (should (equal (evil-delimited-arguments "/a/" 1) + '("a"))) + (should (equal (evil-delimited-arguments "/a" 1) + '("a"))) + (should (equal (evil-delimited-arguments "/" 1) + '(nil))) + (should (equal (evil-delimited-arguments " " 1) + '(nil))) + (should (equal (evil-delimited-arguments "" 1) + '(nil)))) + (ert-info ("Zero arguments") + (should (equal (evil-delimited-arguments "/a" 0) + nil)) + (should (equal (evil-delimited-arguments "/" 0) + nil)) + (should (equal (evil-delimited-arguments " " 0) + nil)) + (should (equal (evil-delimited-arguments "" 0) + nil)))) + +(ert-deftest evil-test-concat-charsets () + "Test `evil-concat-charsets'" + :tags '(evil util) + (ert-info ("Bracket") + (should (equal (evil-concat-charsets "abc" "]def") + "]abcdef"))) + (ert-info ("Complement") + (should (equal (evil-concat-charsets "^abc" "def") + "^abcdef")) + (should (equal (evil-concat-charsets "^abc" "^def") + "^abcdef"))) + (ert-info ("Hyphen") + (should (equal (evil-concat-charsets "abc" "-def") + "-abcdef")) + (should (equal (evil-concat-charsets "^abc" "-def") + "^-abcdef"))) + (ert-info ("Newline") + (should (equal (evil-concat-charsets "^ \t\r\n" "[:word:]_") + "^ \t\r\n[:word:]_")))) + +(ert-deftest evil-test-properties () + "Test `evil-get-property' and `evil-put-property'" + :tags '(evil util) + (defvar evil--alist) + (let (evil--alist) + (ert-info ("Set properties") + (evil-put-property 'evil--alist 'wibble :foo t) + (should (equal evil--alist '((wibble . (:foo t))))) + (evil-put-property 'evil--alist 'wibble :bar nil) + (should (equal evil--alist '((wibble . (:foo t :bar nil))))) + (evil-put-property 'evil--alist 'wobble :foo nil :bar nil :baz t) + (should (equal evil--alist '((wobble . (:foo nil :bar nil :baz t)) + (wibble . (:foo t :bar nil)))))) + (ert-info ("Get properties") + (should (evil-get-property evil--alist 'wibble :foo)) + (should-not (evil-get-property evil--alist 'wibble :bar)) + (should-not (evil-get-property evil--alist 'wobble :foo)) + (should-not (evil-get-property evil--alist 'wibble :baz)) + (should (equal (evil-get-property evil--alist t :foo) + '((wibble . t) (wobble . nil)))) + (should (equal (evil-get-property evil--alist t :bar) + '((wibble . nil) (wobble . nil)))) + (should (equal (evil-get-property evil--alist t :baz) + '((wobble . t))))))) + +(ert-deftest evil-test-filter-list () + "Test `evil-filter-list'" + :tags '(evil util) + (ert-info ("Return filtered list") + (should (null (evil-filter-list #'null '(nil)))) + (should (equal (evil-filter-list #'null '(nil 1)) '(1))) + (should (equal (evil-filter-list #'null '(nil 1 2 nil)) '(1 2))) + (should (equal (evil-filter-list #'null '(nil nil 1)) '(1))) + (should (equal (evil-filter-list #'null '(nil 1 nil 2 nil 3)) + '(1 2 3)))) + (ert-info ("Remove matches by side-effect when possible") + (let (list) + (setq list '(1 nil)) + (evil-filter-list #'null list) + (should (equal list '(1))) + + (setq list '(1 nil nil)) + (evil-filter-list #'null list) + (should (equal list '(1))) + + (setq list '(1 nil nil 2)) + (evil-filter-list #'null list) + (should (equal list '(1 2))) + + (setq list '(1 nil 2 nil 3)) + (evil-filter-list #'null list) + (should (equal list '(1 2 3)))))) + +(ert-deftest evil-test-concat-lists () + "Test `evil-concat-lists' and `evil-concat-alists'" + :tags '(evil util) + (ert-info ("Remove duplicates across lists") + (should (equal (evil-concat-lists + nil '(a b) '(b c)) + '(a b c)))) + (ert-info ("Remove duplicates inside lists") + (should (equal (evil-concat-lists + '(a a b) nil '(b c) nil) + '(a b c)))) + (ert-info ("Remove duplicate associations") + (should (equal (evil-concat-alists + '((a . b)) '((a . c))) + '((a . c)))) + (should-not (equal (evil-concat-lists + '((a . b)) '((a . c))) + '((a . b)))))) + +(ert-deftest evil-test-sort () + "Test `evil-sort'." + :tags '(evil util) + (ert-info ("Two elements") + (let ((a 2) (b 1)) + (evil-sort a b) + (should (= a 1)) + (should (= b 2)))) + (ert-info ("Three elements") + (let ((a 3) (b 1) (c 2)) + (evil-sort a b c) + (should (= a 1)) + (should (= b 2)) + (should (= c 3)))) + (ert-info ("Four elements") + (let ((a 4) (b 3) (c 2) (d 1)) + (evil-sort a b c d) + (should (= a 1)) + (should (= b 2)) + (should (= c 3)) + (should (= d 4))))) + +(ert-deftest evil-test-read-key () + "Test `evil-read-key'" + :tags '(evil util) + (let ((unread-command-events '(?A))) + (ert-info ("Prevent downcasing in `this-command-keys'") + (should (eq (evil-read-key) ?A)) + (should (equal (this-command-keys) "A"))))) + +(ert-deftest evil-test-extract-count () + "Test `evil-extract-count'" + :tags '(evil util) + (evil-test-buffer + (ert-info ("Exact without count") + (should (equal (evil-extract-count "x") + (list nil 'evil-delete-char "x" nil))) + (should (equal (evil-extract-count "g0") + (list nil 'evil-beginning-of-visual-line "g0" nil)))) + + (ert-info ("Exact with count") + (should (equal (evil-extract-count "420x") + (list 420 'evil-delete-char "x" nil))) + (should (equal (evil-extract-count (vconcat "420" [M-right])) + (list 420 (key-binding [M-right]) (vconcat [M-right]) nil))) + (should (equal (evil-extract-count "2301g0") + (list 2301 'evil-beginning-of-visual-line "g0" nil)))) + + (ert-info ("Extra elements without count") + (should (equal (evil-extract-count "xAB") + (list nil 'evil-delete-char "x" "AB"))) + (should (equal (evil-extract-count "g0CD") + (list nil 'evil-beginning-of-visual-line "g0" "CD")))) + + (ert-info ("Extra elements with count") + (should (equal (evil-extract-count "420xAB") + (list 420 'evil-delete-char "x" "AB"))) + (should (equal (evil-extract-count "2301g0CD") + (list 2301 'evil-beginning-of-visual-line "g0" "CD")))) + + (ert-info ("Exact \"0\" count") + (should (equal (evil-extract-count "0") + (list nil 'evil-beginning-of-line + "0" nil)))) + + (ert-info ("Extra elements and \"0\"") + (should (equal (evil-extract-count "0XY") + (list nil 'evil-beginning-of-line + "0" "XY")))) + + (ert-info ("Count only") + (should-error (evil-extract-count "1230"))) + + (ert-info ("Unknown command") + (should-error (evil-extract-count "°")) + (should-error (evil-extract-count "12°"))))) + +(ert-deftest evil-transform-vim-style-regexp () + "Test `evil-transform-vim-style-regexp'" + (dolist (repl '((?s . "[[:space:]]") + (?S . "[^[:space:]]") + (?d . "[[:digit:]]") + (?D . "[^[:digit:]]") + (?x . "[[:xdigit:]]") + (?X . "[^[:xdigit:]]") + (?o . "[0-7]") + (?O . "[^0-7]") + (?a . "[[:alpha:]]") + (?A . "[^[:alpha:]]") + (?l . "[a-z]") + (?L . "[^a-z]") + (?u . "[A-Z]") + (?U . "[^A-Z]") + (?y . "\\s") + (?Y . "\\S") + (?w . "\\w") + (?W . "\\W"))) + (ert-info ((format "Test transform from '\\%c' to '%s'" + (car repl) (cdr repl))) + (should (equal (evil-transform-vim-style-regexp + (concat "xxx\\" + (char-to-string (car repl)) + "\\" + (char-to-string (car repl)) + "\\\\" + (char-to-string (car repl)) + "\\\\\\" + (char-to-string (car repl)) + "yyy")) + (concat "xxx" + (cdr repl) + (cdr repl) + "\\\\" + (char-to-string (car repl)) + "\\\\" + (cdr repl) + "yyy")))))) + +;;; Advice + +(ert-deftest evil-test-eval-last-sexp () + "Test advised `evil-last-sexp'" + :tags '(evil advice) + (ert-info ("Normal state") + (evil-test-buffer + "(+ 1 (+ 2 3[)])" + ("1" (kbd "C-x C-e")) + "(+ 1 (+ 2 35[)])")) + (ert-info ("Insert state") + (evil-test-buffer + "(+ 1 (+ 2 3[)])" + ("i" (kbd "C-u") (kbd "C-x C-e") [escape]) + "(+ 1 (+ 2 3[3]))")) + (ert-info ("Emacs state") + (evil-test-buffer + "(+ 1 (+ 2 3[)])" + ((kbd "C-z") (kbd "C-u") (kbd "C-x C-e")) + "(+ 1 (+ 2 33[)])"))) + +;;; ESC + +(ert-deftest evil-test-esc-count () + "Test if prefix-argument is transferred for key sequences with meta-key" + :tags '(evil esc) + (unless noninteractive + (ert-info ("Test M-") + (evil-test-buffer + "[A]BC DEF GHI JKL MNO" + ("3" (kbd "ESC ")) + "ABC DEF GHI[ ]JKL MNO")) + (ert-info ("Test shell-command") + (evil-test-buffer + "[A]BC DEF GHI JKL MNO" + ("1" (kbd "ESC !") "echo TEST" [return]) + "[T]EST\nABC DEF GHI JKL MNO")))) + +(when (or evil-tests-profiler evil-tests-run) + (evil-tests-initialize)) + +(ert-deftest evil-test-black-hole-register () + :tags '(evil) + (ert-info ("Test \"_ on delete word") + (evil-test-buffer + "[E]vil evil is awesome." + ("dw\"_dwP") + "Evil[ ]is awesome.")) + (ert-info ("Test \"_ on delete line") + (evil-test-buffer + "[T]his line is a keeper!\nThis line is not." + ("dd\"_ddP") + "[T]his line is a keeper!")) + (ert-info ("Test \"_ on delete region") + (evil-test-buffer + "!\nThis line is not." + ("d\gg\"_dGP") + "This region is a keepe[r]"))) + +(ert-deftest evil-test-pasteable-macros () + "Test if we can yank and paste macros containing + " + :tags '(evil) + (ert-info ("Execute yanked macro") + (evil-test-buffer + "[i]foo\e" + ("\"qD@q\"qp" + "fooifoo\e"))) + (ert-info ("Paste recorded marco") + (evil-test-buffer + (evil-set-register ?q (vconcat "ifoo" [escape])) + ("@q\"qp") + "fooifoo\e"))) + +(ert-deftest evil-test-forward-symbol () + :tags '(evil) + (ert-info ("Test symbol deletion") + (evil-test-buffer + "(test [t]his (hello there) with dao)" + ("dao") + "(test [(]hello there) with dao)")) + (ert-info ("Test symbol motion") + (evil-test-buffer + "(test[ ](hello there) with dao)" + (should (eq 0 (forward-evil-symbol 1))) + "(test ([h]ello there) with dao)" + (should (eq 0 (forward-evil-symbol 1))) + "(test (hello[ ]there) with dao)")) + (ert-info ("Test dio on whitespace") + (evil-test-buffer + "(test[ ]dio with whitespace)" + ("dio") + "(test[d]io with whitespace)")) + (ert-info ("Test dao/dio with empty lines") + (evil-test-buffer + "there are two lines in this file\n[\n]and some whitespace between them" + ("dao") + "there are two lines in this file\n[a]nd some whitespace between them") + (evil-test-buffer + "here are another two lines\n[\n]with a blank line between them" + ("dio") + "here are another two lines\n[w]ith a blank line between them")) + (ert-info ("Test dao/dio with empty lines and punctuation") + (evil-test-buffer + "These two lines \n[\n]!have punctuation on them" + ("dao") + "These two lines \n[!]have punctuation on them"))) + +(ert-deftest evil-test-jump () + :tags '(evil jumps) + (let ((evil--jumps-buffer-targets "\\*\\(new\\|scratch\\|test\\)\\*")) + (ert-info ("Test jumping backward and forward in a single buffer") + (evil-test-buffer + "[z] z z z z z z z z z" + ("/z" [return]) + "z [z] z z z z z z z z" + ("nnnn") + "z z z z z [z] z z z z" + ("\C-o") + "z z z z [z] z z z z z" + ("\C-o") + "z z z [z] z z z z z z" + ("\C-i\C-i") + "z z z z z [z] z z z z")) + (ert-info ("Test jumping backward and forward with counts") + (evil-test-buffer + "[z] z z z z z z z z z" + ("/z" [return] "nnnn") + "z z z z z [z] z z z z" + ("3\C-o") + "z z [z] z z z z z z z" + ("2\C-i") + "z z z z [z] z z z z z" + )) + (ert-info ("Jump list branches off when new jump is set") + (evil-test-buffer + "[z] z z z z z z z" + ("/z" [return] "nnnn4\C-o") ;; adds a bunch of jumps after the 2nd z + "z [z] z z z z z z" + ("/z" [return]) ;; sets a new jump, list should be reset + "z z [z] z z z z z" + ("\C-o") + "z [z] z z z z z z" + ("3\C-i") ;; even after jumping forward 3 times it can't get past the 3rd z + "z z [z] z z z z z")) + (ert-info ("Jump across files") + (let ((temp-file (make-temp-file "evil-test-"))) + (unwind-protect + (evil-test-buffer + "[z] z z z z z z" + ("\M-x" "find-file" [return] temp-file [return] "inew buffer" [escape]) + "new buffe[r]" + ("\C-o") + "[z] z z z z z z" + ("\C-i") + "new buffe[r]") + (delete-file temp-file) + (let ((buf (file-name-nondirectory temp-file))) + (when (get-buffer buf) + (with-current-buffer buf (set-buffer-modified-p nil)) + (kill-buffer buf)))))) + (ert-info ("Jump multiple times between files") + (let ((a (make-temp-file "evil-aa-" nil nil "evil-bb\n\nthis is a")) + (b (make-temp-file "evil-bb-" nil nil "evil-cc\n\nthis is b")) + (c (make-temp-file "evil-cc-" nil nil "this is c"))) + (unwind-protect + (evil-test-buffer + (find-file a) + ("gf" [return]) + "evil-cc\n\nthis is b" + ("gf" [return]) + "this is c" + ("\C-o" "\C-o") + "evil-bb\n\nthis is a" + ("\C-i" "\C-i") + "this is c") + (dolist (f (list a b c)) + (let ((buf (file-name-nondirectory f))) + (when (get-buffer buf) + (with-current-buffer buf (set-buffer-modified-p nil)) + (kill-buffer buf))) + (delete-file f))))))) + +(ert-deftest evil-test-find-file () + :tags '(evil jumps) + (when (memq system-type '(cygwin windows-nt ms-dos)) + (ert-skip "[INFO] GitHub Actions has different userprofile name.")) + (ert-info ("Find file at point (normal state)") + (evil-with-temp-file file-name "" + (evil-test-buffer + (vconcat "i" file-name [escape]) + (should-not (equal file-name (buffer-file-name))) + ("gf") + (should (equal file-name (buffer-file-name)))))) + (ert-info ("Find file at point (visual state)") + (evil-with-temp-file file-name "" + (evil-test-buffer + (vconcat "iuser@localhost:" file-name "$" [escape]) + (should-not (equal file-name (buffer-file-name))) + ("0f:lvt$gf") + (should (equal file-name (buffer-file-name)))))) + (ert-info ("Find file at point with line number") + (let* ((line-number 3) + (file-content (make-string (* 2 line-number) ?\n))) + (evil-with-temp-file file-name (insert file-content) + (evil-test-buffer + (vconcat "i" file-name (format ":%d" line-number) [escape]) + (should (and (not (equal file-name (buffer-file-name))) + (not (equal line-number (line-number-at-pos))))) + ("gF") + (should (and (equal file-name (buffer-file-name)) + (equal line-number (line-number-at-pos)))))))) + (ert-info ("Find file at point with line and column numbers") + (let* ((line-number 3) + (column-number 5) + (file-content (mapconcat #'identity + (make-list (* 2 line-number) + (make-string (* 2 column-number) ?\s)) + "\n"))) + (evil-with-temp-file file-name (insert file-content) + (evil-test-buffer + (vconcat "i" file-name (format ":%d:%d" line-number column-number) [escape]) + (should (and (not (equal file-name (buffer-file-name))) + (not (equal line-number (line-number-at-pos))) + (not (equal column-number (current-column))))) + ("gF") + (should (and (equal file-name (buffer-file-name)) + (equal line-number (line-number-at-pos)) + (equal column-number (1+ (current-column)))))))))) + +(ert-deftest evil-test-jump-buffers () + :tags '(evil jumps) + (skip-unless nil) + (ert-info ("Test jumping backward and forward across buffers") + (evil-test-buffer + "[z] z z z z z z z z z" + (":new" [return] "inew buffer" [escape]) + "new buffe[r]" + ("\C-o") + "[z] z z z z z z z z z" + ("\C-i") + "new buffe[r]"))) + +(ert-deftest evil-test-abbrev-expand () + :tags '(evil abbrev) + (ert-info ("Test abbrev expansion on insert state exit") + (define-abbrev-table 'global-abbrev-table + '(("undef" "undefined"))) ; add global abbrev + (evil-test-buffer + "foo unde[f] bar" + (abbrev-mode) + ("a" [escape]) + "foo undefine[d] bar") ; 'undef' should be expanded + (evil-test-buffer + "foo unde[f] bar" + ("a" [escape]) + "foo unde[f] bar") ; 'undef' shouldn't be expanded, + ; abbrev-mode is not enabled + (evil-test-buffer + "fo[o] undef bar" + (abbrev-mode) + ("a" [escape]) + "fo[o] undef bar") ; 'foo' shouldn't be expanded, + ; it's not an abbrev + (kill-all-abbrevs) ; remove all abbrevs + (evil-test-buffer + "foo unde[f] bar" + (abbrev-mode) + ("a" [escape]) + "foo unde[f] bar") ; 'undef' shouldn't be expanded, + ; it's not an abbrev + (setq abbrevs-changed nil))) + +(ert-deftest evil-test-text-object-macro () + :tags '(evil abbrev) + (ert-info ("Test pipe character and other delimiters as object delimiters") + ;; This is the macro that broke after pull #747. + (defmacro evil-test-define-and-bind-text-object (name key start-regex end-regex) + (let ((inner-name (make-symbol (concat "evil-inner-" name))) + (outer-name (make-symbol (concat "evil-a-" name)))) + `(progn + (evil-define-text-object ,inner-name (count &optional beg end _type) + (evil-select-paren ,start-regex ,end-regex beg end type count nil)) + (evil-define-text-object ,outer-name (count &optional beg end _type) + (evil-select-paren ,start-regex ,end-regex beg end type count t)) + (define-key evil-inner-text-objects-map ,key #',inner-name) + (define-key evil-outer-text-objects-map ,key #',outer-name)))) + (evil-test-define-and-bind-text-object "pipe" "|" "|" "|") + (evil-test-define-and-bind-text-object "rackety" "#" "#|" "|#") + + (evil-test-buffer + "#|this i[s] a test #|with rackety|# multiline + and nestable comments|#" + ("vi#") + "#||#") + (evil-test-buffer + "| foo | aoe[u] | bar |" + ("vi|") + "| foo |< aoeu >| bar |" + ("a|") + "| foo <| aoeu |> bar |" + ("a|") + "<| foo | aoeu | bar |>") + (evil-test-buffer + "| foo | aoe[u] | bar |" + ("ci|testing" [escape]) + "| foo |testing| bar |"))) + +(ert-deftest evil-test-kbd-macro () + "Test recording and replaying of macros." + :tags '(evil kbd-macro) + (ert-info ("Execute macro an infinite number of times") + (evil-test-buffer "xxx" + (evil-set-register ?q "ryl") + (error 'end-of-line "\C-u@q") + "yyy"))) + +(ert-deftest evil-test-undo-kbd-macro () + "Test if evil can undo the changes made by a keyboard macro +when an error stops the execution of the macro" + :tags '(evil undo kbd-macro) + (ert-info ("When kbd-macro goes to the end of buffer") + (evil-test-buffer + "[l]ine 1\nline 2\nline 3\nline 4" + (evil-set-register ?q "jdd") + ("jdd") + (should-error (execute-kbd-macro "2@q")) + ("uu") + "line 1\n[l]ine 2\nline 3\nline 4")) + (ert-info ("When kbd-macro goes to the end of line") + (evil-test-buffer + "[f]ofof" + (evil-set-register ?q "lx") + ("lx") + (should-error (execute-kbd-macro "2@q")) + ("uu") + "f[o]fof")) + (ert-info ("When kbd-macro goes to the beginning of buffer") + (evil-test-buffer + "line 1\nline 2\n[l]ine 3" + (evil-set-register ?q "kx") + ("kx") + (should-error (execute-kbd-macro "2@q")) + ("uu") + "line 1\n[l]ine 2\nline 3"))) + +(ert-deftest evil-test-undo-jump () + "Test that undo adds to the jump list." + :tags '(evil) + (let ((evil--jumps-buffer-targets "\\*\\(new\\|scratch\\|test\\)\\*")) + (ert-info ("Undo adds to the jump list") + (evil-test-buffer + "alpha [b]ravo charlie delta" + ("dw" "w") + "alpha charlie [d]elta" + ("u") + "alpha [b]ravo charlie delta" + ("``") + "alpha bravo charlie [d]elta")))) + +(ert-deftest evil-test-insert-state-undo () + "Test that undo isn't lost when done from insert state." + :tags '(evil) + (skip-unless (version<= "28" emacs-version)) + (let (evil-want-fine-undo) + (customize-set-variable 'evil-undo-system 'undo-redo) + (evil-test-buffer + "alpha [ ]delta" + (evil-define-key* 'insert 'local [f8] 'evil-undo) + ("icharlie" [f8] [escape]) + "alpha[ ] delta" + ("\C-r") + "alpha [c]harlie delta"))) + +(ert-deftest evil-test-visual-update-x-selection () + "Test `evil-visual-update-x-selection'." + :tags '(evil) + (ert-info ("Buffer argument isn't a live buffer") + ;; create buffer in normal mode, so we don't try to actually copy anything to + ;; the X selection. + (let ((buf (evil-test-buffer-from-string "foobar"))) + (kill-buffer buf) + ;; should not raise an "Selecting deleted buffer" error + (evil-visual-update-x-selection buf)))) + +(ert-deftest evil-test-append-to-kbd-macro () + "Test if evil can append to a keyboard macro." + :tags '(evil append to kbd-macro) + (ert-info ("When kbd-macro ends in ") + (evil-test-buffer + (evil-set-register ?a (vconcat "ainserted text" [escape])) + (evil-set-register ?A (vconcat "a appended text" [escape])) + ("@a") + "inserted text appended tex[t]"))) + +(ert-deftest evil-test-paren-jumping () + :tags '(evil paren) + (ert-info ("Test doing motions on parens that you aren't in") + (evil-test-buffer + "[m]ain(argc, argv) char **argv; {" + ("dib" [escape]) + "main([)] char **argv; {") + (evil-test-buffer + "[a]lpha (bravo) charlie " + ("yi(") + "alpha ([b]ravo) charlie " + ("$p") + "alpha (bravo) charlie brav[o]") + (evil-test-buffer + "[a]lpha (bravo (charlie))" + ("2di(") + "alpha (bravo ([)]") + (evil-test-buffer + :point-start "(" + :point-end ")" + "(a)lpha [bravo [charlie]] [bravo]" + ("3di[") + "alpha [bravo [charlie]] [(])") + (evil-test-buffer +"[#]include \"stdlib.h\" +main(argc, argv) char **argv; { + while (1) malloc(0); +}" + ("ci{ return 0;" [escape]) +"#include \"stdlib.h\" +main(argc, argv) char **argv; { + return 0[;] +}"))) + +;;; Core + +(ert-deftest evil-test-initial-state () + "Test `evil-initial-state'" + :tags '(evil core) + ;; FIXME: These have a global effect, so better move them out and give them + ;; a proper namespace prefix. + (define-derived-mode test-1-mode prog-mode "Test1") + (define-derived-mode test-2-mode test-1-mode "Test2") + (evil-set-initial-state 'test-1-mode 'insert) + (ert-info ("Check default state") + (should (eq (evil-initial-state 'prog-mode 'normal) 'normal))) + (ert-info ("Basic functionality 1") + (should (eq (evil-initial-state 'test-1-mode) 'insert))) + (ert-info ("Basic functionality 2") + (evil-test-buffer + "abc\ndef\n" + (test-1-mode) + (should (eq evil-state 'insert)))) + (ert-info ("Inherit initial state from a parent") + (evil-test-buffer + "abc\ndef\n" + (test-2-mode) + (should (eq evil-state 'insert)))) + (evil-set-initial-state 'test-1-mode nil) + (ert-info ("Check for inheritance loops") + (evil-test-buffer + "abc\ndef\n" + (unwind-protect + (let ((major-mode 'test-2-mode)) + (put 'test-1-mode 'derived-mode-parent 'test-2-mode) + ;; avoid triggering all of the hooks here, some of which might get + ;; caught in loops depending on the environment. settings major-mode + ;; is sufficient for `evil-initial-state-for-buffer' to work. + (should-error (evil-initial-state-for-buffer))) + (put 'test-1-mode 'derived-mode-parent 'prog-mode)))) + ;; FIXME: Same as above. + (defalias 'test-1-alias-mode #'test-1-mode) + (define-derived-mode test-3-mode test-1-alias-mode "Test3") + (evil-set-initial-state 'test-1-mode 'insert) + (ert-info ("Check inheritance from major mode aliases") + "abc\ndef\n" + (test-3-mode) + (should (eq evil-state 'insert)))) + +(ert-deftest evil-test-keep-input-method () + "Test that the input method is preserved when changing the major mode." + :tags '(evil core input-method) + (cl-flet ((use-german-input-method () (set-input-method "german-prefix"))) + (unwind-protect + (progn + (add-hook 'text-mode-hook #'use-german-input-method) + (evil-test-buffer + "[]" + (should (null evil-input-method)) + ("a\"a" [escape]) + "\"[a]" + (text-mode) + (should (equal evil-input-method "german-prefix")) + ("a\"a" [escape]) + "\"a[ä]" + (emacs-lisp-mode) + (should (equal evil-input-method "german-prefix")))) + (remove-hook 'text-mode-hook #'use-german-input-method)))) + +(ert-deftest evil-retab () + "Test the :retab command" + :tags '(evil) + (ert-info ("From tabs to spaces") + (evil-test-buffer + "def foo():\n\twhile True:\n\t\treturn\n" + (setq indent-tabs-mode nil) + (setq tab-width 4) + (":retab" [return]) + "def foo():\n while True:\n return\n")) + (ert-info ("From spaces to tabs") + (evil-test-buffer + "def foo():\n while True:\n return\n" + (setq indent-tabs-mode t) + (setq tab-width 4) + (":retab" [return]) + "def foo():\n\twhile True:\n\t\treturn\n")) + (ert-info ("Specify tab width as an argument") + (evil-test-buffer + "def foo():\n\twhile True:\n\t\treturn\n" + (setq indent-tabs-mode nil) + (setq tab-width 4) + (":retab 2" [return]) + "def foo():\n while True:\n return\n")) + (ert-info ("Invalid argument") + (evil-test-buffer + "def foo():\n\twhile True:\n\t\treturn\n" + (should-error (execute-kbd-macro ":retab foo" [return]))))) + +(ert-deftest evil-retab-visual () + "Test the :retab command on visual line selection. +This test fails in the batch mode (and therefore in the GitHub CI) +but it works fine in `M-x ert-run-tests-interactively'." + :tags '(evil visual) + (skip-unless (not noninteractive)) + (ert-info ("Retab only selected lines") + (evil-test-buffer + "def foo():\n\twhile True:\n\t\treturn\n" + (setq indent-tabs-mode nil) + (setq tab-width 4) + ("Vj" ":retab" [return]) + "def foo():\n while True:\n\t\treturn\n"))) + +(provide 'evil-tests) + +;;; evil-tests.el ends here diff --git a/.config/emacs/lisp/evil/evil-types.el b/.config/emacs/lisp/evil/evil-types.el new file mode 100644 index 0000000..360316a --- /dev/null +++ b/.config/emacs/lisp/evil/evil-types.el @@ -0,0 +1,465 @@ +;;; evil-types.el --- Type system -*- lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; A type defines a transformation on a pair of buffer positions. +;; Types are used by Visual state (character/line/block selection) +;; and Operator-Pending state (character/line/block motions). +;; +;; The basic transformation is "expansion". For example, the `line' +;; type "expands" a pair of positions to whole lines by moving the +;; first position to the beginning of the line and the last position +;; to the end of the line. That expanded selection is what the rest +;; of Emacs sees and acts on. +;; +;; An optional transformation is "contraction", which is the opposite +;; of expansion. If the transformation is one-to-one, expansion +;; followed by contraction always returns the original range. +;; (The `line' type is not one-to-one, as it may expand multiple +;; positions to the same lines.) +;; +;; Another optional transformation is "normalization", which takes +;; two unexpanded positions and adjusts them before expansion. +;; This is useful for cleaning up "invalid" positions. +;; +;; Types are defined at the end of this file using the macro +;; `evil-define-type'. + +(require 'evil-common) +(require 'evil-macros) + +;;; Code: + +;;; Type definitions + +(evil-define-type exclusive + "Return the positions unchanged, with some exceptions. +If the end position is at the beginning of a line, then: + +* If the beginning position is at or before the first non-blank + character on the line, return `line' (expanded). + +* Otherwise, move the end position to the end of the previous + line and return `inclusive' (expanded)." + :normalize (lambda (beg end) + (cond + ((progn + (goto-char end) + (and (/= beg end) (bolp))) + (setq end (max beg (1- end))) + (cond + ((progn + (goto-char beg) + (looking-back "^[ \f\t\v]*" (line-beginning-position))) + (evil-expand beg end 'line)) + (t + (unless evil-cross-lines + (setq end (max beg (1- end)))) + (evil-expand beg end 'inclusive)))) + (t + (evil-range beg end)))) + :string (lambda (beg end) + (let ((width (- end beg))) + (format "%s character%s" width + (if (= width 1) "" "s"))))) + +(evil-define-type inclusive + "Include the character under point. +Handling for `evil-want-visual-char-semi-exclusive' is deprecated, +and will be removed in a future version." + :expand (lambda (beg end) + (if (and (with-no-warnings evil-want-visual-char-semi-exclusive) + (evil-visual-state-p) + (< beg end) + (save-excursion + (goto-char end) + (or (bolp) (eolp)))) + (evil-range beg end 'exclusive) + (evil-range beg (1+ end)))) + :contract (lambda (beg end) + (evil-range beg (max beg (1- end)))) + :normalize (lambda (beg end) + (evil-range beg (if (eq (char-after end) ?\n) + (max beg (1- end)) end))) + :string (lambda (beg end) + (let ((width (- end beg))) + (format "%s character%s" width + (if (= width 1) "" "s"))))) + +(evil-define-type line + "Include whole lines." + :one-to-one nil + :expand (lambda (beg end) + (evil-range + (progn + (goto-char beg) + ;; move to beginning of line as displayed + (evil-move-beginning-of-line) + (point)) + (progn + (goto-char end) + ;; move to the end of line as displayed + (evil-move-end-of-line) + (line-beginning-position 2)))) + :contract (lambda (beg end) + (evil-range beg (max beg (1- end)))) + :string (lambda (beg end) + (let ((height (count-lines beg end))) + (format "%s line%s" height + (if (= height 1) "" "s"))))) + +(evil-define-type screen-line + "Include whole lines, being aware of `visual-line-mode' +when `evil-respect-visual-line-mode' is non-nil." + :one-to-one nil + :expand (lambda (beg end) + (if (not (and evil-respect-visual-line-mode visual-line-mode)) + (evil-line-expand beg end) + (evil-range + (progn + (goto-char beg) + (save-excursion + (beginning-of-visual-line))) + (progn + (goto-char end) + (save-excursion + ;; `beginning-of-visual-line' reverts to the beginning of the + ;; last visual line if the end of the last line is the end of + ;; the buffer. This would prevent selecting the last screen + ;; line. + (if (= (line-beginning-position 2) (point-max)) + (point-max) + (beginning-of-visual-line 2))))))) + :contract (lambda (beg end) + (evil-range beg (max beg (1- end)))) + :string (lambda (beg end) + (let ((height (count-screen-lines beg end))) + (format "%s screen line%s" height + (if (= height 1) "" "s"))))) + +(evil-define-type block + "Like `inclusive', but for rectangles: +the last column is included." + :expand (lambda (beg end &rest properties) + (let ((beg-col (evil-column beg)) + (end-col (evil-column end)) + (corner (plist-get properties :corner))) + ;; Since blocks are implemented as a pair of buffer + ;; positions, expansion is restricted to what the buffer + ;; allows. In the case of a one-column block, there are + ;; two ways to expand it (either move the upper corner + ;; beyond the lower corner, or the lower beyond the + ;; upper), so try out both possibilities when + ;; encountering the end of the line. + (cond + ((= beg-col end-col) + (goto-char end) + (cond + ((eolp) + (goto-char beg) + (if (eolp) + (evil-range beg end) + (evil-range (1+ beg) end))) + ((memq corner '(lower-right upper-right right)) + (evil-range (1+ beg) end)) + (t + (evil-range beg (1+ end))))) + ((< beg-col end-col) + (goto-char end) + (if (eolp) + (evil-range beg end) + (evil-range beg (1+ end)))) + (t + (goto-char beg) + (if (eolp) + (evil-range beg end) + (evil-range (1+ beg) end)))))) + :contract (lambda (beg end) + (let ((beg-col (evil-column beg)) + (end-col (evil-column end))) + (if (> beg-col end-col) + (evil-range (1- beg) end) + (evil-range beg (max beg (1- end)))))) + :string (lambda (beg end) + (let ((height (count-lines + beg + (progn + (goto-char end) + (if (and (bolp) (not (eobp))) + (1+ end) + end)))) + (width (abs (- (evil-column beg) + (evil-column end))))) + (format "%s row%s and %s column%s" + height + (if (= height 1) "" "s") + width + (if (= width 1) "" "s")))) + :rotate (lambda (beg end &rest properties) + "Rotate block according to :corner property. +:corner can be one of `upper-left',``upper-right', `lower-left' +and `lower-right'." + (let ((left (evil-column beg)) + (right (evil-column end)) + (corner (or (plist-get properties :corner) + 'upper-left))) + (evil-sort left right) + (goto-char beg) + (if (memq corner '(upper-right lower-left)) + (move-to-column right) + (move-to-column left)) + (setq beg (point)) + (goto-char end) + (if (memq corner '(upper-right lower-left)) + (move-to-column left) + (move-to-column right)) + (setq end (point)) + (setq properties (plist-put properties + :corner corner)) + (apply #'evil-range beg end properties)))) + +(evil-define-type rectangle + "Like `exclusive', but for rectangles: +the last column is excluded." + :expand (lambda (beg end) + ;; select at least one column + (if (= (evil-column beg) (evil-column end)) + (evil-expand beg end 'block) + (evil-range beg end 'block)))) + +;;; Standard interactive codes + +(evil-define-interactive-code "*" + "Signal error if the buffer is read-only." + (when buffer-read-only + (signal 'buffer-read-only nil))) + +(evil-define-interactive-code "b" (prompt) + "Name of existing buffer." + (list (read-buffer prompt (current-buffer) t))) + +(evil-define-interactive-code "c" + "Read character." + (list (read-char))) + +(evil-define-interactive-code "p" + "Prefix argument converted to number." + (list (prefix-numeric-value current-prefix-arg))) + +(evil-define-interactive-code "P" + "Prefix argument in raw form." + (list current-prefix-arg)) + +;;; Custom interactive codes + +(evil-define-interactive-code "" + "Prefix argument converted to number, possibly multiplied by evil--window-digit." + (let ((prefix-num (prefix-numeric-value current-prefix-arg))) + (if evil--window-digit + (list (* evil--window-digit prefix-num)) + (list prefix-num)))) + +(evil-define-interactive-code "" + "Count." + (list (when current-prefix-arg + (prefix-numeric-value current-prefix-arg)))) + +(evil-define-interactive-code "" + "Count, but only in visual state. +This should be used by an operator taking a count. In normal +state the count should not be handled by the operator but by the +motion that defines the operator's range. In visual state the +range is specified by the visual region and the count is not used +at all. Thus in the case the operator may use the count +directly." + (list (when (and (evil-visual-state-p) current-prefix-arg) + (prefix-numeric-value + current-prefix-arg)))) + +(evil-define-interactive-code "" + "Prefix argument converted to number, or nil possibly multiplied by +evil--window-digit." + (let ((prefix-num (prefix-numeric-value current-prefix-arg))) + (list + (cond (evil--window-digit (* evil--window-digit prefix-num)) + (current-prefix-arg prefix-num))))) + +(evil-define-interactive-code "" + "Character read through `evil-read-key'." + (list + (if (evil-operator-state-p) + (evil-without-restriction (evil-read-key)) + (evil-read-key)))) + +(evil-define-interactive-code "" + "Untyped motion range (BEG END)." + (evil-operator-range)) + +(evil-define-interactive-code "" + "Typed motion range (BEG END TYPE)." + (evil-operator-range t)) + +(evil-define-interactive-code "" + "Typed motion range of visual range (BEG END TYPE). +If visual state is inactive then those values are nil." + (if (evil-visual-state-p) + (let ((range (evil-visual-range))) + (list (car range) + (cadr range) + (evil-type range))) + (list nil nil nil))) + +(evil-define-interactive-code "" + "Current register." + (list evil-this-register)) + +(evil-define-interactive-code "" + "Current yank-handler." + (list (evil-yank-handler))) + +(evil-define-interactive-code "" + "Ex argument." + :ex-arg t + (list (when evil-called-from-ex-p evil-ex-argument))) + +(evil-define-interactive-code "" () + "Prefix argument or ex-arg, converted to number" + (list (cond + (current-prefix-arg (prefix-numeric-value current-prefix-arg)) + (evil-ex-argument (string-to-number evil-ex-argument)) + (evil-called-from-ex-p nil) + (t 1)))) + +(evil-define-interactive-code "" + "Ex file argument." + :ex-arg file + (list (when evil-called-from-ex-p (evil-ex-file-arg)))) + +(evil-define-interactive-code "" + "Ex buffer argument." + :ex-arg buffer + (list evil-ex-argument)) + +(evil-define-interactive-code "" + "Ex shell command argument." + :ex-arg shell + (list evil-ex-argument)) + +(evil-define-interactive-code "" + "Ex file or shell command argument." + :ex-arg file-or-shell + (list evil-ex-argument)) + +(evil-define-interactive-code "" + "Ex symbolic argument." + :ex-arg sym + (list (and evil-ex-argument (intern evil-ex-argument)))) + +(evil-define-interactive-code "" + "Ex line number." + (list + (when evil-called-from-ex-p + (let ((expr (evil-ex-parse (or evil-ex-argument "")))) + (if (eq (car expr) 'evil-goto-line) + (save-excursion (goto-char evil-ex-point) + (eval (cadr expr) t)) + (user-error "Invalid address")))))) + +(evil-define-interactive-code "" + "Ex bang argument." + :ex-bang t + (list evil-ex-bang)) + +(evil-define-interactive-code "" + "Ex delimited argument." + (when evil-called-from-ex-p + (evil-delimited-arguments (or evil-ex-argument "")))) + +(evil-define-interactive-code "" + "Ex global argument." + (when evil-called-from-ex-p + (evil-ex-parse-global (or evil-ex-argument "")))) + +(evil-define-interactive-code "" + "Ex substitution argument." + :ex-arg substitution + (when evil-called-from-ex-p + (evil-ex-get-substitute-info (or evil-ex-argument "") t))) + +(evil-define-interactive-code "" + "Ex register and count argument, both optional. +Can be used for commands such as :delete [REGISTER] [COUNT] where the +command can be called with either zero, one or two arguments. With one +argument, if it is numeric, it is treated as a COUNT, otherwise, as a +REGISTER." + (when evil-called-from-ex-p + (evil-ex-get-optional-register-and-count evil-ex-argument))) + +(defun evil-ex-get-optional-register-and-count (string) + "Parse STRING as an ex arg with both optional REGISTER and COUNT. +Return a list (REGISTER COUNT)." + (let* ((split-args (split-string (or string ""))) + (arg-count (length split-args)) + (arg0 (car split-args)) + (arg1 (cadr split-args)) + (number-regex "^-?[1-9][0-9]*$") + (register nil) + (count nil)) + (cond + ;; :command REGISTER or :command COUNT + ((= arg-count 1) + (if (string-match-p number-regex arg0) + (setq count arg0) + (setq register arg0))) + ;; :command REGISTER COUNT + ((eq arg-count 2) + (setq register arg0 + count arg1)) + ;; more than 2 args aren't allowed + ((> arg-count 2) + (user-error "Invalid use"))) + + ;; if register is given, check it's valid + (when register + (unless (= (length register) 1) + (user-error "Invalid register")) + (setq register (string-to-char register))) + + ;; if count is given, check it's valid + (when count + (unless (string-match-p number-regex count) + (user-error "Invalid count")) + (setq count (string-to-number count)) + (unless (> count 0) + (user-error "Invalid count"))) + + (list register count))) + +(provide 'evil-types) + +;;; evil-types.el ends here diff --git a/.config/emacs/lisp/evil/evil-vars.el b/.config/emacs/lisp/evil/evil-vars.el new file mode 100644 index 0000000..291a91a --- /dev/null +++ b/.config/emacs/lisp/evil/evil-vars.el @@ -0,0 +1,2121 @@ +;;; evil-vars.el --- Settings and variables -*- lexical-binding: t -*- + +;; Author: Vegard Øye +;; Maintainer: Vegard Øye + +;; Version: 1.15.0 + +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Code: + +(declare-function evil-add-command-properties "evil-common" + (command &rest properties)) +(declare-function evil-update-insert-state-bindings "evil-maps" + (&optional _option-name remove force)) + +;;; Hooks + +(defvar evil-after-load-hook nil + "Functions to be run when loading of Evil is finished. +This hook can be used to execute some initialization routines +when Evil is completely loaded.") + +(defvar evil-after-global-hook nil + "Functions to be run after :global and :vglobal.") + +(defcustom evil-goto-definition-functions + '(evil-goto-definition-imenu + evil-goto-definition-semantic + evil-goto-definition-xref + evil-goto-definition-search) + "List of functions run until success by `evil-goto-definition'." + :type 'hook + :group 'evil) + +;;; Initialization + +(defvar evil-pending-custom-initialize nil + "A list of pending initializations for custom variables. +Each element is a triple (FUNC VAR VALUE). When Evil is +completely loaded then the functions (funcall FUNC VAR VALUE) is +called for each element. FUNC should be a function suitable for +the :initialize property of `defcustom'.") + +(defun evil-custom-initialize-pending-reset (var value) + "Add a pending customization with `custom-initialize-reset'." + (push (list 'custom-initialize-reset var value) + evil-pending-custom-initialize)) + +(defun evil-run-pending-custom-initialize () + "Execute the pending initializations. +See `evil-pending-custom-initialize'." + (dolist (init evil-pending-custom-initialize) + (apply (car init) (cdr init))) + (remove-hook 'evil-after-load-hook #'evil-run-pending-custom-initialize)) +(add-hook 'evil-after-load-hook #'evil-run-pending-custom-initialize) + +;;; Setters + +(defun evil-set-toggle-key (key) + "Set `evil-toggle-key' to KEY. +KEY must be readable by `read-kbd-macro'." + (let ((old-key (read-kbd-macro + (if (boundp 'evil-toggle-key) + evil-toggle-key + "C-z"))) + (key (read-kbd-macro key))) + (with-no-warnings + (dolist (pair '((evil-motion-state-map evil-emacs-state) + (evil-insert-state-map evil-emacs-state) + (evil-emacs-state-map evil-exit-emacs-state))) + (when (boundp (car pair)) + (let ((map (symbol-value (car pair))) + (fun (cadr pair))) + (when (keymapp map) + (define-key map key fun) + (define-key map old-key nil)))))))) + +(defun evil-set-custom-state-maps (var pending-var key _make newlist) + "Change the list of special keymaps. +VAR is the variable containing the list of keymaps. +PENDING-VAR is the variable containing the list of the currently pending + keymaps. +KEY the special symbol to be stored in the keymaps. +MAKE the creation function of the special keymaps. +NEWLIST the list of new special keymaps." + (set-default pending-var newlist) + (when (default-boundp var) + (dolist (map (default-value var)) + (when (and (boundp (car map)) + (keymapp (default-value (car map)))) + (define-key (default-value (car map)) (vector key) nil)))) + (set-default var newlist) + (evil-update-pending-maps)) + +(defun evil-update-pending-maps (&optional _file) + "Try to set pending special keymaps. +This function should be called from an `after-load-functions' +hook." + (let ((maps '((evil-make-overriding-map . evil-pending-overriding-maps) + (evil-make-intercept-map . evil-pending-intercept-maps)))) + (while maps + (let* ((map (pop maps)) + (make (car map)) + (pending-var (cdr map)) + (pending (symbol-value pending-var)) + newlist) + (while pending + (let* ((map (pop pending)) + (kmap (and (boundp (car map)) + (keymapp (symbol-value (car map))) + (symbol-value (car map)))) + (state (cdr map))) + (if kmap + (funcall make kmap state) + (push map newlist)))) + (set-default pending-var newlist))))) + +(defun evil-set-visual-newline-commands (var value) + "Set the value of `evil-visual-newline-commands'. +Setting this variable changes the properties of the appropriate +commands." + (with-no-warnings + (when (default-boundp var) + (dolist (cmd (default-value var)) + (evil-set-command-property cmd :exclude-newline nil))) + (set-default var value) + (dolist (cmd (default-value var)) + (evil-set-command-property cmd :exclude-newline t)))) + +(defun evil-set-custom-motions (var values) + "Set the list of motion commands." + (with-no-warnings + (when (default-boundp var) + (dolist (motion (default-value var)) + (evil-add-command-properties motion :keep-visual nil :repeat nil))) + (set-default var values) + (mapc #'evil-declare-motion (default-value var)))) + +;;; Customization group + +(defgroup evil nil + "Extensible vi layer." + :group 'emulations + :prefix 'evil-) + +(defcustom evil-auto-indent t + "\\ +Whether to auto-indent when opening lines with \\[evil-open-below] \ +and \\[evil-open-above]." + :type 'boolean + :group 'evil) + +(defcustom evil-shift-width 4 + "\\ +The number of columns by which a line is shifted. +This applies to the shifting operators \\[evil-shift-right] and \ +\\[evil-shift-left]." + :type 'integer + :safe #'integerp + :group 'evil) + +(defcustom evil-shift-round t + "\\ +Whether shifting rounds to the nearest multiple. +If non-nil, \\[evil-shift-right] and \\[evil-shift-left] adjust line +indentation to the nearest multiple of `evil-shift-width'." + :type 'boolean + :group 'evil) + +(defcustom evil-indent-convert-tabs t + "\\ +If non-nil, the \\[evil-indent] operator converts between leading tabs and spaces. +Whether tabs are converted to spaces or vice versa depends on the +value of `indent-tabs-mode'." + :type 'boolean + :group 'evil) + +(defcustom evil-default-cursor t + "The default cursor. +May be a cursor type as per `cursor-type', a color string as passed +to `set-cursor-color', a zero-argument function for changing the +cursor, or a list of the above. + +nil is interpreted as t; if you want the nil `cursor-type' (i.e., +you want to disable the cursor), set this variable to (list nil)." + :type '(set symbol (cons symbol symbol) string function) + :group 'evil) + +(defvar evil-force-cursor nil + "Overwrite the current states default cursor.") + +(defcustom evil-start-of-line nil + "Analogue of vim's `startofline'. +If nil, preserve column when making relevant movements of the cursor. +Otherwise, move the cursor to the start of the line." + :type 'boolean + :group 'evil) + +(defcustom evil-repeat-move-cursor t + "\\ +Whether repeating commands with \\[evil-repeat] may move the cursor. +If nil, the original cursor position is preserved, even if the command +normally would have moved the cursor." + :type 'boolean + :group 'evil) + +(defcustom evil-cross-lines nil + "\\ +Whether horizontal motions may move to other lines. If non-nil, +certain motions that conventionally operate in a single line may move +the cursor to other lines. Otherwise, they are restricted to the +current line. This applies to \\[evil-backward-char], \ +\\[evil-forward-char], \\[evil-find-char], \ +\\[evil-find-char-backward], \\[evil-find-char-to], \ +\\[evil-find-char-to-backward], \ +\\\\[evil-invert-char]." + :type 'boolean + :group 'evil) + +(defcustom evil-backspace-join-lines t + "Whether backward delete in insert state may join lines." + :type 'boolean + :group 'evil) + +(defcustom evil-move-cursor-back t + "Whether the cursor is moved backwards when exiting insert state. +If non-nil, the cursor moves \"backwards\" when exiting insert state, +so that it ends up on the character to the left. Otherwise it remains +in place, on the character to the right. + +See also `evil-move-beyond-eol'." + :type 'boolean + :group 'evil) + +(defcustom evil-move-beyond-eol nil + "Whether the cursor can move past the end of the line. +If non-nil, the cursor is allowed to move one character past the +end of the line, as in Emacs." + :type 'boolean + :group 'evil) + +(defcustom evil-respect-visual-line-mode nil + "\\ +Whether movement commands respect `visual-line-mode'. +If non-nil, `visual-line-mode' is generally respected when it is +on. In this case, motions such as \\[evil-next-line] and +\\[evil-previous-line] navigate by visual lines (on the screen) rather +than \"physical\" lines (defined by newline characters). If nil, +the setting of `visual-line-mode' is ignored. + +This variable must be set before Evil is loaded." + :type 'boolean + :group 'evil) + +(defcustom evil-repeat-find-to-skip-next t + "Whether a repeat of t or T should skip an adjacent character." + :type 'boolean + :group 'evil) + +(defcustom evil-kbd-macro-suppress-motion-error nil + "\\ +Whether left/right motions signal errors in keyboard macros. +This variable only affects beginning-of-line or end-of-line errors +regarding the motions \\[evil-backward-char] and \\[evil-forward-char] +respectively. This may be desired since such errors cause macro +definition or execution to be terminated. There are four +possibilities: + +- `record': errors are suppressed when recording macros, but not when + replaying them. +- `replay': errors are suppressed when replaying macros, but not when + recording them. +- `t': errors are suppressed in both cases. +- `nil': errors are never suppressed." + :type '(radio (const :tag "No" :value nil) + (const :tag "Record" :value record) + (const :tag "Replay" :value replay) + (const :tag "Both" :value t)) + :group 'evil) + +(defcustom evil-track-eol t + "\\ +Whether \\[evil-end-of-line] \"sticks\" the cursor to the end of the line. +If non-nil, vertical motions after \\[evil-end-of-line] maintain the cursor at the +end of the line, even if the target line is longer. This is analogous +to `track-eol', but respects Evil's interpretation of end-of-line." + :type 'boolean + :group 'evil) + +(defcustom evil-mode-line-format 'before + "The position of the state tag in the mode line. +If set to `before' or `after', the tag is placed at the beginning +or the end of the mode-line, respectively. If nil, there is no +tag. Otherwise it should be a cons cell (WHERE . WHICH), where +WHERE is either `before' or `after', and WHICH is a symbol in +`mode-line-format'. The tag is then placed before or after that +symbol, respectively." + :type '(radio :value 'before + (const :tag "No tag" nil) + (const before) + (const after) + (cons :tag "Next to symbol" + (choice :value after + (const before) + (const after)) + symbol)) + :group 'evil) + +(defcustom evil-mouse-word 'evil-word + "The thing-at-point symbol for double click selection. +The double-click starts visual state in a special word selection +mode. This symbol is used to determine the words to be +selected. Possible values are `evil-word' or `evil-WORD'." + :type 'symbol + :group 'evil) + +(defcustom evil-bigword "^ \t\r\n" + "The set of characters to be interpreted as WORD boundaries. +This is enclosed with square brackets and used as a regular +expression. By default, whitespace characters are considered +WORD boundaries." + :type 'string + :group 'evil) + +(defcustom evil-want-fine-undo nil + "Whether actions are undone in several steps. +There are two possible choices: nil (\"no\") means that all +changes made during insert state, including a possible delete +after a change operation, are collected in a single undo step. +Non-nil (\"yes\") means that undo steps are determined according +to Emacs heuristics, and no attempt is made to aggregate changes. + +For backward compatibility purposes, the value `fine' is +interpreted as `nil'. This option was removed because it did not +work consistently." + :type '(radio (const :tag "No" :value nil) + (const :tag "Fine (obsolete)" :value fine) + (const :tag "Yes" :value t)) + :group 'evil) + +(defcustom evil-regexp-search t + "\\ +Whether to use regular expressions for searching in \ +\\[evil-search-forward] and \\[evil-search-backward]." + :type 'boolean + :group 'evil) + +(defcustom evil-search-wrap t + "\\ +Whether search with \\[evil-search-forward] and \ +\\[evil-search-backward] wraps around the buffer. +If this is non-nil, search stops at the buffer boundaries." + :type 'boolean + :group 'evil) + +(defcustom evil-flash-delay 2 + "\\ +Time in seconds to flash search matches after \\[evil-search-next] and \ +\\[evil-search-previous]." + :type 'number + :group 'evil) + +(defcustom evil-auto-balance-windows t + "If non-nil window creation and deletion trigger rebalancing." + :type 'boolean + :group 'evil) + +(defcustom evil-split-window-below nil + "If non-nil split windows are created below." + :type 'boolean + :group 'evil) + +(defcustom evil-vsplit-window-right nil + "If non-nil vertically split windows with are created to the right." + :type 'boolean + :group 'evil) + +(defcustom evil-esc-delay 0.01 + "The time, in seconds, to wait for another key after escape. +If no further event arrives during this time, the event is +translated to `ESC'. Otherwise, it is translated according to +`input-decode-map'. This does not apply in Emacs state, and may +also be inhibited by setting `evil-inhibit-esc'." + :type 'number + :group 'evil) + +(defvar evil-esc-mode nil + "Non-nil if `evil-esc-mode' is enabled.") + +(defvar evil-esc-map nil + "Original ESC prefix map in `input-decode-map'. +Used by `evil-esc-mode'.") + +(defvar evil-inhibit-esc nil + "If non-nil, the \"\\e\" event will never be translated to `escape'.") + +(defcustom evil-intercept-esc 'always + "Whether Evil should intercept the escape key. +In the terminal, escape and a meta key sequence both generate the +same event. In order to distingush these, Evil uses +`input-decode-map'. It is not necessary to do this in a graphical +Emacs session. However, if you prefer to use \"C-[\" as escape (which +is identical to the terminal escape key code), this interception must +also happen in graphical Emacs sessions. Set this variable to +`always', t (only in the terminal) or nil (never intercept)." + :type '(radio (const :tag "Never" :value nil) + (const :tag "In terminal only" :value t) + (const :tag "Always" :value always)) + :group 'evil) + +(defcustom evil-show-paren-range 0 + "The maximum distance between point and a parenthesis +which causes the parenthesis to be highlighted." + :type 'integer + :group 'evil) + +(defcustom evil-ex-hl-update-delay 0.02 + "Time in seconds of idle before updating search highlighting. +Setting this to a period shorter than that of keyboard's repeat +rate allows highlights to update while scrolling." + :type 'number + :group 'evil) + +(defcustom evil-highlight-closing-paren-at-point-states + '(not emacs insert replace) + "The states in which the closing parenthesis at point should be highlighted. +All states listed here highlight the closing parenthesis at +point (which is Vim's default behavior). All others highlight the +parenthesis before point (which is Emacs default behavior). If +this list contains the symbol `not' then its meaning is inverted, +i.e. all states listed here highlight the closing parenthesis +before point." + :type '(repeat symbol) + :group 'evil) + +(defcustom evil-kill-on-visual-paste t + "Whether pasting in visual state adds the replaced text to the +kill ring, making it the default for the next paste. The default +replicates the default Vim behavior for `p'. This value is flipped by +`evil-paste-before' (\\[evil-paste-before])." + :type 'boolean + :group 'evil) + +(defcustom evil-want-C-i-jump t + "Whether `C-i' jumps forward in the jump list (like Vim). +Otherwise, `C-i' inserts a tab character." + :type 'boolean + :group 'evil + :set #'(lambda (sym value) + (set-default sym value) + (when (boundp 'evil-motion-state-map) + (cond + ((and (not value) + (eq (lookup-key evil-motion-state-map (kbd "C-i")) + 'evil-jump-forward)) + (define-key evil-motion-state-map (kbd "C-i") nil)) + ((and value + (not (lookup-key evil-motion-state-map (kbd "C-i")))) + (define-key evil-motion-state-map (kbd "C-i") + 'evil-jump-forward)))))) + +(defcustom evil-want-C-u-scroll nil + "Whether `C-u' scrolls up (like Vim). +Otherwise, `C-u' applies a prefix argument. The binding of +`C-u' mirrors Emacs behaviour by default due to the relative +ubiquity of prefix arguments." + :type 'boolean + :group 'evil + :set #'(lambda (sym value) + (set-default sym value) + (when (boundp 'evil-motion-state-map) + (cond + ((and (not value) + (eq (lookup-key evil-motion-state-map (kbd "C-u")) + 'evil-scroll-up)) + (define-key evil-motion-state-map (kbd "C-u") nil)) + ((and value + (not (lookup-key evil-motion-state-map (kbd "C-u")))) + (define-key evil-motion-state-map (kbd "C-u") + 'evil-scroll-up)))))) + +(defcustom evil-want-C-d-scroll t + "Whether `C-d' scrolls down (like Vim)." + :type 'boolean + :group 'evil + :set #'(lambda (sym value) + (set-default sym value) + (when (boundp 'evil-motion-state-map) + (cond + ((and (not value) + (eq (lookup-key evil-motion-state-map (kbd "C-d")) + 'evil-scroll-down)) + (define-key evil-motion-state-map (kbd "C-d") nil)) + ((and value + (not (lookup-key evil-motion-state-map (kbd "C-d")))) + (define-key evil-motion-state-map (kbd "C-d") + 'evil-scroll-down)))))) + +(defcustom evil-want-C-u-delete nil + "Whether `C-u' deletes back to indentation in insert state. +Otherwise, `C-u' applies a prefix argument. The binding of +`C-u' mirrors Emacs behaviour by default due to the relative +ubiquity of prefix arguments." + :type 'boolean + :group 'evil + :set #'(lambda (sym value) + (set-default sym value) + (when (and (boundp 'evil-insert-state-map) + (boundp 'evil-replace-state-map)) + (cond + ((and (not value) + (eq (lookup-key evil-insert-state-map (kbd "C-u")) + 'evil-delete-back-to-indentation)) + (define-key evil-insert-state-map (kbd "C-u") nil) + (define-key evil-replace-state-map (kbd "C-u") nil)) + ((and value + (not (lookup-key evil-insert-state-map (kbd "C-u")))) + (define-key evil-insert-state-map (kbd "C-u") + 'evil-delete-back-to-indentation) + (define-key evil-replace-state-map (kbd "C-u") + 'evil-delete-back-to-indentation)))))) + +(defcustom evil-want-C-w-delete t + "Whether `C-w' deletes a word in Insert/Ex/Search state." + :type 'boolean + :group 'evil + :set (lambda (sym value) + (set-default sym value) + (when (and (boundp 'evil-insert-state-map) + (boundp 'evil-replace-state-map)) + (cond + ((and (not value) + (eq (lookup-key evil-insert-state-map (kbd "C-w")) + 'evil-delete-backward-word)) + (define-key evil-insert-state-map (kbd "C-w") 'evil-window-map) + (define-key evil-replace-state-map (kbd "C-w") 'evil-window-map)) + ((and value + (eq (lookup-key evil-insert-state-map (kbd "C-w")) + 'evil-window-map)) + (define-key evil-insert-state-map (kbd "C-w") 'evil-delete-backward-word) + (define-key evil-replace-state-map (kbd "C-w") 'evil-delete-backward-word)))) + (when (boundp 'evil-command-line-map) + (cond + ((and (not value) + (eq (lookup-key evil-command-line-map (kbd "C-w")) + 'evil-delete-backward-word)) + (define-key evil-command-line-map (kbd "C-w") nil)) + ((and value + (null (lookup-key evil-command-line-map (kbd "C-w")))) + (define-key evil-command-line-map (kbd "C-w") 'evil-delete-backward-word)))) + (when (boundp 'evil-ex-search-keymap) + (cond + ((and (not value) + (null (lookup-key evil-ex-search-keymap (kbd "C-w")))) + (define-key evil-ex-search-keymap (kbd "C-w") 'evil-search-yank-word)) + ((and value + (eq (lookup-key evil-ex-search-keymap (kbd "C-w")) + 'evil-search-yank-word)) + (define-key evil-ex-search-keymap (kbd "C-w") nil)))))) + +(defcustom evil-want-C-h-delete nil + "Whether `C-h' deletes a char in Insert state." + :type 'boolean + :group 'evil + :set #'(lambda (sym value) + (set-default sym value) + (when (and (boundp 'evil-insert-state-map) + (boundp 'evil-replace-state-map)) + (cond + ((and (not value) + (eq (lookup-key evil-insert-state-map (kbd "C-h")) + 'evil-delete-backward-char-and-join)) + (define-key evil-insert-state-map (kbd "C-h") nil) + (define-key evil-replace-state-map (kbd "C-h") nil)) + ((and value + (not (lookup-key evil-insert-state-map (kbd "C-h")))) + (define-key evil-insert-state-map (kbd "C-h") + 'evil-delete-backward-char-and-join) + (define-key evil-replace-state-map (kbd "C-h") + 'evil-replace-backspace)))))) + +(defcustom evil-want-C-g-bindings nil + "Whether `C-g' postfix can be used in bindings." + :type 'boolean + :group 'evil) + +(defcustom evil-want-C-w-in-emacs-state nil + "Whether `C-w' prefixes windows commands in Emacs state." + :type 'boolean + :group 'evil + :set #'(lambda (sym value) + (set-default sym value) + (when (boundp 'evil-emacs-state-map) + (cond + ((and (not value) + (eq (lookup-key evil-emacs-state-map (kbd "C-w")) + 'evil-window-map)) + (define-key evil-emacs-state-map (kbd "C-w") nil)) + ((and value + (not (lookup-key evil-emacs-state-map (kbd "C-w")))) + (define-key evil-emacs-state-map (kbd "C-w") 'evil-window-map)))))) + +(defcustom evil-want-change-word-to-end t + "Whether `cw' behaves like `ce'." + :type 'boolean + :group 'evil) + +(defcustom evil-want-Y-yank-to-eol nil + "Whether `Y' yanks to the end of the line. +The default behavior is to yank the whole line, like Vim." + :group 'evil + :type 'boolean + :initialize #'evil-custom-initialize-pending-reset + :set #'(lambda (sym value) + (set-default sym value) + (evil-add-command-properties + 'evil-yank-line + :motion (if value + 'evil-end-of-line-or-visual-line + 'evil-line-or-visual-line)))) + +(defcustom evil-disable-insert-state-bindings nil + "Whether insert state bindings should be used. +Bindings for escape, delete and `evil-toggle-key' are always +available. If this is non-nil, default Emacs bindings are by and +large accessible in insert state." + :group 'evil + :type 'boolean + :initialize #'evil-custom-initialize-pending-reset + :set #'(lambda (sym value) + (set-default sym value) + (evil-update-insert-state-bindings sym value))) + +(defcustom evil-echo-state t + "Whether to signal the current state in the echo area." + :type 'boolean + :group 'evil) + +(defcustom evil-complete-all-buffers t + "\\ +Whether completion looks for matches in all buffers. +This applies to \\[evil-complete-next] and \\[evil-complete-previous] \ +in insert state." + :type 'boolean + :group 'evil) + +(defcustom evil-search-wrap-ring-bell nil + "Whether to ring the bell when search wraps around the buffer." + :type 'boolean + :group 'evil) + +(defvar dabbrev-search-these-buffers-only) +(defvar dabbrev-case-distinction) +(defcustom evil-complete-next-func + #'(lambda (arg) + (require 'dabbrev) + (let ((dabbrev-search-these-buffers-only + (unless evil-complete-all-buffers + (list (current-buffer)))) + dabbrev-case-distinction) + (condition-case nil + (if (eq last-command this-command) + (dabbrev-expand nil) + (dabbrev-expand (- (abs (or arg 1))))) + (error (dabbrev-expand nil))))) + "Completion function used by \ +\\\\[evil-complete-next]." + :type 'function + :group 'evil) + +(defcustom evil-complete-previous-func + #'(lambda (arg) + (require 'dabbrev) + (let ((dabbrev-search-these-buffers-only + (unless evil-complete-all-buffers + (list (current-buffer)))) + dabbrev-case-distinction) + (dabbrev-expand arg))) + "Completion function used by \ +\\\\[evil-complete-previous]." + :type 'function + :group 'evil) + +(defcustom evil-complete-next-minibuffer-func 'minibuffer-complete + "Minibuffer completion function used by \ +\\\\[evil-complete-next]." + :type 'function + :group 'evil) + +(defcustom evil-complete-previous-minibuffer-func 'minibuffer-complete + "Minibuffer completion function used by \ +\\\\[evil-complete-previous]." + :type 'function + :group 'evil) + +(defcustom evil-complete-next-line-func + #'(lambda (arg) + (let ((hippie-expand-try-functions-list + '(try-expand-line + try-expand-line-all-buffers))) + (hippie-expand arg))) + "Minibuffer completion function used by \ +\\\\[evil-complete-next-line]." + :type 'function + :group 'evil) + +(defcustom evil-complete-previous-line-func + evil-complete-next-line-func + "Minibuffer completion function used by \ +\\\\[evil-complete-previous-line]." + :type 'function + :group 'evil) + +(defcustom evil-lookup-func #'woman + "Lookup function used by \ +\"\\\\[evil-lookup]\"." + :type 'function + :group 'evil) + +(defcustom evil-toggle-key "C-z" + "The key used to change to and from Emacs state. +Must be readable by `read-kbd-macro'. For example: \"C-z\"." + :type 'string + :group 'evil + :set #'(lambda (sym value) + (evil-set-toggle-key value) + (set-default sym value))) + +(defcustom evil-default-state 'normal + "The default Evil state. +This is the state a buffer starts in when it is not otherwise +configured (see `evil-set-initial-state' and +`evil-buffer-regexps'). The value may be one of `normal', +`insert', `visual', `replace', `operator', `motion' and `emacs'." + :type 'symbol + :group 'evil) + +(defcustom evil-buffer-regexps + '(("^ \\*load\\*" . nil)) + "Regular expressions determining the initial state for a buffer. +Entries have the form (REGEXP . STATE), where REGEXP is a regular +expression matching the buffer's name and STATE is one of `normal', +`insert', `visual', `replace', `operator', `motion', `emacs' and +`nil'. If STATE is `nil', Evil is disabled in the buffer." + :type '(alist :key-type string :value-type symbol) + :group 'evil) + +(defcustom evil-emacs-state-modes + '(5x5-mode + archive-mode + bbdb-mode + biblio-selection-mode + blackbox-mode + bookmark-bmenu-mode + bookmark-edit-annotation-mode + browse-kill-ring-mode + bs-mode + bubbles-mode + bzr-annotate-mode + calc-mode + cfw:calendar-mode + completion-list-mode + Custom-mode + custom-theme-choose-mode + debugger-mode + delicious-search-mode + desktop-menu-blist-mode + desktop-menu-mode + doc-view-mode + dun-mode + dvc-bookmarks-mode + dvc-diff-mode + dvc-info-buffer-mode + dvc-log-buffer-mode + dvc-revlist-mode + dvc-revlog-mode + dvc-status-mode + dvc-tips-mode + ediff-mode + ediff-meta-mode + efs-mode + Electric-buffer-menu-mode + emms-browser-mode + emms-mark-mode + emms-metaplaylist-mode + emms-playlist-mode + ess-help-mode + etags-select-mode + fj-mode + gc-issues-mode + gdb-breakpoints-mode + gdb-disassembly-mode + gdb-frames-mode + gdb-locals-mode + gdb-memory-mode + gdb-registers-mode + gdb-threads-mode + gist-list-mode + git-rebase-mode + gnus-article-mode + gnus-browse-mode + gnus-group-mode + gnus-server-mode + gnus-summary-mode + gomoku-mode + google-maps-static-mode + ibuffer-mode + jde-javadoc-checker-report-mode + magit-cherry-mode + magit-diff-mode + magit-log-mode + magit-log-select-mode + magit-popup-mode + magit-popup-sequence-mode + magit-process-mode + magit-reflog-mode + magit-refs-mode + magit-revision-mode + magit-stash-mode + magit-stashes-mode + magit-status-mode + mh-folder-mode + monky-mode + mpuz-mode + mu4e-main-mode + mu4e-headers-mode + mu4e-view-mode + notmuch-hello-mode + notmuch-search-mode + notmuch-show-mode + notmuch-tree-mode + occur-mode + org-agenda-mode + package-menu-mode + pdf-outline-buffer-mode + pdf-view-mode + proced-mode + rcirc-mode + rebase-mode + recentf-dialog-mode + reftex-select-bib-mode + reftex-select-label-mode + reftex-toc-mode + sldb-mode + slime-inspector-mode + slime-thread-control-mode + slime-xref-mode + snake-mode + solitaire-mode + sr-buttons-mode + sr-mode + sr-tree-mode + sr-virtual-mode + tar-mode + tetris-mode + tla-annotate-mode + tla-archive-list-mode + tla-bconfig-mode + tla-bookmarks-mode + tla-branch-list-mode + tla-browse-mode + tla-category-list-mode + tla-changelog-mode + tla-follow-symlinks-mode + tla-inventory-file-mode + tla-inventory-mode + tla-lint-mode + tla-logs-mode + tla-revision-list-mode + tla-revlog-mode + tla-tree-lint-mode + tla-version-list-mode + twittering-mode + urlview-mode + vc-annotate-mode + vc-dir-mode + vc-git-log-view-mode + vc-hg-log-view-mode + vc-svn-log-view-mode + vm-mode + vm-summary-mode + w3m-mode + wab-compilation-mode + xgit-annotate-mode + xgit-changelog-mode + xgit-diff-mode + xgit-revlog-mode + xhg-annotate-mode + xhg-log-mode + xhg-mode + xhg-mq-mode + xhg-mq-sub-mode + xhg-status-extra-mode) + "Modes that should come up in Emacs state." + :type '(repeat symbol) + :group 'evil) + +(defcustom evil-insert-state-modes + '(comint-mode + erc-mode + eshell-mode + geiser-repl-mode + gud-mode + inferior-apl-mode + inferior-caml-mode + inferior-emacs-lisp-mode + inferior-j-mode + inferior-python-mode + inferior-scheme-mode + inferior-sml-mode + internal-ange-ftp-mode + haskell-interactive-mode + prolog-inferior-mode + racket-repl-mode + reb-mode + shell-mode + slime-repl-mode + term-mode + utop-mode + wdired-mode) + "Modes that should come up in Insert state." + :type '(repeat symbol) + :group 'evil) + +(defcustom evil-motion-state-modes + '(apropos-mode + Buffer-menu-mode + calendar-mode + color-theme-mode + command-history-mode + compilation-mode + dictionary-mode + ert-results-mode + help-mode + Info-mode + Man-mode + speedbar-mode + undo-tree-visualizer-mode + woman-mode) + "Modes that should come up in Motion state." + :type '(repeat symbol) + :group 'evil) + +(defvar evil-pending-overriding-maps nil + "An alist of pending overriding maps.") + +(defvar evil-pending-intercept-maps nil + "An alist of pending intercept maps.") + +(defcustom evil-overriding-maps '() + "Keymaps that should override Evil maps. +Entries have the form (MAP-VAR . STATE), where MAP-VAR is +a keymap variable and STATE is the state whose bindings +should be overridden. If STATE is nil, all states are +overridden." + :type '(alist :key-type symbol :value-type symbol) + :group 'evil + :set #'(lambda (var values) + (set-default var values) + (evil-set-custom-state-maps 'evil-overriding-maps + 'evil-pending-overriding-maps + 'override-state + 'evil-make-overriding-map + values)) + :initialize #'evil-custom-initialize-pending-reset) + +(add-hook 'after-load-functions #'evil-update-pending-maps) + +(defcustom evil-intercept-maps + '((edebug-mode-map . nil)) + "Keymaps that should intercept Evil maps. +Entries have the form (MAP-VAR . STATE), where MAP-VAR is +a keymap variable and STATE is the state whose bindings +should be intercepted. If STATE is nil, all states are +intercepted." + :type '(alist :key-type symbol :value-type symbol) + :group 'evil + :set #'(lambda (var values) + (set-default var values) + (evil-set-custom-state-maps 'evil-intercept-maps + 'evil-pending-intercept-maps + 'intercept-state + 'evil-make-intercept-map + values)) + :initialize #'evil-custom-initialize-pending-reset) + +(defcustom evil-motions + '(back-to-indentation + backward-char + backward-list + backward-paragraph + backward-sentence + backward-sexp + backward-up-list + backward-word + beginning-of-buffer + beginning-of-defun + beginning-of-line + beginning-of-visual-line + c-beginning-of-defun + c-end-of-defun + diff-file-next + diff-file-prev + diff-hunk-next + diff-hunk-prev + down-list + end-of-buffer + end-of-defun + end-of-line + end-of-visual-line + exchange-point-and-mark + forward-char + forward-list + forward-paragraph + forward-sentence + forward-sexp + forward-word + goto-char + goto-last-change + ibuffer-backward-line + ibuffer-forward-line + isearch-abort + isearch-cancel + isearch-complete + isearch-del-char + isearch-delete-char + isearch-edit-string + isearch-exit + isearch-highlight-regexp + isearch-occur + isearch-other-control-char + isearch-other-meta-char + isearch-printing-char + isearch-query-replace + isearch-query-replace-regexp + isearch-quote-char + isearch-repeat-backward + isearch-repeat-forward + isearch-ring-advance + isearch-ring-retreat + isearch-toggle-case-fold + isearch-toggle-input-method + isearch-toggle-regexp + isearch-toggle-specified-input-method + isearch-toggle-word + isearch-yank-char + isearch-yank-kill + isearch-yank-line + isearch-yank-word-or-char + keyboard-quit + left-char + left-word + mouse-drag-region + mouse-save-then-kill + mouse-set-point + mouse-set-region + mwheel-scroll + move-beginning-of-line + move-end-of-line + next-error + next-line + paredit-backward + paredit-backward-down + paredit-backward-up + paredit-forward + paredit-forward-down + paredit-forward-up + pop-global-mark + pop-tag-mark + pop-to-mark-command + previous-error + previous-line + right-char + right-word + scroll-down + scroll-down-command + scroll-up + scroll-up-command + sgml-skip-tag-backward + sgml-skip-tag-forward + up-list) + "Non-Evil commands to initialize to motions." + :type '(repeat symbol) + :group 'evil + :set #'evil-set-custom-motions + :initialize #'evil-custom-initialize-pending-reset) + +(defcustom evil-visual-newline-commands + '(LaTeX-section + TeX-font) + "Commands excluding the trailing newline of a Visual Line selection. +These commands work better without this newline." + :type '(repeat symbol) + :group 'evil + :set #'evil-set-visual-newline-commands + :initialize #'evil-custom-initialize-pending-reset) + +(defcustom evil-want-visual-char-semi-exclusive nil + "DEPRECATED. Will be removed in a future version. +Prefer to set `evil-v$-excludes-newline' to non-nil. + +Visual character selection to beginning/end of line is exclusive. +If non nil then an inclusive visual character selection which +ends at the beginning or end of a line is turned into an +exclusive selection. Thus if the selected (inclusive) range ends +at the beginning of a line it is changed to not include the first +character of that line, and if the selected range ends at the end +of a line it is changed to not include the newline character of +that line." + :type 'boolean + :group 'evil) +(make-obsolete-variable + 'evil-want-visual-char-semi-exclusive + "Semi-exclusivity prevents selecting text + 1st char of next line, +without having to introduce new niche functionality. +Prefer to set `evil-v$-excludes-newline' to non-nil." + "1.15.0") + +(defcustom evil-v$-excludes-newline nil + "If non-nil, `evil-end-of-line' does not move as far as to include +the `\n' char at eol. This makes `v$' consistent with `$' used as a +motion (e.g. `v$y' is consistent with `y$' in normal state)." + :type 'boolean + :group 'evil) + +(defcustom evil-text-object-change-visual-type t + "Text objects change the current visual state type. +If non-nil then a text-object changes the type of the visual state to +its default selection type (e.g. a word object always changes to +charwise visual state). Otherwise the current visual state type is +preserved." + :type 'boolean + :group 'evil) + +(defgroup evil-cjk nil + "CJK support" + :prefix "evil-cjk-" + :group 'evil) + +(defcustom evil-cjk-emacs-word-boundary nil + "Determine word boundary exactly the same way as Emacs does." + :type 'boolean + :group 'evil-cjk) + +(defcustom evil-cjk-word-separating-categories + '(;; Kanji + (?C . ?H) (?C . ?K) (?C . ?k) (?C . ?A) (?C . ?G) + ;; Hiragana + (?H . ?C) (?H . ?K) (?H . ?k) (?H . ?A) (?H . ?G) + ;; Katakana + (?K . ?C) (?K . ?H) (?K . ?k) (?K . ?A) (?K . ?G) + ;; half-width Katakana + (?k . ?C) (?k . ?H) (?k . ?K) ; (?k . ?A) (?k . ?G) + ;; full-width alphanumeric + (?A . ?C) (?A . ?H) (?A . ?K) ; (?A . ?k) (?A . ?G) + ;; full-width Greek + (?G . ?C) (?G . ?H) (?G . ?K) ; (?G . ?k) (?G . ?A) + ) + "List of pair (cons) of categories to determine word boundary +used in `evil-cjk-word-boundary-p'. See the documentation of +`word-separating-categories'. Use `describe-categories' to see +the list of categories." + :type '(alist :key-type (choice character (const nil)) + :value-type (choice character (const nil))) + :group 'evil-cjk) + +(defcustom evil-cjk-word-combining-categories + '(;; default value in word-combining-categories + (nil . ?^) (?^ . nil) + ;; Roman + (?r . ?k) (?r . ?A) (?r . ?G) + ;; half-width Katakana + (?k . ?r) (?k . ?A) (?k . ?G) + ;; full-width alphanumeric + (?A . ?r) (?A . ?k) (?A . ?G) + ;; full-width Greek + (?G . ?r) (?G . ?k) (?G . ?A) + ) + "List of pair (cons) of categories to determine word boundary +used in `evil-cjk-word-boundary-p'. See the documentation of +`word-combining-categories'. Use `describe-categories' to see the +list of categories." + :type '(alist :key-type (choice character (const nil)) + :value-type (choice character (const nil))) + :group 'evil-cjk) + +(defcustom evil-ex-complete-emacs-commands 'in-turn + "TAB-completion for Emacs commands in ex command line. +This variable determines when Emacs commands are considered for +completion, always, never, or only if no Evil ex command is +available for completion." + :group 'evil + :type '(radio (const :tag "Only if no ex-command." :value in-turn) + (const :tag "Never" :value nil) + (const :tag "Always" :value t))) + +(defface evil-ex-commands '(( nil + :underline t + :slant italic)) + "Face for the Evil command in completion in ex mode." + :group 'evil) + +(defface evil-ex-info '(( ((supports :slant)) + :slant italic + :foreground "red")) + "Face for the info message in ex mode." + :group 'evil) + +(defcustom evil-ex-visual-char-range nil + "Type of default ex range in visual char state. +If non-nil the default range when starting an ex command from +character visual state is `<,`> otherwise it is '<,'>. In the +first case the ex command will be passed a region covering only +the visual selection. In the second case the passed region will +be extended to contain full lines." + :group 'evil + :type 'boolean) + +(defvar evil--ex-global-active-p nil + "If the :global command is running. +Used to change the behaviour of certain commands like :print.") + +(defvar evil--ex-print-accumulator "" + "Used by :print etc. to accumulate a string when invoked by :global etc.") + +;; Searching +(defcustom evil-symbol-word-search nil + "If nil then * and # search for words otherwise for symbols." + :group 'evil + :type 'boolean) + +(defcustom evil-magic t + "Meaning which characters in a pattern are magic. +The meaning of those values is the same as in Vim. Note that it +only has influence if the Evil search module is chosen in +`evil-search-module'." + :group 'evil + :type '(radio (const :tag "Very magic." :value very-magic) + (const :tag "Magic" :value t) + (const :tag "Nomagic" :value nil) + (const :tag "Very nomagic" :value very-nomagic))) + +(defcustom evil-ex-search-vim-style-regexp nil + "If non-nil Vim-style backslash codes are supported in search patterns. +See `evil-transform-vim-style-regexp' for the supported backslash +codes. Note that this only affects the search command if +`evil-search-module' is set to `evil-search'. The isearch module +always uses plain Emacs regular expressions." + :type 'boolean + :group 'evil) + +(defcustom evil-ex-interactive-search-highlight 'all-windows + "Determine in which windows the interactive highlighting should be shown." + :type '(radio (const :tag "All windows." all-windows) + (const :tag "Selected window." selected-window) + (const :tag "Disable highlighting." nil)) + :group 'evil) + +(defcustom evil-ex-search-persistent-highlight t + "If non-nil matches remain highlighted when the search ends." + :type 'boolean + :group 'evil) + +(defcustom evil-ex-search-case 'smart + "The case behaviour of the search command. +Smart case means that the pattern is case sensitive if and only +if it contains an upper case letter, otherwise it is case +insensitive." + :type '(radio (const :tag "Case sensitive." sensitive) + (const :tag "Case insensitive." insensitive) + (const :tag "Smart case." smart)) + :group 'evil) + +(defcustom evil-ex-substitute-case nil + "The case behaviour of the search command. +Smart case means that the pattern is case sensitive if and only +if it contains an upper case letter, otherwise it is case +insensitive. If nil then the setting of `evil-ex-search-case' is +used." + :type '(radio (const :tag "Same as interactive search." nil) + (const :tag "Case sensitive." sensitive) + (const :tag "Case insensitive." insensitive) + (const :tag "Smart case." smart)) + :group 'evil) + +(defcustom evil-ex-search-interactive t + "If t search is interactive." + :type 'boolean + :group 'evil) + +(defcustom evil-ex-search-incremental t + "If t, use incremental search. Note that this only affects the +search command if `evil-search-module' is set to `evil-search'." + :type 'boolean + :group 'evil) + +(defcustom evil-ex-search-highlight-all t + "If t and interactive search is enabled, all matches are +highlighted." + :type 'boolean + :group 'evil) + +(defcustom evil-ex-substitute-highlight-all t + "If t all matches for the substitute pattern are highlighted." + :type 'boolean + :group 'evil) + +(defcustom evil-ex-substitute-interactive-replace t + "If t and substitute patterns are highlighted, +the replacement is shown interactively." + :type 'boolean + :group 'evil) + +(defcustom evil-ex-substitute-global nil + "If non-nil substitute patterns are global by default. +Standardly (if this variable is nil) a substitution works only on the +first match of the pattern in a line unless the \"g\" flag is +given, in which case the substitution happens on all matches in the +line. If this option is non-nil, this behavior is reversed." + :type 'boolean + :group 'evil) + +(defface evil-ex-search '((t :inherit isearch)) + "Face for interactive search." + :group 'evil) + +(defface evil-ex-lazy-highlight '((t :inherit lazy-highlight)) + "Face for highlighting all matches in interactive search." + :group 'evil) + +(defface evil-ex-substitute-matches '((t :inherit lazy-highlight)) + "Face for interactive substitute matches." + :group 'evil) + +(defface evil-ex-substitute-replacement '((((supports :underline)) + :underline t + :foreground "red")) + "Face for interactive replacement text." + :group 'evil) + +(defcustom evil-command-window-height 7 + "Height (in lines) of the command line window. +Set to 0 to use the default height for `split-window'." + :type 'integer + :group 'evil) + +(defcustom evil-display-shell-error-in-message nil + "Show error output of a shell command in the error buffer. +If this variable is non-nil the error output of a shell command +goes to the messages buffer instead of being mixed with the +regular output. This happens only if the exit status of the +command is non-zero." + :type 'boolean + :group 'evil) + +(defcustom evil-want-abbrev-expand-on-insert-exit t + "If non-nil abbrevs will be expanded when leaving insert state +like in Vim, if `abbrev-mode' is on." + :type 'boolean + :group 'evil) + +;;; Variables + +(defmacro evil-define-local-var (symbol &optional initvalue docstring) + "Define SYMBOL as permanent buffer local variable, and return SYMBOL. +The parameters are the same as for `defvar', but the variable +SYMBOL is made permanent buffer local." + (declare (indent defun) + (doc-string 3) + (debug (symbolp &optional form stringp))) + `(progn + (defvar ,symbol ,initvalue ,docstring) + (make-variable-buffer-local ',symbol) + (put ',symbol 'permanent-local t))) + +(evil-define-local-var evil-scroll-count 0 + "Hold last used prefix for `evil-scroll-up' +and `evil-scroll-down'. +Determine how many lines should be scrolled. +Default value is 0 - scroll half the screen.") + +(evil-define-local-var evil-state nil + "The current Evil state. +To change the state, use `evil-change-state' +or call the state function (e.g., `evil-normal-state').") + +;; these may be used inside `evil-define-state' +(evil-define-local-var evil-next-state nil + "The Evil state being switched to.") + +(evil-define-local-var evil-previous-state-alist nil + "For Each evil state the Evil state being switched from.") + +(evil-define-local-var evil-previous-state nil + "The Evil state being switched from.") + +(defvar evil-execute-in-emacs-state-buffer nil + "The buffer of the latest `evil-execute-in-emacs-state'. +When this command is being executed the current buffer is stored +in this variable. This is necessary in case the Emacs-command to +be called changes the current buffer.") + +(evil-define-local-var evil-mode-line-tag nil + "Mode-Line indicator for the current state.") +(put 'evil-mode-line-tag 'risky-local-variable t) + +(defvar evil-global-keymaps-alist nil + "Association list of keymap variables. +Entries have the form (MODE . KEYMAP), where KEYMAP +is the variable containing the keymap for MODE.") + +(defvar evil-local-keymaps-alist nil + "Association list of keymap variables that must be +reinitialized in each buffer. Entries have the form +\(MODE . KEYMAP), where KEYMAP is the variable containing +the keymap for MODE.") + +(defvar evil-minor-mode-keymaps-alist nil + "Association list of Evil states to minor-mode keymap alists. +Entries have the form (STATE . MODE-MAP-ALIST), where +MODE-MAP-ALIST is an alist taking the form of +`minor-mode-map-alist'.") + +(defvar evil-state-properties nil + "Specifications made by `evil-define-state'. +Entries have the form (STATE . PLIST), where PLIST is a property +list specifying various aspects of the state. To access a property, +use `evil-state-property'.") + +(evil-define-local-var evil-mode-map-alist nil + "Association list of keymaps to use for Evil modes. +Elements have the form (MODE . KEYMAP), with the first keymaps +having higher priority.") + +(defvar evil-change-commands '(evil-change) + "Commands that wrap or replace `evil-change'. +This list exists to apply an inconsistency with vim's change command +to commands that wrap or redefine it. See emacs-evil/evil#916.") + +(defvar evil-transient-vars '(cua-mode transient-mark-mode select-active-regions) + "List of variables pertaining to Transient Mark mode.") + +(defvar evil-transient-vals nil + "Association list of old values for Transient Mark mode variables. +Entries have the form (VARIABLE VALUE LOCAL), where LOCAL is +whether the variable was previously buffer-local.") + +(defvar evil-no-display nil + "If non-nil, various Evil displays are inhibited. +Use the macro `evil-without-display' to set this variable.") + +(defvar evil-type-properties nil + "Specifications made by `evil-define-type'. +Entries have the form (TYPE . PLIST), where PLIST is a property +list specifying functions for handling the type: expanding it, +describing it, etc.") + +(defvar evil-interactive-alist nil + "Association list of Evil-specific interactive codes.") + +(evil-define-local-var evil-motion-marker nil + "Marker for storing the starting position of a motion.") + +(evil-define-local-var evil-this-type nil + "Current motion type.") + +(evil-define-local-var evil-this-type-modified nil + "Non-nil iff current motion type has been modified by the user. +If the type has been modified, this variable contains the new +type.") + +(evil-define-local-var evil-this-register nil + "Current register.") + +(defvar evil-last-=-register-input nil + "Most recent input from the `=' register. A string.") + +(defvar evil-this-macro nil + "Current macro register.") + +(evil-define-local-var evil-this-operator nil + "Current operator.") + +(evil-define-local-var evil-this-motion nil + "Current motion.") + +(evil-define-local-var evil-this-motion-count nil + "Current motion count.") + +;; In light of `evil-last-recorded-register' this should probably +;; be renamed to `evil-last-executed-register' but that may break configs +(defvar evil-last-register nil + "The last executed register.") + +(defvar evil-last-recorded-register nil + "The last recorded register.") + +(defvar evil-inhibit-operator nil + "Inhibit current operator. +If an operator calls a motion and the motion sets this variable +to t, the operator code is not executed.") + +(defvar evil-inhibit-operator-value nil + "This variable is used to transfer the value +of `evil-inhibit-operator' from one local scope to another.") + +;; used by `evil-define-operator' +(defvar evil-operator-range-beginning nil + "Beginning of `evil-operator-range'.") + +(defvar evil-operator-range-end nil + "End of `evil-operator-range'.") + +(defvar evil-operator-range-type nil + "Type of `evil-operator-range'.") + +(defvar evil-operator-range-motion nil + "Motion of `evil-operator-range'.") + +(defvar evil-operator-start-col nil + "Used to restore column (where possible) after an operator has moved it.") + +(defvar evil-restriction-stack nil + "List of previous restrictions. +Using `evil-with-restriction' stores the previous values of +`point-min' and `point-max' as a pair in this list.") + +(evil-define-local-var evil-markers-alist + '((?\( . evil-backward-sentence-begin) + (?\) . evil-forward-sentence-begin) + (?{ . evil-backward-paragraph) + (?} . evil-forward-paragraph) + (?' . evil-jump-backward-swap) + (?` . evil-jump-backward-swap) + (?< . evil-visual-beginning) + (?> . evil-visual-goto-end) + (?. . (lambda () + (let (last-command) + (goto-last-change nil))))) + "Association list for markers. +Entries have the form (CHAR . DATA), where CHAR is the marker's +name and DATA is either a marker object as returned by `make-marker', +a variable, a movement function, or a cons cell (STRING NUMBER), +where STRING is a file path and NUMBER is a buffer position. +The global value of this variable holds markers available from +every buffer, while the buffer-local value holds markers available +only in the current buffer.") + +(defconst evil-suppress-map (make-keymap) + "Full keymap disabling default bindings to `self-insert-command'.") +(suppress-keymap evil-suppress-map t) + +(defvar evil-read-key-map (make-sparse-keymap) + "Keymap active during `evil-read-key'. +This keymap can be used to bind some commands during the +execution of `evil-read-key' which is usually used to read a +character argument for some commands, e.g. `evil-replace'.") + +;; TODO: customize size of ring +(defvar evil-repeat-ring (make-ring 10) + "A ring of repeat-informations to repeat the last command.") + +(defvar evil-recording-repeat nil + "Whether we are recording a repeat.") + +(defvar evil-recording-current-command nil + "Whether we are recording the current command for repeat.") + +(defvar evil-repeat-changes nil + "Accumulated buffer changes in reverse order for change-tracking commands.") + +(defvar evil-repeat-info nil + "Information accumulated during current repeat.") + +(defvar evil-repeat-buffer nil + "The buffer in which the repeat started. +If the buffer is changed, the repeat is cancelled.") + +(defvar evil-repeat-pos nil + "The point position at the start of a change-tracking command.") + +(defvar evil-repeat-keys nil + "The keys that invoked the current command.") + +(defvar evil-last-repeat nil + "Information about the latest repeat command. +This is a list of three elements (POINT COUNT UNDO-POINTER), +where POINT is the position of point before the latest repeat, +COUNT the count-argument of the latest repeat command and +UNDO-POINTER the head of the undo-list before the last command +has been repeated.") + +(defvar evil-repeat-count nil + "The explicit count when repeating a command.") + +(defvar evil-maybe-remove-spaces nil + "Flag to determine if newly inserted spaces should be removed. +See the function `evil-maybe-remove-spaces'.") + +(evil-define-local-var evil-insert-count nil + "The explicit count passed to a command starting Insert or Replace state.") + +(evil-define-local-var evil-insert-vcount nil + "The information about the number of following lines the +insertion should be repeated. This is list (LINE COLUMN COUNT) +where LINE is the line-number where the original insertion +started and COLUMN is either a number or function determining the +column where the repeated insertions should take place. COUNT is +number of repeats (including the original insertion).") + +(defvar evil-insert-skip-empty-lines nil + "Non-nil of the current insertion should not take place on + lines at which the insertion point is behind the end of the + line.") + +(evil-define-local-var evil-insert-lines nil + "Non-nil if the current insertion command is a line-insertion +command o or O.") + +(evil-define-local-var evil-insert-repeat-info nil + "Repeat information accumulated during an insertion.") + +(evil-define-local-var evil-replace-alist nil + "Association list of characters overwritten in Replace state. +The format is (POS . CHAR).") + +(evil-define-local-var evil-echo-area-message nil + "Previous value of `current-message'.") + +(defvar evil-write-echo-area nil + "If set to t inside `evil-save-echo-area', then the echo area +is not restored.") + +(defvar evil-last-find nil + "A pair (FUNCTION . CHAR) describing the lastest character + search command.") + +(defvar evil-last-paste nil + "Information about the latest paste. +This should be a list (CMD COUNT POINT BEG END FIRSTVISUAL) where +CMD is the last paste-command (`evil-paste-before', +`evil-paste-after' or `evil-visual-paste'), COUNT is the repeat +count of the paste, POINT is the position of point before the +paste, BEG end END are the region of the inserted +text. FIRSTVISUAL is t if and only if the previous command was +the first visual paste (i.e. before any paste-pop).") + +(evil-define-local-var evil-last-undo-entry nil + "Information about the latest undo entry in the buffer. +This should be a pair (OBJ . CONS) where OBJ is the entry as an +object, and CONS is a copy of the entry.") + +(evil-define-local-var evil-current-insertion nil + "Information about the latest insertion in insert state. +This should be a pair (BEG . END) that describes the +buffer-region of the newly inserted text.") + +(defvar evil-last-insertion nil + "The last piece of inserted text.") + +(defvar evil-last-small-deletion nil + "The last piece of deleted text. +The text should be less than a line.") + +(defvar evil-was-yanked-without-register t + "Whether text being saved to the numbered-register ring was +not deleted and not yanked to a specific register.") + +(defvar evil-paste-count nil + "The count argument of the current paste command.") + +(defvar evil--cursor-after nil + "Internal flag for gp & gP.") + +(defvar evil-temporary-undo nil + "When undo is disabled in current buffer. +Certain commands depending on undo use this variable +instead of `buffer-undo-list'.") + +(evil-define-local-var evil-undo-list-pointer nil + "Everything up to this mark is united in the undo-list.") + +(defvar evil-in-single-undo nil + "Set to non-nil if the current undo steps are connected.") + +(defvar evil-flash-timer nil + "Timer for flashing search results.") + +(defvar evil-search-prompt nil + "String to use for search prompt.") + +(defvar evil-search-forward-history nil + "History of forward searches.") + +(defvar evil-search-backward-history nil + "History of backward searches.") + +(defvar evil-inner-text-objects-map (make-sparse-keymap) + "Keymap for inner text objects.") + +(defvar evil-outer-text-objects-map (make-sparse-keymap) + "Keymap for outer text objects.") + +(defvar evil-window-map (make-sparse-keymap) + "Keymap for window-related commands.") + +(evil-define-local-var evil-input-method nil + "Input method used in Insert state and Emacs state.") + +;;; Visual state + +(evil-define-local-var evil-visual-beginning nil + "The beginning of the Visual selection, a marker.") + +(evil-define-local-var evil-visual-end nil + "The end of the Visual selection, a marker.") + +(evil-define-local-var evil-visual-point nil + "The position of point in Visual state, a marker.") + +(evil-define-local-var evil-visual-mark nil + "The position of mark in Visual state, a marker.") + +(evil-define-local-var evil-visual-selection nil + "The kind of Visual selection. +This is a selection as defined by `evil-define-visual-selection'.") + +(evil-define-local-var evil-prev-visual-point nil + "The previous position of point in Visual state, a marker.") + +(evil-define-local-var evil-prev-visual-mark nil + "The previous position of mark in Visual state, a marker.") + +(evil-define-local-var evil-prev-visual-selection nil + "The previous kind of Visual selection. +This is a selection as defined by `evil-define-visual-selection'.") + +;; we could infer the direction by comparing `evil-visual-mark' +;; and `evil-visual-point', but destructive operations may +;; displace the markers +(evil-define-local-var evil-visual-direction 0 + "Whether point follows mark in Visual state. +Negative if point precedes mark, otherwise positive. +See also the function `evil-visual-direction'.") + +(evil-define-local-var evil-visual-properties nil + "Property list of miscellaneous Visual properties.") + +(evil-define-local-var evil-visual-region-expanded nil + "Whether the region matches the Visual selection. +That is, whether the positions of point and mark have been +expanded to coincide with the selection's boundaries. +This makes the selection available to functions acting +on Emacs' region.") + +(defvar evil-transient-mouse-selection nil + "Whether the region selected by the mouse is discarded by the next command. +By default, the region is not discarded. If non-nil, exit visual state +immediately before the next command. To use the region selected by the mouse, +execute a command which would usually enter visual state, such as +\\[evil-visual-char] or \\[evil-visual-block].") + +(defvar evil--region-from-mouse nil + "Whether the region was set by the mouse.") + +(evil-define-local-var evil-visual-overlay nil + "Overlay for highlighting the Visual selection. +Not used for blockwise selections, in which case +see `evil-visual-block-overlays'.") + +(evil-define-local-var evil-visual-block-overlays nil + "Overlays for Visual Block selection, one for each line. +They are reused to minimize flicker.") + +(defvar evil-visual-alist nil + "Association list of Visual selection functions. +Elements have the form (NAME . FUNCTION).") + +(evil-define-local-var evil-visual-x-select-timer nil + "Timer for updating the X selection in visual state.") + +(defvar evil-visual-x-select-timeout 0.1 + "Time in seconds for the update of the X selection.") + +(declare-function origami-open-all-nodes "ext:origami.el") +(declare-function origami-close-all-nodes "ext:origami.el") +(declare-function origami-toggle-node "ext:origami.el") +(declare-function origami-open-node "ext:origami.el") +(declare-function origami-open-node-recursively "ext:origami.el") +(declare-function origami-close-node "ext:origami.el") + +(defvar evil-fold-list + `(((vdiff-mode) + :open-all vdiff-open-all-folds + :close-all vdiff-close-all-folds + :toggle ,(lambda () (call-interactively 'vdiff-toggle-fold)) + :open ,(lambda () (call-interactively 'vdiff-open-fold)) + :open-rec ,(lambda () (call-interactively 'vdiff-open-fold)) + :close ,(lambda () (call-interactively 'vdiff-close-fold))) + ((vdiff-3way-mode) + :open-all vdiff-open-all-folds + :close-all vdiff-close-all-folds + :toggle ,(lambda () (call-interactively 'vdiff-toggle-fold)) + :open ,(lambda () (call-interactively 'vdiff-open-fold)) + :open-rec ,(lambda () (call-interactively 'vdiff-open-fold)) + :close ,(lambda () (call-interactively 'vdiff-close-fold))) + ((hs-minor-mode) + :open-all hs-show-all + :close-all hs-hide-all + :toggle hs-toggle-hiding + :open hs-show-block + :open-rec nil + :close hs-hide-block) + ((hide-ifdef-mode) + :open-all show-ifdefs + :close-all hide-ifdefs + :toggle nil + :open show-ifdef-block + :open-rec nil + :close hide-ifdef-block) + ((outline-mode + outline-minor-mode + org-mode + markdown-mode) + :open-all show-all + :close-all ,(lambda () + (with-no-warnings (hide-sublevels 1))) + :toggle outline-toggle-children + :open ,(lambda () + (with-no-warnings + (show-entry) + (show-children))) + :open-rec show-subtree + :close hide-subtree) + ((origami-mode) + :open-all ,(lambda () (origami-open-all-nodes (current-buffer))) + :close-all ,(lambda () (origami-close-all-nodes (current-buffer))) + :toggle ,(lambda () (origami-toggle-node (current-buffer) (point))) + :open ,(lambda () (origami-open-node (current-buffer) (point))) + :open-rec ,(lambda () (origami-open-node-recursively (current-buffer) (point))) + :close ,(lambda () (origami-close-node (current-buffer) (point))))) + "Actions to be performed for various folding operations. + +The value should be a list of fold handlers, were a fold handler has +the format: + + ((MODES) PROPERTIES) + +MODES acts as a predicate, containing the symbols of all major or +minor modes for which the handler should match. For example: + + \\='((outline-minor-mode org-mode) ...) + +would match for either outline-minor-mode or org-mode, even though the +former is a minor mode and the latter is a major. + +PROPERTIES specifies possible folding actions and the functions to be +applied in the event of a match on one (or more) of the MODES; the +supported properties are: + + - `:open-all' + Open all folds. + - `:close-all' + Close all folds. + - `:toggle' + Toggle the display of the fold at point. + - `:open' + Open the fold at point. + - `:open-rec' + Open the fold at point recursively. + - `:close' + Close the fold at point. + +Each value must be a function. A value of `nil' will cause the action +to be ignored for that respective handler. For example: + + `((org-mode) + :close-all nil + :open ,(lambda () + (show-entry) + (show-children)) + :close hide-subtree) + +would ignore `:close-all' actions and invoke the provided functions on +`:open' or `:close'.") + +;;; Ex + +(define-obsolete-variable-alias 'evil-ex-map 'evil-ex-shortcut-map "1.15.0") +(defvar evil-ex-shortcut-map (make-sparse-keymap) + "Keymap for Ex. +Key sequences bound in this map are immediately executed.") + +;; Intentionally does not inherit from `minibuffer-local-map', as users +;; are encouraged to instead set this as the parent of that keymap. +(defvar evil-command-line-map (make-sparse-keymap) + "Keymap used for the various Evil command-lines. +Modifying this keymap corresponds to using the \":cmap\" Vim command. +See `evil-ex-completion-map' and `evil-ex-search-keymap' which inherit +from this keymap.") + +(defvar evil-ex-completion-map (make-sparse-keymap) + "Keymap for Ex.") + +(defvar evil-ex-initial-input nil + "Additional initial content of the Ex command line. +This content of this variable is appended to the Ex command line +when Ex is started interactively.") + +(defvar evil-ex-history nil + "History of Ex commands.") + +(define-obsolete-variable-alias + 'evil-ex-current-buffer 'evil-ex-original-buffer "1.15.0") +(defvar evil-ex-original-buffer nil + "Buffer that was current when the Evil command line was started.") + +(defvar evil-ex-point nil + "The point position when the Ex command was called.") + +(defvar evil-called-from-ex-p nil + "Non-nil if a command is currently being called as an Ex command.") + +(defvar evil-ex-range nil + "The current range of the Ex command.") + +(defvar evil-ex-bang nil + "The \"!\" argument of the current Ex command.") + +(defvar evil-ex-argument nil + "The current argument of the Ex command.") + +(defvar evil-previous-shell-command nil + "The last shell command.") + +;; Eval +(defvar evil-eval-history nil + "History of eval input, from the `=' register.") + +(defvar evil-eval-map (make-sparse-keymap) + "Keymap for eval input.") + +;; Searching +(defvar evil-ex-search-history nil + "The history for the search command.") + +(defvar evil-ex-search-direction nil + "The direction of the current search, either `forward' or `backward'.") + +(defvar evil-ex-search-count nil + "The count of the current search.") + +(defvar evil-ex-search-start-point nil + "The point where the search started.") + +(defvar evil-ex-search-overlay nil + "The overlay for the current search result.") + +(defvar evil-ex-search-pattern nil + "The last search pattern.") + +(defvar evil-ex-search-offset nil + "The last search offset.") + +(defvar evil-ex-search-match-beg nil + "The beginning position of the last match.") + +(defvar evil-ex-search-match-end nil + "The end position of the last match.") + +(defvar evil-ex-substitute-pattern nil + "The last substitute pattern.") + +(defvar evil-ex-substitute-replacement nil + "The last substitute replacement.") + +(defvar evil-ex-substitute-flags nil + "The last substitute flags.") + +(defvar evil-ex-substitute-current-replacement nil + "The actual replacement.") + +(defvar evil-ex-last-was-search nil + "Non-nil if the previous was a search. +Otherwise the previous command is assumed as substitute.") + +;; The lazy-highlighting framework +(evil-define-local-var evil-ex-active-highlights-alist nil + "An alist of currently active highlights.") + +(evil-define-local-var evil-ex-hl-update-timer nil + "Time used for updating highlights.") + +(defvar evil-ex-search-keymap (make-sparse-keymap) + "Keymap used in ex-search-mode.") + +(defcustom evil-want-empty-ex-last-command t + "Whether to default to evil-ex-previous-command at empty ex prompt." + :type 'boolean + :group 'evil) + +(defconst evil-version + (eval-when-compile + (with-temp-buffer + (let ((dir (file-name-directory (or load-file-name + byte-compile-current-file)))) + ;; git repository + (if (and (file-exists-p (concat dir "/.git")) + (ignore-errors + (zerop (call-process "git" nil '(t nil) nil + "rev-parse" + "--short" "HEAD")))) + (progn + (goto-char (point-min)) + (concat "evil-git-" + (buffer-substring (point-min) + (line-end-position)))) + ;; no repo, use plain version + "1.15.0")))) + "The current version of Evil") + +(defcustom evil-want-integration t + "Whether to load evil-integration.el. +This variable must be set before Evil is loaded." + :type 'boolean + :group 'evil) + +(defcustom evil-want-keybinding t + "Whether to load evil-keybindings.el. + +This loads a set of keybindings for evil in other modes as well as +setting the initial evil state in those modes. + +This variable must be set before evil is loaded." + :type 'boolean + :group 'evil) + +(defcustom evil-want-minibuffer nil + "Whether to enable Evil in minibuffer(s)." + :type 'boolean + :group 'evil + :set #'(lambda (sym value) + (set-default sym value) + (if value + (add-hook 'minibuffer-setup-hook 'evil-initialize) + (remove-hook 'minibuffer-setup-hook 'evil-initialize)))) + +(defun evil--redo-placeholder (_count) + (user-error "Customize `evil-undo-system' for redo functionality.")) + +(defvar evil-undo-function #'undo + "Function to be used by `evil-undo'. +Customized via `evil-undo-system'.") + +(defvar evil-redo-function #'evil--redo-placeholder + "Function to be used by `evil-redo'. +Customized via `evil-undo-system'.") + +(defun evil-set-undo-system (system) + "Set `evil-undo-function' and `evil-redo-function' by SYSTEM." + (cond + ((not system) + (setq evil-undo-function #'undo + evil-redo-function #'evil--redo-placeholder)) + ((eq system 'undo-redo) + (setq evil-undo-function #'undo-only + evil-redo-function #'undo-redo)) + ((eq system 'undo-tree) + (setq evil-undo-function 'undo-tree-undo + evil-redo-function 'undo-tree-redo)) + ((eq system 'undo-fu) + (setq evil-undo-function 'undo-fu-only-undo + evil-redo-function 'undo-fu-only-redo)) + (t (error "Unknown undo system `%s'" system)))) + +(defcustom evil-undo-system nil + "Undo system Evil should use. +If equal to `undo-tree' or `undo-fu', those packages must be +installed. If equal to `undo-tree', `undo-tree-mode' must also be +activated. If equal to `undo-redo', Evil uses commands natively +available in Emacs 28." + :type '(choice (const :tag "Vanilla undo" nil) + (const undo-redo) + (const undo-tree) + (const undo-fu)) + :group 'evil + :set #'(lambda (sym value) + (evil-set-undo-system value) + (set-default sym value))) + +(defcustom evil-visual-update-x-selection-p t + "Whether to update the X PRIMARY selection with the current visual region." + :type 'boolean + :group 'evil) + +(defun evil-version () + (interactive) + (message "Evil version %s" evil-version)) + +(provide 'evil-vars) + +;;; evil-vars.el ends here diff --git a/.config/emacs/lisp/evil/evil.el b/.config/emacs/lisp/evil/evil.el new file mode 100644 index 0000000..757a564 --- /dev/null +++ b/.config/emacs/lisp/evil/evil.el @@ -0,0 +1,158 @@ +;;; evil.el --- Extensible vi layer -*- lexical-binding: t; -*- + +;; The following list of authors was kept up to date until the beginning of +;; 2017, when evil moved under new maintainers. For authors since then, please +;; consult the git logs. + +;; Alessandro Piras +;; Alexander Baier +;; Antono Vasiljev +;; Bailey Ling +;; Barry O'Reilly +;; Christoph Lange +;; Daniel Reiter +;; Eivind Fonn +;; Emanuel Evans +;; Eric Siegel +;; Eugene Yaremenko +;; Frank Fischer +;; Frank Terbeck +;; Gordon Gustafson +;; Herbert Jones +;; Jonas Bernoulli +;; Jonathan Claggett +;; José A. Romero L. +;; Justin Burkett +;; Lars Andersen +;; Lintaro Ina +;; Lukasz Wrzosek +;; Marian Schubert +;; Matthew Malcomson <> +;; Michael Markert +;; Mike Gerwitz +;; Nikolai Weibull +;; phaebz +;; ralesi +;; Rodrigo Setti +;; Sanel Zukan +;; Sarah Brofeldt +;; Simon Hafner +;; Stefan Wehr +;; Sune Simonsen +;; Thomas Hisch +;; Tim Harper +;; Tom Willemse +;; Trevor Murphy +;; Ulrich Müller +;; Vasilij Schneidermann +;; Vegard Øye +;; Winfred Lu +;; Wolfgang Jenkner +;; Xiao Hanyu +;; York Zhao + +;; The following line is included for NonGNU ELPA's build script: +;; Maintainer: Tom Dalziel + +;; Maintainers: The emacs-evil team. +;; To get in touch, please use the bug tracker or the +;; mailing list (see below). +;; Created: 2011-03-01 +;; Version: 1.15.0 +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (goto-chg "1.6") (nadvice "0.3")) +;; Keywords: emulations +;; URL: https://github.com/emacs-evil/evil +;; Repository: https://github.com/emacs-evil/evil.git +;; EmacsWiki: http://www.emacswiki.org/emacs/Evil +;; Bug tracker: https://github.com/emacs-evil/evil/issues +;; If you have bug reports, suggestions or patches, please +;; create an issue at the bug tracker (open for everyone). +;; Other discussions (tips, extensions) go to the mailing list. +;; Mailing list: +;; Subscribe: http://tinyurl.com/implementations-list +;; Newsgroup: nntp://news.gmane.org/gmane.emacs.vim-emulation +;; Archives: http://dir.gmane.org/gmane.emacs.vim-emulation +;; You don't have to subscribe to post; we usually reply +;; within a few days and CC our replies back to you. +;; +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; This file is part of Evil. +;; +;; Evil is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Evil is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Evil. If not, see . + +;;; Commentary: + +;; Evil is an extensible vi layer for Emacs. It emulates the main +;; features of Vim, and provides facilities for writing custom +;; extensions. +;; +;; Evil lives in a Git repository. To obtain Evil, do +;; +;; git clone git://github.com/emacs-evil/evil.git +;; +;; Move Evil to ~/.emacs.d/evil (or somewhere else in the `load-path'). +;; Then add the following lines to ~/.emacs: +;; +;; (add-to-list 'load-path "~/.emacs.d/evil") +;; (require 'evil) +;; (evil-mode 1) +;; +;; Evil requires undo-redo (Emacs 28), undo-fu or undo-tree for redo +;; functionality. Otherwise, Evil uses regular Emacs undo. +;; +;; https://codeberg.org/ideasman42/emacs-undo-fu +;; https://melpa.org/#/undo-fu +;; https://gitlab.com/tsc25/undo-tree +;; https://elpa.gnu.org/packages/undo-tree.html +;; +;; Evil requires `goto-last-change' and `goto-last-change-reverse' +;; function for the corresponding motions g; g, as well as the +;; last-change-register `.'. One package providing these functions is +;; goto-chg.el: +;; +;; https://github.com/emacs-evil/goto-chg +;; https://melpa.org/#/goto-chg +;; https://elpa.nongnu.org/nongnu/goto-chg.html +;; +;; Without this package the corresponding motions will raise an error. + +;;; Code: + +(require 'evil-vars) +(require 'evil-common) +(require 'evil-core) +(require 'evil-states) +(require 'evil-repeat) +(require 'evil-macros) +(require 'evil-search) +(require 'evil-ex) +(require 'evil-types) +(require 'evil-commands) +(require 'evil-jumps) +(require 'evil-maps) + +(when evil-want-integration + (require 'evil-integration)) + +(when evil-want-keybinding + (require 'evil-keybindings)) + +(run-hooks 'evil-after-load-hook) + +(provide 'evil) + +;;; evil.el ends here diff --git a/.config/emacs/lisp/evil/scripts/evil-extract-docstrings b/.config/emacs/lisp/evil/scripts/evil-extract-docstrings new file mode 100755 index 0000000..55dbd79 --- /dev/null +++ b/.config/emacs/lisp/evil/scripts/evil-extract-docstrings @@ -0,0 +1,41 @@ +#!/usr/bin/env -S emacs --script + +(setq cur-path (file-name-directory load-file-name)) + +(add-to-list 'load-path (expand-file-name ".." cur-path)) +(require 'evil) +(require 'evil-digraphs) +(require 'json) +(require 'help) + +(defun mark-command-keys (string) + "Mark key bindings in STRING with the \":kbd:\" Sphinx role." + (when string + (cl-loop + for prev = 0 then pos until (>= prev (length string)) + for pos = (next-single-property-change prev 'face string (length string)) + concat + (let ((s (substring string prev pos))) + (if (eq (get-text-property prev 'face string) 'help-key-binding) + (format ":kbd:`%s`" s) + s))))) + +(let (vars) + (mapatoms + (lambda (sym) + (when (string-prefix-p "evil-" (symbol-name sym)) + (let ((default (eval (car (get sym 'standard-value))))) + (push `(,sym (default . ,(cond + ((consp default) (format "%S" default)) + ((symbolp default) (symbol-name default)) + (t default))) + (local . ,(local-variable-if-set-p sym)) + (default-type . ,(type-of default)) + (var-docstring . ,(mark-command-keys (documentation-property sym 'variable-documentation))) + (fn-docstring . ,(ignore-errors (mark-command-keys (documentation sym)))) + (arglist . ,(help-function-arglist sym)) + (functionp . ,(functionp sym)) + (macrop . ,(macrop sym))) + vars))))) + (with-temp-file (expand-file-name "../doc/docstringdb.json" cur-path) + (insert (json-encode vars)))) diff --git a/.config/emacs/lisp/evil/scripts/evil-release-stable b/.config/emacs/lisp/evil/scripts/evil-release-stable new file mode 100755 index 0000000..c075903 --- /dev/null +++ b/.config/emacs/lisp/evil/scripts/evil-release-stable @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby + +# Copyright (c) 2015 Frank Fischer +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see + + +# This script releases a new (minor) stable release given that some +# new commits on the stable branch since the last tag exist. + +Dir.chdir File.dirname(__FILE__) + +def die(msg = "Failed") + STDERR.puts msg + exit 1 +end + +def run(cmd) + system(cmd) or die(cmd) +end + +run 'hg pull' +run 'hg update -r stable' + +tag = `hg tags`.split("\n").select{|line| line =~ /^\d+\.\d+\.\d+/}.map do |line| + tag, rev = line.split + major, minor, tiny = tag.split(/\./).map(&:to_i) + [major, minor, tiny, rev[/\d+/].to_i] +end.max + +tag or die 'No tagged revision found' + +stable = `hg log -r 'branch(stable) and not file("../.hgtags") and not ..tag("re:\\d+\\.\\d+\\.\\d+")' | wc -l`.to_i != 0 + +if stable + new_tag = "#{tag[0]}.#{tag[1]}.#{tag[2]+1}" + run "make -C .. nversion VERSION='#{new_tag}'" + run "hg commit -m 'update version to #{new_tag}'" + run "hg tag -m 'version #{new_tag}' -r stable #{new_tag}" + run "hg push -r stable" +end diff --git a/.config/emacs/lisp/evil/scripts/evilupdate b/.config/emacs/lisp/evil/scripts/evilupdate new file mode 100755 index 0000000..27e832b --- /dev/null +++ b/.config/emacs/lisp/evil/scripts/evilupdate @@ -0,0 +1,177 @@ +#!/usr/bin/emacs --script +;;; evilupate.el -- Publishing script for Evil + +;;; Header: + +;; This file is not part of Emacs + +;; Author: Frank Fischer +;; Maintainer: Frank Fischer +;; Version: 1.0 +;; Package-Requires: ((emacs "24.3") (s "1.9.0") (f "0.17.2") yaoddmuse) + +;; The contents of this file are subject to the GPL License, Version 3.0. + +;; Copyright (C) 2015, 2016, Frank Fischer + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This is a small script to update Evil's version at certain places: +;; +;; - topic of the IRC channel #evil-mode in irc.libera.chat +;; - EmacsWiki page +;; - header of all source files +;; - the ELPA package file +;; +;; The script should be invoked with the new version string +;; +;; evil-update.el 1.2.3 +;; + +;;; Code: + +(package-initialize) + +(require 'f) +(require 'erc) +(require 'yaoddmuse) + +(defconst evilupdate-irc-nick "lyrobot") +(defconst evilupdate-irc-channel "#evil-mode") +(defconst evilupdate-irc-topic + "Evil = Extensible vi layer for Emacs | Current version: VERSION | https://github.com/emacs-evil/evil | If you don't get an answer here, please use the mailing-list implementations-list@lists.ourproject.org") +(defconst evilupdate-wiki-page "Evil") +(defvar evilupdate-version "1.0.0") +(defvar evilupdate-done nil) + +(defun evilupdate-wait () + "Wait until `evilupdate-done' is set to t." + (while (not evilupdate-done) + (sleep-for 1)) + (setq evilupdate-done nil)) + +(defun evilupdate-done () + "Set `evilupdate-done' to t to stop the corresponding `evilupdate-wait'." + (setq evilupdate-done t)) + +(defun evilupdate-update-irc () + "Update the topic of the IRC channel." + (let ((erc-nick evilupdate-irc-nick) + (erc-autojoin-channels-alist nil) + (auth-sources nil)) + (erc-tls :server "irc.libera.chat" :port "6697" :nick evilupdate-irc-nick) + (add-hook 'erc-join-hook + (lambda () + (let (case-fold-search) + (erc-set-topic (replace-regexp-in-string "VERSION" + evilupdate-version + evilupdate-irc-topic + nil t))) + (erc-quit-server "") + (sleep-for 5) + (evilupdate-done))) + (erc-join-channel evilupdate-irc-channel) + (evilupdate-wait))) + +(defun evilupdate-wiki-trace (page-buffer-name) + "Callback for updating the EmacsWiki page in buffer PAGE-BUFFER-NAME." + (with-current-buffer page-buffer-name + (goto-char (point-min)) + (when (re-search-forward "Current stable version: \\*\\*\\([^*]*\\)\\*\\*") + (goto-char (match-beginning 1)) + (delete-region (match-beginning 1) (match-end 1)) + (insert evilupdate-version) + (yaoddmuse-post-buffer (current-buffer) evilupdate-version nil) + (evilupdate-done)))) + +(unless (require 'nadvice nil t) + (defadvice yaoddmuse-display-page (around evilupdate activate) + (evilupdate-wiki-trace (ad-get-arg 0)))) + +(defun evilupdate-update-emacswiki () + "Update the version information on the EmacsWiki page." + (setq yaoddmuse-wikis + '(("EmacsWiki" "https://www.emacswiki.org/emacs" utf-8 "uihnscuskc=1;"))) + (when (featurep 'nadvice) + (advice-add 'yaoddmuse-display-page :after 'evilupdate-wiki-trace)) + (yaoddmuse-edit "EmacsWiki" evilupdate-wiki-page t) + (evilupdate-wait)) + +(defun evilupdate-update-regexp (file regexp) + "Update version tag in FILE. +The version tag is defined by the REGEXP, in particular by the first +match group." + (find-file file) + (goto-char (point-min)) + (when (re-search-forward regexp nil t) + (goto-char (match-beginning 1)) + (delete-region (match-beginning 1) (match-end 1)) + (insert evilupdate-version) + (save-buffer))) + +(defun evilupdate-update-files () + "Replace version comments in all source files." + (dolist (file (f-glob "*.el")) + (evilupdate-update-regexp file ";;\\s-*Version:\\s-*\\([^\n]+\\)"))) + +(defun evilupdate-update-pkg () + "Replace version in package file." + (evilupdate-update-regexp "evil-pkg.el" "\"evil\"[ \t\n\r]*\"\\([^\"]+\\)\"")) + +(defun evilupdate-update-var () + "Replace version in `evil-version' variable." + (evilupdate-update-regexp + "evil-vars.el" + "t \"\\([^\"]*\\)\"[) \n\r\t]*\"The current version of Evil")) + +(defun evilupdate-update-news () + "Send an posting to the mailing list." + (shell-command (concat "thunderbird -compose \"newsgroups=gmane.emacs.vim-emulation,preselectid=id2,subject='[ANN] Evil version " evilupdate-version " released',body='" + "Evil version " evilupdate-version " has been released. + +The stable version can be installed from MELPA stable [1] and NonGNU ELPA [2]. +The latest development version is available from MELPA [3]. + +The Evil developers. + +[1] http://stable.melpa.org +[2] https://elpa.nongnu.org/nongnu/evil.html +[3] http://melpa.org +'\""))) + +(defun evilupdate-update (version nonews) + "Update Evil to VERSION. +If NONEWS is non-nil do not post a message to the newsgroup." + (setq evilupdate-cnt 0) + (setq evilupdate-version version) + (evilupdate-update-emacswiki) + (evilupdate-update-irc) + (evilupdate-update-files) + (evilupdate-update-pkg) + (evilupdate-update-var) + (unless nonews (evilupdate-update-news))) + +(cond + ((= (length argv) 2) + (unless (equal (car argv) "nonews") + (user-error "Usage: evilupdate.el [-n] VERSION")) + (evilupdate-update (cadr argv) t)) + ((= (length argv) 1) + (evilupdate-update (cadr argv))) + (t + (user-error "Usage: evilupdate.el [-n] VERSION"))) + +;;; evilupdate.el ends here -- cgit v1.2.3