Skip to content

Add feedkeys to more closely simulate user input.#12

Merged
mudge merged 2 commits intomasterfrom
feedkeys
Feb 17, 2013
Merged

Add feedkeys to more closely simulate user input.#12
mudge merged 2 commits intomasterfrom
feedkeys

Conversation

@mudge
Copy link
Collaborator

@mudge mudge commented Feb 17, 2013

The current type method uses --remote-send which does not respect
mappings. As a workaround, allow access to feedkeys() which does respect
mappings as if typed by a user.

@AndrewRadev
Copy link
Owner

Looks like a pretty good idea. I've added one more commit, since I noticed a problem with quotes, but feel free to tweak that as you see fit and merge whenever. 👍

To sidetrack a bit, I'm fairly unhappy with the current state of type and normal, since I've gotten pretty confused whenever I've tried to use them for something more complicated. I'm thinking of making an interface closer to Vim's, something like:

def normal(stuff)
  command 'normal #{stuff}'
end

def normal!(stuff)
  command 'normal! #{stuff}'
end

def type(keys)
  # use feedkeys()
end

def insert(keys)
  # use feedkeys('i'...)
end

def append(keys)
  # use feedkeys('a'...)
end

Maybe for starters, I can just re-implement type and insert in terms of your feedkeys(), since that seems much closer to what I'd expect from "type". Afterwards, I can think of expanding the interface.

I've also been doing a lot of raw command, I can't help but think it might be better to do some method_missing trickery instead. Something like:

vim.command  #=> CommandProxy object
vim.function #=> FunctionProxy object

vim.command.echo 'ok' #=> "ok"
vim.command.normal! 'ifoo\\<cr>' #=> ""

vim.function.feedkeys('abc')
vim.function.expand('%:p')

I'll think about this stuff some more, just wanted to give you an update of some recent raw ideas about expanding the gem.

@mudge
Copy link
Collaborator Author

mudge commented Feb 17, 2013

Thanks for the quote fix, I'm not particularly happy with how fragile that interface is regarding quoting (the evils of string interpolation, I suppose).

Re the interface: I had thought that my inability to use vim.command("feedkeys('\<C-R>')") (because it expects expressions to be editor commands, not functions) was something of a smell either in my expectations or in the library itself.

Perhaps we're building on the wrong foundation. What if we made remote_send and remote_expr the most basic public interface and then build all our abstractions on top of that (including those you detail above) instead of command?

I was definitely taken by surprise that --remote-send doesn't respect mappings but that's part of Vim itself that we could mask in our own abstraction by using feedkeys() throughout as you say.

As an aside, I'm going to present Vimrunner at Vim London on the 26th February, I'll let you know if we get any feedback.

mudge added a commit that referenced this pull request Feb 17, 2013
Add feedkeys to more closely simulate user input.
@mudge mudge merged commit c3c55cd into master Feb 17, 2013
@mudge mudge deleted the feedkeys branch February 17, 2013 14:14
@AndrewRadev
Copy link
Owner

Re the interface: I had thought that my inability to use
vim.command("feedkeys('')") (because it expects expressions to be
editor commands, not functions) was something of a smell either in my
expectations or in the library itself.

It's tricky. My first instinct was to use commands, since they are the core building block of Vimscript. Even functions are called by using a command (call), but on the other hand, you can't easily call commands through functions.

Regarding feedkeys(), I definitely had the idea of creating a call or function_call method that would encapsulate a function call, making it simpler to do this stuff without going through command, but I got paralyzed with indecision.

Let's say you want to call a function. The most obvious interface would be vim.call(:expand, '%') or even vim.expand('%'). Serializing a value from ruby to vimscript should really not be very difficult -- I'm almost sure vimscript objects use a JSON syntax. However, variables are going to be tricky.

vim.command("call fnamemodify(b:some_filename, ':h')")
vim.call(:fnamemodify, '?', ':h')

You can't just put a variable name there, since it'll be serialized as a string. You could do something like:

vim.call(:fnamemodify, lit('b:some_filename'), ':h')

And a lit method could just dump this literally in Vimscript, without quoting or escaping, solving the issue not only for variables, but also for any other tricky Vimscript stuff we might encounter (which we could later think about abstracting away in a different fashion). But that means a global lit method, which is a bad idea. We could do other things:

vim.call(:fnamemodify, 'b:some_filename'.lit, ':h')
# but this globally monkeypatches string :/

vim.call(:fnamemodify, 'b:some_filename'.vim_literal, ':h')
# and this monkeypatches string, but at least it's unlikely to collide with anything

vim.call(:fnamemodify, Vimrunner::Literal.new('b:some_filename'), ':h')
# much safer, but verbose

Alternatively, we could just not serialize things:

vim.call(:fnamemodify, %{b:some_filename, '%:h'})

But this doesn't really resolve the quoting issues, it just makes it a slightly better interface.

Perhaps we're building on the wrong foundation. What if we made remote_send
and remote_expr the most basic public interface and then build all our
abstractions on top of that (including those you detail above) instead of
command?

Sounds reasonable. remote_expr can be used for functions, but also generally for any kind of expression. remote_send (implemented through feedkeys maybe) would be used for mappings. And we could implement remote_command with remote_expr("VimrunnerEvaluateCommandOutput('#{command}')") or something like that. Though, this wouldn't help a lot with quoting, you'd still have to vim.remote_expr("feedkeys('\<C-R>')"). It's better than using command + call, though, which is something at least.

I was definitely taken by surprise that --remote-send doesn't respect mappings

Same here. But yeah, I guess it's a good idea to just use feedkeys() instead.

As an aside, I'm going to present Vimrunner at Vim London on the 26th February, I'll let you know if we get any feedback.

Yeah, I saw your tweet, hope it goes well :). Feedback would be great, especially on the public interface and on this topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants