class SimpleScripting::TabCompletion::CommandlineProcessor

Constants

BASE_CURSOR_MARKER

Arbitrary; can be anything (except an empty string).

LONG_OPTIONS_PREFIX
OPTIONS_TERMINATOR

Public Class Methods

process_commandline(source_commandline, cursor_position, switches_definition) click to toggle source
# File lib/simple_scripting/tab_completion/commandline_processor.rb, line 19
def self.process_commandline(source_commandline, cursor_position, switches_definition)
  # An input string with infinite "<tabN>" substrings will cause an infinite cycle (hehe).
  0.upto(Float::INFINITY) do |i|
    cursor_marker = BASE_CURSOR_MARKER.sub(">", "#{i}>")

    if !source_commandline.include?(cursor_marker)
      commandline_with_marker = source_commandline[0...cursor_position] + cursor_marker + source_commandline[cursor_position..-1].to_s

      # Remove the executable.
      processed_argv = Shellwords.split(commandline_with_marker)[1..-1]

      return new(processed_argv, cursor_marker, switches_definition)
    end
  end
end

Public Instance Methods

completing_an_option?() click to toggle source

We're abstracted from the commandline, with this exception. This is because while an option is being completed, the decoder would not recognize the key.

# File lib/simple_scripting/tab_completion/commandline_processor.rb, line 38
def completing_an_option?
  processed_argv[marked_word_position].start_with?(LONG_OPTIONS_PREFIX) && marked_word_position < options_terminator_position
end
completing_word_prefix() click to toggle source
# File lib/simple_scripting/tab_completion/commandline_processor.rb, line 46
def completing_word_prefix
  word = processed_argv[marked_word_position]

  # Regex alternative: [/\A(.*?)#{cursor_marker}/m, 1]
  word[0, word.index(cursor_marker)]
end
parsed_pairs() click to toggle source

Returns key, value prefix (before marker), value suffix (after marker), other_pairs

# File lib/simple_scripting/tab_completion/commandline_processor.rb, line 55
def parsed_pairs
  parsed_pairs = parse_argv || raise("Parsing error")

  key, value = parsed_pairs.detect do |_, value|
    !boolean?(value) && value.include?(cursor_marker)
  end

  # Impossible case, unless there is a programmatic error.
  #
  key || raise("Guru meditation! (#{self.class}##{__method__}:#{__LINE__})")

  value_prefix, value_suffix = value.split(cursor_marker)

  parsed_pairs.delete(key)

  [key, value_prefix || "", value_suffix || "", parsed_pairs]
end
parsing_error?() click to toggle source
# File lib/simple_scripting/tab_completion/commandline_processor.rb, line 42
def parsing_error?
  parse_argv.nil?
end

Private Instance Methods

boolean?(value) click to toggle source

For the lulz.

# File lib/simple_scripting/tab_completion/commandline_processor.rb, line 110
def boolean?(value)
  !!value == value
end
marked_word_position() click to toggle source
# File lib/simple_scripting/tab_completion/commandline_processor.rb, line 75
def marked_word_position
  processed_argv.index { |word| word.include?(cursor_marker) }
end
options_terminator_position() click to toggle source

Returns Float::INFINITY when there is no options terminator.

# File lib/simple_scripting/tab_completion/commandline_processor.rb, line 81
def options_terminator_position
  processed_argv.index(OPTIONS_TERMINATOR) || Float::INFINITY
end
parse_argv() click to toggle source

Helpers

# File lib/simple_scripting/tab_completion/commandline_processor.rb, line 89
def parse_argv
  # We need to convert all the arguments to optional, otherwise it's not possible to
  # autocomplete arguments when not all the mandatory ones are not typed yet, eg:
  #
  #   `my_command <tab>` with definition ['mand1', 'mand2']
  #
  adapted_switches_definition = switches_definition.dup

  adapted_switches_definition.each_with_index do |definition, i|
    adapted_switches_definition[i] = "[#{definition}]" if definition.is_a?(String) && !definition.start_with?('[')
  end

  SimpleScripting::Argv.decode(*adapted_switches_definition, arguments: processed_argv.dup, auto_help: false)
rescue Argv::InvalidCommand, Argv::ArgumentError, OptionParser::InvalidOption
  # OptionParser::InvalidOption: see case "-O<tab>" in test suite.

  # return nil
end