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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/bin/tabry-fish +9 -0
  3. data/bin/tabry-generate-fish-complete +31 -0
  4. data/lib/tabry/cli/all_in_one.rb +27 -6
  5. data/lib/tabry/config_builder/arg_or_flag_builder.rb +5 -0
  6. data/lib/tabry/config_builder/top_level_builder.rb +3 -0
  7. data/lib/tabry/machine.rb +1 -1
  8. data/lib/tabry/models/const_option.rb +1 -1
  9. data/lib/tabry/models/flags_list.rb +6 -2
  10. data/lib/tabry/models/include_option.rb +2 -2
  11. data/lib/tabry/models/method_option.rb +14 -0
  12. data/lib/tabry/models/option.rb +5 -5
  13. data/lib/tabry/models/options_list.rb +2 -2
  14. data/lib/tabry/models/shell_option.rb +1 -1
  15. data/lib/tabry/models/subs_list.rb +13 -2
  16. data/lib/tabry/options_finder.rb +13 -8
  17. data/lib/tabry/replty/base.rb +23 -0
  18. data/lib/tabry/replty/builder.rb +100 -0
  19. data/lib/tabry/runner.rb +2 -2
  20. data/lib/tabry/shell_tokenizer.rb +50 -0
  21. data/lib/tabry/shells/bash/wrapper.rb +2 -2
  22. data/lib/tabry/shells/fish/wrapper.rb +51 -0
  23. data/lib/tabry/shells/fish.rb +82 -0
  24. data/lib/tabry/version.rb +3 -0
  25. data/sh/fish/README.md +2 -1
  26. data/sh/fish/tabry_fish.fish +17 -11
  27. data/spec/fixtures/config_builder/underscoresdashes.yml +1 -1
  28. data/spec/fixtures/vehicles-expectations.json +351 -0
  29. data/spec/tabry/cli/all_in_one_spec.rb +12 -0
  30. data/spec/tabry/machine_spec.rb +5 -82
  31. data/spec/tabry/shell_tokenizer_spec.rb +34 -0
  32. data/tabry.gemspec +2 -4
  33. data/treesitter/corpus/opts.txt +2 -0
  34. data/treesitter/grammar.js +7 -0
  35. data/treesitter/src/grammar.json +21 -0
  36. data/treesitter/src/node-types.json +23 -0
  37. data/treesitter/src/parser.c +959 -893
  38. data/treesitter/tabry-compile.js +6 -0
  39. metadata +16 -5
  40. data/lib/tabry/shell_splitter.rb +0 -34
  41. data/spec/tabry/shell_splitter_spec.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a76bb8b85fa94e61bf625e435711d8e3385f5aa50a87a7c410e4f252947ffea
4
- data.tar.gz: '0632896bf92ca8b57a08db3b153e32aed0da12c71d9604d7260de2fba6fc9707'
3
+ metadata.gz: 87fa1708b1efb9ef81588017419bb6c63455ee786870f882783cf59f22ea462b
4
+ data.tar.gz: f0822419f2a36d48148cf7cf0ecdbd195666e2c5fe284d2e8dd6a563976c0842
5
5
  SHA512:
6
- metadata.gz: 8420bd16657ebbe6e2f13c55a14e62fffab982b5d81f71d76c36ccdd97d36ee4a09ce7ccf1386c5b0b553681b48d7b3fdd90336fb72574d55ba426ca22200ed9
7
- data.tar.gz: 1dcd973c7608319c73ae59ab8c516e0ccb54ec9c6bf909f13235e3be238e1059e93d2ba067d99a66b8de8fb5cca9c69e2cf861b1393aecbd9076580fbbdef37c
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
+
@@ -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
- require_relative "../shells/bash/wrapper"
55
- Tabry::Bash::Wrapper.run(args.cmd_line, args.comp_point, config: config)
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
- require_relative "../shells/bash/wrapper"
81
- Tabry::Bash::Wrapper.run(ARGV[1], ARGV[2], config: config)
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)
@@ -5,7 +5,7 @@ require_relative "option_base"
5
5
  module Tabry
6
6
  module Models
7
7
  class ConstOption < OptionBase
8
- def options(token)
8
+ def options(token, _params)
9
9
  if value.start_with?(token)
10
10
  [value]
11
11
  else
@@ -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
- flag.name_with_dashes
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
@@ -7,8 +7,8 @@ module Tabry
7
7
  class IncludeOption < OptionBase
8
8
  attr_reader :include_name, :_root
9
9
 
10
- def options(token)
11
- flatten.options(token)
10
+ def options(token, params)
11
+ flatten.options(token, params)
12
12
  end
13
13
 
14
14
  def flatten
@@ -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
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "config_error"
4
- require_relative "const_option"
5
- require_relative "shell_option"
6
- require_relative "include_option"
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"
@@ -10,8 +10,8 @@ module Tabry
10
10
  super(**args, klass: Option)
11
11
  end
12
12
 
13
- def options(token)
14
- to_a.map { |option| option.options(token) }.inject(&:|)
13
+ def options(token, params)
14
+ to_a.map { |option| option.options(token, params) }.inject(&:|)
15
15
  end
16
16
  end
17
17
  end
@@ -5,7 +5,7 @@ require_relative "option_base"
5
5
  module Tabry
6
6
  module Models
7
7
  class ShellOption < OptionBase
8
- def options(token)
8
+ def options(token, _params)
9
9
  `#{value}`.chomp.split("\n").select { |opt| opt.start_with?(token) }
10
10
  end
11
11
  end
@@ -20,9 +20,20 @@ module Tabry
20
20
  end
21
21
  end
22
22
 
23
- def options(token)
24
- to_a.map(&:name).select { |name| name.start_with?(token) }
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
@@ -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.sub_stack.map { |sub| sub.flags.options(token, used: state.flags) }.flatten.uniq
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
@@ -19,8 +19,8 @@ module Tabry
19
19
  end
20
20
  end
21
21
 
22
- def options(args, last = nil)
23
- Tabry::OptionsFinder.options(parse(args), last)
22
+ def options(args, last = nil, params = {})
23
+ Tabry::OptionsFinder.options(parse(args), last, params)
24
24
  end
25
25
 
26
26
  def parse(args)
@@ -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 "../../shell_splitter"
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::ShellSplitter.split(cmd_line, comp_point)
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