Skip to content

Commit 4bc3116

Browse files
authored
Merge pull request #9694 from ruby/editor-command-windows-path
Preserve Windows editor paths in gem open and bundle open
2 parents bca99f3 + 5a5532a commit 4bc3116

5 files changed

Lines changed: 90 additions & 3 deletions

File tree

lib/bundler/cli/open.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,22 @@ def run
1818
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."
1919
else
2020
root_path = spec.full_gem_path
21-
require "shellwords"
22-
command = Shellwords.split(editor) << File.join([root_path, path].compact)
21+
command = editor_command(editor) << File.join([root_path, path].compact)
2322
Bundler.with_original_env do
2423
system(*command, { chdir: root_path })
2524
end || Bundler.ui.info("Could not run '#{command.join(" ")}'")
2625
end
2726
end
27+
28+
def editor_command(editor)
29+
# On Windows an editor is often configured with a full path such as
30+
# C:\Program Files\Microsoft VS Code\Code.exe, which shell splitting
31+
# would corrupt. Take a value that names an existing file as a
32+
# single word.
33+
return [editor] if Gem.win_platform? && File.file?(editor)
34+
35+
require "shellwords"
36+
Shellwords.split(editor)
37+
end
2838
end
2939
end

lib/rubygems/commands/open_command.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,18 @@ def open_gem(name)
7070
end
7171

7272
def open_editor(path)
73-
system(*@editor.split(/\s+/) + [path], { chdir: path })
73+
system(*editor_command(@editor), path, { chdir: path })
74+
end
75+
76+
def editor_command(editor) # :nodoc:
77+
# On Windows an editor is often configured with a full path such as
78+
# C:\Program Files\Microsoft VS Code\Code.exe, which shell splitting
79+
# would corrupt. Take a value that names an existing file as a
80+
# single word.
81+
return [editor] if Gem.win_platform? && File.file?(editor)
82+
83+
require "shellwords"
84+
Shellwords.split(editor)
7485
end
7586

7687
def spec_for(name)

spec/bundler/cli/open_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
3+
require "bundler/cli"
4+
require "bundler/cli/open"
5+
6+
RSpec.describe Bundler::CLI::Open do
7+
subject { described_class.new({}, "rack") }
8+
9+
describe "#editor_command" do
10+
it "takes an editor path that names an existing file as a single word on Windows" do
11+
editor = File.join(Dir.mktmpdir, "editor.exe")
12+
FileUtils.touch editor
13+
allow(Gem).to receive(:win_platform?).and_return(true)
14+
15+
expect(subject.editor_command(editor)).to eq([editor])
16+
end
17+
18+
it "keeps backslashes in a quoted path" do
19+
expect(subject.editor_command('"C:\Program Files\Microsoft VS Code\Code.exe" -w')).
20+
to eq(['C:\Program Files\Microsoft VS Code\Code.exe', "-w"])
21+
end
22+
23+
it "splits an editor with arguments" do
24+
expect(subject.editor_command("code -w")).to eq(["code", "-w"])
25+
end
26+
27+
it "splits an existing path on POSIX" do
28+
dir = Dir.mktmpdir
29+
FileUtils.mkdir_p File.join(dir, "editor dir")
30+
editor = File.join(dir, "editor dir", "editor")
31+
FileUtils.touch editor
32+
allow(Gem).to receive(:win_platform?).and_return(false)
33+
34+
expect(subject.editor_command(editor)).to eq(editor.split(" "))
35+
end
36+
end
37+
end

spec/support/shards.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ module Shards
203203
"spec/install/path_spec.rb",
204204
"spec/bundler/fetcher/gem_remote_fetcher_local_ssl_server_spec.rb",
205205
"spec/bundler/plugin/unloaded_source_spec.rb",
206+
"spec/bundler/cli/open_spec.rb",
206207
],
207208
}.freeze
208209
end

test/rubygems/test_gem_commands_open_command.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,34 @@ def test_execute
4343
assert_equal "", @ui.output
4444
end
4545

46+
def test_editor_command_windows_path
47+
win_platform = Gem.win_platform?
48+
49+
editor = if win_platform
50+
FileUtils.mkdir_p File.join(@tempdir, "editor dir")
51+
File.join(@tempdir, "editor dir", "editor.exe").tr("/", "\\")
52+
else
53+
# backslashes and spaces are plain filename characters on POSIX
54+
File.join(@tempdir, 'C:\editor dir\editor.exe')
55+
end
56+
FileUtils.touch editor
57+
58+
Gem.win_platform = true
59+
60+
assert_equal [editor], @cmd.editor_command(editor)
61+
ensure
62+
Gem.win_platform = win_platform
63+
end
64+
65+
def test_editor_command_quoted_path
66+
assert_equal ['C:\editor dir\editor.exe', "-w"],
67+
@cmd.editor_command('"C:\editor dir\editor.exe" -w')
68+
end
69+
70+
def test_editor_command_with_arguments
71+
assert_equal %w[code -w], @cmd.editor_command("code -w")
72+
end
73+
4674
def test_wrong_version
4775
@cmd.options[:version] = "4.0"
4876
@cmd.options[:args] = %w[foo]

0 commit comments

Comments
 (0)