diff --git a/lib/better_errors/editor.rb b/lib/better_errors/editor.rb index 30ce6050..0e998637 100644 --- a/lib/better_errors/editor.rb +++ b/lib/better_errors/editor.rb @@ -56,8 +56,8 @@ def self.for_symbol(symbol) end end - # Automatically sniffs a default editor preset based on the EDITOR - # environment variable. + # Automatically sniffs a default editor preset based on + # environment variables. # # @return [Symbol] def self.default_editor @@ -67,12 +67,15 @@ def self.default_editor end def self.editor_from_environment_editor - editor_command = ENV["EDITOR"] || ENV["BETTER_ERRORS_EDITOR"] - if editor_command - editor = editor_from_command(editor_command) + if ENV["BETTER_ERRORS_EDITOR"] + editor = editor_from_command(ENV["BETTER_ERRORS_EDITOR"]) return editor if editor - - puts "Since EDITOR or BETTER_ERRORS_EDITOR environment variable are not recognized, using Textmate by default." + puts "BETTER_ERRORS_EDITOR environment variable is not recognized as a supported Better Errors editor." + end + if ENV["EDITOR"] + editor = editor_from_command(ENV["EDITOR"]) + return editor if editor + puts "EDITOR environment variable is not recognized as a supported Better Errors editor. Using TextMate by default." else puts "Since there is no EDITOR or BETTER_ERRORS_EDITOR environment variable, using Textmate by default." end diff --git a/spec/better_errors/editor_spec.rb b/spec/better_errors/editor_spec.rb new file mode 100644 index 00000000..efc73608 --- /dev/null +++ b/spec/better_errors/editor_spec.rb @@ -0,0 +1,233 @@ +require "spec_helper" + +RSpec.describe BetterErrors::Editor do + describe ".for_formatting_string" do + it "returns an object that reponds to #url" do + editor = described_class.for_formatting_string("custom://%{file}:%{file_unencoded}:%{line}") + expect(editor.url("/path&file", 42)).to eq("custom://%2Fpath%26file:/path&file:42") + end + end + + describe ".for_proc" do + it "returns an object that responds to #url, which calls the proc" do + editor = described_class.for_proc(proc { |file, line| "result" } ) + expect(editor.url("foo", 42)).to eq("result") + end + end + + describe ".for_symbol" do + subject { described_class.for_symbol(symbol) } + + [:atom].each do |symbol| + context "when symbol is '#{symbol}'" do + let(:symbol) { symbol } + + it "uses atom:// scheme" do + expect(subject.url("file", 42)).to start_with("atom://") + end + end + end + + [:emacs, :emacsclient].each do |symbol| + context "when symbol is '#{symbol}'" do + let(:symbol) { symbol } + it "uses emacs:// scheme" do + expect(subject.url("file", 42)).to start_with("emacs://") + end + end + end + + [:macvim, :mvim].each do |symbol| + context "when symbol is '#{symbol}'" do + let(:symbol) { symbol } + + it "uses mvim:// scheme" do + expect(subject.url("file", 42)).to start_with("mvim://") + end + end + end + + [:sublime, :subl, :st].each do |symbol| + context "when symbol is '#{symbol}'" do + let(:symbol) { symbol } + + it "uses subl:// scheme" do + expect(subject.url("file", 42)).to start_with("subl://") + end + end + end + + [:textmate, :txmt, :tm].each do |symbol| + context "when symbol is '#{symbol}'" do + let(:symbol) { symbol } + + it "uses txmt:// scheme" do + expect(subject.url("file", 42)).to start_with("txmt://") + end + end + end + end + + describe ".default_editor" do + subject(:default_editor) { described_class.default_editor } + before do + ENV['BETTER_ERRORS_EDITOR_URL'] = nil + ENV['BETTER_ERRORS_EDITOR'] = nil + ENV['EDITOR'] = nil + end + + it "returns an object that responds to #url" do + expect(default_editor.url("foo", 123)).to match(/foo/) + end + + context "when $BETTER_ERRORS_EDITOR_URL is set" do + before do + ENV['BETTER_ERRORS_EDITOR_URL'] = "custom://%{file}:%{file_unencoded}:%{line}" + end + + it "uses the value as a formatting string to build the editor URL" do + expect(default_editor.url("/path&file", 42)).to eq("custom://%2Fpath%26file:/path&file:42") + end + end + + context "when $BETTER_ERRORS_EDITOR is set to one of the preset commands" do + before do + ENV['BETTER_ERRORS_EDITOR'] = "subl" + end + + it "returns an object that builds URLs for the corresponding editor" do + expect(default_editor.url("foo", 123)).to start_with('subl://') + end + end + + context "when $EDITOR is set to one of the preset commands" do + before do + ENV['EDITOR'] = "subl" + end + + it "returns an object that builds URLs for the corresponding editor" do + expect(default_editor.url("foo", 123)).to start_with('subl://') + end + + context "when $BETTER_ERRORS_EDITOR is set to one of the preset commands" do + before do + ENV['BETTER_ERRORS_EDITOR'] = "emacs" + end + + it "returns an object that builds URLs for that editor instead" do + expect(default_editor.url("foo", 123)).to start_with('emacs://') + end + end + + context "when $BETTER_ERRORS_EDITOR is set to an unrecognized command" do + before do + ENV['BETTER_ERRORS_EDITOR'] = "fubarcmd" + end + + it "returns an object that builds URLs for the $EDITOR instead" do + expect(default_editor.url("foo", 123)).to start_with('subl://') + end + end + end + + context "when $EDITOR is set to an unrecognized command" do + before do + ENV['EDITOR'] = "fubarcmd" + end + + it "returns an object that builds URLs for TextMate" do + expect(default_editor.url("foo", 123)).to start_with('txmt://') + end + end + + context "when $EDITOR and $BETTER_ERRORS_EDITOR are not set" do + it "returns an object that builds URLs for TextMate" do + expect(default_editor.url("foo", 123)).to start_with('txmt://') + end + end + end + + describe ".editor_from_command" do + subject { described_class.editor_from_command(command_line) } + + ["atom -w", "/usr/bin/atom -w"].each do |command| + context "when editor command is '#{command}'" do + let(:command_line) { command } + + it "uses atom:// scheme" do + expect(subject.url("file", 42)).to start_with("atom://") + end + end + end + + ["emacsclient", "/usr/local/bin/emacsclient"].each do |command| + context "when editor command is '#{command}'" do + let(:command_line) { command } + + it "uses emacs:// scheme" do + expect(subject.url("file", 42)).to start_with("emacs://") + end + end + end + + ["idea"].each do |command| + context "when editor command is '#{command}'" do + let(:command_line) { command } + + it "uses idea:// scheme" do + expect(subject.url("file", 42)).to start_with("idea://") + end + end + end + + ["mate -w", "/usr/bin/mate -w"].each do |command| + context "when editor command is '#{command}'" do + let(:command_line) { command } + + it "uses txmt:// scheme" do + expect(subject.url("file", 42)).to start_with("txmt://") + end + end + end + + ["mine"].each do |command| + context "when editor command is '#{command}'" do + let(:command_line) { command } + + it "uses x-mine:// scheme" do + expect(subject.url("file", 42)).to start_with("x-mine://") + end + end + end + + ["mvim -f", "/usr/local/bin/mvim -f"].each do |command| + context "when editor command is '#{command}'" do + let(:command_line) { command } + + it "uses mvim:// scheme" do + expect(subject.url("file", 42)).to start_with("mvim://") + end + end + end + + ["subl -w", "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl"].each do |command| + context "when editor command is '#{command}'" do + let(:command_line) { command } + + it "uses subl:// scheme" do + expect(subject.url("file", 42)).to start_with("subl://") + end + end + end + + ["vscode", "code"].each do |command| + context "when editor command is '#{command}'" do + let(:command_line) { command } + + it "uses vscode:// scheme" do + expect(subject.url("file", 42)).to start_with("vscode://") + end + end + end + end +end diff --git a/spec/better_errors_spec.rb b/spec/better_errors_spec.rb index 2ab2a486..0b5b6604 100644 --- a/spec/better_errors_spec.rb +++ b/spec/better_errors_spec.rb @@ -1,112 +1,49 @@ require "spec_helper" -describe BetterErrors do - context ".editor" do - it "defaults to textmate" do - expect(subject.editor.url("foo.rb", 123)).to eq("txmt://open?url=file://foo.rb&line=123") - end - - it "url escapes the filename" do - expect(subject.editor.url("&.rb", 0)).to eq("txmt://open?url=file://%26.rb&line=0") - end - - [:emacs, :emacsclient].each do |editor| - it "uses emacs:// scheme when set to #{editor.inspect}" do - subject.editor = editor - expect(subject.editor.url("file", 42)).to start_with "emacs://" - end - end - - [:macvim, :mvim].each do |editor| - it "uses mvim:// scheme when set to #{editor.inspect}" do - subject.editor = editor - expect(subject.editor.url("file", 42)).to start_with "mvim://" - end - end - - [:sublime, :subl, :st].each do |editor| - it "uses subl:// scheme when set to #{editor.inspect}" do - subject.editor = editor - expect(subject.editor.url("file", 42)).to start_with "subl://" - end - end - - [:textmate, :txmt, :tm].each do |editor| - it "uses txmt:// scheme when set to #{editor.inspect}" do - subject.editor = editor - expect(subject.editor.url("file", 42)).to start_with "txmt://" - end - end - - [:atom].each do |editor| - it "uses atom:// scheme when set to #{editor.inspect}" do - subject.editor = editor - expect(subject.editor.url("file", 42)).to start_with "atom://" - end - end - - ["emacsclient", "/usr/local/bin/emacsclient"].each do |editor| - it "uses emacs:// scheme when EDITOR=#{editor}" do - ENV["EDITOR"] = editor - subject.editor = subject.default_editor - expect(subject.editor.url("file", 42)).to start_with "emacs://" +RSpec.describe BetterErrors do + describe ".editor" do + context "when set to a specific value" do + before do + allow(BetterErrors::Editor).to receive(:for_symbol).and_return(:editor_from_symbol) + allow(BetterErrors::Editor).to receive(:for_formatting_string).and_return(:editor_from_formatting_string) + allow(BetterErrors::Editor).to receive(:for_proc).and_return(:editor_from_proc) end - end - ["mvim -f", "/usr/local/bin/mvim -f"].each do |editor| - it "uses mvim:// scheme when EDITOR=#{editor}" do - ENV["EDITOR"] = editor - subject.editor = subject.default_editor - expect(subject.editor.url("file", 42)).to start_with "mvim://" + context "when the value is a string" do + it "uses BetterErrors::Editor.for_formatting_string to set the value" do + subject.editor = "thing://%{file}" + expect(BetterErrors::Editor).to have_received(:for_formatting_string).with("thing://%{file}") + expect(subject.editor).to eq(:editor_from_formatting_string) + end end - end - ["subl -w", "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl"].each do |editor| - it "uses subl:// scheme when EDITOR=#{editor}" do - ENV["EDITOR"] = editor - subject.editor = subject.default_editor - expect(subject.editor.url("file", 42)).to start_with "subl://" + context "when the value is a Proc" do + it "uses BetterErrors::Editor.for_proc to set the value" do + my_proc = proc { "thing" } + subject.editor = my_proc + expect(BetterErrors::Editor).to have_received(:for_proc).with(my_proc) + expect(subject.editor).to eq(:editor_from_proc) + end end - end - ["mate -w", "/usr/bin/mate -w"].each do |editor| - it "uses txmt:// scheme when EDITOR=#{editor}" do - ENV["EDITOR"] = editor - subject.editor = subject.default_editor - expect(subject.editor.url("file", 42)).to start_with "txmt://" + context "when the value is a symbol" do + it "uses BetterErrors::Editor.for_symbol to set the value" do + subject.editor = :subl + expect(BetterErrors::Editor).to have_received(:for_symbol).with(:subl) + expect(subject.editor).to eq(:editor_from_symbol) + end end end - - ["atom -w", "/usr/bin/atom -w"].each do |editor| - it "uses atom:// scheme when EDITOR=#{editor}" do - ENV["EDITOR"] = editor - subject.editor = subject.default_editor - expect(subject.editor.url("file", 42)).to start_with "atom://" + context "when no value has been set" do + before do + BetterErrors.instance_variable_set('@editor', nil) + allow(BetterErrors::Editor).to receive(:default_editor).and_return(:default_editor) end - end - - ["mine"].each do |editor| - it "uses x-mine:// scheme when EDITOR=#{editor}" do - ENV["EDITOR"] = editor - subject.editor = subject.default_editor - expect(subject.editor.url("file", 42)).to start_with "x-mine://" - end - end - - ["idea"].each do |editor| - it "uses idea:// scheme when EDITOR=#{editor}" do - ENV["EDITOR"] = editor - subject.editor = subject.default_editor - expect(subject.editor.url("file", 42)).to start_with "idea://" - end - end - ["vscode", "code"].each do |editor| - it "uses vscode:// scheme when EDITOR=#{editor}" do - ENV["EDITOR"] = editor - subject.editor = subject.default_editor - expect(subject.editor.url("file", 42)).to start_with "vscode://" + it "uses BetterErrors::Editor.default_editor to set the default value" do + expect(subject.editor).to eq(:default_editor) + expect(BetterErrors::Editor).to have_received(:default_editor) end end end