occivink / kakoune-snippets

Snippet support for kakoune
The Unlicense
46 stars 6 forks source link

Implement auto-discard of snippets default text #28

Closed occivink closed 1 year ago

occivink commented 5 years ago

Should fix #8

For lack of a better idea, it's now implemented directly in the script. The auto-discard is removed as soon as insert mode is exited, or the selections were moved.

andreyorst commented 5 years ago

Doesn't work for ${0:text} placeholders.

Other than that works great!

occivink commented 5 years ago

Good catch, fixed

andreyorst commented 5 years ago

Seems that first input is duplicated when selection is single char:

image

Here I've pressed v once when first placeholder was selected. Snippet definition:

for (int ${1:i} = 0; $1 ${2:< 100}; $1${3:++}) {
    $0
}
andreyorst commented 5 years ago

It's more interesting than I've initially thought. Look at the following snippet:

${1:1} ${2:2} ${3:3} ${4:4} ${5:5} ${6:6} ${7:7} ${8:8} ${9:9}

If I jumpt towards the 9th placeholder without changing anything, and press a key it will insert:

1 2 3 4 5 6 7 8 aaaaaaaaaa

but if I change each time I jump, it will always insert two letters:

aa aa aa aa aa aa aa aa aa

It also eats text forward, so if I jump without changing to the 8th placeholder and insert a, ninth placeholder will be gone:

1 2 3 4 5 6 7 aaaaaaaaa
occivink commented 5 years ago

Right, that's a bit annoying. It happens because in one case the inserted letter is selected (for example if you're in append mode) and so exec <a-;>d will remove it, but if it's in regular insert mode then the inserted letter is not selected and we don't need to remove it. I think it's possible to detect in which mode we are when jumping to the placeholders but that's not super trivial. Thanks for testing

andreyorst commented 5 years ago

Thanks for testing

it's like top priority feature for my workflow, so really big thanks to you for working on this.

andreyorst commented 5 years ago

I think it's possible to detect in which mode we are when jumping to the placeholders but that's not super trivial.

I've thought that introduction of has_default will mean that hook should be equal for single and multi-char selections and declared on successful jump by checking this parameter. Since you select whole text anyway, and your cursor stays inside word I don't see the difference between:

[i]

image

and

[insert]

image image

by means of insert or append mode. Because in append mode both selections would looks like so:

[a ]

image

[ppend ]

image

Which requires different hooks indeed. So I mean selections always in insert mode state, despite the fact that cursor is placed on the word's end. Isn't it?

andreyorst commented 5 years ago

I've managed to make it work with this hook:

hook window -group snippets-auto-discard InsertChar .* %sh{
    cursor=${kak_cursor_column}
    selection=${kak_selection_desc}
    # let's get cursor positions from selection description
    selection1=${selection##*,}; selection1=${selection1##*.}
    selection2=${selection%%,*}; selection2=${selection2##*.}
    # let's find which actually is a cursor
    selection1=${selection1##$cursor*}
    selection2=${selection2##$cursor*}
    if [ -z "${selection2}" ] && [ -z "${selection1}" ]; then
        anchor=$cursor
    elif [ -z "${selection2}" ]; then
        anchor=${selection1}
    else
        anchor=${selection2}
    fi
    if [ "$anchor" != "$cursor" ]; then
        printf "%s\n" 'remove-hooks window snippets-auto-discard; exec -draft "c%val{hook_param}"'
    else
        printf "%s\n" 'remove-hooks window snippets-auto-discard; exec -draft "c"'
    fi
}

I think it's overengineered, but it works.

andreyorst commented 5 years ago

probably we could slim it down to

hook window -group snippets-auto-discard InsertChar .* %sh{
    if [ $(printf "%s\n" ${kak_selection} | wc -m) -eq 2 ]; then
        printf "%s\n" 'remove-hooks window snippets-auto-discard; exec -draft "c"'
    else
        printf "%s\n" 'remove-hooks window snippets-auto-discard; exec -draft "c%val{hook_param}"'
    fi
}

Since selection will be always with cursor >= anchor, and mirrored selections are always same size we only need to check if single selection is more then 1 char or not. Probably.

I've tested it with this snippet: ${1:a} ${2:bb} ${3:я} ेे${4:न} ${5:ने} ${6:नमस्ते}. No issues so far.

From 1fefa41c4947ed745547081f315bb8ef05a02d85 Mon Sep 17 00:00:00 2001
From: Andrey Orst <andreyorst@gmail.com>
Date: Tue, 7 May 2019 18:39:27 +0300
Subject: [PATCH] fix autodiscard

---
 snippets.kak | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/snippets.kak b/snippets.kak
index 09657e7..877a316 100644
--- a/snippets.kak
+++ b/snippets.kak
@@ -331,7 +331,14 @@ print("\n");

 def -hidden snippets-setup-auto-discard %{
     remove-hooks window snippets-auto-discard
-    hook window -group snippets-auto-discard InsertChar .* %{ remove-hooks window snippets-auto-discard; exec "<a-;>d%val{hook_param}" }
+
+    hook window -group snippets-auto-discard InsertChar .* %sh{
+        if [ $(printf "%s\n" ${kak_selection} | wc -m) -eq 2 ]; then
+            printf "%s\n" 'remove-hooks window snippets-auto-discard; exec -draft "c"'
+        else
+            printf "%s\n" 'remove-hooks window snippets-auto-discard; exec -draft "c%val{hook_param}"'
+        fi
+    }
     hook window -group snippets-auto-discard InsertEnd .* %{ remove-hooks window snippets-auto-discard }
     hook window -group snippets-auto-discard InsertMove .* %{ remove-hooks window snippets-auto-discard }
 }
-- 
2.21.0
andreyorst commented 5 years ago

@occivink

I've thought about one aspect of allowing user to visually distinguish if default text is going to be discarded or not. But this will require modifications on how you select things in different modes. Here's a thing.

I'm using Emacs's plugin called yasnippet which is a best template system for Emacs, and it is a great snippet engine whatsoever. The thing is, that when I expand the snippet in it, it highlights current placeholder. It isn't selection really, but a highlighting, and text is going to be inserted once i move or replaced if I actually press any key other than movement. Here, I've expanded for snippet for Rust language:

image

You can see that the cursor (represented by |) is placed at the beginning of iterable placeholder. That clearly means that we're going to replace it. This is exactly how it would look in Kakoune if we select something and go to insert mode. The cursor is placed at the beginning of selection, and on pressing a key we replacing text.

image

This way, I believe, we don't need two different hooks for different default text sizes.

If the automatic discard is turned off, I think that your plugin should select default text, but place cursor on the 1 character beyond the placeholder default text, as if we entered append mode. And if user decides to go to normal mode, cursor should go 1 character back to allow user modify the selection, as if user used real selections and append mode. It's difficult, because your script would need to act differently if snippet was expanded in normal mode, since it doesn't change mode to normal one. So if user expand snippet in insert mode it shoul look like this

image

And if user expands it in normal mode, it shoul look like this:

image

Which is how plugin currently works.


So what I'm trying to say can be summarized to:

If discard default is turned on:

If discard default is turned off:

Why this is important? In current state with discard default is turned off, the cursor is placed on the last character of the selection. This means if we start typing directly, text will be inserted before the last character of current selection which isn't really useful.

image

By using appropriate append or insert modes we showing the user what's plugin is going to do.

andreyorst commented 5 years ago

seems like 78852bf91b23005c57cfe6febff9c04f3acf2bc1 doesn't affect zero placeholders

benburk commented 4 years ago

Is this feature now working? Can it be merged to master?

occivink commented 4 years ago

Well you can use the branch if you want to, but the issues reported by @andreyorst have not been addressed so you might run into them. As you can see I haven't done much on this plugin in a while. It's a combination of me not being satisfied with the complexity and not being able to integrate well the plugin into my workflow, such that I'm not using at the moment. So I don't have much motivation to work on this and sort out all this complexity.