tabry 0.2.2 → 0.2.4
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 +4 -4
- data/bin/tabry-fish +9 -0
- data/bin/tabry-generate-fish-complete +31 -0
- data/lib/tabry/cli/all_in_one.rb +27 -6
- data/lib/tabry/config_builder/arg_or_flag_builder.rb +5 -0
- data/lib/tabry/config_builder/top_level_builder.rb +3 -0
- data/lib/tabry/machine.rb +1 -1
- data/lib/tabry/models/const_option.rb +1 -1
- data/lib/tabry/models/flags_list.rb +6 -2
- data/lib/tabry/models/include_option.rb +2 -2
- data/lib/tabry/models/method_option.rb +14 -0
- data/lib/tabry/models/option.rb +5 -5
- data/lib/tabry/models/options_list.rb +2 -2
- data/lib/tabry/models/shell_option.rb +1 -1
- data/lib/tabry/models/subs_list.rb +13 -2
- data/lib/tabry/options_finder.rb +13 -8
- data/lib/tabry/replty/base.rb +23 -0
- data/lib/tabry/replty/builder.rb +100 -0
- data/lib/tabry/runner.rb +2 -2
- data/lib/tabry/shell_tokenizer.rb +50 -0
- data/lib/tabry/shells/bash/wrapper.rb +2 -2
- data/lib/tabry/shells/fish/wrapper.rb +51 -0
- data/lib/tabry/shells/fish.rb +82 -0
- data/lib/tabry/version.rb +3 -0
- data/sh/fish/README.md +2 -1
- data/sh/fish/tabry_fish.fish +17 -11
- data/spec/fixtures/config_builder/underscoresdashes.yml +1 -1
- data/spec/fixtures/vehicles-expectations.json +351 -0
- data/spec/tabry/cli/all_in_one_spec.rb +12 -0
- data/spec/tabry/machine_spec.rb +5 -82
- data/spec/tabry/shell_tokenizer_spec.rb +34 -0
- data/tabry.gemspec +2 -4
- data/treesitter/corpus/opts.txt +2 -0
- data/treesitter/grammar.js +7 -0
- data/treesitter/src/grammar.json +21 -0
- data/treesitter/src/node-types.json +23 -0
- data/treesitter/src/parser.c +959 -893
- data/treesitter/tabry-compile.js +6 -0
- metadata +16 -5
- data/lib/tabry/shell_splitter.rb +0 -34
- data/spec/tabry/shell_splitter_spec.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87fa1708b1efb9ef81588017419bb6c63455ee786870f882783cf59f22ea462b
|
4
|
+
data.tar.gz: f0822419f2a36d48148cf7cf0ecdbd195666e2c5fe284d2e8dd6a563976c0842
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de752dc58cefc4cc1caed94a4f0ae34758fdb40a9973f48ed5471b17fef517b0eecfbfd7e3735179793cce8d16d739d2cb2fe6d833c3f765f5c57e353dac1b7a
|
7
|
+
data.tar.gz: 3e26e31d951fbc7f076b3c6e5b2c96eccfd1287c5469f84447ec549dc6aca51746dc193cbed36276b2e81dab67ce2bee8204cc0e8bb6b4010a07b284bac75f32
|
data/bin/tabry-fish
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Run by the tabry_fish.sh fish tab completion function to plug into Tabry
|
5
|
+
# to get completion options.
|
6
|
+
# Fish-specific entrypoint
|
7
|
+
|
8
|
+
require_relative "../lib/tabry/shells/fish/wrapper"
|
9
|
+
Tabry::Fish::Wrapper.run(*ARGV)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "../lib/tabry/cli/all_in_one"
|
4
|
+
require_relative "../lib/tabry/shells/fish"
|
5
|
+
|
6
|
+
Tabry::CLI::AllInOne.run do
|
7
|
+
config(names_underscores_to_dashes: true) do
|
8
|
+
desc <<~DESC
|
9
|
+
Generates fish completion for the tabry relative to this script
|
10
|
+
for the given command name and tabry JSON/YML file. Generates
|
11
|
+
fish functions unique to the command name to avoid conflicting with
|
12
|
+
other potential tabry installations.
|
13
|
+
DESC
|
14
|
+
arg :command_name, "Name of the command, e.g. 'mycli'"
|
15
|
+
arg :tabry_json_path, "Path to the mycli.json or mycli.yml compiled tabry file"
|
16
|
+
flagarg "uniq-fn-id,i", <<~DESC
|
17
|
+
Unique identifier to use for the unique tabry completion bash functions
|
18
|
+
(default is the capitalized command name)
|
19
|
+
DESC
|
20
|
+
end
|
21
|
+
|
22
|
+
def main
|
23
|
+
puts Tabry::Shells::Fish.generate(
|
24
|
+
args.command_name,
|
25
|
+
args.tabry_json_path,
|
26
|
+
uniq_fn_id: flags.uniq_fn_id,
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
data/lib/tabry/cli/all_in_one.rb
CHANGED
@@ -25,7 +25,7 @@ module Tabry
|
|
25
25
|
|
26
26
|
# Hack to avoid processing block any more if only running completion
|
27
27
|
# That way you an have expensive requires after the include
|
28
|
-
if (ARGV.first == "completion") && ARGV.length == 3 && ARGV[2].to_s =~ /^[0-9]+$/
|
28
|
+
if ((ARGV.first == "completion")) && ARGV.length == 3 && ARGV[2].to_s =~ /^[0-9]+$/
|
29
29
|
throw :run_completion, true
|
30
30
|
end
|
31
31
|
end
|
@@ -45,14 +45,26 @@ module Tabry
|
|
45
45
|
|
46
46
|
def self.define_completion_methods(cli_class, config, cmd_name: nil)
|
47
47
|
cli_class.module_eval do
|
48
|
+
|
48
49
|
define_method :completion__bash do
|
49
50
|
require_relative "../shells/bash"
|
50
51
|
Kernel.puts Tabry::Shells::Bash.generate_self(cmd_name: cmd_name)
|
51
52
|
end
|
52
53
|
|
54
|
+
define_method :completion__fish do
|
55
|
+
require_relative "../shells/fish"
|
56
|
+
Kernel.puts Tabry::Shells::Fish.generate_self(cmd_name: cmd_name)
|
57
|
+
end
|
58
|
+
|
53
59
|
define_method :completion do
|
54
|
-
|
55
|
-
|
60
|
+
fish_mode = ENV.fetch("TABRY_FISH_MODE", false)
|
61
|
+
if fish_mode
|
62
|
+
require_relative "../shells/fish/wrapper"
|
63
|
+
Tabry::Fish::Wrapper.run(args.cmd_line, args.comp_point, config: config)
|
64
|
+
else
|
65
|
+
require_relative "../shells/bash/wrapper"
|
66
|
+
Tabry::Bash::Wrapper.run(args.cmd_line, args.comp_point, config: config)
|
67
|
+
end
|
56
68
|
end
|
57
69
|
end
|
58
70
|
end
|
@@ -77,13 +89,22 @@ module Tabry
|
|
77
89
|
# If we recognize command is going to be a completion command, fast track and
|
78
90
|
# run completion now
|
79
91
|
if run_completion
|
80
|
-
|
81
|
-
|
92
|
+
if ARGV.first == "completion"
|
93
|
+
fish_mode = ENV.fetch("TABRY_FISH_MODE", false)
|
94
|
+
if fish_mode
|
95
|
+
require_relative "../shells/fish/wrapper"
|
96
|
+
Tabry::Fish::Wrapper.run(ARGV[1], ARGV[2], config: config)
|
97
|
+
else
|
98
|
+
require_relative "../shells/bash/wrapper"
|
99
|
+
Tabry::Bash::Wrapper.run(ARGV[1], ARGV[2], config: config)
|
100
|
+
end
|
101
|
+
exit
|
102
|
+
end
|
82
103
|
end
|
83
104
|
|
84
105
|
# If we recognize there is a "completion" subcommand, add completion
|
85
106
|
# methods -- if not already defined by caller in the block
|
86
|
-
if config.main.subs.by_name["completion"] && !cli.instance_methods.include?(:completion__bash)
|
107
|
+
if config.main.subs.by_name["completion"] && !cli.instance_methods.include?(:completion__bash) && !cli.instance_methods.include?(:completion__fish)
|
87
108
|
define_completion_methods(cli, config)
|
88
109
|
end
|
89
110
|
|
@@ -21,6 +21,11 @@ module Tabry
|
|
21
21
|
[{ "type" => "shell", "value" => cmd.to_s }]
|
22
22
|
end
|
23
23
|
|
24
|
+
# maybe I should rename this ... and include... ?
|
25
|
+
def method(met_name)
|
26
|
+
[{ "type" => "method", "value" => met_name.to_s }]
|
27
|
+
end
|
28
|
+
|
24
29
|
def file
|
25
30
|
[{ "type" => "file" }]
|
26
31
|
end
|
@@ -10,11 +10,13 @@ module Tabry
|
|
10
10
|
|
11
11
|
def defargs(name, &blk)
|
12
12
|
name = name.to_s.gsub(/^@/, "")
|
13
|
+
name = name.gsub("_", "-") if _opts[:names_underscores_to_dashes]
|
13
14
|
_set_hash :arg_includes, name, _build(SubBuilder, &blk)
|
14
15
|
end
|
15
16
|
|
16
17
|
def defopts(name, &blk)
|
17
18
|
name = name.to_s.gsub(/^@/, "")
|
19
|
+
name = name.gsub("_", "-") if _opts[:names_underscores_to_dashes]
|
18
20
|
_set_hash :option_includes, name, _build(ArgOrFlagBuilder, &blk)["options"]
|
19
21
|
end
|
20
22
|
|
@@ -22,6 +24,7 @@ module Tabry
|
|
22
24
|
sub :completion do
|
23
25
|
desc "Get tab completion shell config"
|
24
26
|
sub :bash, "Get tab completion for bash or zsh"
|
27
|
+
sub :fish, "Get tab completion for fish"
|
25
28
|
arg :cmd_line, "(for internal usage, when used instead of subcommand) full command line for getting completion options"
|
26
29
|
arg :comp_point, "(for internal usage, when used instead of subcommand) comp point"
|
27
30
|
end
|
data/lib/tabry/machine.rb
CHANGED
@@ -20,7 +20,7 @@ module Tabry
|
|
20
20
|
|
21
21
|
def initialize(config)
|
22
22
|
@config = config
|
23
|
-
@state = State.new(mode: :subcommand, subcommand_stack: [], args: [], flags: {})
|
23
|
+
@state = State.new(mode: :subcommand, subcommand_stack: [], args: [], flags: {}, help: false, dashdash: false)
|
24
24
|
end
|
25
25
|
|
26
26
|
def run(tokens)
|
@@ -16,10 +16,14 @@ module Tabry
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def options(token, used:)
|
19
|
+
def options(token, used:, params:)
|
20
20
|
to_a.map do |flag|
|
21
21
|
if token&.start_with?("-") && flag.name_with_dashes.start_with?(token) && !used[flag.name]
|
22
|
-
|
22
|
+
if params[:descriptions]
|
23
|
+
[flag.name_with_dashes, flag.description]
|
24
|
+
else
|
25
|
+
flag.name_with_dashes
|
26
|
+
end
|
23
27
|
end
|
24
28
|
end.compact
|
25
29
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "option_base"
|
4
|
+
|
5
|
+
module Tabry
|
6
|
+
module Models
|
7
|
+
class MethodOption < OptionBase
|
8
|
+
# TODO: Handled upstream for now, could change later.
|
9
|
+
def options(_token, _params)
|
10
|
+
[value.to_sym]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/tabry/models/option.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "config_error"
|
4
|
-
|
5
|
-
require_relative "
|
6
|
-
|
7
|
-
require_relative "file_option"
|
8
|
-
require_relative "dir_option"
|
4
|
+
%w[const shell include file dir method].each do |type|
|
5
|
+
require_relative "#{type}_option"
|
6
|
+
end
|
9
7
|
|
10
8
|
module Tabry
|
11
9
|
module Models
|
@@ -18,6 +16,8 @@ module Tabry
|
|
18
16
|
ConstOption.new(**args)
|
19
17
|
when "shell"
|
20
18
|
ShellOption.new(**args)
|
19
|
+
when "method"
|
20
|
+
MethodOption.new(**args)
|
21
21
|
when "include"
|
22
22
|
IncludeOption.new(**args)
|
23
23
|
when "file"
|
@@ -20,9 +20,20 @@ module Tabry
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def options(token)
|
24
|
-
|
23
|
+
def options(token, params)
|
24
|
+
::Tabry::Util.debug "checking:::: #{token}"
|
25
|
+
to_a
|
26
|
+
.select { |sub| sub.name.start_with?(token) }
|
27
|
+
.map do |sub|
|
28
|
+
if params[:descriptions]
|
29
|
+
[sub.name, sub.description]
|
30
|
+
else
|
31
|
+
sub.name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
.compact
|
25
35
|
end
|
36
|
+
|
26
37
|
end
|
27
38
|
end
|
28
39
|
end
|
data/lib/tabry/options_finder.rb
CHANGED
@@ -4,15 +4,16 @@ require "json"
|
|
4
4
|
|
5
5
|
module Tabry
|
6
6
|
class OptionsFinder
|
7
|
-
attr_reader :result
|
7
|
+
attr_reader :result, :params
|
8
8
|
|
9
9
|
# Returns an array of options
|
10
|
-
def self.options(result, token)
|
11
|
-
new(result).options(token)
|
10
|
+
def self.options(result, token, params)
|
11
|
+
new(result, params).options(token)
|
12
12
|
end
|
13
13
|
|
14
|
-
def initialize(result)
|
14
|
+
def initialize(result, params)
|
15
15
|
@result = result
|
16
|
+
@params = params
|
16
17
|
end
|
17
18
|
|
18
19
|
def options(token)
|
@@ -59,7 +60,7 @@ module Tabry
|
|
59
60
|
|
60
61
|
def options_flagarg(token)
|
61
62
|
result.sub_stack.map do |sub|
|
62
|
-
sub.flags[state.current_flag]&.options&.options(token)
|
63
|
+
sub.flags[state.current_flag]&.options&.options(token, params)
|
63
64
|
end.compact.flatten.uniq
|
64
65
|
end
|
65
66
|
|
@@ -68,14 +69,18 @@ module Tabry
|
|
68
69
|
# once an arg has been given, can no longer use a subcommand
|
69
70
|
[]
|
70
71
|
else
|
71
|
-
current_sub.subs.options(token)
|
72
|
+
current_sub.subs.options(token, params)
|
72
73
|
end
|
73
74
|
end
|
74
75
|
|
75
76
|
def options_subcommand_flags(token)
|
76
77
|
return [] if state.dashdash
|
77
78
|
|
78
|
-
result
|
79
|
+
result
|
80
|
+
.sub_stack
|
81
|
+
.map { |sub| sub.flags.options(token, used: state.flags, params: params) }
|
82
|
+
.flatten(1)
|
83
|
+
.uniq
|
79
84
|
end
|
80
85
|
|
81
86
|
def options_subcommand_args(token)
|
@@ -85,7 +90,7 @@ module Tabry
|
|
85
90
|
current_sub.args[state.args.length]
|
86
91
|
end
|
87
92
|
|
88
|
-
arg&.options&.options(token) || []
|
93
|
+
arg&.options&.options(token, params) || []
|
89
94
|
end
|
90
95
|
end
|
91
96
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../cli/arg_proxy" # TODO: shared code now, probably move out of cli dir
|
4
|
+
require_relative "../cli/flag_proxy" # TODO: shared code now, probably move out of cli dir
|
5
|
+
|
6
|
+
module Tabry
|
7
|
+
module Replty
|
8
|
+
class Base
|
9
|
+
attr_reader :internals, :args, :flags
|
10
|
+
|
11
|
+
def internals=(internals)
|
12
|
+
@internals = internals
|
13
|
+
@args = ::Tabry::CLI::ArgProxy.new(internals.state.args, internals.result.named_args)
|
14
|
+
@flags = ::Tabry::CLI::FlagProxy.new(internals.state.flags)
|
15
|
+
end
|
16
|
+
|
17
|
+
# NOTE: internal functions (besides internal/args/flags) start with "tabry_"; reconsider scheme
|
18
|
+
def tabry_prompt
|
19
|
+
"> "
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "readline"
|
4
|
+
require_relative "../cli/internals" # TODO: bring out of cli if shared code now, probably
|
5
|
+
require_relative "../shell_tokenizer"
|
6
|
+
require_relative "../runner"
|
7
|
+
|
8
|
+
module Tabry
|
9
|
+
module Replty
|
10
|
+
class Builder
|
11
|
+
attr_reader :runner, :repl
|
12
|
+
|
13
|
+
# TODO: implement before_action, after_action, sub-REPLs; share code in run_result with CLI::Builder
|
14
|
+
|
15
|
+
def initialize(config, repl)
|
16
|
+
@runner = Tabry::Runner.new(config: config)
|
17
|
+
@repl = repl
|
18
|
+
end
|
19
|
+
|
20
|
+
def readline
|
21
|
+
Readline.readline(repl.tabry_prompt, true)
|
22
|
+
rescue Interrupt
|
23
|
+
puts "^C"
|
24
|
+
retry
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_history(file)
|
28
|
+
return unless file
|
29
|
+
|
30
|
+
File.open(file) do |f|
|
31
|
+
f.each do |l|
|
32
|
+
Readline::HISTORY.push l.chomp
|
33
|
+
end
|
34
|
+
end
|
35
|
+
rescue Errno::ENOENT # rubocop:disable Lint/SuppressedException
|
36
|
+
end
|
37
|
+
|
38
|
+
def save_history(file, max_size = 10000)
|
39
|
+
return unless file
|
40
|
+
|
41
|
+
history = Readline::HISTORY.each
|
42
|
+
|
43
|
+
# Skip over excess elements:
|
44
|
+
(Readline::HISTORY.length - max_size).times do
|
45
|
+
history.next
|
46
|
+
end
|
47
|
+
|
48
|
+
File.open(file, "w") do |f|
|
49
|
+
loop { f.puts history.next }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def run(history_file: nil, tokenizer: ShellTokenizer)
|
54
|
+
history_file = history_file&.gsub(%r{^~/}, "#{Dir.home}/")
|
55
|
+
load_history(history_file)
|
56
|
+
|
57
|
+
Readline.completion_proc = proc do
|
58
|
+
cmd, args, last_arg = tokenizer.split_with_comppoint(Readline.line_buffer, Readline.point)
|
59
|
+
options = runner.options([cmd, *args].compact, last_arg)
|
60
|
+
options.map do |opt|
|
61
|
+
# if opt is a symbol, it's a "opts method" option type -- where a REPL method returns the options.
|
62
|
+
# TODO: a bit weird since the REPL's "args" and "flags" are wrong/old when that completion method is run
|
63
|
+
# also filter to start_with? is more appropriate in tabry, not here, but whatever
|
64
|
+
opt.is_a?(Symbol) ? repl.send(opt)&.select { |x| x.start_with?(last_arg) } : opt
|
65
|
+
end.flatten
|
66
|
+
end
|
67
|
+
|
68
|
+
while (cmdline = readline)
|
69
|
+
raw_args = tokenizer.split(cmdline)
|
70
|
+
next if raw_args.empty?
|
71
|
+
|
72
|
+
result = runner.parse(raw_args)
|
73
|
+
# TODO: usage has "myrepl" (command name) in; also, "got 1 args" is confusing when command doesn't exist
|
74
|
+
if result.help?
|
75
|
+
puts result.usage
|
76
|
+
elsif result.invalid_usage_reason
|
77
|
+
puts "Invalid usage: #{result.invalid_usage_reason}"
|
78
|
+
puts
|
79
|
+
puts result.usage
|
80
|
+
else
|
81
|
+
run_result(raw_args, result)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
save_history(history_file)
|
86
|
+
end
|
87
|
+
|
88
|
+
def run_result(raw_args, result)
|
89
|
+
met = result.state.subcommand_stack.join("__").gsub("-", "_")
|
90
|
+
repl.internals = ::Tabry::CLI::Internals.new(
|
91
|
+
runner: runner, raw_args: raw_args,
|
92
|
+
state: result.state, met: met, result: result
|
93
|
+
)
|
94
|
+
catch(:tabry_replty_command_exit) do
|
95
|
+
repl.send(met.to_sym)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/tabry/runner.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shellwords"
|
4
|
+
|
5
|
+
# Code to tokenize whole command lines into command and argument
|
6
|
+
# This may be adjustable in some cases
|
7
|
+
module Tabry
|
8
|
+
module ShellTokenizer
|
9
|
+
module_function
|
10
|
+
|
11
|
+
COMP_POINT_SENTINEL = "\uFFFF"
|
12
|
+
|
13
|
+
# Use Shellwords.split() to split a command line + comp point (index of the
|
14
|
+
# cursor in the command line) into the args up to the current token plus
|
15
|
+
# current token
|
16
|
+
#
|
17
|
+
# Returns [cmd_name, args, last_arg]
|
18
|
+
# cmd_name is the basename of the command run
|
19
|
+
#
|
20
|
+
def split_with_comppoint(cmd_line, comp_point)
|
21
|
+
# TODO: in weird scenarios this acts weird: namely, special shell operators like <(ls), $$((1 + 1))
|
22
|
+
# Also it crashed on unbalanced quotes, like: foo "bar<TAB>
|
23
|
+
# however, this will handle the common scenarios of escaping with quotes, single quotes, and backslashes
|
24
|
+
# Split up args and put the argument that comp_point is in in the `last_arg` variable.
|
25
|
+
# Just cutting off everything after comp_point might have worked, although
|
26
|
+
# maybe we wanted the whole arg? Not sure this is the best.
|
27
|
+
cmd_line = cmd_line.dup
|
28
|
+
cmd_line[comp_point.to_i...comp_point.to_i] = COMP_POINT_SENTINEL
|
29
|
+
all_tokens = Shellwords.split(cmd_line)
|
30
|
+
|
31
|
+
# ignore all tokens after the one with the sentinel, then replace the COMP_POINT_SENTINEL.
|
32
|
+
# the last token is now the token which the cursor is on (the entire token, not just before the cursor)
|
33
|
+
last_arg_index = all_tokens.index { |arg| arg.include?(COMP_POINT_SENTINEL) }
|
34
|
+
all_tokens = all_tokens[0..last_arg_index]
|
35
|
+
all_tokens.last.gsub! COMP_POINT_SENTINEL, ""
|
36
|
+
|
37
|
+
# take last_arg first -- will always be non-null (it will be empty if input string is empty).
|
38
|
+
last_arg = all_tokens.pop
|
39
|
+
cmd = all_tokens.shift
|
40
|
+
|
41
|
+
cmd_name = cmd&.gsub(%r{.*/}, "")
|
42
|
+
|
43
|
+
[cmd_name, all_tokens, last_arg]
|
44
|
+
end
|
45
|
+
|
46
|
+
def split(str)
|
47
|
+
Shellwords.split(str)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -4,14 +4,14 @@ require "shellwords"
|
|
4
4
|
require "yaml"
|
5
5
|
require_relative "../../util"
|
6
6
|
require_relative "../../runner"
|
7
|
-
require_relative "../../
|
7
|
+
require_relative "../../shell_tokenizer"
|
8
8
|
|
9
9
|
# Bash-specific entrypoint, taking COMP_WORDS and COMP_CWORDS and returning possible options
|
10
10
|
module Tabry
|
11
11
|
module Bash
|
12
12
|
module Wrapper
|
13
13
|
def self.run(cmd_line, comp_point, config: nil)
|
14
|
-
cmd_name, args, last_arg = Tabry::
|
14
|
+
cmd_name, args, last_arg = Tabry::ShellTokenizer.split_with_comppoint(cmd_line, comp_point)
|
15
15
|
opts = Tabry::Runner.new(config: config || cmd_name).options(args, last_arg)
|
16
16
|
|
17
17
|
if Tabry::Util.debug?
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shellwords"
|
4
|
+
require "yaml"
|
5
|
+
require_relative "../../util"
|
6
|
+
require_relative "../../runner"
|
7
|
+
require_relative "../../shell_tokenizer"
|
8
|
+
|
9
|
+
# Fish-specific entrypoint
|
10
|
+
module Tabry
|
11
|
+
module Fish
|
12
|
+
module Wrapper
|
13
|
+
DESCRIPTION_MAX_LENGTH = 50
|
14
|
+
|
15
|
+
def self.run(cmd_line, comp_point, config: nil)
|
16
|
+
cmd_name, args, last_arg = Tabry::ShellTokenizer.split_with_comppoint(cmd_line, comp_point)
|
17
|
+
opts = Tabry::Runner.new(config: config || cmd_name).options(args, last_arg, {descriptions: true})
|
18
|
+
|
19
|
+
if Tabry::Util.debug?
|
20
|
+
$stderr.puts
|
21
|
+
warn "debug: got command line and comp_point: #{cmd_line.inspect}, #{comp_point}"
|
22
|
+
warn "using args: #{args.inspect}"
|
23
|
+
warn "using lastarg: #{last_arg.inspect}"
|
24
|
+
warn "results from Tabry#options(): #{opts.inspect}"
|
25
|
+
warn "--- end debug output ---"
|
26
|
+
end
|
27
|
+
|
28
|
+
normal_opts = opts.select { |t,| t.is_a?(String) }
|
29
|
+
special_opts = opts.select { |t| t.is_a?(Symbol) }
|
30
|
+
|
31
|
+
puts normal_opts.map { |opt, desc| "#{Shellwords.escape(opt)}\t#{format_description(desc)}" }.join("\n")
|
32
|
+
if special_opts.any?
|
33
|
+
puts
|
34
|
+
puts special_opts.join("\n")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.format_description(description)
|
39
|
+
if description.nil?
|
40
|
+
""
|
41
|
+
else
|
42
|
+
if description.length > DESCRIPTION_MAX_LENGTH
|
43
|
+
"#{description[0..(DESCRIPTION_MAX_LENGTH - 3)]}..."
|
44
|
+
else
|
45
|
+
description
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|