yap-shell-addon-tab-completion 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/Rakefile +6 -0
- data/lib/yap-shell-addon-tab-completion.rb +178 -0
- data/lib/yap-shell-addon-tab-completion/basic_completion.rb +151 -0
- data/lib/yap-shell-addon-tab-completion/completer.rb +61 -0
- data/lib/yap-shell-addon-tab-completion/completion_result.rb +25 -0
- data/lib/yap-shell-addon-tab-completion/custom_completion.rb +33 -0
- data/lib/yap-shell-addon-tab-completion/dsl_methods.rb +7 -0
- data/lib/yap-shell-addon-tab-completion/version.rb +3 -0
- data/yap-shell-addon-tab-completion.gemspec +25 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 17f0d28a12033e862e37d1fb8cde3087567f02a8
|
4
|
+
data.tar.gz: 931b2fecaad8de83abeb99f497f0d5eca0859a8e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 88a04ac6f611fd87a381ca5bcd8bea6f6a068257d524297826db1d40af302ab95bb19a64d05cc7092b19ae30265722b579f75c47c9249cda51a846f0655a6766
|
7
|
+
data.tar.gz: df594f455eb5d59e4b27802edbea32ab0fdaeb10e4f2d5135a0a602a303fdc33113843f5165ac959efaa7dc1bb251673497ab69f8da789de670e10713b0b1ac2
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
wiki/
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.1
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Zach Dennis
|
4
|
+
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# tab-completion for yap-shell
|
2
|
+
|
3
|
+
Welcome to your new yap addon! In this directory, you'll find the files you need to be able to package up your addon into a gem. Put your Ruby code in the file `lib/yap-shell-addon-tab-completion`.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'yap-shell-addon-tab-completion'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install yap-shell-addon-tab-completion
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/yap-shell-addon-tab-completion.
|
36
|
+
|
37
|
+
|
38
|
+
## License
|
39
|
+
|
40
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'yap/addon'
|
2
|
+
require 'yap-shell-addon-tab-completion/basic_completion'
|
3
|
+
require 'yap-shell-addon-tab-completion/completer'
|
4
|
+
require 'yap-shell-addon-tab-completion/completion_result'
|
5
|
+
require 'yap-shell-addon-tab-completion/custom_completion'
|
6
|
+
require 'yap-shell-addon-tab-completion/dsl_methods'
|
7
|
+
require 'yap-shell-addon-tab-completion/version'
|
8
|
+
|
9
|
+
module YapShellAddonTabCompletion
|
10
|
+
class Addon < ::Yap::Addon::Base
|
11
|
+
self.export_as :'tab-completion'
|
12
|
+
|
13
|
+
class CompletionResult
|
14
|
+
attr_accessor :text, :type, :descriptive_text
|
15
|
+
|
16
|
+
def initialize(text:, type:, descriptive_text: nil)
|
17
|
+
@descriptive_text = descriptive_text || text
|
18
|
+
@text = text
|
19
|
+
@type = type
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==(other)
|
23
|
+
other.is_a?(self.class) && @text == other.text && @type == other.type
|
24
|
+
end
|
25
|
+
|
26
|
+
def <=>(other)
|
27
|
+
@text <=> other.text
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
@text.to_s
|
32
|
+
end
|
33
|
+
alias_method :to_str, :to_s
|
34
|
+
alias_method :inspect, :to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
COMPLETIONS = [ BasicCompletion ]
|
38
|
+
|
39
|
+
Color = Term::ANSIColor
|
40
|
+
|
41
|
+
DISPLAY_PROCS = Hash.new{ |h,k| h[k] = ->(text){ text } }.merge(
|
42
|
+
directory: -> (text){ text + "/" }
|
43
|
+
)
|
44
|
+
|
45
|
+
STYLE_PROCS = Hash.new{ |h,k| h[k] = ->(text){ text } }.merge(
|
46
|
+
alias: -> (text){ Color.bold(Color.color("#ff00d7"){ text } ) },
|
47
|
+
builtin: -> (text){ Color.bold(Color.color("#d7af00"){ text } ) },
|
48
|
+
directory: -> (text){ Color.bold(Color.red(text)) },
|
49
|
+
command: -> (text){ Color.bold(Color.green(text)) },
|
50
|
+
shell_command: -> (text){ Color.bold(Color.color("#ffafff"){ text } ) },
|
51
|
+
symlink: -> (text){ Color.bold(Color.cyan(text)) },
|
52
|
+
selected: -> (text){ Color.negative(text) }
|
53
|
+
)
|
54
|
+
|
55
|
+
DECORATION_PROCS = Hash.new{ |h,k| h[k] = ->(text){ text } }.merge(
|
56
|
+
directory: -> (text){ text + "/" },
|
57
|
+
command: -> (text){ text + "@" },
|
58
|
+
shell_command: -> (text) { text + "🐚" }
|
59
|
+
)
|
60
|
+
|
61
|
+
attr_reader :editor, :world
|
62
|
+
|
63
|
+
def initialize_world(world)
|
64
|
+
@world = world
|
65
|
+
@world.extend YapShellAddonTabCompletion::DslMethods
|
66
|
+
@editor = @world.editor
|
67
|
+
@editor.completion_proc = -> (word, line, word_index){
|
68
|
+
complete(word, line, word_index)
|
69
|
+
}
|
70
|
+
@editor.bind(:tab){ @editor.complete }
|
71
|
+
@completions = COMPLETIONS.dup
|
72
|
+
|
73
|
+
@style_procs = STYLE_PROCS.dup
|
74
|
+
@decoration_procs = DECORATION_PROCS.dup
|
75
|
+
@display_procs = DISPLAY_PROCS.dup
|
76
|
+
|
77
|
+
editor.on_word_complete do |event|
|
78
|
+
logger.puts "on_word_complete event: #{event}"
|
79
|
+
|
80
|
+
sub_word = event[:payload][:sub_word]
|
81
|
+
word = event[:payload][:word]
|
82
|
+
actual_completion = event[:payload][:completion]
|
83
|
+
possible_completions = event[:payload][:possible_completions]
|
84
|
+
|
85
|
+
semi_formatted_possibilities = possible_completions.map.with_index do |completion, i|
|
86
|
+
if completion == actual_completion
|
87
|
+
style_text_for_selected_match(completion) + "\e[0m"
|
88
|
+
else
|
89
|
+
style_text_for_nonselected_match(completion) + "\e[0m"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
max_width = @editor.terminal_width
|
94
|
+
max_item_width = semi_formatted_possibilities.map(&:length).max + 2
|
95
|
+
most_per_line = max_width / max_item_width
|
96
|
+
padding_at_the_end = max_width % max_item_width
|
97
|
+
|
98
|
+
formatted_possibilities = semi_formatted_possibilities.map.with_index do |completion, i|
|
99
|
+
spaces_to_pad = max_item_width - completion.length
|
100
|
+
completion + (" " * spaces_to_pad)
|
101
|
+
end
|
102
|
+
|
103
|
+
editor.content_box.children = formatted_possibilities.map do |str|
|
104
|
+
TerminalLayout::Box.new(content: str, style: { display: :float, float: :left, height: 1, width: max_item_width })
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
editor.on_word_complete_no_match do |event|
|
109
|
+
logger.puts "on_word_complete_no_match event: #{event}"
|
110
|
+
|
111
|
+
sub_word = event[:payload][:sub_word]
|
112
|
+
word = event[:payload][:word]
|
113
|
+
editor.content_box.children = []
|
114
|
+
# editor.content_box.content = "Failed to find a match to complete #{sub_word} portion of #{word}"
|
115
|
+
end
|
116
|
+
|
117
|
+
editor.on_word_complete_done do |event|
|
118
|
+
logger.puts "on_word_complete_done event: #{event}"
|
119
|
+
|
120
|
+
# TODO: add a better way to clear content
|
121
|
+
editor.content_box.children = []
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def add_completion(name, pattern, &blk)
|
126
|
+
raise ArgumentError, "Must supply block!" unless block_given?
|
127
|
+
logger.puts "NO-OP add_completion for name=#{name.inspect} pattern=#{pattern.inspect} block?=#{block_given?}"
|
128
|
+
# @completions.push CustomCompletion.new(name:name, pattern:pattern, world:world, &blk)
|
129
|
+
end
|
130
|
+
|
131
|
+
def set_decoration(type, &blk)
|
132
|
+
raise ArgumentError, "Must supply block!" unless block_given?
|
133
|
+
logger.puts "set_decoration for type=#{name.inspect}"
|
134
|
+
@style_procs[type] = blk
|
135
|
+
end
|
136
|
+
|
137
|
+
def complete(word, words, word_index)
|
138
|
+
logger.puts "complete word=#{word.inspect} words=#{words.inspect} word_index=#{word_index.inspect}"
|
139
|
+
|
140
|
+
matches = @completions.sort_by(&:priority).reverse.map do |completion|
|
141
|
+
if completion.respond_to?(:call)
|
142
|
+
completion.call
|
143
|
+
else
|
144
|
+
completions = completion.new(
|
145
|
+
world: @world,
|
146
|
+
word_break_characters: editor.word_break_characters
|
147
|
+
).completions_for(word, words, word_index)
|
148
|
+
completions.each do |completion|
|
149
|
+
completion.text = display_text_for_match(completion)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end.flatten
|
153
|
+
|
154
|
+
logger.puts "complete possible matches are #{matches.inspect}"
|
155
|
+
matches
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def display_text_for_match(match)
|
161
|
+
ANSIString.new @display_procs[match.type].call(match.text.dup)
|
162
|
+
end
|
163
|
+
|
164
|
+
def style_text_for_selected_match(match)
|
165
|
+
styled_text = @style_procs[match.type].call(match.descriptive_text.dup).to_s
|
166
|
+
styled_text = @decoration_procs[match.type].call(styled_text).to_s
|
167
|
+
uncolored_text = Color.uncolored(styled_text)
|
168
|
+
ANSIString.new @style_procs[:selected].call(uncolored_text)
|
169
|
+
end
|
170
|
+
|
171
|
+
def style_text_for_nonselected_match(match)
|
172
|
+
str = @decoration_procs[match.type].call(
|
173
|
+
@style_procs[match.type].call(match.descriptive_text.dup)
|
174
|
+
)
|
175
|
+
ANSIString.new str
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module YapShellAddonTabCompletion
|
2
|
+
class BasicCompletion
|
3
|
+
class << self
|
4
|
+
attr_accessor :priority
|
5
|
+
end
|
6
|
+
self.priority = 1
|
7
|
+
|
8
|
+
attr_reader :world
|
9
|
+
|
10
|
+
def initialize(world:, word_break_characters:, path:nil)
|
11
|
+
@world = world
|
12
|
+
@word_break_characters = word_break_characters
|
13
|
+
path ||= @world.env["PATH"]
|
14
|
+
@paths = path.split(":")
|
15
|
+
end
|
16
|
+
|
17
|
+
def completions_for(word, words, word_index)
|
18
|
+
completions_by_name = {}
|
19
|
+
if looking_for_command?(word, words, word_index)
|
20
|
+
# Lowest Priority
|
21
|
+
completions_by_name.merge! command_completion_matches_for(word, words)
|
22
|
+
|
23
|
+
# Low Priority
|
24
|
+
completions_by_name.merge! builtin_completion_matches_for(word, words)
|
25
|
+
|
26
|
+
# Medium Priority
|
27
|
+
completions_by_name.merge! executable_filename_completion_matches_for(word, words)
|
28
|
+
|
29
|
+
# High Priority
|
30
|
+
completions_by_name.merge! shell_command_completion_matches_for(word, words)
|
31
|
+
|
32
|
+
# Highest Priority
|
33
|
+
completions_by_name.merge! alias_completion_matches_for(word, words)
|
34
|
+
else
|
35
|
+
completions_by_name.merge! filename_completion_matches_for(word, words)
|
36
|
+
end
|
37
|
+
completions_by_name.merge! environment_variable_completions_for(word, words)
|
38
|
+
completions_by_name.values
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def looking_for_command?(word, words, word_index)
|
44
|
+
return false unless word_index
|
45
|
+
return true if word_index == 0
|
46
|
+
return true if words[word_index - 1] =~ /[;&]/
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
50
|
+
def alias_completion_matches_for(word, words)
|
51
|
+
@world.aliases.names.each_with_object({}) do |name, result|
|
52
|
+
if name =~ /^#{Regexp.escape(word)}/
|
53
|
+
result[name] ||= CompletionResult.new(
|
54
|
+
type: :alias,
|
55
|
+
text: name
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def builtin_completion_matches_for(word, words)
|
62
|
+
@world.builtins.each_with_object({}) do |builtin, result|
|
63
|
+
if builtin =~ /^#{Regexp.escape(word)}/
|
64
|
+
result[builtin] ||= CompletionResult.new(
|
65
|
+
type: :builtin,
|
66
|
+
text: builtin
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def command_completion_matches_for(word, words)
|
73
|
+
@paths.each_with_object({}) do |path, matches|
|
74
|
+
glob = File.join(path, "#{word}*")
|
75
|
+
arr = Dir[glob].select { |path| File.executable?(path) && File.file?(path) }
|
76
|
+
arr.map { |path| File.basename(path) }.uniq.each do |command|
|
77
|
+
matches[command] = CompletionResult.new(type: :command, text: command)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def environment_variable_completions_for(word, words)
|
83
|
+
return {} unless word =~ /^\$/
|
84
|
+
prefix, word_sans_prefix = word[0], word[1..-1]
|
85
|
+
@world.env.keys.each_with_object({}) do |env_var, result|
|
86
|
+
if env_var =~ /^#{Regexp.escape(word_sans_prefix)}/
|
87
|
+
result[env_var] ||= CompletionResult.new(
|
88
|
+
type: :env_var,
|
89
|
+
text: prefix + env_var
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def executable_filename_completion_matches_for(word, words)
|
96
|
+
glob = "#{word}*"
|
97
|
+
glob.gsub!("~", world.env["HOME"])
|
98
|
+
Dir.glob(glob, File::FNM_CASEFOLD).each_with_object({}) do |path, result|
|
99
|
+
text = path.gsub(filtered_work_break_characters_rgx, '\\\\\1')
|
100
|
+
descriptive_text = File.basename(text)
|
101
|
+
if !File.directory?(path) && File.executable?(path)
|
102
|
+
result[path] = CompletionResult.new(
|
103
|
+
type: :command,
|
104
|
+
text: text,
|
105
|
+
descriptive_text: descriptive_text
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def shell_command_completion_matches_for(word, words)
|
112
|
+
@world.shell_commands.each_with_object({}) do |shell_command, result|
|
113
|
+
if shell_command =~ /^#{Regexp.escape(word)}/
|
114
|
+
result[shell_command] ||= CompletionResult.new(
|
115
|
+
type: :shell_command,
|
116
|
+
text: shell_command
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def filename_completion_matches_for(word, line)
|
123
|
+
glob = "#{word}*"
|
124
|
+
glob.gsub!("~", world.env["HOME"])
|
125
|
+
Dir.glob(glob, File::FNM_CASEFOLD).each_with_object({}) do |path, result|
|
126
|
+
text = path.gsub(filtered_work_break_characters_rgx, '\\\\\1')
|
127
|
+
descriptive_text = File.basename(text)
|
128
|
+
result[path] = if File.directory?(path)
|
129
|
+
CompletionResult.new(type: :directory, text: text, descriptive_text: descriptive_text)
|
130
|
+
elsif File.symlink?(path)
|
131
|
+
CompletionResult.new(type: :symlink, text: text, descriptive_text: descriptive_text)
|
132
|
+
elsif File.file?(path) && File.executable?(path)
|
133
|
+
CompletionResult.new(type: :command, text: text, descriptive_text: descriptive_text)
|
134
|
+
else
|
135
|
+
CompletionResult.new(type: :file, text: text, descriptive_text: descriptive_text)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Remove file separator and the back-slash from word break characters when determining
|
141
|
+
# the pre-word-context
|
142
|
+
def filtered_word_break_characters
|
143
|
+
@word_break_characters.sub(File::Separator, "").sub('\\', '')
|
144
|
+
end
|
145
|
+
|
146
|
+
def filtered_work_break_characters_rgx
|
147
|
+
/([#{Regexp.escape(filtered_word_break_characters)}])/
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module YapShellAddonTabCompletion
|
2
|
+
class Completer
|
3
|
+
def initialize(char:, line:, completion:, completion_found:, completion_not_found:, done:)
|
4
|
+
@completion_char = char
|
5
|
+
@line = line
|
6
|
+
@completion_proc = completion
|
7
|
+
@completion_found_proc = completion_found
|
8
|
+
@completion_not_found_proc = completion_not_found
|
9
|
+
@done_proc = done
|
10
|
+
|
11
|
+
@completion_matches = HistoryBuffer.new(0) do |h|
|
12
|
+
h.duplicates = false
|
13
|
+
h.cycle = true
|
14
|
+
end
|
15
|
+
@completion_matches.empty
|
16
|
+
|
17
|
+
@first_time = true
|
18
|
+
@word_start = @line.word[:start]
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_bytes(bytes)
|
22
|
+
return unless bytes.any?
|
23
|
+
|
24
|
+
if bytes.map(&:ord) != @completion_char
|
25
|
+
@done_proc.call(bytes)
|
26
|
+
elsif @first_time
|
27
|
+
matches = @completion_proc.call(sub_word) unless !@completion_proc || @completion_proc == []
|
28
|
+
matches = matches.to_a.compact.sort.reverse
|
29
|
+
|
30
|
+
if matches.any?
|
31
|
+
@completion_matches.resize(matches.length)
|
32
|
+
matches.each { |w| @completion_matches << w }
|
33
|
+
|
34
|
+
# Get first match
|
35
|
+
@completion_matches.back
|
36
|
+
match = @completion_matches.get
|
37
|
+
|
38
|
+
# completion matches is a history implementation and its in reverse order from what
|
39
|
+
# a user would expect
|
40
|
+
@completion_found_proc.call(completion: match, possible_completions: @completion_matches.reverse)
|
41
|
+
else
|
42
|
+
@completion_not_found_proc.call
|
43
|
+
@done_proc.call
|
44
|
+
end
|
45
|
+
@first_time = false
|
46
|
+
else
|
47
|
+
@completion_matches.back
|
48
|
+
match = @completion_matches.get
|
49
|
+
|
50
|
+
@completion_found_proc.call(completion: match, possible_completions: @completion_matches.reverse)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def sub_word
|
57
|
+
@line.text[@line.word[:start]..@line.position-1] || ""
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module YapShellAddonTabCompletion
|
2
|
+
class CompletionResult
|
3
|
+
attr_accessor :text, :type, :descriptive_text
|
4
|
+
|
5
|
+
def initialize(text:, type:, descriptive_text: nil)
|
6
|
+
@descriptive_text = descriptive_text || text
|
7
|
+
@text = text
|
8
|
+
@type = type
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
other.is_a?(self.class) && @text == other.text && @type == other.type
|
13
|
+
end
|
14
|
+
|
15
|
+
def <=>(other)
|
16
|
+
@text <=> other.text
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@text.to_s
|
21
|
+
end
|
22
|
+
alias_method :to_str, :to_s
|
23
|
+
alias_method :inspect, :to_s
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module YapShellAddonTabCompletion
|
2
|
+
class CustomCompletion
|
3
|
+
PRIORITY = 2
|
4
|
+
|
5
|
+
attr_reader :name, :pattern, :priority
|
6
|
+
|
7
|
+
def initialize(world:, name:nil, pattern:nil, priority:PRIORITY, &blk)
|
8
|
+
@world = world
|
9
|
+
@name = name
|
10
|
+
@pattern = pattern
|
11
|
+
@priority = priority
|
12
|
+
@blk = blk
|
13
|
+
end
|
14
|
+
|
15
|
+
def new(world:)
|
16
|
+
@world = world
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def completions_for(word, line)
|
21
|
+
# TODO
|
22
|
+
return []
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def match_rgx
|
28
|
+
return // if pattern.nil?
|
29
|
+
return pattern if pattern.is_a?(Regexp)
|
30
|
+
/^#{Regexp.escape(pattern.to_s)}\s/
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/lib/yap-shell-addon-tab-completion/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'yap-shell-addon-tab-completion'
|
5
|
+
spec.version = YapShellAddonTabCompletion::VERSION
|
6
|
+
spec.authors = ['Your name']
|
7
|
+
spec.email = 'you@example.com'
|
8
|
+
spec.date = Date.today.to_s
|
9
|
+
|
10
|
+
spec.summary = 'tab-completion summary goes here.'
|
11
|
+
spec.description = 'tab-completion description goes here.'
|
12
|
+
spec.homepage = ''
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(/^(test|spec|features)\//) }
|
16
|
+
spec.bindir = "exe"
|
17
|
+
spec.executables = spec.files.grep(/^exe\//) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency "yap-shell", "~> 0.6"
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
23
|
+
spec.add_development_dependency "rake", "~> 11.2"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.4"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yap-shell-addon-tab-completion
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Your name
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: yap-shell
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.12'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.12'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '11.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '11.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.4'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.4'
|
69
|
+
description: tab-completion description goes here.
|
70
|
+
email: you@example.com
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- ".gitignore"
|
76
|
+
- ".ruby-version"
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- lib/yap-shell-addon-tab-completion.rb
|
82
|
+
- lib/yap-shell-addon-tab-completion/basic_completion.rb
|
83
|
+
- lib/yap-shell-addon-tab-completion/completer.rb
|
84
|
+
- lib/yap-shell-addon-tab-completion/completion_result.rb
|
85
|
+
- lib/yap-shell-addon-tab-completion/custom_completion.rb
|
86
|
+
- lib/yap-shell-addon-tab-completion/dsl_methods.rb
|
87
|
+
- lib/yap-shell-addon-tab-completion/version.rb
|
88
|
+
- yap-shell-addon-tab-completion.gemspec
|
89
|
+
homepage: ''
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.5.1
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: tab-completion summary goes here.
|
113
|
+
test_files: []
|
114
|
+
has_rdoc:
|