# Filter your selections more easily

**URL:** <https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296>\
**Category:** Recipes & Guides\
**Created:** [October 9, 2020, 12:36pm UTC](https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296 "2020-10-09T12:36:20Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![scr](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/scr/32/486_2.png) [@scr](https://discuss.kakoune.com/u/scr)\
**Post date:** [October 9, 2020, 12:36pm UTC](https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296/1 "2020-10-09T12:36:20Z")

</div>

Inspired by @gustavo-hms’s post [Array/iterator and looping behaviour in kak script](https://discuss.kakoune.com/t/array-iterator-and-looping-behaviour-in-kak-script/1287/12)

The following snippets is build on top of kakoune’s `$` command.  
It helps you filter your selections more conveniently by using Boolean expression from more expressive language ( `lua` and `python` supported for the moment)

## Example:

Lets say you have the following text:

`Só outro silêncio. O senhor sabe o que o silêncio é? É a gente mesmo, demais`

And you select all the word with `S<space><ret>`  
You should have:

`[Só] [outro] [silêncio]. [O] [senhor] [sabe] [o] [que] [o] [silêncio] [é]? [É] [a] [gente] [mesmo], [demais].`

Now let’s keep only the selections of word longer than 5 character, using our plugin and a simple python expression. `sel` is the default name of the variable holding the selection:

`$ kak_filter.py "len(sel) > 5"`

You should have:

`Só [outro] [silêncio.] O [senhor] [sabe] o que o [silêncio] é? É a [gente] [mesmo,] [demais]`

You could also use more complex expression:

`$ kak_filter.py "len(sel) > 3 and 'o' in sel"`

To get:

`Só [outro] [silêncio.] O [senhor] sabe o que o [silêncio] é? É a gente [mesmo,] demais `

## Install

Paste one of the following into an executable file in a directory within your `$PATH`:

### python script

_kak\_filter.py_

```python
#! /usr/bin/env python3.7

from sys import argv

f = 'lambda sel: ' + argv[1]
filter = eval(f)

def main(input):
    if filter(input):
        exit(0)
    else:
        exit(1)

main(input())

```

### lua script

_kak\_filter.lua_

```auto
#! /usr/bin/env lua

f = 'function (sel) return ' .. arg[1] .. ' end'
func, err = load('return ' .. f)
ok, filter = pcall(func)
function main (input)
    if filter(input) then
        os.exit(0)
    else
        os.exit(1)
    end
end
main(io.read())

```

### mapping

I suggest to also put the following mapping inside your `kakrc`:

`map global normal <a-$> '$ kak_filter.py ""<left>'`

---

<div class="post-metadata">

**Author:** ![Delapouite](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/delapouite/32/10_2.png) [@Delapouite](https://discuss.kakoune.com/u/Delapouite)\
**Post date:** [October 9, 2020, 2:54pm UTC](https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296/2 "2020-10-09T14:54:12Z")

</div>

Thanks for these wrappers.

I’ve added the link to this discussion on a wiki page which was dealing with this filtering problematic but with a shell only approach: [https://github.com/mawww/kakoune/wiki/Selections#filtering-reducing-the-number-of-multiple-selections](https://github.com/mawww/kakoune/wiki/Selections#filtering-reducing-the-number-of-multiple-selections)

---

<div class="post-metadata">

**Author:** ![scr](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/scr/32/486_2.png) [@scr](https://discuss.kakoune.com/u/scr)\
**Post date:** [October 9, 2020, 8:19pm UTC](https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296/3 "2020-10-09T20:19:49Z")

</div>

Thx, I remember now that I pumped into this wiki page a while ago.

I tried to implement the selection based on index feature, but couldn’t grab the `kak_main_reg_hash` variable. I used  
`os.environ['kak_main_reg_hash']` but didn’t work. Any idea why ?

---

<div class="post-metadata">

**Author:** ![scr](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/scr/32/486_2.png) [@scr](https://discuss.kakoune.com/u/scr)\
**Post date:** [October 9, 2020, 8:30pm UTC](https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296/4 "2020-10-09T20:30:43Z")

</div>

OK. got it.

Apparently `$kak_main_reg_hash` is evaluated inside kakoune’s prompt. It doesn’t exist outside.

The solution:  
`$kak_filter.py "$kak_main_reg_hash % 2 == 0"`

---

<div class="post-metadata">

**Author:** ![Screwtapello](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/screwtapello/32/12_2.png) [@Screwtapello](https://discuss.kakoune.com/u/Screwtapello)\
**Post date:** [October 10, 2020, 12:15am UTC](https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296/5 "2020-10-10T00:15:12Z")

</div>

It’s a bit more subtle than that. Before Kakoune runs a shell command (such as a `%sh{}` block, or the `$` command, or the `|` command), it scans the text for `kak_*` tokens. If it sees any it recognises, it exports those values into the environment where they’ll be accessible to all downstream processes. If it doesn’t see any, it exports nothing.

Taking an example from `:doc expansions shell-expansions`, if you run this command:

```
echo %sh{ env | grep ^kak_ }

```

… you get no output, because Kakoune doesn’t put any kak\_\* variables into the environment. However, if you run this command:

```
echo %sh{ env | grep ^kak_ # kak_session }

```

…then the session ID gets printed, because the shell block contains `kak_session`, even though it’s just in a comment.

---

<div class="post-metadata">

**Author:** ![scr](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/scr/32/486_2.png) [@scr](https://discuss.kakoune.com/u/scr)\
**Post date:** [October 10, 2020, 9:26am UTC](https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296/6 "2020-10-10T09:26:24Z")

</div>

Interesting. Is this for performance issue ? Why not exporting all the values ?

---

<div class="post-metadata">

**Author:** ![Screwtapello](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/screwtapello/32/12_2.png) [@Screwtapello](https://discuss.kakoune.com/u/Screwtapello)\
**Post date:** [October 10, 2020, 10:13am UTC](https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296/7 "2020-10-10T10:13:56Z")

</div>

I don’t know about performance _specifically_, but Kakoune has a _lot_ of options and values, and many of them can be expanded in different ways (`kak_*`, `kak_main_*`, `kak_quoted_*`) which I can imagine might add a lot of setup overhead for every shell process.

---

<div class="post-metadata">

**Author:** ![gustavo-hms](https://discuss.kakoune.com/user_avatar/discuss.kakoune.com/gustavo-hms/32/598_2.png) [@gustavo-hms](https://discuss.kakoune.com/u/gustavo-hms)\
**Post date:** [October 17, 2020, 12:34am UTC](https://discuss.kakoune.com/t/filter-your-selections-more-easily/1296/8 "2020-10-17T00:34:04Z")

</div>

Nice idea, @scr!

Let me share then I trick I’ve been using for mapping things with the `|` key. I made this script

```auto
#!/usr/bin/env lua
local command = arg[1]

local map = assert(load("return " .. command))

for line_ in io.lines() do
    _ = line_
	print((map()))
end

```

wrote it to a file called `map`, made it executable (`chmod +x map`) and put it on my PATH.

Then, from within Kakoune, I can change selections with, for instance, `| map 'tonumber(_) + 1'`. Here, `_` is the line coming from `stdin`.

Some things to note:

- it uses a loop to iterate over the lines. Although it’s not really necessary with the `|` key, it’s useful to run simple one line scripts from a pipe on a terminal:

```auto
cat "music.txt" | map '_:find("Forró") and "Found!" or "Not found!"'

```

- since it iterates over the lines, multi line operations aren’t possible. But it’s easy to extend the plugin to handle them, since Lua [patterns](https://www.lua.org/manual/5.4/manual.html#6.4.1) (the equivalent of regular expressions in Lua’s standard library) work across lines by default.

And to make the script even more handy, I implemented a `split` function to split strings (since the `string` module don’t have such a function) and made some aliases for useful functions:

```auto
#!/usr/bin/lua

concat = table.concat
sort = table.sort
format = string.format
unpack = table.unpack

function split(s, boundary)
    local t = {}
    local pattern = format("[^%s]+", boundary)
    for elem in s:gmatch(pattern) do
        t[#t+1] = elem
    end
    return unpack(t)
end

local command = arg[1]

local map = assert(load("return " .. command))

for line_ in io.lines() do
    _ = line_
	print((map()))
end

```
