vimrunner 0.2.1 → 0.3.0

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
@@ -12,7 +12,8 @@ contributions are very welcome on the
12
12
 
13
13
  ## Usage
14
14
 
15
- Vimrunner can be used in one of two main ways:
15
+ If you don't already have a running Vim server, Vimrunner can be used in one
16
+ of two main ways:
16
17
 
17
18
  ```ruby
18
19
  # Vim will automatically be started and killed.
@@ -66,33 +67,75 @@ you can control Vim. For a full list of methods you can invoke on the remote
66
67
  Vim instance, check out the [`Client`
67
68
  documentation](http://rubydoc.info/gems/vimrunner/Vimrunner/Client).
68
69
 
70
+ If you already have a remote-capable Vim server running, you can connect
71
+ Vimrunner to it directly by using `Vimrunner.connect` or `Vimrunner.connect!`
72
+ like so:
73
+
74
+ ```ruby
75
+ # Assuming a running Vim server called FOO...
76
+ vim = Vimrunner.connect("FOO")
77
+ if vim
78
+ vim.insert("Hello world!")
79
+ end
80
+
81
+ # Or, if you're confident there's a running server...
82
+ vim = Vimrunner.connect!("FOO")
83
+ vim.insert("Hello world!")
84
+ ```
85
+
86
+ In case of failure to find the server `FOO`, the first form will return `nil`,
87
+ while the second form will raise an exception.
88
+
69
89
  ## Testing
70
90
 
71
- If you're using Vimrunner for testing vim plugins, take a look at the
72
- documentation for the
73
- [Vimrunner::Testing](http://rubydoc.info/gems/vimrunner/Vimrunner/Testing)
74
- module. It contains a few simple helpers that may make it a bit easier to write
75
- regression tests in rspec. With them, it could work something like this:
91
+ If you're using Vimrunner for testing vim plugins, a simple way to get up and
92
+ running is by requiring the `vimrunner/rspec` file. With that, your
93
+ `spec_helper.rb` would look like this:
76
94
 
77
95
  ``` ruby
78
- require 'spec_helper'
79
- require 'vimrunner/testing'
96
+ require 'vimrunner'
97
+ require 'vimrunner/rspec'
80
98
 
81
- describe "My Vim plugin" do
82
- let(:vim) { some_instance_of_vim }
99
+ Vimrunner::RSpec.configure do |config|
100
+ # Use a single Vim instance for the test suite. Set to false to use an
101
+ # instance per test (slower, but can be easier to manage).
102
+ config.reuse_server = true
103
+
104
+ # Decide how to start a Vim instance. In this block, an instance should be
105
+ # spawned and set up with anything project-specific.
106
+ config.start_vim do
107
+ vim = Vimrunner.start
83
108
 
84
- around :each do |example|
85
- # needed only once for any Vim instance:
86
- vim.add_plugin(File.expand_path('../my_plugin_path'), 'plugin/my_plugin.vim')
109
+ # Or, start a GUI instance:
110
+ # vim = Vimrunner.start_gvim
87
111
 
88
- # ensure a clean temporary directory for each test:
89
- Vimrunner::Testing.tmpdir(vim) do
90
- example.call
91
- end
112
+ # Setup your plugin in the Vim instance
113
+ plugin_path = File.expand_path('.')
114
+ vim.add_plugin(plugin_path, 'plugin/my_plugin.vim')
115
+
116
+ # The returned value is the Client available in the tests.
117
+ vim
92
118
  end
119
+ end
120
+ ```
121
+
122
+ This will result in:
123
+
124
+ - A `vim` helper in every rspec example, returning the configured
125
+ `Vimrunner::Client` instance.
126
+ - Every example is executed in a separate temporary directory to make it easier
127
+ to manipulate files.
128
+ - A few helper methods from the `Vimrunner::Testing` module
129
+ ([documentation](http://rubydoc.info/gems/vimrunner/Vimrunner/Testing)).
93
130
 
131
+ The specs would then look something like this:
132
+
133
+ ``` ruby
134
+ require 'spec_helper'
135
+
136
+ describe "My Vim plugin" do
94
137
  specify "some behaviour" do
95
- Vimrunner::Testing.write_file('test.rb', <<-EOF)
138
+ write_file('test.rb', <<-EOF)
96
139
  def foo
97
140
  bar
98
141
  end
@@ -102,7 +145,7 @@ describe "My Vim plugin" do
102
145
  do_plugin_related_stuff_with(vim)
103
146
  vim.write
104
147
 
105
- IO.read('test.rb').should eq Vimrunner::Testing.normalize_string_indent(<<-EOF)
148
+ IO.read('test.rb').should eq normalize_string_indent(<<-EOF)
106
149
  def bar
107
150
  foo
108
151
  end
@@ -111,9 +154,8 @@ describe "My Vim plugin" do
111
154
  end
112
155
  ```
113
156
 
114
- It's possible to make this a lot more concise by including
115
- `Vimrunner::Testing`, by making your own helper methods that wrap common
116
- behaviour, by extracting some code to `spec_helper.rb`, and so on.
157
+ If you need a different setup, please look through the file
158
+ `lib/vimrunner/rspec.rb` for ideas on how to build your own testing scaffold.
117
159
 
118
160
  ## Requirements
119
161
 
@@ -23,7 +23,7 @@ module Vimrunner
23
23
  #
24
24
  # Returns a Client for the started Server.
25
25
  def self.start(vim = Platform.vim, &blk)
26
- Server.new(vim).start(&blk)
26
+ Server.new(:executable => vim).start(&blk)
27
27
  end
28
28
 
29
29
  # Public: Start a Vim process with a GUI and return a Client through which
@@ -46,6 +46,36 @@ module Vimrunner
46
46
  #
47
47
  # Returns a Client for the started Server.
48
48
  def self.start_gvim(&blk)
49
- Server.new(Platform.gvim).start(&blk)
49
+ Server.new(:executable => Platform.gvim).start(&blk)
50
+ end
51
+
52
+ # Public: Connect to an existing Vim process by name. Returns nil in case of
53
+ # failure.
54
+ #
55
+ # name - The String name of the Vim server to connect to.
56
+ #
57
+ # Examples
58
+ #
59
+ # client = Vimrunner.connect("FOO")
60
+ # # => #<Vimrunner::Client>
61
+ #
62
+ # Returns a Client for the named server.
63
+ def self.connect(name)
64
+ Server.new(:name => name).connect
65
+ end
66
+
67
+ # Public: Connect to an existing Vim process by name. Raises an exception in
68
+ # case of failure.
69
+ #
70
+ # name - The String name of the Vim server to connect to.
71
+ #
72
+ # Examples
73
+ #
74
+ # client = Vimrunner.connect("FOO")
75
+ # # => #<Vimrunner::Client>
76
+ #
77
+ # Returns a Client for the named server.
78
+ def self.connect!(name)
79
+ Server.new(:name => name).connect!
50
80
  end
51
81
  end
@@ -25,6 +25,19 @@ module Vimrunner
25
25
  command("runtime #{entry_script}") if entry_script
26
26
  end
27
27
 
28
+ # Public: source a script in Vim server
29
+ #
30
+ # script - The Vim script to be sourced
31
+ #
32
+ # Examples
33
+ #
34
+ # vim.source '/path/to/plugin/rails.vim'
35
+ #
36
+ # Returns nothing.
37
+ def source(script)
38
+ feedkeys(":\\<C-u>source #{escape_filename(script)}\\<CR>")
39
+ end
40
+
28
41
  # Public: Appends a directory to Vim's runtimepath
29
42
  #
30
43
  # dir - The directory added to the path
@@ -96,6 +109,28 @@ module Vimrunner
96
109
  command "echo #{expressions.join(' ')}"
97
110
  end
98
111
 
112
+ # Public: Send keys as if they come from a mapping or typed by a user.
113
+ #
114
+ # Vim's usual remote-send functionality to send keys to a server does not
115
+ # respect mappings. As a workaround, the feedkeys() function can be used
116
+ # to more closely simulate user input.
117
+ #
118
+ # Any keys are sent in a double-quoted string so that special keys such as
119
+ # <CR> and <C-L> can be used. Note that, as per Vim documentation, such
120
+ # keys should be preceded by a backslash, e.g. '\<CR>' for a carriage
121
+ # return, '<CR>' will send those four characters separately.
122
+ #
123
+ # Examples
124
+ #
125
+ # vim.command 'map <C-R> ihello'
126
+ # vim.feedkeys '\<C-R>'
127
+ #
128
+ # Returns nothing.
129
+ def feedkeys(string)
130
+ string = string.gsub('"', '\"')
131
+ server.remote_expr(%Q{feedkeys("#{string}")})
132
+ end
133
+
99
134
  # Public: Sets a setting in Vim. If +value+ is nil, the setting is
100
135
  # considered to be a boolean.
101
136
  #
@@ -122,7 +157,7 @@ module Vimrunner
122
157
  #
123
158
  # Returns the Client instance.
124
159
  def edit(filename)
125
- command "edit #{filename}"
160
+ command "edit #{escape_filename(filename)}"
126
161
  self
127
162
  end
128
163
 
@@ -132,7 +167,7 @@ module Vimrunner
132
167
  #
133
168
  # Returns the Client instance.
134
169
  def edit!(filename)
135
- command "edit! #{filename}"
170
+ command "edit! #{escape_filename(filename)}"
136
171
  self
137
172
  end
138
173
 
@@ -142,9 +177,7 @@ module Vimrunner
142
177
  # Returns the String output.
143
178
  # Raises InvalidCommandError if the command is not recognised by vim.
144
179
  def command(commands)
145
- expression = "VimrunnerEvaluateCommandOutput('#{escape(commands)}')"
146
-
147
- server.remote_expr(expression).tap do |output|
180
+ server.remote_expr("VimrunnerEvaluateCommandOutput('#{escape_single_quote(commands)}')").tap do |output|
148
181
  raise InvalidCommandError.new(output) if output =~ /^Vim:E\d+:/
149
182
  end
150
183
  end
@@ -156,7 +189,11 @@ module Vimrunner
156
189
 
157
190
  private
158
191
 
159
- def escape(string)
192
+ def escape_filename(name)
193
+ name.gsub(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1")
194
+ end
195
+
196
+ def escape_single_quote(string)
160
197
  string.to_s.gsub("'", "''")
161
198
  end
162
199
  end
@@ -0,0 +1,69 @@
1
+ require 'vimrunner'
2
+ require 'vimrunner/testing'
3
+
4
+ module Vimrunner
5
+ module RSpec
6
+ class Configuration
7
+ attr_accessor :reuse_server
8
+
9
+ def start_vim(&block)
10
+ @start_vim_method = block
11
+ end
12
+
13
+ def start_vim_method
14
+ @start_vim_method || lambda { Vimrunner.start }
15
+ end
16
+ end
17
+
18
+ def self.configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+
22
+ def self.configure
23
+ yield configuration
24
+ end
25
+ end
26
+
27
+ module Testing
28
+ class << self
29
+ attr_accessor :instance
30
+ end
31
+
32
+ def vim
33
+ Testing.instance ||= Vimrunner::RSpec.configuration.start_vim_method.call
34
+ end
35
+ end
36
+ end
37
+
38
+ # Default configuration
39
+ Vimrunner::RSpec.configure do |config|
40
+ config.reuse_server = false
41
+ end
42
+
43
+ RSpec.configure do |config|
44
+
45
+ # Include the Testing DSL into all examples.
46
+ config.include(Vimrunner::Testing)
47
+
48
+ # Each example is executed in a separate directory
49
+ config.around(:each) do |example|
50
+ Dir.mktmpdir do |dir|
51
+ Dir.chdir(dir) do
52
+ vim.command("cd #{dir}")
53
+ example.run
54
+ end
55
+ end
56
+ end
57
+
58
+ config.before(:each) do
59
+ unless Vimrunner::RSpec.configuration.reuse_server
60
+ Vimrunner::Testing.instance.kill if Vimrunner::Testing.instance
61
+ Vimrunner::Testing.instance = nil
62
+ end
63
+ end
64
+
65
+ # Kill the Vim server after all tests are over.
66
+ config.after(:suite) do
67
+ Vimrunner::Testing.instance.kill if Vimrunner::Testing.instance
68
+ end
69
+ end
@@ -3,25 +3,41 @@ require "pty"
3
3
 
4
4
  require "vimrunner/errors"
5
5
  require "vimrunner/client"
6
+ require "vimrunner/platform"
6
7
 
7
8
  module Vimrunner
8
9
 
9
10
  # Public: A Server has the responsibility of starting a Vim process and
10
11
  # communicating with it through the clientserver interface. The process can
11
- # be started with "start" and stopped with "kill". A Client would be
12
- # necessary as the actual interface, though it is possible to use a Server
13
- # directly to invoke --remote commands on its Vim instance.
12
+ # be started with "start" and stopped with "kill". If given the servername of
13
+ # an existing Vim instance, it can control that instance without the need to
14
+ # start a new process.
15
+ #
16
+ # A Client would be necessary as there's a ctual interface, though it is possible
17
+ # to use a Server directly to invoke --remote commands on its Vim instance.
14
18
  class Server
15
- VIMRC = File.expand_path("../../../vim/vimrc", __FILE__)
19
+ VIMRC = File.expand_path("../../../vim/vimrc", __FILE__)
20
+ VIMRUNNER_RC = File.expand_path("../../../vim/vimrunner_rc", __FILE__)
16
21
 
17
- attr_reader :name, :executable
22
+ attr_reader :name, :executable, :vimrc
18
23
 
19
24
  # Public: Initialize a Server
20
25
  #
21
- # executable - a String representing a Vim executable.
22
- def initialize(executable)
23
- @executable = executable
24
- @name = "VIMRUNNER#{rand}"
26
+ # options - The Hash options used to define a server (default: {}):
27
+ # :executable - The String Vim executable to use (optional)
28
+ # (default: Platform.vim).
29
+ # :name - The String name of the Vim server (optional)
30
+ # (default: "VIMRUNNER#{rand}").
31
+ # :vimrc - The String vimrc file to source in the client (optional)
32
+ # (default: Server::VIMRC).
33
+ # :foreground - Boolean, whether to start Vim with the -f option (optional)
34
+ # (default: true).
35
+ #
36
+ def initialize(options = {})
37
+ @executable = options.fetch(:executable) { Platform.vim }
38
+ @name = options.fetch(:name) { "VIMRUNNER#{rand}" }
39
+ @vimrc = options.fetch(:vimrc) { VIMRC }
40
+ @foreground = options.fetch(:foreground, true)
25
41
  end
26
42
 
27
43
  # Public: Start a Server. This spawns a background process.
@@ -38,27 +54,64 @@ module Vimrunner
38
54
  # Returns a new Client instance initialized with this Server.
39
55
  # Yields a new Client instance initialized with this Server.
40
56
  def start
57
+ @r, @w, @pid = spawn
58
+
41
59
  if block_given?
42
- spawn do |r, w, pid|
43
- begin
44
- wait_until_started
45
- @result = yield(new_client)
46
- ensure
47
- r.close
48
- w.close
49
- Process.kill(9, pid) rescue Errno::ESRCH
50
- end
60
+ begin
61
+ @result = yield(connect!)
62
+ ensure
63
+ @r.close
64
+ @w.close
65
+ Process.kill(9, @pid) rescue Errno::ESRCH
51
66
  end
52
-
53
67
  @result
54
68
  else
55
- @r, @w, @pid = spawn
56
- wait_until_started
57
-
58
- new_client
69
+ connect!
59
70
  end
60
71
  end
61
72
 
73
+ # Public: Connects to the running server by name, blocking if need be.
74
+ # Returns nil if no server was found in the given time.
75
+ #
76
+ # options - An optional Hash. For now, only used for specifying a timeout
77
+ # (default: {}):
78
+ #
79
+ # :timeout - The Integer timeout to wait for a running server (optional)
80
+ # (default: 5).
81
+ #
82
+ # Returns a new Client instance initialized with this Server.
83
+ def connect(options = {})
84
+ connect!(options)
85
+ rescue TimeoutError
86
+ nil
87
+ end
88
+
89
+ # Public: Connects to the running server by name, blocking if need be.
90
+ # Raises a TimeoutError if no server was found in the given time in
91
+ # seconds.
92
+ #
93
+ # options - An optional Hash. For now, only used for specifying a timeout
94
+ # (default: {}):
95
+ #
96
+ # :timeout - The Integer timeout to wait for a running server
97
+ # (default: 5).
98
+ #
99
+ # Returns a new Client instance initialized with this Server.
100
+ def connect!(options = {})
101
+ wait_until_running(options[:timeout] || 5)
102
+
103
+ client = new_client
104
+ client.source(VIMRUNNER_RC)
105
+ client
106
+ end
107
+
108
+ # Public: Checks if there's a running Vim instance with the server's name.
109
+ #
110
+ # Returns a Boolean
111
+ def running?
112
+ serverlist.include?(name)
113
+ end
114
+
62
115
  # Public: Kills the Vim instance in the background.
63
116
  #
64
117
  # Returns self.
@@ -107,17 +160,21 @@ module Vimrunner
107
160
 
108
161
  private
109
162
 
163
+ def foreground_option
164
+ @foreground ? '-f' : ''
165
+ end
166
+
110
167
  def execute(command)
111
168
  IO.popen(command) { |io| io.read.strip }
112
169
  end
113
170
 
114
- def spawn(&blk)
115
- PTY.spawn(executable, "-f", "--servername", name, "-u", VIMRC, &blk)
171
+ def spawn
172
+ PTY.spawn("#{executable} #{foreground_option} --servername #{name} -u #{vimrc}")
116
173
  end
117
174
 
118
- def wait_until_started
119
- Timeout.timeout(5, TimeoutError) do
120
- sleep 0.1 while !serverlist.include?(name)
175
+ def wait_until_running(seconds)
176
+ Timeout.timeout(seconds, TimeoutError) do
177
+ sleep 0.1 while !running?
121
178
  end
122
179
  end
123
180
  end
@@ -6,28 +6,6 @@ module Vimrunner
6
6
  # testing purposes.
7
7
  module Testing
8
8
 
9
- # Public: Within the given block, switches to a temporary directory for
10
- # isolation purposes.
11
- #
12
- # Example:
13
- #
14
- # tmpdir(vim) do
15
- # puts vim.command('pwd')
16
- # end
17
- #
18
- # vim - a Vimrunner::Client instance
19
- #
20
- # Returns nothing.
21
- # Yields nothing.
22
- def tmpdir(vim)
23
- Dir.mktmpdir do |dir|
24
- Dir.chdir(dir) do
25
- vim.command("cd #{dir}")
26
- yield
27
- end
28
- end
29
- end
30
-
31
9
  # Public: Writes the given string to the file identified by "filename".
32
10
  #
33
11
  # Uses #normalize_string_indent to ensure consistent indentation when given
@@ -1,3 +1,3 @@
1
1
  module Vimrunner
2
- VERSION = '0.2.1'
2
+ VERSION = '0.3.0'
3
3
  end
data/vim/vimrc CHANGED
@@ -10,17 +10,4 @@ set noswapfile nobackup
10
10
  set runtimepath-=~/.vim
11
11
  set runtimepath-=~/.vim/after
12
12
 
13
- function! VimrunnerEvaluateCommandOutput(command)
14
- let base_command = split(a:command, '\s\+')[0]
15
- let base_command = substitute(base_command, '!$', '', '')
16
-
17
- if !exists(':'.base_command)
18
- let output = 'Vim:E492: Not an editor command: '.base_command
19
- else
20
- redir => output
21
- silent exe a:command
22
- redir END
23
- endif
24
-
25
- return output
26
- endfunction
13
+ execute "source " . expand('<sfile>:p:h') . "/vimrunner_rc"
@@ -0,0 +1,56 @@
1
+ let has_error_handling_bugfix =
2
+ \ v:version > 703 ||
3
+ \ v:version == 703 && has('patch860')
4
+
5
+ if has_error_handling_bugfix
6
+ function! VimrunnerEvaluateCommandOutput(command)
7
+ let output = ''
8
+
9
+ try
10
+ redir => output
11
+ silent exe a:command
12
+ redir END
13
+
14
+ let output = s:StripSilencedErrors(output)
15
+ catch
16
+ let output = v:exception
17
+ endtry
18
+
19
+ return output
20
+ endfunction
21
+ else
22
+ " Use some fake error handling to provide at least rudimentary errors for
23
+ " missing commands.
24
+ function! VimrunnerEvaluateCommandOutput(command)
25
+ let base_command = split(a:command, '\s\+')[0]
26
+ let base_command = substitute(base_command, '!$', '', '')
27
+
28
+ if !exists(':'.base_command)
29
+ let output = 'Vim:E492: Not an editor command: '.base_command
30
+ else
31
+ redir => output
32
+ silent exe a:command
33
+ redir END
34
+ endif
35
+
36
+ return output
37
+ endfunction
38
+ endif
39
+
40
+ " Remove errors from the output that have been silenced by :silent!. These are
41
+ " visible in the captured output since all messages are captured by :redir.
42
+ function! s:StripSilencedErrors(output)
43
+ let processed_output = []
44
+
45
+ for line in reverse(split(a:output, "\n"))
46
+ if line =~ '^E\d\+:'
47
+ break
48
+ endif
49
+
50
+ call add(processed_output, line)
51
+ endfor
52
+
53
+ return join(reverse(processed_output), "\n")
54
+ endfunction
55
+
56
+ " vim: set ft=vim
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.2.1
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-17 00:00:00.000000000 Z
12
+ date: 2013-04-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -66,7 +66,7 @@ dependencies:
66
66
  requirements:
67
67
  - - ! '>='
68
68
  - !ruby/object:Gem::Version
69
- version: 2.0.0
69
+ version: 2.13.0
70
70
  type: :development
71
71
  prerelease: false
72
72
  version_requirements: !ruby/object:Gem::Requirement
@@ -74,7 +74,7 @@ dependencies:
74
74
  requirements:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
- version: 2.0.0
77
+ version: 2.13.0
78
78
  description: ! " Using Vim's client/server functionality, this library exposes
79
79
  a way to\n spawn a Vim instance and control it programatically. Apart from being
80
80
  a fun\n party trick, this can be used to integration test Vim script.\n"
@@ -88,10 +88,12 @@ files:
88
88
  - lib/vimrunner.rb
89
89
  - lib/vimrunner/client.rb
90
90
  - lib/vimrunner/testing.rb
91
+ - lib/vimrunner/rspec.rb
91
92
  - lib/vimrunner/errors.rb
92
93
  - lib/vimrunner/version.rb
93
94
  - lib/vimrunner/server.rb
94
95
  - lib/vimrunner/platform.rb
96
+ - vim/vimrunner_rc
95
97
  - vim/vimrc
96
98
  - bin/vimrunner
97
99
  - LICENSE
@@ -110,7 +112,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
112
  version: '0'
111
113
  segments:
112
114
  - 0
113
- hash: 3996768695942180221
115
+ hash: -1164770989909868074
114
116
  required_rubygems_version: !ruby/object:Gem::Requirement
115
117
  none: false
116
118
  requirements: