Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions lib/bundler/cli/open.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,22 @@ def run
Bundler.ui.info "Unable to open #{name} because it's a default gem, so the directory it would normally be installed to does not exist."
else
root_path = spec.full_gem_path
require "shellwords"
command = Shellwords.split(editor) << File.join([root_path, path].compact)
command = editor_command(editor) << File.join([root_path, path].compact)
Bundler.with_original_env do
system(*command, { chdir: root_path })
end || Bundler.ui.info("Could not run '#{command.join(" ")}'")
end
end

def editor_command(editor)
# On Windows an editor is often configured with a full path such as
# C:\Program Files\Microsoft VS Code\Code.exe, which shell splitting
# would corrupt. Take a value that names an existing file as a
# single word.
return [editor] if Gem.win_platform? && File.file?(editor)

require "shellwords"
Shellwords.split(editor)
end
end
end
13 changes: 12 additions & 1 deletion lib/rubygems/commands/open_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,18 @@ def open_gem(name)
end

def open_editor(path)
system(*@editor.split(/\s+/) + [path], { chdir: path })
system(*editor_command(@editor), path, { chdir: path })
end

def editor_command(editor) # :nodoc:
# On Windows an editor is often configured with a full path such as
# C:\Program Files\Microsoft VS Code\Code.exe, which shell splitting
# would corrupt. Take a value that names an existing file as a
# single word.
return [editor] if Gem.win_platform? && File.file?(editor)

require "shellwords"
Shellwords.split(editor)
end

def spec_for(name)
Expand Down
37 changes: 37 additions & 0 deletions spec/bundler/cli/open_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require "bundler/cli"
require "bundler/cli/open"

RSpec.describe Bundler::CLI::Open do
subject { described_class.new({}, "rack") }

describe "#editor_command" do
it "takes an editor path that names an existing file as a single word on Windows" do
editor = File.join(Dir.mktmpdir, "editor.exe")
FileUtils.touch editor
allow(Gem).to receive(:win_platform?).and_return(true)

expect(subject.editor_command(editor)).to eq([editor])
end

it "keeps backslashes in a quoted path" do
expect(subject.editor_command('"C:\Program Files\Microsoft VS Code\Code.exe" -w')).
to eq(['C:\Program Files\Microsoft VS Code\Code.exe', "-w"])
end

it "splits an editor with arguments" do
expect(subject.editor_command("code -w")).to eq(["code", "-w"])
end

it "splits an existing path on POSIX" do
dir = Dir.mktmpdir
FileUtils.mkdir_p File.join(dir, "editor dir")
editor = File.join(dir, "editor dir", "editor")
FileUtils.touch editor
allow(Gem).to receive(:win_platform?).and_return(false)

expect(subject.editor_command(editor)).to eq(editor.split(" "))
end
end
end
1 change: 1 addition & 0 deletions spec/support/shards.rb
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ module Shards
"spec/install/path_spec.rb",
"spec/bundler/fetcher/gem_remote_fetcher_local_ssl_server_spec.rb",
"spec/bundler/plugin/unloaded_source_spec.rb",
"spec/bundler/cli/open_spec.rb",
],
}.freeze
end
Expand Down
28 changes: 28 additions & 0 deletions test/rubygems/test_gem_commands_open_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,34 @@ def test_execute
assert_equal "", @ui.output
end

def test_editor_command_windows_path
win_platform = Gem.win_platform?

editor = if win_platform
FileUtils.mkdir_p File.join(@tempdir, "editor dir")
File.join(@tempdir, "editor dir", "editor.exe").tr("/", "\\")
else
# backslashes and spaces are plain filename characters on POSIX
File.join(@tempdir, 'C:\editor dir\editor.exe')
end
FileUtils.touch editor

Gem.win_platform = true

assert_equal [editor], @cmd.editor_command(editor)
ensure
Gem.win_platform = win_platform
end

def test_editor_command_quoted_path
assert_equal ['C:\editor dir\editor.exe', "-w"],
@cmd.editor_command('"C:\editor dir\editor.exe" -w')
end

def test_editor_command_with_arguments
assert_equal %w[code -w], @cmd.editor_command("code -w")
end

def test_wrong_version
@cmd.options[:version] = "4.0"
@cmd.options[:args] = %w[foo]
Expand Down