howzit 2.0.4 → 2.0.5

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: fba7a6b730f2aabb0e92a26b772cc06fa4182ad07edf7f8620733be70debf766
4
- data.tar.gz: 60900d73ffee99002daa4e32b5970c81986756c117fec172c824d59e87e72995
3
+ metadata.gz: 95a5f6c56ebff1fb944ca8ea23cba45ca5b71647654a315fd2cf7e486d522309
4
+ data.tar.gz: f9ac61f6fad2aff412d4302d0c586e9b287f284659255215a9565c8632bdfc8c
5
5
  SHA512:
6
- metadata.gz: 819083c882f7f330d050d23486e390179beb95c4f220458c4a66c496a0fe99eb69746e762d90ce4c033983764693e5dc77499779e1138ae41e64c346b3930d45
7
- data.tar.gz: 38d58943e9f8a8d32794ecc638257eb64f67cf09f0a22bc9811ca367627bafb48d22ceed7091551453700fd392fa857b94e37551208735433f0cf4a39ef27f6e
6
+ metadata.gz: be7ac525fc55a0e199b5762830bd0c6ef41b0b099627a45f1ca2aacd4508df3574f06e803513a22d870985631520c58b7f75c9e8519b5b02cf274eaeec577d56
7
+ data.tar.gz: e60252f5081ae563fc7eb8c207bb6ba0674b316455247cbc886e1b386dd33ecef234cfa493c6453bc02403daa11f79afb5f514c95a2f4e080302010466a7d4a6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ### 2.0.5
2
+
3
+ 2022-08-04 10:50
4
+
5
+ #### NEW
6
+
7
+ - Make any task optional when running by adding a ? (@open?(...))
8
+ - Optional tasks default to yes when you hit enter, invert by using a ! (@open?!(...)) to make default "no"
9
+
10
+ #### FIXED
11
+
12
+ - Replace escaped newlines in task list output so that they don't trigger a newline in the shell
13
+
1
14
  ### 2.0.4
2
15
 
3
16
  2022-08-04 06:25
data/README.md CHANGED
@@ -94,6 +94,18 @@ You can include commands that can be executed by howzit. Commands start at the b
94
94
 
95
95
  A block defined between @after and @end markers will be displayed after a topic is run. Use it to remind yourself of additional tasks after automated ones have been executed. The content between these markers is still included when viewing the topic, but the tags themselves do not show up in output.
96
96
 
97
+ ### Adding Titles
98
+
99
+ When displaying a topic, howzit can display a title instead of the contents of a directive. Any text after the closing parenthesis will be considered the task title.
100
+
101
+ ```
102
+ @run(~/scripts/deploy.sh) Deploy script
103
+ ```
104
+
105
+ When the topic is displayed, it will now show "Run Deploy script" instead of the path to the script. This can be overridden with the `--show-code` flag, which will instead show the contents of the directive.
106
+
107
+ For adding titles to run blocks (fenced code), see below.
108
+
97
109
  ### Run blocks (embedded scripts)
98
110
 
99
111
  For longer scripts you can write shell scripts and then call them with `@run(myscript.sh)`. For those in-between cases where you need a few commands but aren't motivated to create a separate file, you can use fenced code blocks with `run` as the language.
@@ -117,6 +129,12 @@ Example:
117
129
 
118
130
  Multiple blocks can be included in a topic. @commands take priority over code blocks and will be run first if they exist in the same topic.
119
131
 
132
+ ### Requiring Confirmation
133
+
134
+ You can have howzit confirm whether to execute an @command at runtime by including a question mark to the directive, e.g. `@run?(...)`. This will present a yes/no dialog before running the directive, with a default response of "yes" if you just hit return. To make the default response "no," add an exclamation point, e.g. `@run?!(...)`.
135
+
136
+ This works for any directive, including `@include`, and run blocks, which can use `run?` as the language specifier.
137
+
120
138
  ### Variables
121
139
 
122
140
  When running commands in a topic, you can use a double dash (`--`) in the command line (surrounded by spaces) and anything after it will be interpreted as shell arguments. These can be used in commands with `$` placeholders. `$1` represents the first argument, counting up from there. Use `$@` to pass all arguments as a shell-escaped string.
data/bin/howzit CHANGED
@@ -115,7 +115,7 @@ OptionParser.new do |opts|
115
115
 
116
116
  parts = key.split(/=/)
117
117
  k = parts[0].sub(/^:/, '')
118
- v = parts[1..-1].join(' ')
118
+ v = parts[1..].join(' ')
119
119
 
120
120
  if Howzit.options.key?(k.to_sym)
121
121
  Howzit.options[k.to_sym] = v.to_config_value(Howzit.options[k.to_sym])
data/lib/howzit/hash.rb CHANGED
@@ -32,4 +32,24 @@ class ::Hash
32
32
  def deep_thaw!
33
33
  replace deep_thaw
34
34
  end
35
+
36
+ # Turn all keys into string
37
+ #
38
+ # Return a copy of the hash where all its keys are strings
39
+ def stringify_keys
40
+ each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v.is_a?(Hash) ? v.stringify_keys : v }
41
+ end
42
+
43
+ def stringify_keys!
44
+ replace stringify_keys
45
+ end
46
+
47
+ # Turn all keys into symbols
48
+ def symbolize_keys
49
+ each_with_object({}) { |(k, v), hsh| hsh[k.to_sym] = v.is_a?(Hash) ? v.symbolize_keys : v }
50
+ end
51
+
52
+ def symbolize_keys!
53
+ replace symbolize_keys
54
+ end
35
55
  end
data/lib/howzit/task.rb CHANGED
@@ -2,13 +2,15 @@
2
2
 
3
3
  module Howzit
4
4
  class Task
5
- attr_reader :type, :title, :action, :parent
5
+ attr_reader :type, :title, :action, :parent, :optional, :default
6
6
 
7
- def initialize(type, title, action, parent = nil)
7
+ def initialize(type, title, action, parent = nil, optional: false, default: true)
8
8
  @type = type
9
9
  @title = title
10
10
  @action = action.render_arguments
11
11
  @parent = parent
12
+ @optional = optional
13
+ @default = default
12
14
  end
13
15
 
14
16
  def to_s
@@ -16,7 +18,7 @@ module Howzit
16
18
  end
17
19
 
18
20
  def to_list
19
- " * #{@type}: #{@title}"
21
+ " * #{@type}: #{@title.gsub(/\\n/, '\​n')}"
20
22
  end
21
23
  end
22
24
  end
data/lib/howzit/topic.rb CHANGED
@@ -28,12 +28,19 @@ module Howzit
28
28
  if @tasks.count.positive?
29
29
  unless @prereqs.empty?
30
30
  puts @prereqs.join("\n\n")
31
- res = Prompt.yn('This task has prerequisites, have they been met?', default: true)
31
+ res = Prompt.yn('This topic has prerequisites, have they been met?', default: true)
32
32
  Process.exit 1 unless res
33
33
 
34
34
  end
35
35
 
36
36
  @tasks.each do |task|
37
+ if task.optional
38
+ q = %({bg}#{task.type.to_s.capitalize} {xw}"{bw}#{task.title}{xw}"{x}).c
39
+ res = Prompt.yn(q, default: task.default)
40
+ next unless res
41
+
42
+ end
43
+
37
44
  if task.type == :block
38
45
  warn "{bg}Running block {bw}#{title}{x}".c if Howzit.options[:log_level] < 2
39
46
  block = task.action
@@ -74,7 +81,7 @@ module Howzit
74
81
  end
75
82
  end
76
83
  else
77
- warn "{r}--run: No {br}@directive{xr} found in {bw}#{key}{x}".c
84
+ warn "{r}--run: No {br}@directive{xr} found in {bw}#{@title}{x}".c
78
85
  end
79
86
  output.push("{bm}Ran #{tasks} #{tasks == 1 ? 'task' : 'tasks'}{x}".c) if Howzit.options[:log_level] < 2 && !nested
80
87
 
@@ -139,49 +146,57 @@ module Howzit
139
146
  output.push('')
140
147
  end
141
148
  topic = @content.dup
142
- topic.gsub!(/(?mi)^(`{3,})run *([^\n]*)[\s\S]*?\n\1\s*$/, '@@@run \2') unless Howzit.options[:show_all_code]
149
+ topic.gsub!(/(?mi)^(`{3,})run([?!]*) *([^\n]*)[\s\S]*?\n\1\s*$/, '@@@run\2 \3') unless Howzit.options[:show_all_code]
143
150
  topic.split(/\n/).each do |l|
144
151
  case l
145
152
  when /@(before|after|prereq|end)/
146
153
  next
147
- when /@include\((.*?)\)/
148
- m = Regexp.last_match
149
- matches = Howzit.buildnote.find_topic(m[1])
154
+ when /@include(?<optional>[!?]{1,2})?\((?<action>.*?)\)/
155
+ m = Regexp.last_match.named_captures.symbolize_keys
156
+ matches = Howzit.buildnote.find_topic(m[:action])
150
157
  unless matches.empty?
151
- if opt[:single]
152
- title = "From #{matches[0].title}:"
153
- color = '{Kyd}'
154
- rule = '{kKd}'
155
- else
156
- title = "Include #{matches[0].title}"
157
- color = '{Kyd}'
158
- rule = '{kKd}'
159
- end
160
- unless Howzit.inclusions.include?(matches[0])
161
- output.push("#{'> ' * @nest_level}#{title}".format_header({ color: color, hr: '.', border: rule }))
158
+ i_topic = matches[0]
159
+ rule = '{kKd}'
160
+ color = '{Kyd}'
161
+ option = if i_topic.tasks.empty?
162
+ ''
163
+ else
164
+ optional = m[:optional] =~ /[?!]+/ ? true : false
165
+ default = m[:optional] =~ /!/ ? false : true
166
+ if optional
167
+ default ? " {xKk}[{gbK}Y{xKk}/{dbwK}n{xKk}]{x}#{color}".c : " {xKk}[{dbwK}y{xKk}/{bgK}N{xKk}]{x}#{color}".c
168
+ else
169
+ ''
170
+ end
171
+ end
172
+ title = "#{opt[:single] ? 'From' : 'Include'} #{i_topic.title}#{option}:"
173
+ options = { color: color, hr: '.', border: rule }
174
+ unless Howzit.inclusions.include?(i_topic)
175
+ output.push("#{'> ' * @nest_level}#{title}".format_header(options))
162
176
  end
163
177
 
164
- if opt[:single]
165
- if Howzit.inclusions.include?(matches[0])
166
- output.push("#{'> ' * @nest_level}#{title} included above".format_header({
167
- color: color, hr: '.', border: rule }))
168
- else
169
- @nest_level += 1
170
- output.concat(matches[0].print_out({ single: true, header: false }))
171
- @nest_level -= 1
172
- end
173
- unless Howzit.inclusions.include?(matches[0])
174
- output.push("#{'> ' * @nest_level}...".format_header({ color: color, hr: '.', border: rule }))
175
- end
178
+ if opt[:single] && Howzit.inclusions.include?(i_topic)
179
+ output.push("#{'> ' * @nest_level}#{title} included above".format_header(options))
180
+ elsif opt[:single]
181
+ @nest_level += 1
182
+ output.concat(i_topic.print_out({ single: true, header: false }))
183
+ output.push("#{'> ' * @nest_level}...".format_header(options))
184
+ @nest_level -= 1
176
185
  end
177
- Howzit.inclusions.push(matches[0])
186
+ Howzit.inclusions.push(i_topic)
178
187
  end
179
-
180
- when /@(run|copy|open|url|include)\((.*?)\)(.*?)$/
181
- m = Regexp.last_match
182
- cmd = m[1]
183
- obj = m[2]
184
- title = m[3].empty? ? obj : m[3]
188
+ when /@(?<cmd>run|copy|open|url|include)(?<optional>[?!]{1,2})?\((?<action>.*?)\) *(?<title>.*?)$/
189
+ m = Regexp.last_match.named_captures.symbolize_keys
190
+ cmd = m[:cmd]
191
+ obj = m[:action]
192
+ title = m[:title].empty? ? obj : m[:title].strip
193
+ optional = m[:optional] =~ /[?!]+/ ? true : false
194
+ default = m[:optional] =~ /!/ ? false : true
195
+ option = if optional
196
+ default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
197
+ else
198
+ ''
199
+ end
185
200
  icon = case cmd
186
201
  when 'run'
187
202
  "\u{25B6}"
@@ -191,14 +206,28 @@ module Howzit
191
206
  "\u{279A}"
192
207
  end
193
208
 
194
- output.push("{bmK}#{icon} {bwK}#{title.gsub(/\\n/, '\​n')}{x}".c)
195
- when /(`{3,})run *(.*?)$/i
196
- m = Regexp.last_match
197
- desc = m[2].length.positive? ? "Block: #{m[2]}" : 'Code Block'
209
+ output.push("{bmK}#{icon} {bwK}#{title.gsub(/\\n/, '\​n')}{x}#{option}".c)
210
+ when /(?<fence>`{3,})run(?<optional>[!?]{1,2})? *(?<title>.*?)$/i
211
+ m = Regexp.last_match.named_captures.symbolize_keys
212
+ optional = m[:optional] =~ /[?!]+/ ? true : false
213
+ default = m[:optional] =~ /!/ ? false : true
214
+ option = if optional
215
+ default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
216
+ else
217
+ ''
218
+ end
219
+ desc = m[:title].length.positive? ? "Block: #{m[:title]}#{option}" : "Code Block#{option}"
198
220
  output.push("{bmK}\u{25B6} {bwK}#{desc}{x}\n```".c)
199
- when /@@@run *(.*?)$/i
200
- m = Regexp.last_match
201
- desc = m[1].length.positive? ? "Block: #{m[1]}" : 'Code Block'
221
+ when /@@@run(?<optional>[!?]{1,2})? *(?<title>.*?)$/i
222
+ m = Regexp.last_match.named_captures.symbolize_keys
223
+ optional = m[:optional] =~ /[?!]+/ ? true : false
224
+ default = m[:optional] =~ /!/ ? false : true
225
+ option = if optional
226
+ default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
227
+ else
228
+ ''
229
+ end
230
+ desc = m[:title].length.positive? ? "Block: #{m[:title]}#{option}" : "Code Block#{option}"
202
231
  output.push("{bmK}\u{25B6} {bwK}#{desc}{x}".c)
203
232
  else
204
233
  l.wrap!(Howzit.options[:wrap]) if Howzit.options[:wrap].positive?
@@ -215,18 +244,27 @@ module Howzit
215
244
  @prereqs = @content.scan(/(?<=@before\n).*?(?=\n@end)/im).map(&:strip)
216
245
  @postreqs = @content.scan(/(?<=@after\n).*?(?=\n@end)/im).map(&:strip)
217
246
 
218
- rx = /(?:@(include|run|copy|open|url)\((.*?)\) *(.*?)(?=$)|(`{3,})run(?: +([^\n]+))?(.*?)\4)/mi
219
- directives = @content.scan(rx)
247
+ rx = /(?mix)(?:
248
+ @(?<cmd>include|run|copy|open|url)(?<optional>[!?]{1,2})?\((?<action>[^)]*?)\)(?<title>[^\n]+)?
249
+ |(?<fence>`{3,})run(?<optional2>[!?]{1,2})?(?<title2>[^\n]+)?(?<block>.*?)\k<fence>
250
+ )/
251
+ matches = []
252
+ @content.scan(rx) { matches << Regexp.last_match }
253
+ matches.each do |m|
254
+ c = m.named_captures.symbolize_keys
220
255
 
221
- directives.each do |c|
222
- if c[0].nil?
223
- title = c[4] ? c[4].strip : ''
224
- block = c[5].strip
225
- runnable << Howzit::Task.new(:block, title, block)
256
+ if c[:cmd].nil?
257
+ optional = c[:optional2] =~ /[?!]{1,2}/ ? true : false
258
+ default = c[:optional2] =~ /!/ ? false : true
259
+ title = c[:title2].nil? ? '' : c[:title2].strip
260
+ block = c[:block]&.strip
261
+ runnable << Howzit::Task.new(:block, title, block, optional: optional, default: default)
226
262
  else
227
- cmd = c[0]
228
- obj = c[1]
229
- title = c[3] || obj
263
+ cmd = c[:cmd]
264
+ optional = c[:optional] =~ /[?!]{1,2}/ ? true : false
265
+ default = c[:optional] =~ /!/ ? false : true
266
+ obj = c[:action]
267
+ title = c[:title].nil? ? obj : c[:title].strip
230
268
 
231
269
  case cmd
232
270
  when /include/i
@@ -239,17 +277,15 @@ module Howzit
239
277
  # end
240
278
  # runnable.concat(tasks)
241
279
  # end
242
- title = c[3] || obj
243
- runnable << Howzit::Task.new(:include, title, obj)
280
+ runnable << Howzit::Task.new(:include, title, obj, optional: optional, default: default)
244
281
  when /run/i
245
- title = c[3] || obj
246
282
  # warn "{bg}Running {bw}#{obj}{x}".c if Howzit.options[:log_level] < 2
247
- runnable << Howzit::Task.new(:run, title, obj)
283
+ runnable << Howzit::Task.new(:run, title, obj, optional: optional, default: default)
248
284
  when /copy/i
249
285
  # warn "{bg}Copied {bw}#{obj}{bg} to clipboard{x}".c if Howzit.options[:log_level] < 2
250
- runnable << Howzit::Task.new(:copy, title, Shellwords.escape(obj))
286
+ runnable << Howzit::Task.new(:copy, title, Shellwords.escape(obj), optional: optional, default: default)
251
287
  when /open|url/i
252
- runnable << Howzit::Task.new(:open, title, obj)
288
+ runnable << Howzit::Task.new(:open, title, obj, optional: optional, default: default)
253
289
  end
254
290
  end
255
291
  end
@@ -3,5 +3,5 @@
3
3
  # Primary module for this gem.
4
4
  module Howzit
5
5
  # Current Howzit version.
6
- VERSION = '2.0.4'
6
+ VERSION = '2.0.5'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: howzit
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.4
4
+ version: 2.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra