Module: Complete

Defined in:
lib/droxi/complete.rb

Overview

Module containing tab-completion logic and methods.

Class Method Summary collapse

Class Method Details

.basename(string) ⇒ Object



109
110
111
# File 'lib/droxi/complete.rb', line 109

def self.basename(string)
  string.end_with?('/') ? '' : File.basename(string)
end

.collapse(path) ⇒ Object

Return a version of a path with .. and . resolved to appropriate directories.



129
130
131
132
133
134
# File 'lib/droxi/complete.rb', line 129

def self.collapse(path)
  new_path = path.dup
  nil while new_path.sub!(%r{[^/]+/\.\./}, '/')
  nil while new_path.sub!('./', '')
  new_path
end

.command(string, names) ⇒ Object

Return an Array of potential command name tab-completions for a String.



46
47
48
# File 'lib/droxi/complete.rb', line 46

def self.command(string, names)
  names.select { |n| n.start_with?(string) }.map { |n| n + ' ' }
end

.complete(line, state) ⇒ Object

Return an Array of completion options for the given input line and client state.



8
9
10
11
12
13
14
15
16
# File 'lib/droxi/complete.rb', line 8

def self.complete(line, state)
  tokens = Text.tokenize(line, include_empty: true)
  type = completion_type(tokens)
  completion_options(type, tokens.last, state).map do |option|
    option.gsub(' ', '\ ').sub(/\\ $/, ' ')
      .split.drop(tokens.last.count(' ')).join(' ')
      .sub(/[^\\\/]$/, '\0 ')
  end
end

.completion_options(type, word, state) ⇒ Object

Return an Array of potential tab-completion options for a given completion type, word, and client state.



22
23
24
25
26
27
28
29
30
31
# File 'lib/droxi/complete.rb', line 22

def self.completion_options(type, word, state)
  case type
  when 'COMMAND'     then command(word, Commands::NAMES)
  when 'LOCAL_FILE'  then local(word)
  when 'LOCAL_DIR'   then local_dir(word)
  when 'REMOTE_FILE' then remote(word, state)
  when 'REMOTE_DIR'  then remote_dir(word, state)
  else []
  end
end

.completion_type(tokens) ⇒ Object

Return a String representing the type of tab-completion that should be performed, given the current line buffer state.



35
36
37
38
39
40
41
42
43
# File 'lib/droxi/complete.rb', line 35

def self.completion_type(tokens)
  index = tokens.drop_while { |token| token[/^-\w+$/] }.size
  if index <= 1
    'COMMAND'
  elsif Commands::NAMES.include?(tokens.first)
    cmd = Commands.const_get(tokens.first.upcase.to_sym)
    cmd.type_of_arg(index - 2)
  end
end

.final_match(string, candidate, is_dir) ⇒ Object



117
118
119
# File 'lib/droxi/complete.rb', line 117

def self.final_match(string, candidate, is_dir)
  string + candidate.partition(basename(string))[2] + (is_dir ? '/' : ' ')
end

.local(string) ⇒ Object

Return an Array of potential local tab-completions for a String.



59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/droxi/complete.rb', line 59

def self.local(string)
  dir = local_search_path(string)
  basename = basename(string)

  begin
    matches = Dir.entries(dir).select { |entry| match?(basename, entry) }
    matches.map do |entry|
      final_match(string, entry, File.directory?(dir + '/' + entry))
    end
  rescue Errno::ENOENT
    []
  end
end

.local_dir(string) ⇒ Object

Return an Array of potential local tab-completions for a String, including only directories.



75
76
77
# File 'lib/droxi/complete.rb', line 75

def self.local_dir(string)
  local(string).select { |match| match.end_with?('/') }
end

.local_search_path(string) ⇒ Object

Return the directory in which to search for potential local tab-completions for a String.



52
53
54
55
56
# File 'lib/droxi/complete.rb', line 52

def self.local_search_path(string)
  File.expand_path(strip_filename(string))
rescue ArgumentError
  string
end

.match?(prefix, candidate) ⇒ Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/droxi/complete.rb', line 113

def self.match?(prefix, candidate)
  candidate.start_with?(prefix) && !candidate[/^\.\.?$/]
end

.remote(string, state) ⇒ Object

Return an Array of potential remote tab-completions for a String.



92
93
94
95
96
97
98
99
100
101
# File 'lib/droxi/complete.rb', line 92

def self.remote(string, state)
  dir = remote_search_path(string, state)
  basename = basename(string)

  entries = state.contents(dir).map { |entry| File.basename(entry) }
  matches = entries.select { |entry| match?(basename, entry) }
  matches.map do |entry|
    final_match(string, entry, state.directory?(dir + '/' + entry))
  end
end

.remote_dir(string, state) ⇒ Object

Return an Array of potential remote tab-completions for a String, including only directories.



105
106
107
# File 'lib/droxi/complete.rb', line 105

def self.remote_dir(string, state)
  remote(string, state).select { |result| result.end_with?('/') }
end

.remote_search_path(string, state) ⇒ Object

Return the directory in which to search for potential remote tab-completions for a String.



81
82
83
84
85
86
87
88
89
# File 'lib/droxi/complete.rb', line 81

def self.remote_search_path(string, state)
  path = case
         when string.empty? then state.pwd + '/'
         when string.start_with?('/') then string
         else state.pwd + '/' + string
         end

  strip_filename(collapse(path))
end

.strip_filename(path) ⇒ Object

Return the name of the directory indicated by a path.



122
123
124
125
# File 'lib/droxi/complete.rb', line 122

def self.strip_filename(path)
  return path if path == '/'
  path.end_with?('/') ? path.sub(/\/$/, '') : File.dirname(path)
end