vimrunner 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -2,8 +2,60 @@ Using Vim's client/server functionality, this library exposes a way to spawn a
2
2
  Vim instance and control it programatically. Apart from being a fun party
3
3
  trick, this could be used to do integration testing on vimscript.
4
4
 
5
+ This is still fairly experimental, so use with caution. Any issue reports or
6
+ contributions are very welcome on the
7
+ [github issue tracker](https://github.com/AndrewRadev/Vimrunner/issues)
8
+
9
+ ## Usage
10
+
11
+ There are two objects that can be used to control Vim, a "server" and a
12
+ "client". The server takes care of spawning a server vim instance that will be
13
+ controlled, and the client is its interface.
14
+
15
+ A server can be started in two ways:
16
+
17
+ - `Vimrunner::Server.start`: Starts a terminal instance. Since it's
18
+ "invisible", it's nice for use in automated tests. If it's impossible to
19
+ start a terminal instance due to missing requirements for the `vim` binary
20
+ (see "Requirements" below), a GUI instance will be started.
21
+ - `Vimrunner::Server.start(:gui => true)`: Starts a GUI instance of Vim. On
22
+ Linux, it'll be a `gvim`, on Mac it defaults to `mvim`.
23
+
24
+ Both methods return a `Server` instance. For a more comprehensive list of
25
+ options you can start the server with, check out
26
+ [the docs](http://rubydoc.info/gems/vimrunner/Vimrunner/Server).
27
+
28
+ To be able to send commands to the server, you need a client that knows some
29
+ details about it:
30
+
31
+ ``` ruby
32
+ server = Vimrunner::Server.start
33
+
34
+ client = Vimrunner::Client.new(server)
35
+ # or,
36
+ client = server.new_client
37
+ ```
38
+
39
+ There are also two convenience methods that start a server and return a
40
+ connected client -- `Vimrunner.start_vim` and `Vimrunner.start_gui_vim`.
41
+
42
+ For a full list of methods you can invoke on the remote vim instance, check out
43
+ the methods on the `Client` class in
44
+ [the docs](http://rubydoc.info/gems/vimrunner/Vimrunner/Client).
45
+
46
+ ## Requirements
47
+
48
+ Vim needs to be compiled with `+clientserver`. This should be available with
49
+ the `normal`, `big` and `huge` featuresets. The client/server functionality
50
+ (regrettably) needs a running X server to function, even for a terminal vim.
51
+ This means that if you're using it for automated tests on a remote server,
52
+ you'll probably need to start it with xvfb.
53
+
54
+ ## Experimenting
55
+
5
56
  The `vimrunner` executable opens up an irb session with `$vim` set to a running
6
- `gvim` instance. A few things you can try:
57
+ `gvim` (or `mvim`) client. You can use this for interactive experimentation. A
58
+ few things you can try:
7
59
 
8
60
  ``` ruby
9
61
  $vim.edit 'some_file_name' # edit a file
@@ -12,13 +64,3 @@ $vim.normal 'T,' # go back to the nearest comma
12
64
  $vim.type 'a<cr>' # append a newline after the comma
13
65
  $vim.write # write file to disk
14
66
  ```
15
-
16
- For more examples of what you can do, you could take a look at the specs, they
17
- should be fairly readable.
18
-
19
- Note that this should work on a Linux box, but probably won't on a Mac. I'm
20
- assuming you'd need to change the binary to `mvim` at the very least.
21
-
22
- This is still fairly experimental, so use with caution. Any issue reports or
23
- contributions are very welcome on the
24
- [github issue tracker](https://github.com/AndrewRadev/Vimrunner/issues)
data/bin/vimrunner CHANGED
@@ -5,6 +5,6 @@ $: << File.expand_path('../../lib', __FILE__)
5
5
  require 'irb'
6
6
  require 'vimrunner'
7
7
 
8
- $vim = Vimrunner::Runner.start_gvim
8
+ $vim = Vimrunner.start_gui_vim
9
9
 
10
10
  IRB.start
data/lib/vimrunner.rb CHANGED
@@ -1 +1,16 @@
1
- require 'vimrunner/runner'
1
+ require 'vimrunner/client'
2
+ require 'vimrunner/server'
3
+
4
+ module Vimrunner
5
+ # Starts a new Server with a terminal vim instance and returns a client,
6
+ # connected to it.
7
+ def self.start_vim
8
+ Client.new(Server.start)
9
+ end
10
+
11
+ # Starts a new Server with a GUI vim instance and returns a client, connected
12
+ # to it.
13
+ def self.start_gui_vim
14
+ Client.new(Server.start(:gui => true))
15
+ end
16
+ end
@@ -0,0 +1,109 @@
1
+ require 'vimrunner/shell'
2
+
3
+ module Vimrunner
4
+
5
+ # A Client is simply a proxy to a Vim server. It's initialized with a Server
6
+ # instance and sends commands, keys and signals to it.
7
+ class Client
8
+ def initialize(server)
9
+ @server = server
10
+ end
11
+
12
+ # Adds a plugin to Vim's runtime. Initially, Vim is started without
13
+ # sourcing any plugins to ensure a clean state. This method can be used to
14
+ # populate the instance's environment.
15
+ #
16
+ # dir - The base directory of the plugin, the one that contains
17
+ # its autoload, plugin, ftplugin, etc. directories.
18
+ # entry_script - The Vim script that's runtime'd to initialize the plugin.
19
+ # Optional.
20
+ #
21
+ # Example:
22
+ #
23
+ # vim.add_plugin 'rails', 'plugin/rails.vim'
24
+ #
25
+ def add_plugin(dir, entry_script = nil)
26
+ command("set runtimepath+=#{dir}")
27
+ command("runtime #{entry_script}") if entry_script
28
+ end
29
+
30
+ # Invokes one of the basic actions the Vim server supports, sending a key
31
+ # sequence. The keys are sent as-is, so it'd probably be better to use the
32
+ # wrapper methods, #normal, #insert and so on.
33
+ def type(keys)
34
+ invoke_vim '--remote-send', keys
35
+ end
36
+
37
+ # Executes the given command in the Vim instance and returns its output,
38
+ # stripping all surrounding whitespace.
39
+ def command(vim_command)
40
+ normal
41
+
42
+ expression = "VimrunnerEvaluateCommandOutput('#{vim_command.to_s}')"
43
+
44
+ invoke_vim('--remote-expr', expression).strip.tap do |output|
45
+ raise InvalidCommandError if output =~ /^Vim:E\d+:/
46
+ end
47
+ end
48
+
49
+ # Starts a search in Vim for the given text. The result is that the cursor
50
+ # is positioned on its first occurrence.
51
+ def search(text)
52
+ normal
53
+ type "/#{text}<cr>"
54
+ end
55
+
56
+ # Sets a setting in Vim. If +value+ is nil, the setting is considered to be
57
+ # a boolean.
58
+ #
59
+ # Examples:
60
+ #
61
+ # vim.set 'expandtab' # invokes ":set expandtab"
62
+ # vim.set 'tabstop', 3 # invokes ":set tabstop=3"
63
+ #
64
+ def set(setting, value = nil)
65
+ if value
66
+ command "set #{setting}=#{value}"
67
+ else
68
+ command "set #{setting}"
69
+ end
70
+ end
71
+
72
+ # Edits the file +filename+ with Vim.
73
+ #
74
+ # Note that this doesn't use the '--remote' Vim flag, it simply types in
75
+ # the command manually. This is necessary to avoid the Vim instance getting
76
+ # focus.
77
+ def edit(filename)
78
+ command "edit #{filename}"
79
+ end
80
+
81
+ # Writes the file being edited to disk. Note that you probably want to set
82
+ # the file's name first by using Runner#edit.
83
+ def write
84
+ command :write
85
+ end
86
+
87
+ # Switches Vim to insert mode and types in the given text.
88
+ def insert(text = '')
89
+ normal "i#{text}"
90
+ end
91
+
92
+ # Switches Vim to normal mode and types in the given keys.
93
+ def normal(keys = '')
94
+ type "<c-\\><c-n>#{keys}"
95
+ end
96
+
97
+ # Kills the server it's connected to.
98
+ def kill
99
+ @server.kill
100
+ end
101
+
102
+ private
103
+
104
+ def invoke_vim(*args)
105
+ args = [@server.vim_path, '--servername', @server.name, *args]
106
+ Shell.run *args
107
+ end
108
+ end
109
+ end
@@ -1,3 +1,9 @@
1
1
  module Vimrunner
2
2
  class InvalidCommandError < RuntimeError; end
3
+
4
+ class TimeoutError < RuntimeError
5
+ def message
6
+ "Timed out while waiting for serverlist. Is an X11 server running?"
7
+ end
8
+ end
3
9
  end
@@ -0,0 +1,151 @@
1
+ require 'timeout'
2
+ require 'rbconfig'
3
+ require 'pty'
4
+
5
+ require 'vimrunner/errors'
6
+ require 'vimrunner/shell'
7
+ require 'vimrunner/client'
8
+
9
+ module Vimrunner
10
+
11
+ # The Server is a wrapper around the Vim server process that is controlled by
12
+ # clients. It will attempt to start the most appropriate Vim binary available
13
+ # on the system, though there are some options that can control this
14
+ # behaviour. See #initialize for more details.
15
+ class Server
16
+ class << self
17
+
18
+ # A convenience method that initializes a new server and starts it.
19
+ def start(options = {})
20
+ server = new(options)
21
+ server.start
22
+ end
23
+
24
+ # A convenience method that returns a new Client instance, connected to
25
+ # the server.
26
+ def new_client
27
+ Client.new(self)
28
+ end
29
+
30
+ # Retrieve a list of names of currently running Vim servers.
31
+ def list
32
+ %x[#{vim_path} --serverlist].strip.split "\n"
33
+ end
34
+
35
+ # The default path to use when starting a server with a terminal Vim. If
36
+ # the "vim" executable is not compiled with clientserver capabilities,
37
+ # the GUI version is started instead.
38
+ def vim_path
39
+ if clientserver_enabled? 'vim'
40
+ 'vim'
41
+ else
42
+ gui_vim_path
43
+ end
44
+ end
45
+
46
+ # The default path to use when starting a server with the GUI version of
47
+ # Vim. Defaults to "mvim" on a mac and "gvim" on linux.
48
+ def gui_vim_path
49
+ if mac?
50
+ 'mvim'
51
+ else
52
+ 'gvim'
53
+ end
54
+ end
55
+
56
+ # The path to a vimrc file containing some required vimscript. The server
57
+ # is started with no settings or a vimrc, apart from this one.
58
+ def vimrc_path
59
+ File.join(File.expand_path('../../..', __FILE__), 'vim', 'vimrc')
60
+ end
61
+
62
+ # Returns true if the current operating system is Mac OS X.
63
+ def mac?
64
+ host_os =~ /darwin/
65
+ end
66
+
67
+ # Returns true if the given Vim binary is compiled with support for the
68
+ # client/server functionality.
69
+ def clientserver_enabled?(vim_path)
70
+ vim_version = %x[#{vim_path} --version]
71
+ vim_version.include? '+clientserver' and vim_version.include? '+xterm_clipboard'
72
+ end
73
+
74
+ private
75
+
76
+ def host_os
77
+ RbConfig::CONFIG['host_os']
78
+ end
79
+ end
80
+
81
+ attr_accessor :pid
82
+ attr_reader :name, :vim_path
83
+
84
+ # A Server is initialized with two options that control its behaviour:
85
+ #
86
+ # :gui - Whether or not to start Vim with a GUI, either 'gvim' or 'mvim'
87
+ # depending on the OS. The default is false, which means that
88
+ # the server will start itself as a terminal instance. Note
89
+ # that, if the terminal Vim doesn't have client/server
90
+ # support, a GUI version will be started anyway.
91
+ #
92
+ # :vim_path - A path to a custom Vim binary. If this option is not set,
93
+ # the server attempts to guess an appropriate one, given the
94
+ # :gui option and the current OS.
95
+ #
96
+ # Note that simply initializing a Server doesn't start the binary. You need
97
+ # to call the #start method to do that.
98
+ #
99
+ # Examples:
100
+ #
101
+ # server = Server.new # Will start a 'vim' if possible
102
+ # server = Server.new(:gui => true) # Will start a 'gvim' or 'mvim' depending on the OS
103
+ # server = Server.new(:vim_path => '/opt/bin/vim') # Will start a server with the given vim instance
104
+ #
105
+ def initialize(options = {})
106
+ @gui = options.fetch(:gui) { false }
107
+ @vim_path = options.fetch(:vim_path) { gui? ? Server.gui_vim_path : Server.vim_path }
108
+
109
+ @name = "VIMRUNNER#{rand.to_s}"
110
+ end
111
+
112
+ # Starts a Vim server.
113
+ def start
114
+ command = "#{vim_path} -f -u #{Server.vimrc_path} --noplugin --servername #{name}"
115
+
116
+ if gui?
117
+ @pid = Kernel.spawn(command, [:in, :out, :err] => :close)
118
+ else
119
+ _out, _in, @pid = PTY.spawn(command)
120
+ end
121
+
122
+ wait_until_started
123
+ self
124
+ end
125
+
126
+ # Returns true if the server was initialized with :gui => true. Note that
127
+ # this may not be consistent with the binary that was actually started. If
128
+ # a terminal Vim instance was attempted, but an appropriate one was not
129
+ # available, it may still have a GUI.
130
+ def gui?
131
+ @gui
132
+ end
133
+
134
+ # Kills the Vim instance in the background by sending it a TERM signal.
135
+ def kill
136
+ Shell.kill(@pid)
137
+ end
138
+
139
+ private
140
+
141
+ def wait_until_started
142
+ Timeout.timeout(5, TimeoutError) do
143
+ serverlist = Server.list
144
+ while serverlist.empty? or not serverlist.include? name
145
+ sleep 0.1
146
+ serverlist = Server.list
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -12,18 +12,10 @@ module Vimrunner
12
12
  IO.popen(command) { |io| io.read.strip }
13
13
  end
14
14
 
15
- # Sends a TERM signal to the given PID if it corresponds to a running
16
- # process.
15
+ # Sends a TERM signal to the given PID. Returns true if the process was
16
+ # found and killed, false otherwise.
17
17
  def kill(pid)
18
- Process.kill(Signal.list['TERM'], pid) if running?(pid)
19
- end
20
-
21
- private
22
-
23
- # Checks if the given PID corresponds to a running process
24
- def running?(pid)
25
- return false if pid.nil?
26
- Process.getpgid(pid)
18
+ Process.kill('TERM', pid)
27
19
  true
28
20
  rescue Errno::ESRCH
29
21
  false
@@ -1,3 +1,3 @@
1
1
  module Vimrunner
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vimrunner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-29 00:00:00.000000000Z
12
+ date: 2012-04-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &22761920 !ruby/object:Gem::Requirement
16
+ requirement: &23987740 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *22761920
24
+ version_requirements: *23987740
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rdoc
27
- requirement: &22761500 !ruby/object:Gem::Requirement
27
+ requirement: &23987320 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *22761500
35
+ version_requirements: *23987320
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &22761000 !ruby/object:Gem::Requirement
38
+ requirement: &23986780 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 2.0.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *22761000
46
+ version_requirements: *23986780
47
47
  description: ! " Using vim's client/server functionality, this library exposes
48
48
  a way to\n spawn a vim instance and control it programatically. Apart from being
49
49
  a fun\n party trick, this could be used to do integration testing on vimscript.\n"
@@ -54,11 +54,12 @@ executables:
54
54
  extensions: []
55
55
  extra_rdoc_files: []
56
56
  files:
57
+ - lib/vimrunner.rb
58
+ - lib/vimrunner/errors.rb
57
59
  - lib/vimrunner/shell.rb
60
+ - lib/vimrunner/server.rb
58
61
  - lib/vimrunner/version.rb
59
- - lib/vimrunner/runner.rb
60
- - lib/vimrunner/errors.rb
61
- - lib/vimrunner.rb
62
+ - lib/vimrunner/client.rb
62
63
  - vim/vimrc
63
64
  - bin/vimrunner
64
65
  - LICENSE
@@ -77,7 +78,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
78
  version: '0'
78
79
  segments:
79
80
  - 0
80
- hash: -4179387824099540343
81
+ hash: -3146226263794770349
81
82
  required_rubygems_version: !ruby/object:Gem::Requirement
82
83
  none: false
83
84
  requirements:
@@ -86,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
87
  version: 1.3.6
87
88
  requirements: []
88
89
  rubyforge_project: vimrunner
89
- rubygems_version: 1.8.11
90
+ rubygems_version: 1.8.15
90
91
  signing_key:
91
92
  specification_version: 3
92
93
  summary: Lets you control a vim instance through ruby
@@ -1,158 +0,0 @@
1
- require 'vimrunner/shell'
2
- require 'vimrunner/errors'
3
-
4
- module Vimrunner
5
-
6
- # The Runner class acts as the actual proxy to a vim instance. Upon
7
- # initialization, a vim process is started in the background. The Runner
8
- # instance's public methods correspond to actions the instance will perform.
9
- #
10
- # Use Runner#kill to manually destroy the background process.
11
- class Runner
12
- attr_reader :servername
13
-
14
- class << self
15
- def start_gvim
16
- servername = "VIMRUNNER#{rand.to_s.gsub '.', ''}"
17
-
18
- child_stdin, parent_stdin = IO::pipe
19
- parent_stdout, child_stdout = IO::pipe
20
- parent_stderr, child_stderr = IO::pipe
21
-
22
- pid = Kernel.fork do
23
- [parent_stdin, parent_stdout, parent_stderr].each { |io| io.close }
24
-
25
- STDIN.reopen(child_stdin)
26
- STDOUT.reopen(child_stdout)
27
- STDERR.reopen(child_stderr)
28
-
29
- [child_stdin, child_stdout, child_stderr].each { |io| io.close }
30
-
31
- exec 'gvim', '-f', '-u', vimrc_path, '--noplugin', '--servername', servername
32
- end
33
-
34
- [child_stdin, child_stdout, child_stderr].each { |io| io.close }
35
-
36
- new(pid, servername)
37
- end
38
-
39
- def vimrc_path
40
- File.join(File.expand_path('../../..', __FILE__), 'vim', 'vimrc')
41
- end
42
-
43
- def serverlist
44
- %x[vim --serverlist].strip.split "\n"
45
- end
46
- end
47
-
48
- def initialize(pid, servername)
49
- @pid = pid
50
- @servername = servername
51
- wait_until_started
52
- end
53
-
54
- # Adds a plugin to Vim's runtime. Initially, Vim is started without
55
- # sourcing any plugins to ensure a clean state. This method can be used to
56
- # populate the instance's environment.
57
- #
58
- # dir - The base directory of the plugin, the one that contains
59
- # its autoload, plugin, ftplugin, etc. directories.
60
- # entry_script - The vim script that's runtime'd to initialize the plugin.
61
- #
62
- # Example:
63
- #
64
- # vim.add_plugin 'rails', 'plugin/rails.vim'
65
- #
66
- def add_plugin(dir, entry_script)
67
- command("set runtimepath+=#{dir}")
68
- command("runtime #{entry_script}")
69
- end
70
-
71
- # Invokes one of the basic actions the vim server supports, sending a key
72
- # sequence. The keys are sent as-is, so it'd probably be better to use the
73
- # wrapper methods, #normal, #insert and so on.
74
- def type(keys)
75
- invoke_vim '--remote-send', keys
76
- end
77
-
78
- # Executes the given command in the vim instance and returns its output,
79
- # stripping all surrounding whitespace.
80
- def command(vim_command)
81
- normal
82
-
83
- expression = "VimrunnerEvaluateCommandOutput('#{vim_command.to_s}')"
84
-
85
- invoke_vim('--remote-expr', expression).strip.tap do |output|
86
- raise InvalidCommandError if output =~ /^Vim:E\d+:/
87
- end
88
- end
89
-
90
- # Starts a search in vim for the given text. The result is that the cursor
91
- # is positioned on its first occurrence.
92
- def search(text)
93
- normal
94
- type "/#{text}<cr>"
95
- end
96
-
97
- # Sets a setting in vim. If +value+ is nil, the setting is considered to be
98
- # a boolean.
99
- #
100
- # Examples:
101
- #
102
- # vim.set 'expandtab' # invokes ":set expandtab"
103
- # vim.set 'tabstop', 3 # invokes ":set tabstop=3"
104
- #
105
- def set(setting, value = nil)
106
- if value
107
- command "set #{setting}=#{value}"
108
- else
109
- command "set #{setting}"
110
- end
111
- end
112
-
113
- # Edits the file +filename+ with Vim.
114
- #
115
- # Note that this doesn't use the '--remote' vim flag, it simply types in
116
- # the command manually. This is necessary to avoid the vim instance getting
117
- # focus.
118
- def edit(filename)
119
- command "edit #{filename}"
120
- end
121
-
122
- # Writes the file being edited to disk. Note that you need to set the
123
- # file's name first by using Runner#edit.
124
- def write
125
- command :write
126
- end
127
-
128
- # Switches vim to insert mode and types in the given text.
129
- def insert(text = '')
130
- normal "i#{text}"
131
- end
132
-
133
- # Switches vim to insert mode and types in the given keys.
134
- def normal(keys = '')
135
- type "<c-\\><c-n>#{keys}"
136
- end
137
-
138
- # Kills the vim instance in the background by sending it a TERM signal.
139
- def kill
140
- Shell.kill(@pid)
141
- end
142
-
143
- private
144
-
145
- def invoke_vim(*args)
146
- args = ['vim', '--servername', @servername, *args]
147
- Shell.run *args
148
- end
149
-
150
- def wait_until_started
151
- serverlist = Runner.serverlist
152
- while serverlist.empty? or not serverlist.include? @servername
153
- sleep 0.1
154
- serverlist = Runner.serverlist
155
- end
156
- end
157
- end
158
- end