From ebc6c4a72e3265305f34e62cc7f769ee1e31c610 Mon Sep 17 00:00:00 2001 From: Luc Hermitte Date: Fri, 4 Dec 2015 14:25:28 +0100 Subject: [PATCH 1/2] Support for native win32 gvim from cygwin ruby This patch is a first draft toward the resolution of RfC #32. The following modifications were required: - In vimrc, ~/.vim becomes ~/vimfiles under Windows - The win32 gvim version won't display anything to the console. Even when called with --version, --remote*, ou --server* All commands need to be send with plain "vim" (the version I have cannot do anything else than sending commands to a vim server) And the spawn has to be gvim -- don't ask me why (of course, both have to be in the PATH) Hence the new functions `spawn_executable()` and `windows?()` - When ruby comes from cygwin distribution, and when gvim version is the native one, paths need to be fixed with cygpath executable. In order to ease its use, I've encapsulated its calls from Client methods, and provide some new function like `Client.cd` and `Platform.fix_path` - I've also added `Client.runtime`. - When using `feedkeys` to `:source` a script, the call seems to be asynchronous. Vim doesn't wait and the next command is sent much to early. I've changed the call to source to use --remote-send. May be we need a special version that simply injects vimrunner_rc. - When we request to rmdir vim current directory (set with `:cd`), cygwin-ruby Dir.mktmpdir finalizer fails. Vim needs to be set back into a neutral directory that we won't try to remove. The following issues are still pending: - The cygwin version of gvim distributed with cygwin package manager doesn't support +clientserver. As such, I didn't tried this version - I suspect that start/start_gvim only concern the spawn. In order to send remote commands, `vim` is always enough -- no need for `gvim` (but I don't know regarding `mvim`) It may be possible to simplify the codebase. - As my ruby version comes from cygwin distribution, I haven't tested native win32/64 versions of ruby, nor other shells mingw, ... - It'd certainly be best to merge vimrunner_rc code into the vimrc. Why two files? It causes troubles: - feedkeys is used to implement `Client.source` as `Client.command` relies on a function from vimrunner_rc - I still have errors I cannot explain with UTF-8. I'm not sure where they come from. May be vim-client knows how to solve them. https://www.omniref.com/ruby/gems/vim_client-ruby/0.1.0/symbols/VimClient --- lib/vimrunner/client.rb | 37 ++++++++++++++++++++++++++++++++----- lib/vimrunner/platform.rb | 31 +++++++++++++++++++++++++++++++ lib/vimrunner/rspec.rb | 7 ++++++- lib/vimrunner/server.rb | 7 ++++--- vim/vimrc | 2 ++ 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/lib/vimrunner/client.rb b/lib/vimrunner/client.rb index b0883d9..666c918 100644 --- a/lib/vimrunner/client.rb +++ b/lib/vimrunner/client.rb @@ -1,5 +1,6 @@ require "vimrunner/path" require "vimrunner/command" +require "vimrunner/platform" module Vimrunner class Client @@ -26,8 +27,7 @@ def initialize(server) def add_plugin(dir, entry_script = nil) append_runtimepath(dir) if entry_script - entry_script_path = Path.new(entry_script) - command("runtime #{entry_script_path}") + runtime(entry_script) end end @@ -41,8 +41,25 @@ def add_plugin(dir, entry_script = nil) # # Returns nothing. def source(script) + script_path = Path.new(Platform.fix_path(script)) + normal(":source #{script_path}") + # feedkeys doesn't seem to wait long enough + # feedkeys(":\\source #{script_path}\\") + end + + # Public: source a script in Vim server + # + # script - The Vim script to be sourced. The path is relative to + # &rtp. + # + # Examples + # + # vim.runtime 'plugin/rails.vim' + # + # Returns nothing. + def runtime(script) script_path = Path.new(script) - feedkeys(":\\source #{script_path}\\") + command("runtime #{script_path}") end # Public: Appends a directory to Vim's runtimepath @@ -51,7 +68,7 @@ def source(script) # # Returns nothing. def append_runtimepath(dir) - dir_path = Path.new(dir) + dir_path = Path.new(Platform.fix_path(dir)) command("set runtimepath+=#{dir_path}") end @@ -63,7 +80,7 @@ def append_runtimepath(dir) # # Returns nothing. def prepend_runtimepath(dir) - dir_path = Path.new(dir) + dir_path = Path.new(Platform.fix_path(dir)) runtimepath = Path.new(echo('&runtimepath')) command("set runtimepath=#{dir_path},#{runtimepath}") end @@ -182,6 +199,16 @@ def edit!(filename) self end + # Public: Changes the current directory + # + # The +path+ will be fixed on the fly + # + # Returns the String output. + def cd(path) + path = Platform.fix_path(path) + command("cd #{path}") + end + # Public: Executes the given command in the Vim instance and returns its # output, stripping all surrounding whitespace. # diff --git a/lib/vimrunner/platform.rb b/lib/vimrunner/platform.rb index 2fd9aae..d2cd9d5 100644 --- a/lib/vimrunner/platform.rb +++ b/lib/vimrunner/platform.rb @@ -34,6 +34,20 @@ def gvim gvims.find { |gvim| suitable?(gvim) } or raise NoSuitableVimError end + def fix_path(path) + return `cygpath -ml "#{path}"`.chomp if need_to_fix_path? + return path + end + + def spawn_executable + if windows? + the_exec = "gvim.exe" + else + the_exec = executable + end + return the_exec + end + private def gvims @@ -48,16 +62,24 @@ def vims %w( vim ) + gvims end + @need_to_fix_path = false def suitable?(vim) features = features(vim) + @need_to_fix_path = ! features.include?("cygwin") && cygwin? if gui?(vim) features.include?("+clientserver") + elsif cygwin? + features.include?("+clientserver") else features.include?("+clientserver") && features.include?("+xterm_clipboard") end end + def need_to_fix_path?() + @need_to_fix_path + end + def gui?(vim) executable = File.basename(vim) @@ -73,5 +95,14 @@ def features(vim) def mac? RbConfig::CONFIG["host_os"] =~ /darwin/ end + + def cygwin? + RbConfig::CONFIG["host_os"] == "cygwin" + end + + def windows? + RbConfig::CONFIG["host_os"] =~ /mswin|cygwin/ + end + end end diff --git a/lib/vimrunner/rspec.rb b/lib/vimrunner/rspec.rb index 21a827e..2e736b6 100644 --- a/lib/vimrunner/rspec.rb +++ b/lib/vimrunner/rspec.rb @@ -46,11 +46,16 @@ def vim config.include(Vimrunner::Testing) # Each example is executed in a separate directory + # No trace shall be left in the tmp directory otherwise cygwin won't permit + # rmdir => vim is outside the directory at the end + # TODO: ensure a cd(pwd) à la RAII + pwd = Dir.pwd config.around(:each) do |example| Dir.mktmpdir do |dir| Dir.chdir(dir) do - vim.command("cd #{dir}") + vim.cd(dir) example.run + vim.cd(pwd) end end end diff --git a/lib/vimrunner/server.rb b/lib/vimrunner/server.rb index 53d64f6..fe0a9e2 100644 --- a/lib/vimrunner/server.rb +++ b/lib/vimrunner/server.rb @@ -138,7 +138,7 @@ def new_client # # Returns an Array of String server names currently running. def serverlist - execute([executable, "--serverlist"]).split("\n") + execute([executable, "--serverlist"]).split(/\r?\n/) end # Public: Evaluates an expression in the Vim server and returns the result. @@ -172,8 +172,9 @@ def execute(command) end def spawn - PTY.spawn(executable, *%W[ - #{foreground_option} --servername #{name} -u #{vimrc} -U #{gvimrc} + the_exec = Platform.spawn_executable + PTY.spawn(the_exec, *%W[ + #{foreground_option} --servername #{name} -u #{Platform.fix_path vimrc} -U #{gvimrc} ]) end diff --git a/vim/vimrc b/vim/vimrc index 3dc2d44..4c7e7b4 100644 --- a/vim/vimrc +++ b/vim/vimrc @@ -7,5 +7,7 @@ syntax on set noswapfile nobackup " remove default ~/.vim directories to avoid loading plugins +set runtimepath-=~/vimfiles +set runtimepath-=~/vimfiles/after set runtimepath-=~/.vim set runtimepath-=~/.vim/after From 172017bf41f2fa90ec18e6a8a9751314297b2578 Mon Sep 17 00:00:00 2001 From: Luc Hermitte Date: Fri, 4 Dec 2015 15:01:57 +0100 Subject: [PATCH 2/2] Fix spawn detection in usual cases --- lib/vimrunner/platform.rb | 2 +- lib/vimrunner/server.rb | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/vimrunner/platform.rb b/lib/vimrunner/platform.rb index d2cd9d5..ddc122b 100644 --- a/lib/vimrunner/platform.rb +++ b/lib/vimrunner/platform.rb @@ -43,7 +43,7 @@ def spawn_executable if windows? the_exec = "gvim.exe" else - the_exec = executable + the_exec = vim end return the_exec end diff --git a/lib/vimrunner/server.rb b/lib/vimrunner/server.rb index fe0a9e2..20959b3 100644 --- a/lib/vimrunner/server.rb +++ b/lib/vimrunner/server.rb @@ -19,13 +19,15 @@ class Server VIMRC = File.expand_path("../../../vim/vimrc", __FILE__) VIMRUNNER_RC = File.expand_path("../../../vim/vimrunner_rc", __FILE__) - attr_reader :name, :executable, :vimrc, :gvimrc + attr_reader :name, :executable, :spawn_executable, :vimrc, :gvimrc # Public: Initialize a Server # # options - The Hash options used to define a server (default: {}): # :executable - The String Vim executable to use (optional) # (default: Platform.vim). + # :spawn_executable - The String Vim spawn executable to use (optional) + # (default: Platform.spawn_executable). # :name - The String name of the Vim server (optional) # (default: "VIMRUNNER#{rand}"). # :vimrc - The String vimrc file to source in the client (optional) @@ -35,6 +37,7 @@ class Server # def initialize(options = {}) @executable = options.fetch(:executable) { Platform.vim } + @spawn_executable = options.fetch(:spawn_executable) { Platform.spawn_executable } @name = options.fetch(:name) { "VIMRUNNER#{rand}" }.upcase @vimrc = options.fetch(:vimrc) { VIMRC } @gvimrc = options.fetch(:gvimrc) { "NONE" } @@ -172,7 +175,8 @@ def execute(command) end def spawn - the_exec = Platform.spawn_executable + the_exec = spawn_executable + # pp (%W[ #{the_exec} #{foreground_option} --servername #{name} -u #{vimrc} -U #{gvimrc} ]) PTY.spawn(the_exec, *%W[ #{foreground_option} --servername #{name} -u #{Platform.fix_path vimrc} -U #{gvimrc} ])