lib/tty/editor.rb in tty-editor-0.6.0 vs lib/tty/editor.rb in tty-editor-0.7.0

- old
+ new

@@ -1,8 +1,8 @@ # frozen_string_literal: true -require "fileutils" +require "pathname" require "shellwords" require "tempfile" require "tty-prompt" require_relative "editor/version" @@ -23,64 +23,92 @@ # Raised when editor cannot be found class EditorNotFoundError < RuntimeError; end # List possible command line text editors # - # @return [Array[String]] + # @return [Array<String>] # # @api public EXECUTABLES = [ - "nano -w", "notepad", "vim", "vi", "emacs", - "code", "subl -n -w", "mate -w", "atom", - "pico", "qe", "mg", "jed" + "atom", "code", "emacs", "gedit", "jed", "kate", + "mate -w", "mg", "nano -w", "notepad", "pico", + "qe", "subl -n -w", "vi", "vim" ].freeze # Check if editor command exists # # @example # exist?("vim") # => true # + # @example + # exist?("/usr/local/bin/vim") # => true + # + # @example + # exist?("C:\\Program Files\\Vim\\vim.exe") # => true + # + # @param [String] command + # the command to check for the existence + # # @return [Boolean] # # @api private def self.exist?(command) - exts = ENV.fetch("PATHEXT", "").split(::File::PATH_SEPARATOR) + path = Pathname(command) + exts = [""].concat(ENV.fetch("PATHEXT", "").split(::File::PATH_SEPARATOR)) + + if path.absolute? + return exts.any? { |ext| ::File.exist?("#{command}#{ext}") } + end + ENV.fetch("PATH", "").split(::File::PATH_SEPARATOR).any? do |dir| file = ::File.join(dir, command) - ::File.exist?(file) || exts.any? { |ext| ::File.exist?("#{file}#{ext}") } + exts.any? { |ext| ::File.exist?("#{file}#{ext}") } end end # Check editor from environment variables # - # @return [Array[String]] + # @return [Array<String>] # # @api public def self.from_env [ENV["VISUAL"], ENV["EDITOR"]].compact end # Find available text editors # - # @param [Array[String]] commands + # @param [Array<String>] commands # the commands to use intstead of defaults # - # @return [Array[String]] + # @return [Array<String>] # the existing editor commands # # @api public def self.available(*commands) - execs = if !commands.empty? - commands.map(&:to_s) - elsif from_env.any? - [from_env.first] - else - EXECUTABLES - end + if commands.any? + execs = search_executables(commands.map(&:to_s)) + return execs unless execs.empty? + end + + if from_env.any? + execs = search_executables(from_env) + return execs unless execs.empty? + end + + search_executables(EXECUTABLES) + end + + # Search for existing executables + # + # @return [Array<String>] + # + # @api private + def self.search_executables(execs) execs.compact.map(&:strip).reject(&:empty?).uniq .select { |exec| exist?(exec.split.first) } end + private_class_method :search_executables # Open file in system editor # # @example # TTY::Editor.open("/path/to/filename") @@ -91,15 +119,15 @@ # @example # TTY::Editor.open(text: "Some text") # # @param [Array<String>] files # the files to open in an editor - # @param [String] :command + # @param [String] command # the editor command to use, by default auto detects - # @param [String] :text + # @param [String] text # the text to edit in an editor - # @param [Hash] :env + # @param [Hash] env # environment variables to forward to the editor # # @return [Object] # # @api public @@ -108,57 +136,76 @@ editor.open(*files, text: text) end # Initialize an Editor # - # @param [String] :command + # @example + # TTY::Editor.new(command: "vim") + # + # @param [String] command # the editor command to use, by default auto detects - # @param [Hash] :env + # @param [Hash] env # environment variables to forward to the editor - # @param [IO] :input + # @param [IO] input # the standard input - # @param [IO] :output + # @param [IO] output # the standard output - # @param [Boolean] :raise_on_failure + # @param [Boolean] raise_on_failure # whether or not raise on command failure, false by default - # @param [Boolean] :show_menu - # whether or not show commands menu, true by default + # @param [Boolean] hide_menu + # whether or not to hide commands menu, false by default + # @param [Boolean] enable_color + # disable or force prompt coloring, defaults to nil + # @param [Symbol] menu_interrupt + # how to handle Ctrl+C key interrupt out of :error, :signal, :exit, :noop # # @api public - def initialize(command: nil, raise_on_failure: false, show_menu: true, - prompt: "Select an editor?", env: {}, - input: $stdin, output: $stdout, &block) + def initialize(command: nil, raise_on_failure: false, hide_menu: false, + prompt: "Select an editor?", env: {}, enable_color: nil, + input: $stdin, output: $stdout, menu_interrupt: :error, + &block) @env = env @command = nil @input = input @output = output @raise_on_failure = raise_on_failure - @show_menu = show_menu + @enable_color = enable_color + @hide_menu = hide_menu @prompt = prompt + @menu_interrupt = menu_interrupt block.(self) if block command(*Array(command)) end # Read or update environment vars # + # @example + # editor.env({"FOO" => "bar"}) + # + # @param [Hash{String => String}] value + # the environment variables to use + # # @return [Hash] # # @api public def env(value = (not_set = true)) return @env if not_set @env = value end - # Finds command using a configured command(s) or detected shell commands. + # Finds command using a configured command(s) or detected shell commands # - # @param [Array[String]] commands + # @example + # editor.command("vim") + # + # @param [Array<String>] commands # the optional command to use, by default auto detecting # - # @raise [TTY::CommandInvocationError] + # @raise [TTY::Editor::CommandInvocationError] # # @return [String] # # @api public def command(*commands) @@ -176,14 +223,14 @@ # Run editor command in a shell # # @param [Array<String>] files # the files to open in an editor - # @param [String] :text + # @param [String] text # the text to edit in an editor # - # @raise [TTY::CommandInvocationError] + # @raise [TTY::Editor::CommandInvocationError] # # @return [Boolean] # whether editor command suceeded or not # # @api private @@ -232,10 +279,12 @@ # Check if filename and text arguments are valid # # @raise [InvalidArgumentError] # + # @return [nil] + # # @api private def validate_arguments(files, text) return if files.empty? if files.all? { |file| ::File.exist?(file) } && !text.nil? @@ -267,11 +316,13 @@ # @return [String] # the chosen editor # # @api private def choose_exec_from(execs) - if @show_menu && execs.size > 1 - prompt = TTY::Prompt.new(input: @input, output: @output, env: @env) + if !@hide_menu && execs.size > 1 + prompt = TTY::Prompt.new(input: @input, output: @output, env: @env, + enable_color: @enable_color, + interrupt: @menu_interrupt) exec = prompt.enum_select(@prompt, execs) @output.print(prompt.cursor.up + prompt.cursor.clear_line) exec else execs[0]