├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── autoload ├── color.vim └── scorchedEarth.vim ├── init.vim ├── plugin └── scorchedEarth.vim └── src ├── args.rs ├── event.rs ├── handler.rs ├── main.rs └── position.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "autocfg" 5 | version = "0.1.5" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | 8 | [[package]] 9 | name = "byteorder" 10 | version = "1.3.2" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [[package]] 14 | name = "cfg-if" 15 | version = "0.1.9" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | 18 | [[package]] 19 | name = "kernel32-sys" 20 | version = "0.2.2" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | dependencies = [ 23 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 24 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 25 | ] 26 | 27 | [[package]] 28 | name = "libc" 29 | version = "0.2.60" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | 32 | [[package]] 33 | name = "log" 34 | version = "0.3.9" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | dependencies = [ 37 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 38 | ] 39 | 40 | [[package]] 41 | name = "log" 42 | version = "0.4.8" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | dependencies = [ 45 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 46 | ] 47 | 48 | [[package]] 49 | name = "neovim-lib" 50 | version = "0.6.1" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | dependencies = [ 53 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "rmpv 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 56 | "unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 57 | ] 58 | 59 | [[package]] 60 | name = "neovim-scorched-earth" 61 | version = "0.1.0" 62 | dependencies = [ 63 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "neovim-lib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 65 | "simplelog 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 66 | ] 67 | 68 | [[package]] 69 | name = "num-traits" 70 | version = "0.1.43" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | dependencies = [ 73 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 74 | ] 75 | 76 | [[package]] 77 | name = "num-traits" 78 | version = "0.2.8" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | dependencies = [ 81 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 82 | ] 83 | 84 | [[package]] 85 | name = "redox_syscall" 86 | version = "0.1.56" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | 89 | [[package]] 90 | name = "rmp" 91 | version = "0.8.7" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | dependencies = [ 94 | "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 95 | "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 96 | ] 97 | 98 | [[package]] 99 | name = "rmpv" 100 | version = "0.4.0" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | dependencies = [ 103 | "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 106 | "serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", 107 | ] 108 | 109 | [[package]] 110 | name = "serde" 111 | version = "1.0.98" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | 114 | [[package]] 115 | name = "serde_bytes" 116 | version = "0.10.5" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | dependencies = [ 119 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 120 | ] 121 | 122 | [[package]] 123 | name = "simplelog" 124 | version = "0.4.4" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | dependencies = [ 127 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 128 | "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 129 | "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 130 | ] 131 | 132 | [[package]] 133 | name = "term" 134 | version = "0.4.6" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | dependencies = [ 137 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 138 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 139 | ] 140 | 141 | [[package]] 142 | name = "time" 143 | version = "0.1.42" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | dependencies = [ 146 | "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 147 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 148 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 149 | ] 150 | 151 | [[package]] 152 | name = "unix_socket" 153 | version = "0.5.0" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | dependencies = [ 156 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 157 | "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 158 | ] 159 | 160 | [[package]] 161 | name = "winapi" 162 | version = "0.2.8" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | 165 | [[package]] 166 | name = "winapi" 167 | version = "0.3.7" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | dependencies = [ 170 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 171 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 172 | ] 173 | 174 | [[package]] 175 | name = "winapi-build" 176 | version = "0.1.1" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | 179 | [[package]] 180 | name = "winapi-i686-pc-windows-gnu" 181 | version = "0.4.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | 184 | [[package]] 185 | name = "winapi-x86_64-pc-windows-gnu" 186 | version = "0.4.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | 189 | [metadata] 190 | "checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" 191 | "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" 192 | "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 193 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 194 | "checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" 195 | "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 196 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 197 | "checksum neovim-lib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a8f5a1e1be160ce2b669c2c495a34ade6f3a525d4afafd7370c1792070f587" 198 | "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" 199 | "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" 200 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 201 | "checksum rmp 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a3d45d7afc9b132b34a2479648863aa95c5c88e98b32285326a6ebadc80ec5c9" 202 | "checksum rmpv 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29af0205707de955a396a1d3c657677c65f791ebabb63c0596c0b2fec0bf6325" 203 | "checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" 204 | "checksum serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "defbb8a83d7f34cc8380751eeb892b825944222888aff18996ea7901f24aec88" 205 | "checksum simplelog 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "24b615b1a3cc51ffa565d9a1d0cfcc49fe7d64737ada84eca284cddb0292d125" 206 | "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" 207 | "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" 208 | "checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564" 209 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 210 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 211 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 212 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 213 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 214 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Justin Charette "] 3 | name = "neovim-scorched-earth" 4 | version = "0.1.0" 5 | edition = "2018" 6 | 7 | [dependencies] 8 | log = "0.3.7" 9 | simplelog = "0.4.2" 10 | neovim-lib = "0.6.1" 11 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Justin Charette 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # neovim-scorched-earth 2 | 3 | A stupid proof-of-concept for a Neovim plugin written in Rust using 4 | [daa84/neovim-lib](https://github.com/daa84/neovim-lib). 5 | 6 | ## What does it do? 7 | 8 | Scorched Earth highlights the region of text that was touched by the cursor 9 | while in Insert or Replace modes. 10 | 11 | ![demo](https://boxofrox.github.com/neovim-scorched-earth/assets/images/demo.gif) 12 | 13 | ## Why... this? 14 | 15 | 1. This is my first Neovim plugin in Rust. 16 | 17 | 2. A highlight plugin is new territory and interests me, so I'm less likely to 18 | give up on it. 19 | 20 | 3. I'm unfamiliar with the nuances of Msgpack RPC communications with Neovim 21 | using neovim-lib. The way was fraught with danger. This project was just 22 | deep enough to burn myself a few times tinkering with different designs, 23 | and shallow enough that I didn't invest a depressing amount of work that 24 | needed refactoring. 25 | 26 | 4. Experiment with a directory structure to combine the rust source and 27 | vimscript plugin. 28 | 29 | ## Try it out 30 | 31 | 1. It's dangerous to go alone. Take this! https://www.rustup.rs/ 32 | 33 | 2. Use the stable rust compiler. 34 | 35 | ```sh 36 | rustup install stable 37 | rustup default stable 38 | 39 | ``` 40 | 41 | 2. Fetch the plugin. 42 | 43 | ```sh 44 | $ git clone https://github.com/boxofrox/neovim-scorched-earth.git 45 | ``` 46 | 47 | 3. Build the binary portion of the plugin. 48 | 49 | ```sh 50 | $ cd neovim-scorched-earth 51 | $ cargo build --release 52 | ``` 53 | 54 | 4. Test it out in a fresh instance of Neovim. *(If Windows requires any 55 | changes, open an issue!)* 56 | 57 | ```sh 58 | nvim -u ./init.vim --noplugin -c ":ScorchedEarthConnect" 59 | ``` 60 | 61 | The `ScorchedEarthConnect` command spawns the Rust plugin in a separate process and 62 | establishes a channel. 63 | 64 | ## Todo 65 | 66 | - [ ] Add vimscript variable `g:scorched_earth_syntax_group` to select 67 | highlight color using existing syntax groups. 68 | 69 | - [x] Add vimscript variable `g:scorched_earth_program` to specify location of Rust 70 | plugin binary. Will facilitate testing development binaries. 71 | -------------------------------------------------------------------------------- /autoload/color.vim: -------------------------------------------------------------------------------- 1 | " Code adapted from jellybeans.vim 2 | " 3 | " URL: github.com/nanotech/jellybeans.vim 4 | " Scripts URL: vim.org/scripts/script.php?script_id=2555 5 | " Version: 1.6~git 6 | " Last Change: January 15th, 2012 7 | " License: MIT 8 | " Contributors: Daniel Herbert (pocketninja) 9 | " Henry So, Jr. 10 | " David Liang 11 | " Rich Healey (richo) 12 | " Andrew Wong (w0ng) 13 | " 14 | " Copyright (c) 2009-2012 NanoTech 15 | " Copyright (c) 2017 Justin Charette (boxofrox) 16 | " 17 | " Permission is hereby granted, free of charge, to any per‐ 18 | " son obtaining a copy of this software and associated doc‐ 19 | " umentation files (the “Software”), to deal in the Soft‐ 20 | " ware without restriction, including without limitation 21 | " the rights to use, copy, modify, merge, publish, distrib‐ 22 | " ute, sublicense, and/or sell copies of the Software, and 23 | " to permit persons to whom the Software is furnished to do 24 | " so, subject to the following conditions: 25 | " 26 | " The above copyright notice and this permission notice 27 | " shall be included in all copies or substantial portions 28 | " of the Software. 29 | " 30 | " THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY 31 | " KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 32 | " THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICU‐ 33 | " LAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 35 | " DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CON‐ 36 | " TRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON‐ 37 | " NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 38 | " THE SOFTWARE. 39 | 40 | if has("gui_running") || &t_Co == 88 || &t_Co == 256 41 | let s:low_color = 0 42 | else 43 | let s:low_color = 1 44 | endif 45 | 46 | " Color approximation functions by Henry So, Jr. and David Liang {{{ 47 | " Added to jellybeans.vim by Daniel Herbert 48 | 49 | " returns an approximate grey index for the given grey level 50 | fun! s:grey_number(x) 51 | if &t_Co == 88 52 | if a:x < 23 53 | return 0 54 | elseif a:x < 69 55 | return 1 56 | elseif a:x < 103 57 | return 2 58 | elseif a:x < 127 59 | return 3 60 | elseif a:x < 150 61 | return 4 62 | elseif a:x < 173 63 | return 5 64 | elseif a:x < 196 65 | return 6 66 | elseif a:x < 219 67 | return 7 68 | elseif a:x < 243 69 | return 8 70 | else 71 | return 9 72 | endif 73 | else 74 | if a:x < 14 75 | return 0 76 | else 77 | let l:n = (a:x - 8) / 10 78 | let l:m = (a:x - 8) % 10 79 | if l:m < 5 80 | return l:n 81 | else 82 | return l:n + 1 83 | endif 84 | endif 85 | endif 86 | endfun 87 | 88 | " returns the actual grey level represented by the grey index 89 | fun! s:grey_level(n) 90 | if &t_Co == 88 91 | if a:n == 0 92 | return 0 93 | elseif a:n == 1 94 | return 46 95 | elseif a:n == 2 96 | return 92 97 | elseif a:n == 3 98 | return 115 99 | elseif a:n == 4 100 | return 139 101 | elseif a:n == 5 102 | return 162 103 | elseif a:n == 6 104 | return 185 105 | elseif a:n == 7 106 | return 208 107 | elseif a:n == 8 108 | return 231 109 | else 110 | return 255 111 | endif 112 | else 113 | if a:n == 0 114 | return 0 115 | else 116 | return 8 + (a:n * 10) 117 | endif 118 | endif 119 | endfun 120 | 121 | " returns the palette index for the given grey index 122 | fun! s:grey_color(n) 123 | if &t_Co == 88 124 | if a:n == 0 125 | return 16 126 | elseif a:n == 9 127 | return 79 128 | else 129 | return 79 + a:n 130 | endif 131 | else 132 | if a:n == 0 133 | return 16 134 | elseif a:n == 25 135 | return 231 136 | else 137 | return 231 + a:n 138 | endif 139 | endif 140 | endfun 141 | 142 | " returns an approximate color index for the given color level 143 | fun! s:rgb_number(x) 144 | if &t_Co == 88 145 | if a:x < 69 146 | return 0 147 | elseif a:x < 172 148 | return 1 149 | elseif a:x < 230 150 | return 2 151 | else 152 | return 3 153 | endif 154 | else 155 | if a:x < 75 156 | return 0 157 | else 158 | let l:n = (a:x - 55) / 40 159 | let l:m = (a:x - 55) % 40 160 | if l:m < 20 161 | return l:n 162 | else 163 | return l:n + 1 164 | endif 165 | endif 166 | endif 167 | endfun 168 | 169 | " returns the actual color level for the given color index 170 | fun! s:rgb_level(n) 171 | if &t_Co == 88 172 | if a:n == 0 173 | return 0 174 | elseif a:n == 1 175 | return 139 176 | elseif a:n == 2 177 | return 205 178 | else 179 | return 255 180 | endif 181 | else 182 | if a:n == 0 183 | return 0 184 | else 185 | return 55 + (a:n * 40) 186 | endif 187 | endif 188 | endfun 189 | 190 | " returns the palette index for the given R/G/B color indices 191 | fun! s:rgb_color(x, y, z) 192 | if &t_Co == 88 193 | return 16 + (a:x * 16) + (a:y * 4) + a:z 194 | else 195 | return 16 + (a:x * 36) + (a:y * 6) + a:z 196 | endif 197 | endfun 198 | 199 | " returns the palette index to approximate the given R/G/B color levels 200 | fun! s:color(r, g, b) 201 | " get the closest grey 202 | let l:gx = s:grey_number(a:r) 203 | let l:gy = s:grey_number(a:g) 204 | let l:gz = s:grey_number(a:b) 205 | 206 | " get the closest color 207 | let l:x = s:rgb_number(a:r) 208 | let l:y = s:rgb_number(a:g) 209 | let l:z = s:rgb_number(a:b) 210 | 211 | if l:gx == l:gy && l:gy == l:gz 212 | " there are two possibilities 213 | let l:dgr = s:grey_level(l:gx) - a:r 214 | let l:dgg = s:grey_level(l:gy) - a:g 215 | let l:dgb = s:grey_level(l:gz) - a:b 216 | let l:dgrey = (l:dgr * l:dgr) + (l:dgg * l:dgg) + (l:dgb * l:dgb) 217 | let l:dr = s:rgb_level(l:gx) - a:r 218 | let l:dg = s:rgb_level(l:gy) - a:g 219 | let l:db = s:rgb_level(l:gz) - a:b 220 | let l:drgb = (l:dr * l:dr) + (l:dg * l:dg) + (l:db * l:db) 221 | if l:dgrey < l:drgb 222 | " use the grey 223 | return s:grey_color(l:gx) 224 | else 225 | " use the color 226 | return s:rgb_color(l:x, l:y, l:z) 227 | endif 228 | else 229 | " only one possibility 230 | return s:rgb_color(l:x, l:y, l:z) 231 | endif 232 | endfun 233 | 234 | " returns the palette index to approximate the 'rrggbb' hex string 235 | fun! s:rgb(rgb) 236 | let l:r = ("0x" . strpart(a:rgb, 0, 2)) + 0 237 | let l:g = ("0x" . strpart(a:rgb, 2, 2)) + 0 238 | let l:b = ("0x" . strpart(a:rgb, 4, 2)) + 0 239 | return s:color(l:r, l:g, l:b) 240 | endfun 241 | 242 | " sets the highlighting for the given group 243 | fun! color#highlight(group, fg, bg, attr, lcfg, lcbg) 244 | if s:low_color 245 | let l:fge = empty(a:lcfg) 246 | let l:bge = empty(a:lcbg) 247 | 248 | if !l:fge && !l:bge 249 | exec "hi ".a:group." ctermfg=".a:lcfg." ctermbg=".a:lcbg 250 | elseif !l:fge && l:bge 251 | exec "hi ".a:group." ctermfg=".a:lcfg." ctermbg=NONE" 252 | elseif l:fge && !l:bge 253 | exec "hi ".a:group." ctermfg=NONE ctermbg=".a:lcbg 254 | endif 255 | else 256 | let l:fge = empty(a:fg) 257 | let l:bge = empty(a:bg) 258 | 259 | if !l:fge && !l:bge 260 | exec "hi ".a:group." guifg=#".a:fg." guibg=#".a:bg." ctermfg=".s:rgb(a:fg)." ctermbg=".s:rgb(a:bg) 261 | elseif !l:fge && l:bge 262 | exec "hi ".a:group." guifg=#".a:fg." guibg=NONE ctermfg=".s:rgb(a:fg)." ctermbg=NONE" 263 | elseif l:fge && !l:bge 264 | exec "hi ".a:group." guifg=NONE guibg=#".a:bg." ctermfg=NONE ctermbg=".s:rgb(a:bg) 265 | endif 266 | endif 267 | 268 | if a:attr == "" 269 | exec "hi ".a:group." gui=none cterm=none" 270 | else 271 | let l:noitalic = join(filter(split(a:attr, ","), "v:val !=? 'italic'"), ",") 272 | if empty(l:noitalic) 273 | let l:noitalic = "none" 274 | endif 275 | exec "hi ".a:group." gui=".a:attr." cterm=".l:noitalic 276 | endif 277 | endfun 278 | -------------------------------------------------------------------------------- /autoload/scorchedEarth.vim: -------------------------------------------------------------------------------- 1 | " Copyright 2017 Justin Charette 2 | " 3 | " Licensed under the Apache License, Version 2.0 or the MIT license 5 | " , at your 6 | " option. This file may not be copied, modified, or distributed 7 | " except according to those terms. 8 | 9 | if ! exists('s:jobid') 10 | let s:jobid = 0 11 | endif 12 | 13 | let s:scriptdir = resolve(expand(':p:h') . '/..') 14 | 15 | if ! exists('g:scorched_earth_program') 16 | let g:scorched_earth_program = s:scriptdir . '/target/release/neovim-scorched-earth' 17 | endif 18 | 19 | function! scorchedEarth#init() 20 | call scorchedEarth#connect() 21 | endfunction 22 | 23 | function! scorchedEarth#connect() 24 | let result = s:StartJob() 25 | 26 | if 0 == result 27 | echoerr "scortched earth: cannot start rpc process" 28 | elseif -1 == result 29 | echoerr "scortched earth: rpc process is not executable" 30 | else 31 | let s:jobid = result 32 | call s:ConfigureJob(result) 33 | endif 34 | endfunction 35 | 36 | function! scorchedEarth#reset() 37 | let s:jobid = 0 38 | endfunction 39 | 40 | function! s:ConfigureJob(jobid) 41 | augroup scortchedEarth 42 | " clear all previous autocommands 43 | autocmd! 44 | 45 | autocmd VimLeavePre * :call s:StopJob() 46 | 47 | autocmd InsertChange * :call s:NotifyInsertChange() 48 | autocmd InsertEnter * :call s:NotifyInsertEnter() 49 | autocmd InsertLeave * :call s:NotifyInsertLeave() 50 | 51 | autocmd CursorMovedI * :call s:NotifyCursorMovedI() 52 | augroup END 53 | endfunction 54 | 55 | function! s:NotifyCursorMovedI() 56 | let [ bufnum, lnum, column, off ] = getpos('.') 57 | call rpcnotify(s:jobid, 'cursor-moved-i', lnum, column) 58 | endfunction 59 | 60 | function! s:NotifyInsertChange() 61 | let [ bufnum, lnum, column, off ] = getpos('.') 62 | call rpcnotify(s:jobid, 'insert-change', v:insertmode, lnum, column) 63 | endfunction 64 | 65 | function! s:NotifyInsertEnter() 66 | let [ bufnum, lnum, column, off ] = getpos('.') 67 | call rpcnotify(s:jobid, 'insert-enter', v:insertmode, lnum, column) 68 | endfunction 69 | 70 | function! s:NotifyInsertLeave() 71 | call rpcnotify(s:jobid, 'insert-leave') 72 | endfunction 73 | 74 | function! s:OnStderr(id, data, event) dict 75 | echom 'scorched earth: stderr: ' . join(a:data, "\n") 76 | endfunction 77 | 78 | function! s:StartJob() 79 | if 0 == s:jobid 80 | let id = jobstart([g:scorched_earth_program], { 'rpc': v:true, 'on_stderr': function('s:OnStderr') }) 81 | return id 82 | else 83 | return 0 84 | endif 85 | endfunction 86 | 87 | function! s:StopJob() 88 | if 0 < s:jobid 89 | augroup scortchedEarth 90 | autocmd! " clear all previous autocommands 91 | augroup END 92 | 93 | call rpcnotify(s:jobid, 'quit') 94 | let result = jobwait(s:jobid, 500) 95 | 96 | if -1 == result 97 | " kill the job 98 | call jobstop(s:jobid) 99 | endif 100 | 101 | " reset job id back to zero 102 | let s:jobid = 0 103 | endif 104 | endfunction 105 | 106 | call color#highlight('default ScorchedEarth', 'dddddd', '550000', 'bold', '', '') 107 | -------------------------------------------------------------------------------- /init.vim: -------------------------------------------------------------------------------- 1 | " Copyright 2017 Justin Charette 2 | " 3 | " Licensed under the Apache License, Version 2.0 or the MIT license 5 | " , at your 6 | " option. This file may not be copied, modified, or distributed 7 | " except according to those terms. 8 | 9 | set rtp+=$PWD 10 | 11 | source $PWD/plugin/scorchedEarth.vim 12 | 13 | " Usage: nvim -u init.vim --noplugin 14 | -------------------------------------------------------------------------------- /plugin/scorchedEarth.vim: -------------------------------------------------------------------------------- 1 | " File: scortchedEarth.vim 2 | " Last Modified: 2017-03-20 3 | " Version: 0.0.1 4 | " Description: Scorched Earth -- highlights changes made in replacement mode. 5 | " Website: 6 | " Author: Justin Charette (charetjc@gmail.com) 7 | " License: MIT + Apache 2.0 8 | " 9 | " Copyright 2017 Justin Charette 10 | " 11 | " Licensed under the Apache License, Version 2.0 or the MIT license 13 | " , at your 14 | " option. This file may not be copied, modified, or distributed 15 | " except according to those terms. 16 | 17 | command! -nargs=0 ScorchedEarthConnect call scorchedEarth#connect() 18 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Justin Charette 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use neovim_lib::Value; 10 | 11 | pub fn parse_string(value: &Value) -> Result { 12 | value 13 | .as_str() 14 | .ok_or("cannot parse error".to_owned()) 15 | .map(|s| String::from(s)) 16 | } 17 | 18 | pub fn parse_usize(value: &Value) -> Result { 19 | value 20 | .as_u64() 21 | .ok_or("cannot parse usize".to_owned()) 22 | .map(|n| n as usize) 23 | } 24 | -------------------------------------------------------------------------------- /src/event.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Justin Charette 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::fmt; 10 | 11 | pub enum Event { 12 | CursorMovedI { 13 | line: usize, 14 | column: usize, 15 | }, 16 | InsertEnter { 17 | mode: String, 18 | line: usize, 19 | column: usize, 20 | }, 21 | InsertLeave, 22 | Quit, 23 | } 24 | 25 | impl fmt::Debug for Event { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | use Event::*; 28 | 29 | match self { 30 | &CursorMovedI { 31 | ref line, 32 | ref column, 33 | } => write!( 34 | f, 35 | "Event::CursorMovedI{{ line: {}, column: {} }}", 36 | line, column 37 | ), 38 | &InsertEnter { 39 | ref mode, 40 | ref line, 41 | ref column, 42 | } => write!( 43 | f, 44 | "Event::InsertEnter{{ mode: {}, line: {}, column: {}}}", 45 | mode, line, column 46 | ), 47 | &InsertLeave => write!(f, "Event::InsertLeave"), 48 | &Quit => write!(f, "Event::Quit"), 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/handler.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Justin Charette 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::args; 10 | use crate::event::Event; 11 | 12 | use log::*; 13 | 14 | use neovim_lib::{Handler, RequestHandler, Value}; 15 | 16 | use std::sync::mpsc; 17 | 18 | pub struct NeovimHandler(pub mpsc::Sender); 19 | 20 | impl NeovimHandler { 21 | pub fn parse_cursor_moved_i(&mut self, args: &Vec) -> Result { 22 | if 2 != args.len() { 23 | return Err(format!( 24 | "Wrong number of arguments for 'CursorMoveI'. Expected 2, found \ 25 | {}", 26 | args.len() 27 | )); 28 | } 29 | 30 | let line = args::parse_usize(&args[0])?; 31 | let column = args::parse_usize(&args[1])?; 32 | 33 | Ok(Event::CursorMovedI { 34 | line: line, 35 | column: column, 36 | }) 37 | } 38 | 39 | pub fn parse_insert_enter(&mut self, args: &Vec) -> Result { 40 | if 3 != args.len() { 41 | return Err(format!( 42 | "Wrong number of arguments for 'InsertEnter'. Expected 3, found \ 43 | {}", 44 | args.len() 45 | )); 46 | } 47 | 48 | let mode = args::parse_string(&args[0])?; 49 | let line = args::parse_usize(&args[1])?; 50 | let column = args::parse_usize(&args[2])?; 51 | 52 | Ok(Event::InsertEnter { 53 | mode: mode, 54 | line: line, 55 | column: column, 56 | }) 57 | } 58 | } 59 | 60 | impl Handler for NeovimHandler { 61 | fn handle_notify(&mut self, name: &str, args: Vec) { 62 | info!("event: {}", name); 63 | //print_args(&args); 64 | match name { 65 | "cursor-moved-i" => { 66 | if let Ok(event) = self.parse_cursor_moved_i(&args) { 67 | info!("cursor moved i: {:?}", event); 68 | if let Err(reason) = self.0.send(event) { 69 | error!("{}", reason); 70 | } 71 | } 72 | } 73 | "insert-enter" => { 74 | if let Ok(event) = self.parse_insert_enter(&args) { 75 | info!("insert enter: {:?}", event); 76 | if let Err(reason) = self.0.send(event) { 77 | error!("{}", reason); 78 | } 79 | } 80 | } 81 | "insert-leave" => { 82 | if let Err(reason) = self.0.send(Event::InsertLeave) { 83 | error!("{}", reason); 84 | } 85 | } 86 | "quit" => { 87 | if let Err(reason) = self.0.send(Event::Quit) { 88 | error!("{}", reason); 89 | } 90 | } 91 | _ => {} 92 | } 93 | } 94 | } 95 | 96 | impl RequestHandler for NeovimHandler { 97 | fn handle_request(&mut self, _name: &str, _args: Vec) -> Result { 98 | Err(Value::from("not implemented")) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Justin Charette 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod args; 10 | mod event; 11 | mod handler; 12 | mod position; 13 | 14 | use crate::event::Event; 15 | use crate::handler::NeovimHandler; 16 | use crate::position::Position; 17 | 18 | use log::*; 19 | 20 | use neovim_lib::neovim::Neovim; 21 | use neovim_lib::neovim_api::NeovimApi; 22 | use neovim_lib::session::Session; 23 | 24 | use simplelog::{Config, LogLevel, LogLevelFilter, WriteLogger}; 25 | 26 | use std::collections::HashSet; 27 | use std::error::Error; 28 | use std::fmt; 29 | use std::sync::mpsc; 30 | 31 | fn main() { 32 | use std::process; 33 | 34 | init_logging().expect("scorched earth: unable to initialize logger."); 35 | 36 | match start_program() { 37 | Ok(_) => process::exit(0), 38 | 39 | Err(msg) => { 40 | error!("{}", msg); 41 | process::exit(1); 42 | } 43 | }; 44 | } 45 | 46 | fn init_logging() -> Result<(), Box> { 47 | use std::env; 48 | use std::env::VarError; 49 | use std::fs::File; 50 | 51 | let log_level_filter = match env::var("LOG_LEVEL") 52 | .unwrap_or(String::from("trace")) 53 | .to_lowercase() 54 | .as_ref() 55 | { 56 | "debug" => LogLevelFilter::Debug, 57 | "error" => LogLevelFilter::Error, 58 | "info" => LogLevelFilter::Info, 59 | "off" => LogLevelFilter::Off, 60 | "trace" => LogLevelFilter::Trace, 61 | "warn" => LogLevelFilter::Warn, 62 | _ => LogLevelFilter::Off, 63 | }; 64 | 65 | let config = Config { 66 | time: Some(LogLevel::Error), 67 | level: Some(LogLevel::Error), 68 | target: Some(LogLevel::Error), 69 | location: Some(LogLevel::Error), 70 | }; 71 | 72 | let filepath = match env::var("LOG_FILE") { 73 | Err(err) => match err { 74 | VarError::NotPresent => return Ok(()), 75 | e @ VarError::NotUnicode(_) => { 76 | return Err(Box::new(e)); 77 | } 78 | }, 79 | Ok(path) => path.to_owned(), 80 | }; 81 | 82 | let log_file = File::create(filepath)?; 83 | 84 | WriteLogger::init(log_level_filter, config, log_file).unwrap(); 85 | 86 | Ok(()) 87 | } 88 | 89 | fn start_program() -> Result<(), Box> { 90 | info!("connecting to neovim via stdin/stdout"); 91 | 92 | let (sender, receiver) = mpsc::channel(); 93 | let mut session = Session::new_parent()?; 94 | session.start_event_loop_handler(NeovimHandler(sender)); 95 | 96 | let mut nvim = Neovim::new(session); 97 | 98 | info!("let's notify neovim the plugin is connected!"); 99 | nvim.command("echom \"rust client connected to neovim\"") 100 | .unwrap(); 101 | info!("notification complete!"); 102 | 103 | nvim.subscribe("cursor-moved-i") 104 | .expect("error: cannot subscribe to event: change-cursor-i"); 105 | nvim.subscribe("insert-enter") 106 | .expect("error: cannot subscribe to event: insert-enter"); 107 | nvim.subscribe("insert-leave") 108 | .expect("error: cannot subscribe to event: insert-leave"); 109 | nvim.subscribe("quit") 110 | .expect("error: cannot subscribe to event: quit"); 111 | 112 | start_event_loop(receiver, nvim); 113 | 114 | Ok(()) 115 | } 116 | 117 | enum Mode { 118 | Insert, 119 | Replace, 120 | Other, 121 | } 122 | 123 | /* 124 | fn print_args(args: &Vec) { 125 | for (i, val) in args.iter().enumerate() { 126 | info!("arg {}: {:?}", i, val); 127 | } 128 | } 129 | */ 130 | 131 | fn start_event_loop(receiver: mpsc::Receiver, mut nvim: Neovim) { 132 | let mut cursor_start: Option = None; 133 | let mut cursor_end: Option = None; 134 | let mut mode = Mode::Other; 135 | 136 | let highlight_groups = HighlightGroup::load(&mut nvim); 137 | 138 | loop { 139 | match receiver.recv() { 140 | Ok(Event::CursorMovedI { line, column }) => { 141 | if let Mode::Other = mode { 142 | continue; 143 | } 144 | 145 | let pos = Position::new(line, column); 146 | 147 | cursor_start = keep_min_position(&cursor_start, &pos); 148 | cursor_end = keep_max_position(&cursor_end, &pos); 149 | 150 | info!("start: sending echo message to neovim"); 151 | define_syntax_region( 152 | &mut nvim, 153 | cursor_start.as_ref().unwrap(), 154 | cursor_end.as_ref().unwrap(), 155 | ); 156 | info!("finish: sending echo message to neovim"); 157 | } 158 | Ok(Event::InsertEnter { 159 | mode: neovim_mode, 160 | line, 161 | column, 162 | }) => { 163 | info!("insert enter: mode is {}", neovim_mode); 164 | 165 | match neovim_mode.as_ref() { 166 | "r" => mode = Mode::Replace, 167 | "i" => mode = Mode::Insert, 168 | _ => continue, 169 | } 170 | 171 | cursor_start = Some(Position::new(line, column)); 172 | cursor_end = Some(Position::new(line, column)); 173 | 174 | if let Some(parent_group) = 175 | get_valid_parent_highlight_group(&mut nvim, &highlight_groups) 176 | { 177 | link_highlight_group(&mut nvim, &parent_group); 178 | } 179 | } 180 | Ok(Event::InsertLeave) => { 181 | mode = Mode::Other; 182 | cursor_start = None; 183 | cursor_end = None; 184 | remove_syntax_group(&mut nvim); 185 | } 186 | Ok(Event::Quit) => break, 187 | _ => {} 188 | } 189 | } 190 | info!("quitting"); 191 | nvim.command("echom \"rust client disconnected from neovim\"") 192 | .unwrap(); 193 | } 194 | 195 | fn keep_min_position(target: &Option, pos: &Position) -> Option { 196 | match target { 197 | &None => Some(pos.clone()), 198 | &Some(ref start) => { 199 | if pos < start { 200 | Some(pos.clone()) 201 | } else { 202 | Some(start.clone()) 203 | } 204 | } 205 | } 206 | } 207 | 208 | fn keep_max_position(target: &Option, pos: &Position) -> Option { 209 | match target { 210 | &None => Some(pos.clone()), 211 | &Some(ref end) => { 212 | if pos > end { 213 | Some(pos.clone()) 214 | } else { 215 | Some(end.clone()) 216 | } 217 | } 218 | } 219 | } 220 | 221 | fn link_highlight_group(nvim: &mut Neovim, parent_highlight_group: &HighlightGroup) { 222 | nvim.command(&format!( 223 | "highlight link ScorchedEarth {}", 224 | parent_highlight_group 225 | )) 226 | .unwrap() 227 | } 228 | 229 | fn define_syntax_region(nvim: &mut Neovim, cursor_start: &Position, cursor_end: &Position) { 230 | nvim.command(&format!( 231 | "syntax region ScorchedEarth start=/\\%{}l\\%{}c/ end=/\\%{}l\\%{}c/", 232 | cursor_start.line, cursor_start.column, cursor_end.line, cursor_end.column 233 | )) 234 | .unwrap(); 235 | } 236 | 237 | fn remove_syntax_group(nvim: &mut Neovim) { 238 | nvim.command("syntax clear ScorchedEarth").unwrap(); 239 | } 240 | 241 | fn get_valid_parent_highlight_group( 242 | nvim: &mut Neovim, 243 | group_set: &HashSet, 244 | ) -> Option { 245 | match get_parent_highlight_group(nvim) { 246 | None => None, 247 | Some(group) => { 248 | if group_set.contains(&group) { 249 | Some(group) 250 | } else { 251 | None 252 | } 253 | } 254 | } 255 | } 256 | 257 | fn get_parent_highlight_group(nvim: &mut Neovim) -> Option { 258 | match nvim.get_var("scorched_earth_parent_highlight_group") { 259 | Err(_) => None, 260 | Ok(ref v) => v.as_str().map(HighlightGroup::from), 261 | } 262 | } 263 | 264 | #[derive(Debug, Eq, Hash, PartialEq)] 265 | struct HighlightGroup(String); 266 | 267 | impl HighlightGroup { 268 | pub fn new(name: String) -> HighlightGroup { 269 | HighlightGroup(name) 270 | } 271 | 272 | pub fn load(nvim: &mut Neovim) -> HashSet { 273 | let highlight = nvim 274 | .command_output("silent highlight") 275 | .expect("unable to list highlights"); 276 | 277 | let highlight = highlight 278 | .lines() 279 | .filter(|line| line.starts_with(char::is_alphabetic)) 280 | .flat_map(|line| line.split_whitespace().take(1)) 281 | .map(str::to_owned) 282 | .map(HighlightGroup::new) 283 | .collect::>(); 284 | 285 | info!("highlight groups:\n{:?}", highlight); 286 | 287 | highlight 288 | } 289 | } 290 | 291 | impl fmt::Display for HighlightGroup { 292 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 293 | writeln!(f, "{}", self.0) 294 | } 295 | } 296 | 297 | impl From for HighlightGroup { 298 | fn from(name: String) -> HighlightGroup { 299 | HighlightGroup(name) 300 | } 301 | } 302 | 303 | impl<'a> From<&'a str> for HighlightGroup { 304 | fn from(name: &'a str) -> HighlightGroup { 305 | HighlightGroup(name.to_owned()) 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /src/position.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Justin Charette 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::cmp::Ordering; 10 | use std::fmt; 11 | 12 | #[derive(Clone, PartialEq, Eq)] 13 | pub struct Position { 14 | pub line: usize, 15 | pub column: usize, 16 | } 17 | 18 | impl Position { 19 | pub fn new(line: usize, column: usize) -> Position { 20 | Position { 21 | line: line, 22 | column: column, 23 | } 24 | } 25 | } 26 | 27 | impl PartialOrd for Position { 28 | fn partial_cmp(&self, other: &Position) -> Option { 29 | Some(self.cmp(other)) 30 | } 31 | } 32 | 33 | impl Ord for Position { 34 | fn cmp(&self, other: &Position) -> Ordering { 35 | match self.line.cmp(&other.line) { 36 | Ordering::Equal => self.column.cmp(&other.column), 37 | x => x, 38 | } 39 | } 40 | } 41 | 42 | impl fmt::Debug for Position { 43 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 44 | write!( 45 | f, 46 | "Position{{ line: {}, column: {} }}", 47 | self.line, self.column 48 | ) 49 | } 50 | } 51 | --------------------------------------------------------------------------------