doing 2.0.19 → 2.0.20

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd0eba2b8d88c5bb38ecf0e69043cf2c5e9e5c9819b5601a8ec75b7c0ab1fbde
4
- data.tar.gz: b03c879cdc2a2aa8b32bcd406ffdcae7f3f23a3c632125dda70c76461cb22127
3
+ metadata.gz: 29a3fd2f95b1eb4a02f49bddca32255e0f56b93693a4114d004181d6c309d453
4
+ data.tar.gz: 696931fdda6f08e136225ceb6e47425f171f86b222a8313cd485a21ab7222ac3
5
5
  SHA512:
6
- metadata.gz: eeff9de378f4f3989b8e3a4748fa92c11159d0880479b6fd964b9b83676054810f98e84017acc00a1bfa36dde397d6a606d8f428e80c9495e4cba06d09657005
7
- data.tar.gz: 90eb380657ca922c8a67f1b8479f36d873d12f11a93482831151e606427f6ea4ea7bdc187808c58acb191ad8f6f192b33b600b57d1e6c353194182a9f0fb3266
6
+ metadata.gz: a60c786a031b42be76cc10cacecf7247363369facf8584df5fd7790a9ea0dd6ccd148b460fa121aacada61a9d86a33b6a0827cda20e0b28d9614affe464ccccc
7
+ data.tar.gz: f47889440ee5ed7e53f227086139705b90b30004bb0c815f1aff3ffc9f6faa35cb47707c330476f73a47d56fd88963430e1ffb6b26e8c2b9f045e6c18685bdff
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ### 2.0.20
2
+
3
+ #### IMPROVED
4
+
5
+ - completion script generator refactor and progress bars
6
+
7
+ #### FIXED
8
+
9
+ - compile fzf for current operating system
10
+
1
11
  ### 2.0.19
2
12
 
3
13
  #### FIXED
data/Gemfile.lock CHANGED
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- doing (2.0.19)
4
+ doing (2.0.20)
5
5
  chronic (~> 0.10, >= 0.10.2)
6
6
  deep_merge (~> 1.2, >= 1.2.1)
7
7
  gli (~> 2.19, >= 2.19.2)
8
8
  haml (~> 5.0.0, >= 5.0.0)
9
9
  safe_yaml (~> 1.0)
10
+ tty-progressbar (~> 0.18, >= 0.18.2)
10
11
 
11
12
  GEM
12
13
  remote: http://rubygems.org/
@@ -89,6 +90,7 @@ GEM
89
90
  rspec-support (~> 3.10.0)
90
91
  rspec-support (3.10.3)
91
92
  safe_yaml (1.0.5)
93
+ strings-ansi (0.2.0)
92
94
  sys-uname (1.2.2)
93
95
  ffi (~> 1.1)
94
96
  temple (0.8.2)
@@ -97,8 +99,16 @@ GEM
97
99
  thor (1.1.0)
98
100
  thread_safe (0.3.6)
99
101
  tilt (2.0.10)
102
+ tty-cursor (0.7.1)
103
+ tty-progressbar (0.18.2)
104
+ strings-ansi (~> 0.2)
105
+ tty-cursor (~> 0.7)
106
+ tty-screen (~> 0.8)
107
+ unicode-display_width (>= 1.6, < 3.0)
108
+ tty-screen (0.8.1)
100
109
  tzinfo (2.0.4)
101
110
  concurrent-ruby (~> 1.0)
111
+ unicode-display_width (2.1.0)
102
112
  yard (0.9.26)
103
113
  zeitwerk (2.5.1)
104
114
 
@@ -108,13 +118,13 @@ PLATFORMS
108
118
  DEPENDENCIES
109
119
  aruba (~> 1.0.2)
110
120
  doing!
111
- github-markup
112
- parallel_tests
121
+ github-markup (~> 4.0, >= 4.0.0)
122
+ parallel_tests (~> 3.7, >= 3.7.3)
113
123
  rake (~> 13.0, >= 13.0.1)
114
124
  rdoc (~> 6.3.1)
115
- redcarpet
125
+ redcarpet (~> 3.5, >= 3.5.1)
116
126
  test-unit (~> 3.4.4)
117
- yard
127
+ yard (~> 0.9, >= 0.9.26)
118
128
 
119
129
  BUNDLED WITH
120
130
  2.2.17
data/README.md CHANGED
@@ -6,7 +6,7 @@ _If you're one of the rare people like me who find this useful, feel free to [bu
6
6
 
7
7
  <!--README-->
8
8
 
9
- The current version of `doing` is <!--VER-->2.0.18<!--END VER-->.
9
+ The current version of `doing` is <!--VER-->2.0.19<!--END VER-->.
10
10
 
11
11
  Find all of the documentation in the [doing wiki](https://github.com/ttscoff/doing/wiki).
12
12
 
data/bin/doing CHANGED
@@ -2149,28 +2149,12 @@ command :completion do |c|
2149
2149
 
2150
2150
  c.desc 'File to write output to'
2151
2151
  c.arg_name 'PATH'
2152
- c.flag %i[f file], default_value: 'stdout'
2152
+ c.flag %i[f file], default_value: 'STDOUT'
2153
2153
 
2154
2154
  c.action do |_global_options, options, _args|
2155
2155
  script_dir = File.join(File.dirname(__FILE__), '..', 'scripts')
2156
2156
 
2157
- case options[:type]
2158
- when /^b/
2159
- result = `ruby #{File.join(script_dir, 'generate_bash_completions.rb')}`
2160
- when /^z/
2161
- result = `ruby #{File.join(script_dir, 'generate_zsh_completions.rb')}`
2162
- when /^f/
2163
- result = `ruby #{File.join(script_dir, 'generate_fish_completions.rb')}`
2164
- end
2165
-
2166
- if options[:file] =~ /^stdout$/i
2167
- $stdout.puts result
2168
- else
2169
- File.open(File.expand_path(options[:file]), 'w') do |f|
2170
- f.puts result
2171
- end
2172
- Doing.logger.warn('File written:', "#{options[:type]} completions written to #{options[:file]}")
2173
- end
2157
+ Doing::Completion.generate_completion(type: options[:type], file: options[:file])
2174
2158
  end
2175
2159
  end
2176
2160
 
data/doing.gemspec CHANGED
@@ -27,10 +27,11 @@ spec = Gem::Specification.new do |s|
27
27
  s.add_development_dependency 'rdoc', '~> 6.3.1'
28
28
  s.add_development_dependency 'aruba', '~> 1.0.2'
29
29
  s.add_development_dependency 'test-unit', '~> 3.4.4'
30
- s.add_development_dependency 'yard'
31
- s.add_development_dependency 'redcarpet'
32
- s.add_development_dependency 'github-markup'
33
- s.add_development_dependency 'parallel_tests'
30
+ s.add_development_dependency 'yard', '~> 0.9', '>= 0.9.26'
31
+ s.add_development_dependency 'redcarpet', '~> 3.5', '>= 3.5.1'
32
+ s.add_development_dependency 'github-markup', '~> 4.0', '>= 4.0.0'
33
+ s.add_development_dependency 'parallel_tests', '~> 3.7', '>= 3.7.3'
34
+ s.add_runtime_dependency('tty-progressbar', '~> 0.18', '>= 0.18.2')
34
35
  s.add_runtime_dependency('gli', '~> 2.19', '>= 2.19.2')
35
36
  s.add_runtime_dependency('haml','~>5.0.0', '>= 5.0.0')
36
37
  s.add_runtime_dependency('chronic','~> 0.10', '>= 0.10.2')
data/doing.rdoc CHANGED
@@ -5,7 +5,7 @@ record of what you've been doing, complete with tag-based time tracking. The
5
5
  command line tool allows you to add entries, annotate with tags and notes, and
6
6
  view your entries with myriad options, with a focus on a "natural" language syntax.
7
7
 
8
- v2.0.19
8
+ v2.0.20
9
9
 
10
10
  === Global Options
11
11
  === --config_file arg
@@ -374,7 +374,7 @@ Generate shell completion scripts
374
374
 
375
375
  File to write output to
376
376
 
377
- [Default Value] stdout
377
+ [Default Value] STDOUT
378
378
 
379
379
 
380
380
  ===== -t|--type SHELL
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
2
 
3
- scripts/generate_fish_completions.rb > lib/completion/doing.fish
4
- scripts/generate_bash_completions.rb > lib/completion/doing.bash
5
- scripts/generate_zsh_completions.rb > lib/completion/_doing.zsh
3
+ bundle exec bin/doing completion --type fish --file lib/completion/doing.fish
4
+ bundle exec bin/doing completion --type bash --file lib/completion/doing.bash
5
+ bundle exec bin/doing completion --type zsh --file lib/completion/_doing.zsh
@@ -14,8 +14,12 @@ module Status
14
14
  $stderr.print format("#{esc['kill']}#{esc['boldyellow']}> #{esc['boldgreen']}%s #{esc['white']}[#{esc['boldwhite']}%#{@commands.count.to_s.length}d#{esc['boldblack']}/#{esc['boldyellow']}%d#{esc['white']}]: #{esc['boldcyan']}%s#{esc['default']}\r", msg, idx, total, tail)
15
15
  end
16
16
 
17
- def status(msg, reset: true)
18
- $stderr.print format("#{esc['kill']}#{esc['boldyellow']}> #{esc['whiteboard']}%s#{esc['default']}%s", msg, reset ? "\r" : "\n")
17
+ def status(msg, reset: true, end_char: "\n")
18
+ $stderr.print format("#{esc['kill']}#{esc['boldyellow']}> #{esc['whiteboard']}%s#{esc['default']}%s", msg, reset ? "\r" : end_char)
19
+ end
20
+
21
+ def msg(msg, reset: true, color: 'green', end_char: "\n")
22
+ $stderr.print format("#{esc['kill']}#{esc[color]}%s#{esc['default']}%s", msg, reset ? "\r" : end_char)
19
23
  end
20
24
 
21
25
  def clear
@@ -0,0 +1,185 @@
1
+ module Doing
2
+ module Completion
3
+ class BashCompletions
4
+ attr_accessor :commands, :global_options
5
+
6
+ def main_function
7
+ first = true
8
+ out = []
9
+ logic = []
10
+ need_export = []
11
+
12
+ @commands.each_with_index do |cmd, i|
13
+ @bar.advance
14
+
15
+ data = get_help_sections(cmd[:commands].first)
16
+
17
+ arg = data[:synopsis].join(' ').strip.split(/ /).last
18
+ case arg
19
+ when /(path|file)/i
20
+ type = :file
21
+ when /sect/i
22
+ type = 'sections'
23
+ when /view/i
24
+ type = 'views'
25
+ else
26
+ type = nil
27
+ end
28
+
29
+ if data[:command_options]
30
+ options = parse_options(data[:command_options])
31
+ out << command_function(cmd[:commands].first, options, type)
32
+
33
+ if first
34
+ op = 'if'
35
+ first = false
36
+ else
37
+ op = 'elif'
38
+ end
39
+ logic << %(#{op} [[ $last =~ (#{cmd[:commands].join('|')}) ]]; then _doing_#{cmd[:commands].first})
40
+ end
41
+ end
42
+
43
+ out << <<~EOFUNC
44
+ _doing()
45
+ {
46
+ local last="${@: -1}"
47
+ local token=${COMP_WORDS[$COMP_CWORD]}
48
+
49
+ #{logic.join("\n ")}
50
+ else
51
+ OLD_IFS="$IFS"
52
+ IFS=$'\n'
53
+ COMPREPLY=( $(compgen -W "$(doing help -c)" -- $token) )
54
+ IFS="$OLD_IFS"
55
+ fi
56
+ }
57
+ EOFUNC
58
+ out.join("\n")
59
+ end
60
+
61
+ def command_function(command, options, type)
62
+ long_options = []
63
+ short_options = []
64
+
65
+ options.each do |o|
66
+ next if o.nil?
67
+
68
+ long_options << o[:long] if o[:long]
69
+ short_options << o[:short] if o[:short]
70
+ end
71
+
72
+ long = long_options.map! {|o| "--#{o}"}.join(' ')
73
+ short = short_options.map! {|o| "-#{o}"}.join(' ')
74
+ words = ''
75
+ logic = ''
76
+ words, logic = get_words(type) if type && type.is_a?(String)
77
+
78
+ func = <<~ENDFUNC
79
+ _doing_#{command}() {
80
+ #{words}
81
+ if [[ "$token" == --* ]]; then
82
+ COMPREPLY=( $( compgen -W '#{long}' -- $token ) )
83
+ elif [[ "$token" == -* ]]; then
84
+ COMPREPLY=( $( compgen -W '#{short} #{long}' -- $token ) )
85
+ #{logic}
86
+ fi
87
+ }
88
+ ENDFUNC
89
+
90
+ func
91
+ end
92
+
93
+ def get_words(type)
94
+ func = <<~EOFUNC
95
+ OLD_IFS="$IFS"
96
+ local token=${COMP_WORDS[$COMP_CWORD]}
97
+ IFS=$'\t'
98
+ local words=$(doing #{type})
99
+ IFS="$OLD_IFS"
100
+ EOFUNC
101
+
102
+ logic = <<~EOLOGIC
103
+ else
104
+ local nocasematchWasOff=0
105
+ shopt nocasematch >/dev/null || nocasematchWasOff=1
106
+ (( nocasematchWasOff )) && shopt -s nocasematch
107
+ local w matches=()
108
+ OLD_IFS="$IFS"
109
+ IFS=$'\t'‰
110
+ for w in $words; do
111
+ if [[ "$w" == "$token"* ]]; then
112
+ matches+=("${w// /\ }")
113
+ fi
114
+ done
115
+ IFS="$OLD_IFS"
116
+ (( nocasematchWasOff )) && shopt -u nocasematch
117
+ COMPREPLY=("${matches[@]}")
118
+ EOLOGIC
119
+
120
+ [func, logic]
121
+ end
122
+
123
+
124
+
125
+ def get_help_sections(command = '')
126
+ res = `doing help #{command}`.strip
127
+ scanned = res.scan(/(?m-i)^([A-Z ]+)\n([\s\S]*?)(?=\n+[A-Z]+|\Z)/)
128
+ sections = {}
129
+ scanned.each do |sect|
130
+ title = sect[0].downcase.strip.gsub(/ +/, '_').to_sym
131
+ content = sect[1].split(/\n/).map(&:strip).delete_if(&:empty?)
132
+ sections[title] = content
133
+ end
134
+ sections
135
+ end
136
+
137
+ def parse_option(option)
138
+ res = option.match(/(?:-(?<short>\w), )?(?:--(?:\[no-\])?(?<long>[\w_]+)(?:=(?<arg>\w+))?)\s+- (?<desc>.*?)$/)
139
+ return nil unless res
140
+ {
141
+ short: res['short'],
142
+ long: res['long'],
143
+ arg: res[:arg],
144
+ description: res['desc'].short_desc
145
+ }
146
+ end
147
+
148
+ def parse_options(options)
149
+ options.map { |opt| parse_option(opt) }
150
+ end
151
+
152
+ def parse_command(command)
153
+ res = command.match(/^(?<cmd>[^, \t]+)(?<alias>(?:, [^, \t]+)*)?\s+- (?<desc>.*?)$/)
154
+ commands = [res['cmd']]
155
+ commands.concat(res['alias'].split(/, /).delete_if(&:empty?)) if res['alias']
156
+
157
+ {
158
+ commands: commands,
159
+ description: res['desc'].short_desc
160
+ }
161
+ end
162
+
163
+ def parse_commands(commands)
164
+ commands.map { |cmd| parse_command(cmd) }
165
+ end
166
+
167
+ def initialize
168
+ data = get_help_sections
169
+ @global_options = parse_options(data[:global_options])
170
+ @commands = parse_commands(data[:commands])
171
+ @bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Bash completions: \033[0;35;40m[:bar]\033[0m", total: @commands.count, bar_format: :blade)
172
+ @bar.resize(25)
173
+ end
174
+
175
+ def generate_completions
176
+ @bar.start
177
+ out = []
178
+ out << main_function
179
+ out << 'complete -F _doing doing'
180
+ @bar.finish
181
+ out.join("\n")
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,175 @@
1
+ module Doing
2
+ module Completion
3
+ class FishCompletions
4
+
5
+ attr_accessor :commands, :global_options
6
+
7
+ def generate_helpers
8
+ <<~EOFUNCTIONS
9
+ function __fish_doing_needs_command
10
+ # Figure out if the current invocation already has a command.
11
+
12
+ set -l opts h-help config_file= f-doing_file= n-notes v-version stdout d-debug default x-noauto
13
+ set cmd (commandline -opc)
14
+ set -e cmd[1]
15
+ argparse -s $opts -- $cmd 2>/dev/null
16
+ or return 0
17
+ # These flags function as commands, effectively.
18
+ if set -q argv[1]
19
+ # Also print the command, so this can be used to figure out what it is.
20
+ echo $argv[1]
21
+ return 1
22
+ end
23
+ return 0
24
+ end
25
+
26
+ function __fish_doing_using_command
27
+ set -l cmd (__fish_doing_needs_command)
28
+ test -z "$cmd"
29
+ and return 1
30
+ contains -- $cmd $argv
31
+ and return 0
32
+ end
33
+
34
+ function __fish_doing_complete_sections
35
+ doing sections -c
36
+ end
37
+
38
+ function __fish_doing_complete_views
39
+ doing views -c
40
+ end
41
+
42
+ function __fish_doing_subcommands
43
+ doing help -c
44
+ end
45
+
46
+ function __fish_doing_export_plugins
47
+ doing plugins --type export -c
48
+ end
49
+
50
+ function __fish_doing_import_plugins
51
+ doing plugins --type import -c
52
+ end
53
+
54
+ function __fish_doing_complete_templates
55
+ doing template -c
56
+ end
57
+
58
+ complete -c doing -f
59
+ complete -xc doing -n '__fish_doing_needs_command' -a '(__fish_doing_subcommands)'
60
+
61
+ complete -f -c doing -n '__fish_doing_using_command show' -a '(__fish_doing_complete_sections)'
62
+ complete -f -c doing -n '__fish_doing_using_command view' -a '(__fish_doing_complete_views)'
63
+ complete -f -c doing -n '__fish_doing_using_command template' -a '(__fish_doing_complete_templates)'
64
+ complete -f -c doing -s t -l type -x -n '__fish_doing_using_command import' -a '(__fish_doing_import_plugins)'
65
+
66
+ complete -xc doing -n '__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from (doing help -c)' -a "(doing help -c)"
67
+ EOFUNCTIONS
68
+ end
69
+
70
+ def get_help_sections(command = '')
71
+ res = `doing help #{command}`.strip
72
+ scanned = res.scan(/(?m-i)^([A-Z ]+)\n([\s\S]*?)(?=\n+[A-Z]+|\Z)/)
73
+ sections = {}
74
+ scanned.each do |sect|
75
+ title = sect[0].downcase.strip.gsub(/ +/, '_').to_sym
76
+ content = sect[1].split(/\n/).map(&:strip).delete_if(&:empty?)
77
+ sections[title] = content
78
+ end
79
+ sections
80
+ end
81
+
82
+ def parse_option(option)
83
+ res = option.match(/(?:-(?<short>\w), )?(?:--(?:\[no-\])?(?<long>[\w_]+)(?:=(?<arg>\w+))?)\s+- (?<desc>.*?)$/)
84
+ return nil unless res
85
+ {
86
+ short: res['short'],
87
+ long: res['long'],
88
+ arg: res[:arg],
89
+ description: res['desc'].short_desc
90
+ }
91
+ end
92
+
93
+ def parse_options(options)
94
+ options.map { |opt| parse_option(opt) }
95
+ end
96
+
97
+ def parse_command(command)
98
+ res = command.match(/^(?<cmd>[^, \t]+)(?<alias>(?:, [^, \t]+)*)?\s+- (?<desc>.*?)$/)
99
+ commands = [res['cmd']]
100
+ commands.concat(res['alias'].split(/, /).delete_if(&:empty?)) if res['alias']
101
+
102
+ {
103
+ commands: commands,
104
+ description: res['desc'].short_desc
105
+ }
106
+ end
107
+
108
+ def parse_commands(commands)
109
+ commands.map { |cmd| parse_command(cmd) }
110
+ end
111
+
112
+ def generate_subcommand_completions
113
+ out = []
114
+ @commands.each_with_index do |cmd, i|
115
+ out << "complete -xc doing -n '__fish_doing_needs_command' -a '#{cmd[:commands].join(' ')}' -d #{Shellwords.escape(cmd[:description])}"
116
+ end
117
+
118
+ out.join("\n")
119
+ end
120
+
121
+ def generate_subcommand_option_completions
122
+
123
+ out = []
124
+ need_export = []
125
+
126
+ @commands.each_with_index do |cmd, i|
127
+ @bar.advance
128
+ data = get_help_sections(cmd[:commands].first)
129
+
130
+ if data[:synopsis].join(' ').strip.split(/ /).last =~ /(path|file)/i
131
+ out << "complete -c doing -F -n '__fish_doing_using_command #{cmd[:commands].join(" ")}'"
132
+ end
133
+
134
+ if data[:command_options]
135
+ parse_options(data[:command_options]).each do |option|
136
+ next if option.nil?
137
+
138
+ arg = option[:arg] ? '-r' : ''
139
+ short = option[:short] ? "-s #{option[:short]}" : ''
140
+ long = option[:long] ? "-l #{option[:long]}" : ''
141
+ out << "complete -c doing #{long} #{short} -f #{arg} -n '__fish_doing_using_command #{cmd[:commands].join(' ')}' -d #{Shellwords.escape(option[:description])}"
142
+
143
+ need_export.concat(cmd[:commands]) if option[:long] == 'output'
144
+ end
145
+ end
146
+ end
147
+
148
+ unless need_export.empty?
149
+ out << "complete -f -c doing -s o -l output -x -n '__fish_doing_using_command #{need_export.join(' ')}' -a '(__fish_doing_export_plugins)'"
150
+ end
151
+
152
+ # clear
153
+ out.join("\n")
154
+ end
155
+
156
+ def initialize
157
+ data = get_help_sections
158
+ @global_options = parse_options(data[:global_options])
159
+ @commands = parse_commands(data[:commands])
160
+ @bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Fish completions: \033[0;35;40m[:bar]\033[0m", total: @commands.count, bar_format: :blade)
161
+ @bar.resize(25)
162
+ end
163
+
164
+ def generate_completions
165
+ @bar.start
166
+ out = []
167
+ out << generate_helpers
168
+ out << generate_subcommand_completions
169
+ out << generate_subcommand_option_completions
170
+ @bar.finish
171
+ out.join("\n")
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,17 @@
1
+ class ::String
2
+ def short_desc
3
+ split(/[,.]/)[0].sub(/ \(.*?\)?$/, '').strip
4
+ end
5
+
6
+ def ltrunc(max)
7
+ if length > max
8
+ sub(/^.*?(.{#{max - 3}})$/, '...\1')
9
+ else
10
+ self
11
+ end
12
+ end
13
+
14
+ def ltrunc!(max)
15
+ replace ltrunc(max)
16
+ end
17
+ end
@@ -0,0 +1,140 @@
1
+ module Doing
2
+ module Completion
3
+ class ZshCompletions
4
+
5
+ attr_accessor :commands, :global_options
6
+
7
+ def generate_helpers
8
+ out=<<~EOFUNCTIONS
9
+ compdef _doing doing
10
+
11
+ function _doing() {
12
+ local line state
13
+
14
+ function _commands {
15
+ local -a commands
16
+
17
+ commands=(
18
+ #{generate_subcommand_completions.join("\n ")}
19
+ )
20
+ _describe 'command' commands
21
+ }
22
+
23
+ _arguments -C \
24
+ "1: :_commands" \
25
+ "*::arg:->args"
26
+
27
+
28
+
29
+ case $line[1] in
30
+ #{generate_subcommand_option_completions(indent: ' ').join("\n ")}
31
+ esac
32
+
33
+ _arguments -s $args
34
+ }
35
+
36
+ EOFUNCTIONS
37
+ @bar.finish
38
+ out
39
+ end
40
+
41
+ def get_help_sections(command = '')
42
+ res = `doing help #{command}`.strip
43
+ scanned = res.scan(/(?m-i)^([A-Z ]+)\n([\s\S]*?)(?=\n+[A-Z]+|\Z)/)
44
+ sections = {}
45
+ scanned.each do |sect|
46
+ title = sect[0].downcase.strip.gsub(/ +/, '_').to_sym
47
+ content = sect[1].split(/\n/).map(&:strip).delete_if(&:empty?)
48
+ sections[title] = content
49
+ end
50
+ sections
51
+ end
52
+
53
+ def parse_option(option)
54
+ res = option.match(/(?:-(?<short>\w), )?(?:--(?:\[no-\])?(?<long>[\w_]+)(?:=(?<arg>\w+))?)\s+- (?<desc>.*?)$/)
55
+ return nil unless res
56
+
57
+ {
58
+ short: res['short'],
59
+ long: res['long'],
60
+ arg: res[:arg],
61
+ description: res['desc'].short_desc
62
+ }
63
+ end
64
+
65
+ def parse_options(options)
66
+ options.map { |opt| parse_option(opt) }
67
+ end
68
+
69
+ def parse_command(command)
70
+ res = command.match(/^(?<cmd>[^, \t]+)(?<alias>(?:, [^, \t]+)*)?\s+- (?<desc>.*?)$/)
71
+ commands = [res['cmd']]
72
+ commands.concat(res['alias'].split(/, /).delete_if(&:empty?)) if res['alias']
73
+
74
+ {
75
+ commands: commands,
76
+ description: res['desc'].short_desc
77
+ }
78
+ end
79
+
80
+ def parse_commands(commands)
81
+ commands.map { |cmd| parse_command(cmd) }
82
+ end
83
+
84
+ def generate_subcommand_completions
85
+ out = []
86
+ @commands.each_with_index do |cmd, i|
87
+ cmd[:commands].each do |c|
88
+ out << "'#{c}:#{cmd[:description].gsub(/'/, '\\\'')}'"
89
+ end
90
+ end
91
+ out
92
+ end
93
+
94
+ def generate_subcommand_option_completions(indent: ' ')
95
+
96
+ out = []
97
+
98
+ @commands.each_with_index do |cmd, i|
99
+ @bar.advance
100
+
101
+ data = get_help_sections(cmd[:commands].first)
102
+ option_arr = []
103
+
104
+ if data[:command_options]
105
+ parse_options(data[:command_options]).each do |option|
106
+ next if option.nil?
107
+
108
+ arg = option[:arg] ? '=' : ''
109
+
110
+ option_arr << if option[:short]
111
+ %({-#{option[:short]},--#{option[:long]}#{arg}}"[#{option[:description].gsub(/'/, '\\\'')}]")
112
+ else
113
+ %("(--#{option[:long]}#{arg})--#{option[:long]}#{arg}}[#{option[:description].gsub(/'/, '\\\'')}]")
114
+ end
115
+ end
116
+ end
117
+
118
+ cmd[:commands].each do |c|
119
+ out << "#{c}) \n#{indent} args=( #{option_arr.join(' ')} )\n#{indent};;"
120
+ end
121
+ end
122
+
123
+ out
124
+ end
125
+
126
+ def initialize
127
+ data = get_help_sections
128
+ @global_options = parse_options(data[:global_options])
129
+ @commands = parse_commands(data[:commands])
130
+ @bar = TTY::ProgressBar.new(" \033[0;0;33mGenerating Zsh completions: \033[0;35;40m[:bar]\033[0m", total: @commands.count, bar_format: :blade)
131
+ @bar.resize(25)
132
+ end
133
+
134
+ def generate_completions
135
+ @bar.start
136
+ generate_helpers
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-progressbar'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'completion'))
6
+ require 'string'
7
+ require 'fish_completion'
8
+ require 'zsh_completion'
9
+ require 'bash_completion'
10
+
11
+ module Doing
12
+ # Completion script generator
13
+ module Completion
14
+ class << self
15
+ def generate_completion(type: 'zsh', file: 'stdout')
16
+
17
+ generator = case type.to_s
18
+ when /^f/
19
+ FishCompletions.new
20
+ when /^b/
21
+ BashCompletions.new
22
+ else
23
+ ZshCompletions.new
24
+ end
25
+
26
+ result = generator.generate_completions
27
+
28
+ if file =~ /^stdout$/i
29
+ $stdout.puts result
30
+ else
31
+ File.open(File.expand_path(file), 'w') do |f|
32
+ f.puts result
33
+ end
34
+ Doing.logger.warn('File written:', "#{type} completions written to #{file}")
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '2.0.19'
2
+ VERSION = '2.0.20'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -611,6 +611,22 @@ module Doing
611
611
  last_entry
612
612
  end
613
613
 
614
+ def fzf
615
+ fzf_dir = File.join(File.dirname(__FILE__), '../helpers/fzf')
616
+ FileUtils.mkdir_p(fzf_dir) unless File.directory?(fzf_dir)
617
+ fzf = File.join(fzf_dir, 'bin/fzf')
618
+ return fzf if File.exist?(fzf)
619
+
620
+ Doing.logger.log_now(:warn, 'Downloading and installing FZF. This will only happen once')
621
+ Doing.logger.log_now(:warn, 'fzf is copyright Junegunn Choi <https://github.com/junegunn/fzf/blob/master/LICENSE>')
622
+ res = `git clone --depth 1 https://github.com/junegunn/fzf.git #{fzf_dir}`
623
+ res = `#{fzf_dir}/install --bin --no-key-bindings --no-completion --no-update-rc --no-bash --no-zsh --no-fish`
624
+
625
+ raise DoingRuntimeError unless File.exist?(fzf)
626
+
627
+ fzf
628
+ end
629
+
614
630
  ##
615
631
  ## Generate a menu of options and allow user selection
616
632
  ##
@@ -619,7 +635,6 @@ module Doing
619
635
  def choose_from(options, prompt: 'Make a selection: ', multiple: false, sorted: true, fzf_args: [])
620
636
  return nil unless $stdout.isatty
621
637
 
622
- fzf = File.join(File.dirname(__FILE__), '../helpers/fuzzyfilefinder')
623
638
  # fzf_args << '-1' # User is expecting a menu, and even if only one it seves as confirmation
624
639
  fzf_args << %(--prompt "#{prompt}")
625
640
  fzf_args << '--multi' if multiple
@@ -653,8 +668,6 @@ module Doing
653
668
  def fuzzy_filter_items(items, opt: {})
654
669
  scannable = items.map.with_index { |item, idx| "#{item.title} #{item.note.join(' ')}".gsub(/[|*?!]/, '') + "|#{idx}" }.join("\n")
655
670
 
656
- fzf = File.join(File.dirname(__FILE__), '../helpers/fuzzyfilefinder')
657
-
658
671
  fzf_args = [
659
672
  '--multi',
660
673
  %(--filter="#{opt[:search].sub(/^'?/, "'")}"),
@@ -869,8 +882,6 @@ module Doing
869
882
  out.join('')
870
883
  end
871
884
 
872
- fzf = File.join(File.dirname(__FILE__), '../helpers/fuzzyfilefinder')
873
-
874
885
  fzf_args = [
875
886
  %(--header="#{opt[:header]}"),
876
887
  %(--prompt="#{opt[:prompt].sub(/ *$/, ' ')}"),
data/lib/doing.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  require 'doing/version'
4
3
  require 'time'
5
4
  require 'date'
@@ -30,6 +29,7 @@ require 'doing/errors'
30
29
  require 'doing/hooks'
31
30
  require 'doing/plugin_manager'
32
31
  require 'doing/pager'
32
+ require 'doing/completion'
33
33
  # require 'doing/markdown_document_listener'
34
34
 
35
35
  # Main doing module
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- $LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
3
- require 'doing/cli_status'
4
-
2
+ require 'tty-progressbar'
5
3
  require 'shellwords'
6
4
 
7
5
  class ::String
@@ -23,8 +21,6 @@ class ::String
23
21
  end
24
22
 
25
23
  class BashCompletions
26
- include Status
27
-
28
24
  attr_accessor :commands, :global_options
29
25
 
30
26
  def main_function
@@ -32,12 +28,9 @@ class BashCompletions
32
28
  out = []
33
29
  logic = []
34
30
  need_export = []
35
- # processing = []
36
31
 
37
32
  @commands.each_with_index do |cmd, i|
38
- # processing << cmd[:commands].first
39
- processing = cmd[:commands]
40
- progress('Processing subcommand options', i, @commands.count, processing)
33
+ @bar.advance
41
34
 
42
35
  data = get_help_sections(cmd[:commands].first)
43
36
 
@@ -82,7 +75,6 @@ class BashCompletions
82
75
  fi
83
76
  }
84
77
  EOFUNC
85
- clear
86
78
  out.join("\n")
87
79
  end
88
80
 
@@ -193,17 +185,19 @@ class BashCompletions
193
185
  end
194
186
 
195
187
  def initialize
196
- status('Generating Bash completions', reset: false)
197
188
  data = get_help_sections
198
189
  @global_options = parse_options(data[:global_options])
199
190
  @commands = parse_commands(data[:commands])
191
+ @bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Bash completions: \033[0;35;40m[:bar]\033[0m", total: @commands.count, bar_format: :blade)
192
+ @bar.resize(25)
200
193
  end
201
194
 
202
195
  def generate_completions
196
+ @bar.start
203
197
  out = []
204
198
  out << main_function
205
199
  out << 'complete -F _doing doing'
206
- status('Complete', reset: false)
200
+ @bar.finish
207
201
  out.join("\n")
208
202
  end
209
203
  end
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- $LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
3
- require 'doing/cli_status'
4
-
2
+ require 'tty-progressbar'
5
3
  require 'shellwords'
6
4
 
7
5
  class ::String
@@ -23,7 +21,6 @@ class ::String
23
21
  end
24
22
 
25
23
  class FishCompletions
26
- include Status
27
24
 
28
25
  attr_accessor :commands, :global_options
29
26
 
@@ -134,11 +131,7 @@ class FishCompletions
134
131
 
135
132
  def generate_subcommand_completions
136
133
  out = []
137
- # processing = []
138
134
  @commands.each_with_index do |cmd, i|
139
- # processing << cmd[:commands].first
140
- processing = cmd[:commands]
141
- progress('Processing subcommands', i, @commands.count, processing)
142
135
  out << "complete -xc doing -n '__fish_doing_needs_command' -a '#{cmd[:commands].join(' ')}' -d #{Shellwords.escape(cmd[:description])}"
143
136
  end
144
137
 
@@ -149,13 +142,9 @@ class FishCompletions
149
142
 
150
143
  out = []
151
144
  need_export = []
152
- # processing = []
153
145
 
154
146
  @commands.each_with_index do |cmd, i|
155
- # processing << cmd[:commands].first
156
- processing = cmd[:commands]
157
- progress('Processing subcommand options', i, @commands.count, processing)
158
-
147
+ @bar.advance
159
148
  data = get_help_sections(cmd[:commands].first)
160
149
 
161
150
  if data[:synopsis].join(' ').strip.split(/ /).last =~ /(path|file)/i
@@ -180,23 +169,25 @@ class FishCompletions
180
169
  out << "complete -f -c doing -s o -l output -x -n '__fish_doing_using_command #{need_export.join(' ')}' -a '(__fish_doing_export_plugins)'"
181
170
  end
182
171
 
183
- clear
172
+ # clear
184
173
  out.join("\n")
185
174
  end
186
175
 
187
176
  def initialize
188
- status('Generating Fish completions', reset: false)
189
177
  data = get_help_sections
190
178
  @global_options = parse_options(data[:global_options])
191
179
  @commands = parse_commands(data[:commands])
180
+ @bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Fish completions: \033[0;35;40m[:bar]\033[0m", total: @commands.count, bar_format: :blade)
181
+ @bar.resize(25)
192
182
  end
193
183
 
194
184
  def generate_completions
185
+ @bar.start
195
186
  out = []
196
187
  out << generate_helpers
197
188
  out << generate_subcommand_completions
198
189
  out << generate_subcommand_option_completions
199
- status('Complete', reset: false)
190
+ @bar.finish
200
191
  out.join("\n")
201
192
  end
202
193
  end
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- $LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
3
- require 'doing/cli_status'
4
-
2
+ require 'tty-progressbar'
5
3
  require 'shellwords'
6
4
 
7
5
  class ::String
@@ -23,7 +21,6 @@ class ::String
23
21
  end
24
22
 
25
23
  class ZshCompletions
26
- include Status
27
24
 
28
25
  attr_accessor :commands, :global_options
29
26
 
@@ -57,7 +54,7 @@ class ZshCompletions
57
54
  }
58
55
 
59
56
  EOFUNCTIONS
60
- status('Complete', reset: false)
57
+ @bar.finish
61
58
  out
62
59
  end
63
60
 
@@ -106,28 +103,20 @@ class ZshCompletions
106
103
 
107
104
  def generate_subcommand_completions
108
105
  out = []
109
- # processing = []
110
106
  @commands.each_with_index do |cmd, i|
111
- # processing << cmd[:commands].first
112
- processing = cmd[:commands]
113
- progress('Processing subcommands', i, @commands.count, processing)
114
107
  cmd[:commands].each do |c|
115
108
  out << "'#{c}:#{cmd[:description].gsub(/'/, '\\\'')}'"
116
109
  end
117
110
  end
118
- clear
119
111
  out
120
112
  end
121
113
 
122
114
  def generate_subcommand_option_completions(indent: ' ')
123
115
 
124
116
  out = []
125
- # processing = []
126
117
 
127
118
  @commands.each_with_index do |cmd, i|
128
- # processing << cmd[:commands].first
129
- processing = cmd[:commands]
130
- progress('Processing subcommand options', i, @commands.count, processing)
119
+ @bar.advance
131
120
 
132
121
  data = get_help_sections(cmd[:commands].first)
133
122
  option_arr = []
@@ -155,13 +144,15 @@ class ZshCompletions
155
144
  end
156
145
 
157
146
  def initialize
158
- status('Generating Zsh completions', reset: false)
159
147
  data = get_help_sections
160
148
  @global_options = parse_options(data[:global_options])
161
149
  @commands = parse_commands(data[:commands])
150
+ @bar = TTY::ProgressBar.new(" \033[0;0;33mGenerating Zsh completions: \033[0;35;40m[:bar]\033[0m", total: @commands.count, bar_format: :blade)
151
+ @bar.resize(25)
162
152
  end
163
153
 
164
154
  def generate_completions
155
+ @bar.start
165
156
  generate_helpers
166
157
  end
167
158
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doing
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.19
4
+ version: 2.0.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -90,58 +90,102 @@ dependencies:
90
90
  name: yard
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.9'
93
96
  - - ">="
94
97
  - !ruby/object:Gem::Version
95
- version: '0'
98
+ version: 0.9.26
96
99
  type: :development
97
100
  prerelease: false
98
101
  version_requirements: !ruby/object:Gem::Requirement
99
102
  requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '0.9'
100
106
  - - ">="
101
107
  - !ruby/object:Gem::Version
102
- version: '0'
108
+ version: 0.9.26
103
109
  - !ruby/object:Gem::Dependency
104
110
  name: redcarpet
105
111
  requirement: !ruby/object:Gem::Requirement
106
112
  requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '3.5'
107
116
  - - ">="
108
117
  - !ruby/object:Gem::Version
109
- version: '0'
118
+ version: 3.5.1
110
119
  type: :development
111
120
  prerelease: false
112
121
  version_requirements: !ruby/object:Gem::Requirement
113
122
  requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '3.5'
114
126
  - - ">="
115
127
  - !ruby/object:Gem::Version
116
- version: '0'
128
+ version: 3.5.1
117
129
  - !ruby/object:Gem::Dependency
118
130
  name: github-markup
119
131
  requirement: !ruby/object:Gem::Requirement
120
132
  requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '4.0'
121
136
  - - ">="
122
137
  - !ruby/object:Gem::Version
123
- version: '0'
138
+ version: 4.0.0
124
139
  type: :development
125
140
  prerelease: false
126
141
  version_requirements: !ruby/object:Gem::Requirement
127
142
  requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '4.0'
128
146
  - - ">="
129
147
  - !ruby/object:Gem::Version
130
- version: '0'
148
+ version: 4.0.0
131
149
  - !ruby/object:Gem::Dependency
132
150
  name: parallel_tests
133
151
  requirement: !ruby/object:Gem::Requirement
134
152
  requirements:
153
+ - - "~>"
154
+ - !ruby/object:Gem::Version
155
+ version: '3.7'
135
156
  - - ">="
136
157
  - !ruby/object:Gem::Version
137
- version: '0'
158
+ version: 3.7.3
138
159
  type: :development
139
160
  prerelease: false
140
161
  version_requirements: !ruby/object:Gem::Requirement
141
162
  requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '3.7'
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: 3.7.3
169
+ - !ruby/object:Gem::Dependency
170
+ name: tty-progressbar
171
+ requirement: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - "~>"
174
+ - !ruby/object:Gem::Version
175
+ version: '0.18'
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: 0.18.2
179
+ type: :runtime
180
+ prerelease: false
181
+ version_requirements: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - "~>"
184
+ - !ruby/object:Gem::Version
185
+ version: '0.18'
142
186
  - - ">="
143
187
  - !ruby/object:Gem::Version
144
- version: '0'
188
+ version: 0.18.2
145
189
  - !ruby/object:Gem::Dependency
146
190
  name: gli
147
191
  requirement: !ruby/object:Gem::Requirement
@@ -306,6 +350,11 @@ files:
306
350
  - lib/doing/array.rb
307
351
  - lib/doing/cli_status.rb
308
352
  - lib/doing/colors.rb
353
+ - lib/doing/completion.rb
354
+ - lib/doing/completion/bash_completion.rb
355
+ - lib/doing/completion/fish_completion.rb
356
+ - lib/doing/completion/string.rb
357
+ - lib/doing/completion/zsh_completion.rb
309
358
  - lib/doing/configuration.rb
310
359
  - lib/doing/errors.rb
311
360
  - lib/doing/hash.rb