doing 2.0.19 → 2.0.20

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 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