├── LICENSE ├── README.md ├── luar.asciidoc ├── luar.fnl ├── luar.kak ├── luar.lua └── luarmodule.lua /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Luar 2 | 3 | Luar is a minimalist plugin to script [Kakoune](http://kakoune.org/) using 4 | either [Lua](https://www.lua.org/) or [Fennel](https://fennel-lang.org/). It's 5 | not designed to expose Kakoune's internals like 6 | [Vis](https://github.com/martanne/vis) or [Neovim](https://neovim.io/) do. 7 | Instead, it's conceived with Kakoune's extension model in mind. It does so by 8 | defining a sole `lua` command which can execute whatever string is passed to it 9 | in an external `lua` interpreter, and an equivalent `fennel` command which 10 | executes whatever string is passed to it in an external `fennel` interpreter. By 11 | doing so, it can act as a complement for the `%sh{}` expansion when you need to 12 | run some logic inside Kakoune. 13 | 14 | ## Usage 15 | 16 | First of all, require the provided module: 17 | 18 | ```kak 19 | require-module luar 20 | ``` 21 | 22 | The `luar` module exports a `lua` command, which executes the code passed to it in an external `lua` interpreter. The code is interpreted as the body of an anonymous function, and whatever this anonymous function returns replaces the current selections. 23 | 24 | So, the following code: 25 | 26 | ```lua 27 | lua %{ 28 | return "Olá!" 29 | } 30 | ``` 31 | replaces your selections with `Olá!`. 32 | 33 | The module also exposes a `fennel` command with the same semantics. See section 34 | [Executing fennel code](https://github.com/gustavo-hms/luar#executing-fennel-code) for some examples. 35 | 36 | In the same vein, if you have, say, three selections, the code: 37 | 38 | ```lua 39 | lua %{ 40 | return 17, 19, 23 41 | } 42 | ``` 43 | 44 | replaces each selection with `17`, `19` and `23` respectivelly. The same can be achieved by returning a single table with three elements: 45 | 46 | 47 | ```lua 48 | lua %{ 49 | return {17, 19, 23} 50 | } 51 | ``` 52 | 53 | The two forms are equivalent. 54 | 55 | If, on the other hand, you return nothing, the content of the selections won't be modified: 56 | 57 | ```lua 58 | lua %{ 59 | if true then 60 | return 61 | end 62 | } 63 | ``` 64 | 65 | The anonymous function can take arguments by passing values before the `%{}` block: 66 | 67 | ```lua 68 | lua 17 19 %{ 69 | return arg[1] + arg[2] 70 | } 71 | ``` 72 | 73 | The above code will replace all the selections with `36`. As you can see, the arguments can be accessed with the `arg` table. 74 | 75 | As a convenience, you can use the provided `args` function to name your arguments: 76 | 77 | ```lua 78 | lua 17 19 %{ 79 | local first, second = args() 80 | return second - first 81 | } 82 | ``` 83 | 84 | Since Kakoune does not process expansions inside these `lua %{}` blocks, you need to pass expansions as arguments if you need to inspect Kakoune's state: 85 | 86 | ```lua 87 | lua %val{client} %{ 88 | local client = args() 89 | kak.echo(string.format("I'm client “%s”", client)) 90 | } 91 | ``` 92 | 93 | ## Calling commands 94 | 95 | You can run all commands defined in Kakoune (including third party ones) from `lua` code using the provided `kak` module: 96 | 97 | ```kak 98 | define-command custom-echo -params 1.. %{ 99 | echo %arg{@} 100 | } 101 | 102 | lua %{ 103 | kak.set_register("/", "Search this!") 104 | kak.execute_keys('%scSearch that!') 105 | -- Calling custom commands is also possible. 106 | kak.custom_echo("Text selected!") 107 | } 108 | ``` 109 | As you can see, hyphens are replaced by underscores in command names. Luar also takes care of quoting the arguments to avoid unexpected results. 110 | 111 | ## Executing fennel code 112 | 113 | Anything you can do with the `lua` command you can do with the equivalent `fennel` command. So, to replace your selections with the string `"Olá!"`: 114 | ```fennel 115 | fennel %{ 116 | "Olá!" 117 | } 118 | ``` 119 | Or, to replace three selections with some numbers: 120 | ```fennel 121 | fennel %{ 122 | (values 17 19 23) 123 | } 124 | ``` 125 | 126 | Expansions work the same way: 127 | 128 | ```fennel 129 | fennel %val{client} %{ 130 | (let [client (args)] 131 | (kak.echo (string.format "I'm client “%s”" client))) 132 | } 133 | ``` 134 | 135 | The only difference is that you don't need to replace hyphens with underscores in command names: 136 | 137 | ```kak 138 | define-command custom-echo -params 1.. %{ 139 | echo %arg{@} 140 | } 141 | 142 | fennel %{ 143 | (kak.set-register "/" "Search this!") 144 | (kak.execute-keys "%scSearch that!") 145 | (kak.custom-echo "Text selected!") 146 | } 147 | ``` 148 | 149 | ## External modules 150 | 151 | Since Lua modules are just plain tables and `require` is just a simple function, you can import modules everywhere in your program, not just at the beginning of a file. In particular, you can import external modules inside the `:lua` command. For instance, if you need to parse the contents of a file, you can use the elegant [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg/) library: 152 | 153 | ```lua 154 | lua %val{buffile} %{ 155 | local lpeg = require "lpeg" 156 | 157 | local function parse(file) 158 | -- do the lpeg's magic here 159 | end 160 | 161 | local tree = parse(arg[1]) 162 | -- ... 163 | } 164 | ``` 165 | 166 | You can also use this functionality to split your plugin into separate modules 167 | and use `:lua` to glue them together. To make that easier, `luar` provides the 168 | `addpackagepath` convenience function. It configures the lua interpreter to 169 | search for lua modules in the provided directory. It's meant to be used like 170 | this: 171 | 172 | ```kak 173 | declare-option -hidden str my_plugin_path %sh{ dirname $kak_source } 174 | 175 | define-command my-command %{ 176 | lua %opt{my_plugin_path} %{ 177 | addpackagepath(arg[1]) 178 | local module = require "my_local_module" 179 | -- ... 180 | } 181 | } 182 | ``` 183 | 184 | ## Debugging 185 | 186 | Passing the `-debug` flag, the `luar` command will print in the `*debug*` buffer all Kakoune commands it would otherwise execute. This way, you can see the exact commands your script would execute. For instance, running 187 | 188 | ```kak 189 | lua -debug %val{client} %{ 190 | local keys = "%ssomethingcanything" 191 | kak.execute_keys(keys) 192 | kak.echo("Word something replaced by word anything on client " .. arg[1]) 193 | kak.write() 194 | } 195 | ``` 196 | would print the following text in the debug buffer: 197 | ``` 198 | luar: execute-keys %☾%ssomethingcanything☾ 199 | luar: echo %☾Word something replaced by word anything on client client0☾ 200 | luar: write 201 | ``` 202 | 203 | ## Some examples 204 | The following examples are for didactic purposes. There are other ways to achieve the same results. 205 | 206 | Suppose you want to execute `ctags-update-tags` whenever you write to a file, but only if there's already a `tags` file in the current directory. Using `:lua` you can write the following lines to your `kakrc`: 207 | 208 | ```lua 209 | hook global BufWritePost .* %{ 210 | lua %{ 211 | if io.open("tags") then kak.ctags_update_tags() end 212 | } 213 | } 214 | ``` 215 | 216 | Now suppose you want to define a mapping to toggle the highlight of search patterns in the current window when you press `F2`. To achieve that, you can do something like this: 217 | 218 | ```lua 219 | declare-option -hidden bool highlight_search_on false 220 | 221 | define-command highlight-search-toggle %{ 222 | lua %opt{highlight_search_on} %{ 223 | local is_on = args() 224 | 225 | if is_on then 226 | kak.remove_highlighter("window/highlight-search") 227 | else 228 | kak.add_highlighter("window/highlight-search", "dynregex", "%reg{/}", "0:default,+ub") 229 | end 230 | 231 | kak.set_option("window", "highlight_search_on", not is_on) 232 | } 233 | } 234 | 235 | map global normal ': highlight-search-toggle' 236 | ``` 237 | 238 | You can find more examples [searching Github by topic](https://github.com/search?q=topic%3Akakoune+topic%3Aplugin+topic%3Alua). 239 | 240 | ## Installation 241 | 242 | You must have a `lua` interpreter installed on your system. Then you can add the following line to your `kakrc` (supposing you use [plug.kak](https://github.com/robertmeta/plug.kak)): 243 | 244 | ```kak 245 | plug "gustavo-hms/luar" %{ 246 | require-module luar 247 | } 248 | ``` 249 | 250 | ## Configuration 251 | 252 | You can also change the Lua interpreter used by this plugin by changing the `luar_interpreter` option, e.g.: 253 | 254 | ```kak 255 | # use luajit to run all Lua snippets 256 | set-option global luar_interpreter luajit 257 | ``` 258 | -------------------------------------------------------------------------------- /luar.asciidoc: -------------------------------------------------------------------------------- 1 | == Luar 2 | 3 | Luar is a minimalist plugin to script Kakoune using either Lua or Fennel. It’s 4 | not designed to expose Kakoune’s internals like Vis or Neovim do. Instead, it’s 5 | conceived with Kakoune’s extension model in mind. It does so by defining a sole 6 | `lua` command which can execute whatever string is passed to it in an external 7 | `lua` interpreter, and an equivalent `fennel` command which executes whatever 8 | string is passed to it in an external `fennel` interpreter. By doing so, it can 9 | act as a complement for the `%sh{}` expansion when you need to run some logic 10 | inside Kakoune. 11 | 12 | === Usage 13 | 14 | First of all, require the provided module: 15 | 16 | ---- 17 | require-module luar 18 | ---- 19 | 20 | The `luar` module exports a `lua` command, which executes the code 21 | passed to it in an external `lua` interpreter. The code is interpreted 22 | as the body of an anonymous function, and whatever this anonymous 23 | function returns replaces the current selections. 24 | 25 | So, the following code: 26 | 27 | ---- 28 | lua %{ 29 | return "Olá!" 30 | } 31 | ---- 32 | 33 | replaces your selections with `Olá!`. 34 | 35 | The module also exposes a `fennel` command with the same semantics. See section 36 | _Executing fennel code_ for some examples. 37 | 38 | In the same vein, if you have, say, three selections, the code: 39 | 40 | ---- 41 | lua %{ 42 | return 17, 19, 23 43 | } 44 | ---- 45 | 46 | replaces each selection with `17`, `19` and `23` respectivelly. 47 | The same can be achieved by returning a single table with three 48 | elements: 49 | 50 | ---- 51 | lua %{ 52 | return {17, 19, 23} 53 | } 54 | ---- 55 | 56 | The two forms are equivalent. 57 | 58 | If, on the other hand, you return nothing, the content of the selections 59 | won’t be modified: 60 | 61 | ---- 62 | lua %{ 63 | if true then 64 | return 65 | end 66 | } 67 | ---- 68 | 69 | The anonymous function can take arguments by passing values before the 70 | `%{}` block: 71 | 72 | ---- 73 | lua 17 19 %{ 74 | return arg[1] + arg[2] 75 | } 76 | ---- 77 | 78 | The above code will replace all the selections with `36`. As you can 79 | see, the arguments can be accessed with the `arg` table. 80 | 81 | As a convenience, you can use the provided `args` function to name 82 | your arguments: 83 | 84 | ---- 85 | lua 17 19 %{ 86 | local first, second = args() 87 | return second - first 88 | } 89 | ---- 90 | 91 | Since Kakoune does not process expansions inside these `lua %{}` 92 | blocks, you need to pass expansions as arguments if you need to inspect 93 | Kakoune’s state: 94 | 95 | ---- 96 | lua %val{client} %{ 97 | local client = args() 98 | kak.echo(string.format("I'm client “%s”", client)) 99 | } 100 | ---- 101 | 102 | === Calling custom commands 103 | 104 | You can run all commands defined in Kakoune (including third 105 | party ones) from `lua` code using the provided `kak` module: 106 | 107 | ---- 108 | define-command custom-echo -params 1.. %{ 109 | echo %arg{@} 110 | } 111 | 112 | lua %{ 113 | kak.set_register("/", "Search this!") 114 | kak.execute_keys('%scSearch that!') 115 | -- Calling custom commands is also possible. 116 | kak.custom_echo("Text selected!") 117 | } 118 | ---- 119 | 120 | As you can see, hyphens are replaced by underscores in command names. 121 | Luar also takes care of quoting the arguments to avoid unexpected 122 | results. 123 | 124 | === Executing fennel code 125 | 126 | Anything you can do with the `lua` command you can do with the 127 | equivalent `fennel` command. So, to replace your selections with the 128 | string `"Olá!"`: 129 | 130 | ---- 131 | fennel %{ 132 | "Olá!" 133 | } 134 | ---- 135 | 136 | Or, to replace three selections with some numbers: 137 | 138 | ---- 139 | fennel %{ 140 | (values 17 19 23) 141 | } 142 | ---- 143 | 144 | Expansions work the same way: 145 | 146 | ---- 147 | fennel %val{client} %{ 148 | (let [client (args)] 149 | (kak.echo (string.format "I'm client “%s”" client))) 150 | } 151 | ---- 152 | 153 | The only difference is that you don’t need to replace hyphens with 154 | underscores in command names: 155 | 156 | ---- 157 | define-command custom-echo -params 1.. %{ 158 | echo %arg{@} 159 | } 160 | 161 | fennel %{ 162 | (kak.set-register "/" "Search this!") 163 | (kak.execute-keys "%scSearch that!") 164 | (kak.custom-echo "Text selected!") 165 | } 166 | ---- 167 | 168 | === External modules 169 | 170 | Since Lua modules are just plain tables and `require` is just a simple 171 | function, you can import modules everywhere in your program, not just at 172 | the beginning of a file. In particular, you can import external modules 173 | inside the `:lua` command. For instance, if you need to parse the 174 | contents of a file, you can use the elegant [LPeg] library: 175 | 176 | ---- 177 | lua %val{buffile} %{ 178 | local lpeg = require "lpeg" 179 | 180 | local function parse(file) 181 | -- do the lpeg's magic here 182 | end 183 | 184 | local tree = parse(arg[1]) 185 | -- ... 186 | } 187 | ---- 188 | 189 | You can also use this functionality to split your plugin into separate 190 | modules and use `:lua` to glue them together. To make that easier, 191 | `luar` provides the `addpackagepath` convenience function. It 192 | configures the lua interpreter to search for lua modules in the provided 193 | directory. It’s meant to be used like this: 194 | 195 | ---- 196 | declare-option -hidden str my_plugin_path %sh{ dirname $kak_source } 197 | 198 | define-command my-command %{ 199 | lua %opt{my_plugin_path} %{ 200 | addpackagepath(arg[1]) 201 | local module = require "my_local_module" 202 | -- ... 203 | } 204 | } 205 | ---- 206 | 207 | === Debugging 208 | 209 | Passing the `-debug` flag, the `luar` command will print in the 210 | `*debug*` buffer all Kakoune commands it would otherwise execute. This 211 | way, you can see the exact commands your script would execute. For 212 | instance, running 213 | 214 | ---- 215 | lua -debug %val{client} %{ 216 | local keys = "%ssomethingcanything" 217 | kak.execute_keys(keys) 218 | kak.echo("Word something replaced by word anything on client " .. arg[1]) 219 | kak.write() 220 | } 221 | ---- 222 | 223 | would print the following text in the debug buffer: 224 | 225 | ---- 226 | luar: execute-keys %☾%ssomethingcanything☾ 227 | luar: echo %☾Word something replaced by word anything on client client0☾ 228 | luar: write 229 | ---- 230 | 231 | === Some examples 232 | 233 | The following examples are for didactic purposes. There are other ways 234 | to achieve the same results. 235 | 236 | Suppose you want to execute `ctags-update-tags` whenever you write to 237 | a file, but only if there’s already a `tags` file in the current 238 | directory. Using `:lua` you can write the following lines to your 239 | `kakrc`: 240 | 241 | ---- 242 | hook global BufWritePost .* %{ 243 | lua %{ 244 | if io.open("tags") then kak.ctags_update_tags() end 245 | } 246 | } 247 | ---- 248 | 249 | Now suppose you want to define a mapping to toggle the highlight of 250 | search patterns in the current window when you press `F2`. To achieve 251 | that, you can do something like this: 252 | 253 | ---- 254 | declare-option -hidden bool highlight_search_on false 255 | 256 | define-command highlight-search-toggle %{ 257 | lua %opt{highlight_search_on} %{ 258 | local is_on = args() 259 | 260 | if is_on then 261 | kak.remove_highlighter("window/highlight-search") 262 | else 263 | kak.add_highlighter("window/highlight-search", "dynregex", "%reg{/}", "0:default,+ub") 264 | end 265 | 266 | kak.set_option("window", "highlight_search_on", not is_on) 267 | } 268 | } 269 | 270 | map global normal ': highlight-search-toggle' 271 | ---- 272 | 273 | === Configuration 274 | 275 | You can also change the Lua interpreter used by this plugin by changing 276 | the `luar_interpreter` option, e.g.: 277 | 278 | ---- 279 | # use luajit to run all Lua snippets 280 | set-option global luar_interpreter luajit 281 | ---- 282 | -------------------------------------------------------------------------------- /luar.fnl: -------------------------------------------------------------------------------- 1 | (local fennel (require :fennel)) 2 | (local luar (require :luarmodule)) 3 | 4 | (set _G.kak luar.kak) 5 | (set _G.args luar.args) 6 | (set _G.addpackagepath luar.addpackagepath) 7 | 8 | (fn eval [chunk] 9 | (fennel.eval chunk {:env _G :filename "fennel" :unfriendly true})) 10 | 11 | (fn abort [_ chunk err] 12 | (let [message "error while executing fennel block:\n\nfennel %%{%s}\n\n%s\n"] 13 | (luar.debug (message:format chunk err)) 14 | (print "fail \"'fennel': check *debug* buffer\"") 15 | (os.exit 1))) 16 | 17 | (luar.execute eval abort) 18 | -------------------------------------------------------------------------------- /luar.kak: -------------------------------------------------------------------------------- 1 | declare-option -hidden str luar_path %sh{ dirname "$kak_source" } 2 | 3 | declare-option -docstring "The Lua interpreter used to execute Lua code" str luar_interpreter lua 4 | 5 | provide-module luar %# 6 | define-command lua -params 1.. -docstring %{ 7 | lua [] [args...] code: Execute provided Lua code as an anonymous function whose arguments are the args list. 8 | Switches: 9 | -debug Print Kakoune commands to *debug* buffer instead of executing them. 10 | } %{ 11 | evaluate-commands %sh{ 12 | export LUA_PATH="$kak_opt_luar_path/?.lua;$LUA_PATH" 13 | exec "$kak_opt_luar_interpreter" "$kak_opt_luar_path/luar.lua" "$@" 14 | } 15 | } 16 | 17 | define-command fennel -params 1.. -docstring %{ 18 | fennel [] [args...] code: Execute provided Fennel code as an anonymous function whose arguments are the args list. 19 | Switches: 20 | -debug Print Kakoune commands to *debug* buffer instead of executing them. 21 | } %{ 22 | evaluate-commands %sh{ 23 | export LUA_PATH="$kak_opt_luar_path/?.lua;$LUA_PATH" 24 | 25 | if [ $kak_opt_luar_interpreter != "lua" ]; then 26 | exec fennel --lua "$kak_opt_luar_interpreter" "$kak_opt_luar_path/luar.fnl" "$@" 27 | else 28 | exec fennel "$kak_opt_luar_path/luar.fnl" "$@" 29 | fi 30 | } 31 | } 32 | 33 | require-module kak 34 | require-module lua 35 | require-module fennel 36 | 37 | add-highlighter shared/kakrc/code/lua regex (?:\s|\A)\Klua(?:(?=\s)|\z) 0:keyword 38 | add-highlighter shared/kakrc/lua1 region -recurse '\{' '(^|\h)lua([\s{}\w%/$-|''"])* %\{\K' '\}' ref lua 39 | add-highlighter shared/kakrc/lua2 region -recurse '\(' '(^|\h)lua([\s{}\w%/$-|''"])* %\(\K' '\)' ref lua 40 | add-highlighter shared/kakrc/lua3 region -recurse '\[' '(^|\h)lua([\s{}\w%/$-|''"])* %\[\K' '\]' ref lua 41 | add-highlighter shared/kakrc/lua4 region -recurse '<' '(^|\h)lua([\s{}\w%/$-|''"])* %<\K' '>' ref lua 42 | 43 | add-highlighter shared/kakrc/code/fennel regex (?:\s|\A)\Kfennel(?:(?=\s)|\z) 0:keyword 44 | add-highlighter shared/kakrc/fennel1 region -recurse '\{' '(^|\h)fennel([\s{}\w%/$-|''"])* %\{\K' '\}' ref fennel 45 | add-highlighter shared/kakrc/fennel2 region -recurse '\(' '(^|\h)fennel([\s{}\w%/$-|''"])* %\(\K' '\)' ref fennel 46 | add-highlighter shared/kakrc/fennel3 region -recurse '\[' '(^|\h)fennel([\s{}\w%/$-|''"])* %\[\K' '\]' ref fennel 47 | add-highlighter shared/kakrc/fennel4 region -recurse '<' '(^|\h)fennel([\s{}\w%/$-|''"])* %<\K' '>' ref fennel 48 | # 49 | -------------------------------------------------------------------------------- /luar.lua: -------------------------------------------------------------------------------- 1 | local luar = require 'luarmodule' 2 | 3 | args, kak, addpackagepath = luar.args, luar.kak, luar.addpackagepath 4 | 5 | local function abort(action, chunk, err) 6 | err = err:match('%[string "luar"%]:(.+)') or err 7 | local message = "error while %s lua block:\n\nlua %%{%s}\n\nlua:%s\n" 8 | luar.debug(message:format(action, chunk, err)) 9 | print([[fail "'lua': check *debug* buffer"]]) 10 | os.exit(1) 11 | end 12 | 13 | local function eval(chunk) 14 | local fn, err = load(chunk, "luar") 15 | if not fn then abort("parsing", chunk, err) end 16 | return fn() 17 | end 18 | 19 | luar.execute(eval, abort) 20 | -------------------------------------------------------------------------------- /luarmodule.lua: -------------------------------------------------------------------------------- 1 | local function debug(text) 2 | local first = true 3 | 4 | if not text:find("\n") then 5 | -- We need to insert a newline here to be able to use pattern bellow 6 | -- instead of just using `[^\n]*`. The later pattern can't be use because 7 | -- otherwise luajit would insert random newlines at the end of the match. 8 | text = text .. "\n" 9 | end 10 | 11 | for line in text:gmatch('([^\n]*)\n') do 12 | if first then 13 | print(string.format([[echo -debug %%☽luar: %s☽]], line)) 14 | first = false 15 | else 16 | print(string.format([[echo -debug %%☽ %s☽]], line)) 17 | end 18 | end 19 | end 20 | 21 | local function quote(words) 22 | for i, v in ipairs(words) do 23 | words[i] = string.format("%%☾%s☾", v) 24 | end 25 | 26 | return table.concat(words, " ") 27 | end 28 | 29 | local function args() 30 | local unpack = unpack or table.unpack 31 | return unpack(arg) 32 | end 33 | 34 | local function addpackagepath(path) 35 | package.path = string.format("%s/?.lua;%s", path, package.path) 36 | end 37 | 38 | local write = print 39 | 40 | local kak = setmetatable({}, { 41 | __index = function(t, command) 42 | local name = command:gsub("_", "-") 43 | t[command] = function(...) 44 | write(name .. " " .. quote { ... }) 45 | end 46 | 47 | return t[command] 48 | end 49 | }) 50 | 51 | local function parseargs() 52 | local chunk = arg[#arg] 53 | arg[0], arg[#arg] = nil, nil -- Hide file name and chunk 54 | local debug_flag 55 | 56 | for i, v in ipairs(arg) do 57 | if v == "-debug" then 58 | write = debug 59 | debug_flag = i 60 | elseif v == "true" then 61 | arg[i] = true 62 | elseif v == "false" then 63 | arg[i] = false 64 | else 65 | arg[i] = tonumber(v) or v 66 | end 67 | end 68 | 69 | if debug_flag then 70 | table.remove(arg, debug_flag) 71 | end 72 | 73 | return chunk 74 | end 75 | 76 | local function execute(fn, abort) 77 | local chunk = parseargs() 78 | local results = { pcall(fn, chunk) } 79 | if not results[1] then abort("executing", chunk, results[2]) end 80 | 81 | if #results > 1 then 82 | table.remove(results, 1) 83 | -- Allow returning either many values or a single table 84 | if type(results[1]) == "table" then results = results[1] end 85 | 86 | local command = string.format([[ 87 | evaluate-commands -save-regs dquote %%{ 88 | set-register dquote %s 89 | execute-keys R 90 | } 91 | ]], quote(results)) 92 | print(command) 93 | end 94 | end 95 | 96 | return { 97 | args = args, 98 | addpackagepath = addpackagepath, 99 | kak = kak, 100 | execute = execute, 101 | debug = debug, 102 | } 103 | --------------------------------------------------------------------------------