howzit 2.0.10 → 2.0.13

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: bd8307dc1bdbf51c7a78333c5880990b0bf82acb3fb2401d9e9969fe312ce125
4
- data.tar.gz: 72ef1a744da1debcd5faf9ff9ee170eae3389b04e585cd652450eb65ba177c30
3
+ metadata.gz: e269532605e044c35e2a0ebe322fe51ba5199d690a57385a358a3bad4fc2921f
4
+ data.tar.gz: 4ca045a0c3c2d1627b04a47363beb8e2de6718f5c708e1f4bf96285af96e09d4
5
5
  SHA512:
6
- metadata.gz: affa665e5f83a8b836f5fe0a3d3c8e178d6fc880bcf3cecfe13afe44e61f4392831a45ff57f1a412a6ae7878f5cd7c00cd541aea1d780eb2b61aa01247268855
7
- data.tar.gz: 85449c9184c5927d56acbd00e4086f11a9f4ee85085bfe07c12eb3e7b27e71c00eccee203fd73bda94919dfef000ead8f99f61e275b4d73700b442e25caa38c9
6
+ metadata.gz: 803b06d4a85632357eb1e61a898f88c39971df388107a62f67479e34788346f1824f78a3b3473cf5ee45dc445a494e7a144590d5df33ffd1b73d2114ba19049d
7
+ data.tar.gz: 51c6ec7213c4b7c531e53c32140dc485d897f40ee79d242ee8ec446804ad0edac8a9bac61d3b68189bf60884a3dbee28fe6d5f5f3040dae23ec8cb7ddb24903c
data/.rubocop.yml CHANGED
@@ -6,6 +6,7 @@ AllCops:
6
6
 
7
7
  Metrics/BlockLength:
8
8
  Exclude:
9
+ - Rakefile
9
10
  - bin/howzit
10
11
  - lib/*.rb
11
12
 
data/.travis.yml CHANGED
@@ -4,10 +4,10 @@ sudo: required
4
4
  dist: trusty
5
5
  cache: bundler
6
6
  rvm:
7
+ - ruby-2.6.4
7
8
  - ruby-2.7.0
8
9
  - ruby-3.0.1
9
10
  install:
10
- - gem uninstall bundler
11
11
  - gem install bundler --version '2.2.29'
12
12
  - bundle install
13
- script: "rake"
13
+ script: "bundle exec rspec spec --exclude-pattern 'cli*'"
data/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ ### 2.0.13
2
+
3
+ 2022-08-05 13:19
4
+
5
+ #### IMPROVED
6
+
7
+ - Add tests for more ruby versions via Docker
8
+
9
+ ### 2.0.12
10
+
11
+ 2022-08-05 11:27
12
+
13
+ #### IMPROVED
14
+
15
+ - Show how many tasks will be included when requesting confirmation for an include? directive
16
+
17
+ ### 2.0.11
18
+
19
+ 2022-08-05 11:21
20
+
21
+ #### IMPROVED
22
+
23
+ - Update fish completions with all current command line options
24
+
1
25
  ### 2.0.10
2
26
 
3
27
  2022-08-05 08:17
data/Rakefile CHANGED
@@ -5,6 +5,7 @@ require 'bundler/gem_tasks'
5
5
  require 'rspec/core/rake_task'
6
6
  require 'rubocop/rake_task'
7
7
  require 'yard'
8
+ require 'tty-spinner'
8
9
 
9
10
  task default: %i[test yard]
10
11
 
@@ -34,3 +35,43 @@ desc 'Changelog version check'
34
35
  task :cver do
35
36
  puts IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
36
37
  end
38
+
39
+ desc 'Run tests in Docker'
40
+ task :dockertest, :version, :login do |_, args|
41
+ args.with_defaults(version: 'all', login: false)
42
+ case args[:version]
43
+ when /^a/
44
+ %w[6 7 3].each do |v|
45
+ Rake::Task['dockertest'].reenable
46
+ Rake::Task['dockertest'].invoke(v, false)
47
+ end
48
+ Process.exit 0
49
+ when /^3/
50
+ img = 'howzittest3'
51
+ file = 'docker/Dockerfile-3.0'
52
+ when /6$/
53
+ img = 'howzittest26'
54
+ file = 'docker/Dockerfile-2.6'
55
+ when /(^2|7$)/
56
+ img = 'howzittest27'
57
+ file = 'docker/Dockerfile-2.7'
58
+ else
59
+ img = 'howzittest'
60
+ file = 'docker/Dockerfile'
61
+ end
62
+
63
+ puts `docker build . --file #{file} -t #{img}`
64
+
65
+ exec "docker run -v #{File.dirname(__FILE__)}:/howzit -it #{img} /bin/bash -l" if args[:login]
66
+
67
+ spinner = TTY::Spinner.new('[:spinner] Running tests ...', hide_cursor: true)
68
+
69
+ spinner.auto_spin
70
+ res = `docker run --rm -v #{File.dirname(__FILE__)}:/howzit -it #{img}`
71
+ # commit = puts `bash -c "docker commit $(docker ps -a|grep #{img}|awk '{print $1}'|head -n 1) #{img}"`.strip
72
+ spinner.success
73
+ spinner.stop
74
+
75
+ puts res
76
+ # puts commit&.empty? ? "Error commiting Docker tag #{img}" : "Committed Docker tag #{img}"
77
+ end
data/bin/howzit CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby -W1
1
+ #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
4
  $LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
data/docker/Dockerfile ADDED
@@ -0,0 +1,11 @@
1
+ FROM ruby:3.0.1
2
+ # RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
3
+ RUN mkdir /howzit
4
+ WORKDIR /howzit
5
+ # COPY ./ /howzit/
6
+ RUN gem install bundler:2.2.17
7
+ RUN apt-get update -y
8
+ RUN apt-get install -y less vim
9
+ COPY ./docker/inputrc /root/.inputrc
10
+ COPY ./docker/bash_profile /root/.bash_profile
11
+ CMD ["scripts/runtests.sh"]
@@ -0,0 +1,11 @@
1
+ FROM ruby:2.6
2
+ # RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
3
+ RUN mkdir /howzit
4
+ WORKDIR /howzit
5
+ # COPY ./ /howzit/
6
+ RUN gem install bundler:2.2.17
7
+ RUN apt-get update -y
8
+ RUN apt-get install -y less vim
9
+ COPY ./docker/inputrc /root/.inputrc
10
+ COPY ./docker/bash_profile /root/.bash_profile
11
+ CMD ["scripts/runtests.sh"]
@@ -0,0 +1,11 @@
1
+ FROM ruby:2.7
2
+ # RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
3
+ RUN mkdir /howzit
4
+ WORKDIR /howzit
5
+ # COPY ./ /howzit/
6
+ RUN gem install bundler:2.2.17
7
+ RUN apt-get update -y
8
+ RUN apt-get install -y less vim
9
+ COPY ./docker/inputrc /root/.inputrc
10
+ COPY ./docker/bash_profile /root/.bash_profile
11
+ CMD ["scripts/runtests.sh"]
@@ -0,0 +1,11 @@
1
+ FROM ruby:3.0.0
2
+ # RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
3
+ RUN mkdir /howzit
4
+ WORKDIR /howzit
5
+ # COPY ./ /howzit/
6
+ RUN gem install bundler:2.2.17
7
+ RUN apt-get update -y
8
+ RUN apt-get install -y less vim
9
+ COPY ./docker/inputrc /root/.inputrc
10
+ COPY ./docker/bash_profile /root/.bash_profile
11
+ CMD ["scripts/runtests.sh"]
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+ export GLI_DEBUG=true
3
+ export EDITOR="/usr/bin/vim"
4
+ alias b="bundle exec bin/howzit"
5
+
6
+ shopt -s nocaseglob
7
+ shopt -s histappend
8
+ shopt -s histreedit
9
+ shopt -s histverify
10
+ shopt -s cmdhist
11
+
12
+ cd /howzit
13
+ bundle install
data/docker/inputrc ADDED
@@ -0,0 +1,57 @@
1
+ "\e[3~": delete-char
2
+ "\ex": 'cd !$ \015ls\015'
3
+ "\ez": 'cd -\015'
4
+ "\e\C-m": '\C-a "$(\C-e|fzf)"\C-a'
5
+ "\e/": '"$(!!|fzf)"\C-a \C-m\C-m'
6
+ # these allow you to use alt+left/right arrow keys
7
+ # to jump the cursor over words
8
+ "\e[1;5C": forward-word
9
+ "\e[1;5D": backward-word
10
+ # "\e[D": backward-word
11
+ # "\e[C": forward-word
12
+ "\ea": menu-complete
13
+ # TAB: menu-complete
14
+ # "\e[Z": "\e-1\C-i"
15
+
16
+ "\e\C-l": history-and-alias-expand-line
17
+
18
+ # these allow you to start typing a command and
19
+ # use the up/down arrow to auto complete from
20
+ # commands in your history
21
+ "\e[B": history-search-forward
22
+ "\e[A": history-search-backward
23
+ "\ew": history-search-backward
24
+ "\es": history-search-forward
25
+ # this lets you hit tab to auto-complete a file or
26
+ # directory name ignoring case
27
+ set completion-ignore-case On
28
+ set mark-symlinked-directories On
29
+ set completion-prefix-display-length 2
30
+ set bell-style none
31
+ # set bell-style visible
32
+ set meta-flag on
33
+ set convert-meta off
34
+ set input-meta on
35
+ set output-meta on
36
+ set show-all-if-ambiguous on
37
+ set show-all-if-unmodified on
38
+ set completion-map-case on
39
+ set visible-stats on
40
+
41
+ # Do history expansion when space entered?
42
+ $if bash
43
+ Space: magic-space
44
+ $endif
45
+
46
+ # Show extra file information when completing, like `ls -F` does
47
+ set visible-stats on
48
+
49
+ # Be more intelligent when autocompleting by also looking at the text after
50
+ # the cursor. For example, when the current line is "cd ~/src/mozil", and
51
+ # the cursor is on the "z", pressing Tab will not autocomplete it to "cd
52
+ # ~/src/mozillail", but to "cd ~/src/mozilla". (This is supported by the
53
+ # Readline used by Bash 4.)
54
+ set skip-completed-text on
55
+
56
+ # Use Alt/Meta + Delete to delete the preceding word
57
+ "\e[3;3~": kill-word
@@ -1,3 +1,35 @@
1
1
  complete -xc howzit -a "(howzit -L)"
2
- complete -xc howzit -s r -a "(howzit -T)"
3
-
2
+ complete -c howzit -l default -d "Answer all prompts with default response"
3
+ complete -c howzit -x -s m -l matching -a "partial exact fuzzy beginswith" -d "Topics matching type"
4
+ complete -c howzit -x -l multiple -a "first best all choose" -d "Multiple result handling"
5
+ complete -c howzit -s u -l no-upstream -d "Don't traverse up parent directories for additional build notes"
6
+ complete -c howzit -s u -l upstream -d "Traverse up parent directories for additional build notes"
7
+ complete -c howzit -s L -l list-completions -d "List topics for completion"
8
+ complete -c howzit -s l -l list -d "List available topics"
9
+ complete -c howzit -s R -l list-runnable -d "List topics containing @ directives (verbose)"
10
+ complete -c howzit -s T -l task-list -d "List topics containing @ directives (completion-compatible)"
11
+ complete -c howzit -l templates -d "List available templates"
12
+ complete -c howzit -l title-only -d "Output title only"
13
+ complete -c howzit -s c -l create -d "Create a skeleton build note in the current working directory"
14
+ complete -c howzit -f -l config-get -d "Display the configuration settings or setting for a specific key"
15
+ complete -c howzit -f -l config-set -d "Set a config value (must be a valid key)"
16
+ complete -c howzit -l edit-config -d "Edit configuration file using editor (subl)"
17
+ complete -c howzit -s e -l edit -d "Edit buildnotes file in current working directory using editor (subl)"
18
+ complete -c howzit -x -l grep -d "Display sections matching a search pattern"
19
+ complete -c howzit -s r -l run -a "(howzit -T)" -d "Execute @run, @open, and/or @copy commands for given topic"
20
+ complete -c howzit -s s -l select -d "Select topic from menu"
21
+ complete -c howzit -l color -d "Colorize output (default on)"
22
+ complete -c howzit -l no-color -d "Don't colorize output (default on)"
23
+ complete -c howzit -x -l header-format -d "Formatting style for topic titles (border, block)"
24
+ complete -c howzit -l md-highlight -d "Highlight Markdown syntax (default on), requires mdless or mdcat"
25
+ complete -c howzit -l no-md-highlight -d "Don't highlight Markdown syntax (default on), requires mdless or mdcat"
26
+ complete -c howzit -l pager -d "Paginate output (default on)"
27
+ complete -c howzit -l no-pager -d "Don't paginate output (default on)"
28
+ complete -c howzit -l show-code -d "Display the content of fenced run blocks"
29
+ complete -c howzit -s t -l title -d "Output title with build notes"
30
+ complete -c howzit -x -s w -l wrap -d "Wrap to specified width (default 80, 0 to disable)"
31
+ complete -c howzit -s d -l debug -d "Show debug messages (and all messages)"
32
+ complete -c howzit -s q -l quiet -d "Silence info message"
33
+ complete -c howzit -l verbose -d "Show all messages"
34
+ complete -c howzit -s h -l help -d "Display this screen"
35
+ complete -c howzit -s v -l version -d "Display version number"
data/howzit.gemspec CHANGED
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency 'yard', '~> 0.9.5'
39
39
  spec.add_development_dependency 'redcarpet', '~> 3.2'
40
40
  spec.add_development_dependency 'github-markup', '~> 1.3'
41
+ spec.add_development_dependency 'tty-spinner', '~> 0.9'
41
42
 
42
43
  spec.add_runtime_dependency 'mdless', '~> 1.0', '>= 1.0.28'
43
44
  spec.add_runtime_dependency 'tty-screen', '~> 0.8'
@@ -120,7 +120,7 @@ module Howzit
120
120
 
121
121
  ##
122
122
  ## Return a formatted list of topics containing
123
- ## @directives suitable for console output
123
+ ## directives suitable for console output
124
124
  ##
125
125
  ## @return [String] formatted list
126
126
  ##
@@ -275,38 +275,15 @@ module Howzit
275
275
  buildnotes.reverse
276
276
  end
277
277
 
278
- ##
279
- ## Test if the filename matches the conditions to be a build note
280
- ##
281
- ## @param filename [String] The filename to test
282
- ##
283
- ## @return [Boolean] true if filename passes test
284
- ##
285
- def build_note?(filename)
286
- return false if filename.downcase !~ /^(howzit[^.]*|build[^.]+)/
287
-
288
- return false if Howzit.config.should_ignore(filename)
289
-
290
- true
291
- end
292
-
293
278
  ##
294
279
  ## Glob current directory for valid build note filenames
280
+ ## (must start with "build" or "howzit" and have
281
+ ## extension of "txt", "md", or "markdown")
295
282
  ##
296
283
  ## @return [String] file path
297
284
  ##
298
285
  def glob_note
299
- filename = nil
300
- # Check for a build note file in the current folder. Filename must start
301
- # with "build" and have an extension of txt, md, or markdown.
302
-
303
- Dir.glob('*.{txt,md,markdown}').each do |f|
304
- if build_note?(f)
305
- filename = f
306
- break
307
- end
308
- end
309
- filename
286
+ Dir.glob('*.{txt,md,markdown}').select(&:build_note?)[0]
310
287
  end
311
288
 
312
289
  ##
data/lib/howzit/hash.rb CHANGED
@@ -5,7 +5,7 @@ class ::Hash
5
5
  ##
6
6
  ## Freeze all values in a hash
7
7
  ##
8
- ## @return Hash with all values frozen
8
+ ## @return [Hash] Hash with all values frozen
9
9
  ##
10
10
  def deep_freeze
11
11
  chilled = {}
@@ -16,10 +16,18 @@ class ::Hash
16
16
  chilled.freeze
17
17
  end
18
18
 
19
+ ##
20
+ ## Deep freeze a hash in place (destructive)
21
+ ##
19
22
  def deep_freeze!
20
23
  replace deep_thaw.deep_freeze
21
24
  end
22
25
 
26
+ ##
27
+ ## Unfreeze nested hash values
28
+ ##
29
+ ## @return [Hash] Hash with all values unfrozen
30
+ ##
23
31
  def deep_thaw
24
32
  chilled = {}
25
33
  each do |k, v|
@@ -29,26 +37,39 @@ class ::Hash
29
37
  chilled.dup
30
38
  end
31
39
 
40
+ ##
41
+ ## Unfreeze nested hash values in place (destructive)
42
+ ##
32
43
  def deep_thaw!
33
44
  replace deep_thaw
34
45
  end
35
46
 
36
47
  # Turn all keys into string
37
48
  #
38
- # Return a copy of the hash where all its keys are strings
49
+ # @return [Hash] hash with all keys as strings
50
+ #
39
51
  def stringify_keys
40
52
  each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v.is_a?(Hash) ? v.stringify_keys : v }
41
53
  end
42
54
 
55
+ ##
56
+ ## Turn all keys into strings in place (destructive)
57
+ ##
43
58
  def stringify_keys!
44
59
  replace stringify_keys
45
60
  end
46
61
 
47
62
  # Turn all keys into symbols
63
+ #
64
+ # @return [Hash] hash with all keys as symbols
65
+ #
48
66
  def symbolize_keys
49
67
  each_with_object({}) { |(k, v), hsh| hsh[k.to_sym] = v.is_a?(Hash) ? v.symbolize_keys : v }
50
68
  end
51
69
 
70
+ ##
71
+ ## Turn all keys into symbols in place (destructive)
72
+ ##
52
73
  def symbolize_keys!
53
74
  replace symbolize_keys
54
75
  end
data/lib/howzit/prompt.rb CHANGED
@@ -4,10 +4,22 @@ module Howzit
4
4
  # Command line prompt utils
5
5
  module Prompt
6
6
  class << self
7
- def yn(prompt, default: true)
7
+
8
+ ##
9
+ ## Display and read a Yes/No prompt
10
+ ##
11
+ ## @param prompt [String] The prompt string
12
+ ## @param default [Boolean] default value if
13
+ ## return is pressed or prompt is
14
+ ## skipped
15
+ ##
16
+ ## @return [Boolean] result
17
+ ##
18
+ def yn(prompt, default: true)
8
19
  return default unless $stdout.isatty
9
20
 
10
21
  return default if Howzit.options[:default]
22
+
11
23
  tty_state = `stty -g`
12
24
  system 'stty raw -echo cbreak isig'
13
25
  yn = color_single_options(default ? %w[Y n] : %w[y N])
@@ -20,6 +32,14 @@ module Howzit
20
32
  res.empty? ? default : res =~ /y/i
21
33
  end
22
34
 
35
+ ##
36
+ ## Helper function to colorize the Y/N prompt
37
+ ##
38
+ ## @param choices [Array] The choices with
39
+ ## default capitalized
40
+ ##
41
+ ## @return [String] colorized string
42
+ ##
23
43
  def color_single_options(choices = %w[y n])
24
44
  out = []
25
45
  choices.each do |choice|
@@ -33,6 +53,12 @@ module Howzit
33
53
  Color.template("{xg}[#{out.join('/')}{xg}]{x}")
34
54
  end
35
55
 
56
+ ##
57
+ ## Create a numbered list of options. Outputs directly
58
+ ## to console, returns nothing
59
+ ##
60
+ ## @param matches [Array] The list items
61
+ ##
36
62
  def options_list(matches)
37
63
  counter = 1
38
64
  puts
@@ -43,6 +69,15 @@ module Howzit
43
69
  puts
44
70
  end
45
71
 
72
+ ##
73
+ ## Choose from a list of items. If fzf is available,
74
+ ## uses that, otherwise generates its own list of
75
+ ## options and accepts a numeric response
76
+ ##
77
+ ## @param matches [Array] The options list
78
+ ##
79
+ ## @return [Array] the selected results
80
+ ##
46
81
  def choose(matches)
47
82
  if Util.command_exist?('fzf')
48
83
  settings = [
@@ -80,7 +115,7 @@ module Howzit
80
115
  end
81
116
  line = line == '' ? 1 : line.to_i
82
117
 
83
- return matches[line - 1] if line.positive? && line <= matches.length
118
+ return [matches[line - 1]] if line.positive? && line <= matches.length
84
119
 
85
120
  puts 'Out of range'
86
121
  options_list(matches)
@@ -3,7 +3,37 @@
3
3
  module Howzit
4
4
  # String Extensions
5
5
  module StringUtils
6
+ ##
7
+ ## Test if the filename matches the conditions to be a build note
8
+ ##
9
+ ## @return [Boolean] true if filename passes test
10
+ ##
11
+ def build_note?
12
+ return false if downcase !~ /^(howzit[^.]*|build[^.]+)/
13
+
14
+ return false if Howzit.config.should_ignore(self)
15
+
16
+ true
17
+ end
18
+
19
+ ##
20
+ ## Replace slash escaped characters in a string with a
21
+ ## zero-width space that will prevent a shell from
22
+ ## interpreting them when output to console
23
+ ##
24
+ ## @return [String] new string
25
+ ##
26
+ def preserve_escapes
27
+ gsub(/\\([a-z])/, '\​\1')
28
+ end
29
+
6
30
  # Convert a string to a valid YAML value
31
+ #
32
+ # @param orig_value The original value from which
33
+ # type will be determined
34
+ #
35
+ # @return coerced value
36
+ #
7
37
  def to_config_value(orig_value = nil)
8
38
  if orig_value
9
39
  case orig_value.class.to_s
@@ -28,10 +58,21 @@ module Howzit
28
58
  end
29
59
  end
30
60
 
61
+ ##
62
+ ## Shortcut for calling Color.template
63
+ ##
64
+ ## @return [String] colorized string
65
+ ##
31
66
  def c
32
67
  Color.template(self)
33
68
  end
34
69
 
70
+
71
+ ##
72
+ ## Convert a string to a regex object based on matching settings
73
+ ##
74
+ ## @return [Regexp] Receive regex representation of the object.
75
+ ##
35
76
  def to_rx
36
77
  case Howzit.options[:matching]
37
78
  when 'exact'
@@ -50,9 +91,17 @@ module Howzit
50
91
  gsub(/\e\[[\d;]+m/, '').gsub(/\e\]1337;SetMark/,'')
51
92
  end
52
93
 
94
+ # Wrap text at a specified width.
95
+ #
53
96
  # Adapted from https://github.com/pazdera/word_wrap/,
54
- # copyright (c) 2014, 2015 Radek Pazdera
55
- # Distributed under the MIT License
97
+ # copyright (c) 2014, 2015 Radek Pazdera Distributed
98
+ # under the MIT License
99
+ #
100
+ # @param width [Integer] The width at which to
101
+ # wrap lines
102
+ #
103
+ # @return [String] wrapped string
104
+ #
56
105
  def wrap(width)
57
106
  width ||= 80
58
107
  output = []
@@ -84,12 +133,19 @@ module Howzit
84
133
  output.join("\n")
85
134
  end
86
135
 
136
+ ##
137
+ ## Wrap string in place (destructive)
138
+ ##
139
+ ## @param width [Integer] The width at which to wrap
140
+ ##
87
141
  def wrap!(width)
88
142
  replace(wrap(width))
89
143
  end
90
144
 
91
145
  # Truncate string to nearest word
92
- # @param len <number> max length of string
146
+ #
147
+ # @param len [Integer] max length of string
148
+ #
93
149
  def trunc(len)
94
150
  split(/ /).each_with_object([]) do |x, ob|
95
151
  break ob unless ob.join(' ').length + ' '.length + x.length <= len
@@ -98,10 +154,21 @@ module Howzit
98
154
  end.join(' ').strip
99
155
  end
100
156
 
157
+ ##
158
+ ## Truncate string in place (destructive)
159
+ ##
160
+ ## @param len [Integer] The length to truncate at
161
+ ##
101
162
  def trunc!(len)
102
163
  replace trunc(len)
103
164
  end
104
165
 
166
+ ##
167
+ ## Splits a line at nearest word break
168
+ ##
169
+ ## @param width [Integer] The width of the first segment
170
+ ## @param indent [String] The indent string
171
+ ##
105
172
  def split_line(width, indent = '')
106
173
  line = dup
107
174
  at = line.index(/\s/)
@@ -119,10 +186,23 @@ module Howzit
119
186
  end
120
187
  end
121
188
 
189
+ ##
190
+ ## Test if an executable is available on the system
191
+ ##
192
+ ## @return [Boolean] executable is available
193
+ ##
122
194
  def available?
123
195
  Util.valid_command?(self)
124
196
  end
125
197
 
198
+ ##
199
+ ## Render [%variable] placeholders in a templated string
200
+ ##
201
+ ## @param vars [Hash] Key/value pairs of variable
202
+ ## values
203
+ ##
204
+ ## @return [String] Rendered string
205
+ ##
126
206
  def render_template(vars)
127
207
  vars.each do |k, v|
128
208
  gsub!(/\[%#{k}(:.*?)?\]/, v)
@@ -131,10 +211,20 @@ module Howzit
131
211
  gsub(/\[%(.*?):(.*?)\]/, '\2')
132
212
  end
133
213
 
214
+ ##
215
+ ## Render [%variable] placeholders in place
216
+ ##
217
+ ## @param vars [Hash] Key/value pairs of variable values
218
+ ##
134
219
  def render_template!(vars)
135
220
  replace render_template(vars)
136
221
  end
137
222
 
223
+ ##
224
+ ## Render $X placeholders based on positional arguments
225
+ ##
226
+ ## @return [String] rendered string
227
+ ##
138
228
  def render_arguments
139
229
  return self if Howzit.arguments.nil? || Howzit.arguments.empty?
140
230
 
@@ -145,6 +235,13 @@ module Howzit
145
235
  gsub(/\$[@*]/, Shellwords.join(Howzit.arguments))
146
236
  end
147
237
 
238
+ ##
239
+ ## Split the content at the first top-level header and
240
+ ## assume everything before it is metadata. Passes to
241
+ ## #get_metadata for processing
242
+ ##
243
+ ## @return [Hash] key/value pairs
244
+ ##
148
245
  def extract_metadata
149
246
  if File.exist?(self)
150
247
  leader = Util.read_file(self).split(/^#/)[0].strip
@@ -154,6 +251,11 @@ module Howzit
154
251
  end
155
252
  end
156
253
 
254
+ ##
255
+ ## Examine text for multimarkdown-style metadata and return key/value pairs
256
+ ##
257
+ ## @return [Hash] The metadata as key/value pairs
258
+ ##
157
259
  def get_metadata
158
260
  data = {}
159
261
  scan(/(?mi)^(\S[\s\S]+?): ([\s\S]*?)(?=\n\S[\s\S]*?:|\Z)/).each do |m|
@@ -162,6 +264,13 @@ module Howzit
162
264
  normalize_metadata(data)
163
265
  end
164
266
 
267
+ ##
268
+ ## Autocorrect some keys
269
+ ##
270
+ ## @param meta [Hash] The metadata
271
+ ##
272
+ ## @return [Hash] corrected metadata
273
+ ##
165
274
  def normalize_metadata(meta)
166
275
  data = {}
167
276
  meta.each do |k, v|
@@ -177,15 +286,33 @@ module Howzit
177
286
  data
178
287
  end
179
288
 
289
+ ##
290
+ ## Test if iTerm markers should be output. Requires that
291
+ ## the $TERM_PROGRAM be iTerm and howzit is not running
292
+ ## directives or paginating output
293
+ ##
294
+ ## @return [Boolean] should mark?
295
+ ##
180
296
  def should_mark_iterm?
181
297
  ENV['TERM_PROGRAM'] =~ /^iTerm/ && !Howzit.options[:run] && !Howzit.options[:paginate]
182
298
  end
183
299
 
300
+ ##
301
+ ## Output an iTerm marker
302
+ ##
303
+ ## @return [String] ANSI escape sequence for iTerm
304
+ ## marker
305
+ ##
184
306
  def iterm_marker
185
307
  "\e]1337;SetMark\a" if should_mark_iterm?
186
308
  end
187
309
 
188
310
  # Make a fancy title line for the topic
311
+ #
312
+ # @param opts [Hash] options
313
+ #
314
+ # @return [String] formatted string
315
+ #
189
316
  def format_header(opts = {})
190
317
  title = dup
191
318
  options = {
data/lib/howzit/task.rb CHANGED
@@ -5,11 +5,14 @@ module Howzit
5
5
  class Task
6
6
  attr_reader :type, :title, :action, :parent, :optional, :default
7
7
 
8
- def initialize(type, title, action, parent = nil, optional: false, default: true)
9
- @type = type
10
- @title = title
11
- @action = action.render_arguments
12
- @parent = parent
8
+ ##
9
+ ## Initialize a Task object
10
+ ##
11
+ def initialize(params, optional: false, default: true)
12
+ @type = params[:type]
13
+ @title = params[:title]
14
+ @action = params[:action].render_arguments
15
+ @parent = params[:parent] || nil
13
16
  @optional = optional
14
17
  @default = default
15
18
  end
@@ -23,7 +26,7 @@ module Howzit
23
26
  end
24
27
 
25
28
  def to_list
26
- " * #{@type}: #{@title.gsub(/\\n/, '\​n')}"
29
+ " * #{@type}: #{@title.preserve_escapes}"
27
30
  end
28
31
  end
29
32
  end
data/lib/howzit/topic.rb CHANGED
@@ -52,7 +52,13 @@ module Howzit
52
52
 
53
53
  @tasks.each do |task|
54
54
  if task.optional
55
- q = %({bg}#{task.type.to_s.capitalize} {xw}"{bw}#{task.title}{xw}"{x}).c
55
+ note = if task.type == :include
56
+ task_count = Howzit.buildnote.find_topic(task.action)[0].tasks.count
57
+ " (#{task_count} tasks)"
58
+ else
59
+ ""
60
+ end
61
+ q = %({bg}#{task.type.to_s.capitalize} {xw}"{bw}#{task.title}{xw}"#{note}{x}).c
56
62
  res = Prompt.yn(q, default: task.default)
57
63
  next unless res
58
64
 
@@ -81,7 +87,7 @@ module Howzit
81
87
 
82
88
  $stderr.puts "{by}Running tasks from {bw}#{matches[0].title}{x}".c if Howzit.options[:log_level] < 2
83
89
  output.push(matches[0].run(nested: true))
84
- $stderr.puts "{by}End include: #{matches[0].tasks.count} tasks".c if Howzit.options[:log_level] < 2
90
+ $stderr.puts "{by}End include: #{matches[0].tasks.count} tasks{x}".c if Howzit.options[:log_level] < 2
85
91
  tasks += matches[0].tasks.count
86
92
  when :run
87
93
  $stderr.puts "{bg}Running {bw}#{title}{x}".c if Howzit.options[:log_level] < 2
@@ -107,6 +113,11 @@ module Howzit
107
113
  output
108
114
  end
109
115
 
116
+ ##
117
+ ## Platform-agnostic copy-to-clipboard
118
+ ##
119
+ ## @param string [String] The string to copy
120
+ ##
110
121
  def os_copy(string)
111
122
  os = RbConfig::CONFIG['target_os']
112
123
  out = "{bg}Copying {bw}#{string}".c
@@ -131,6 +142,11 @@ module Howzit
131
142
  end
132
143
  end
133
144
 
145
+ ##
146
+ ## Platform-agnostic open command
147
+ ##
148
+ ## @param command [String] The command
149
+ ##
134
150
  def os_open(command)
135
151
  os = RbConfig::CONFIG['target_os']
136
152
  out = "{bg}Opening {bw}#{command}".c
@@ -153,6 +169,11 @@ module Howzit
153
169
  end
154
170
 
155
171
  # Output a topic with fancy title and bright white text.
172
+ #
173
+ # @param options [Hash] The options
174
+ #
175
+ # @return [Array] array of formatted lines
176
+ #
156
177
  def print_out(options = {})
157
178
  defaults = { single: false, header: true }
158
179
  opt = defaults.merge(options)
@@ -223,7 +244,7 @@ module Howzit
223
244
  "\u{279A}"
224
245
  end
225
246
 
226
- output.push("{bmK}#{icon} {bwK}#{title.gsub(/\\n/, '\​n')}{x}#{option}".c)
247
+ output.push("{bmK}#{icon} {bwK}#{title.preserve_escapes}{x}#{option}".c)
227
248
  when /(?<fence>`{3,})run(?<optional>[!?]{1,2})? *(?<title>.*?)$/i
228
249
  m = Regexp.last_match.named_captures.symbolize_keys
229
250
  optional = m[:optional] =~ /[?!]+/ ? true : false
@@ -256,6 +277,11 @@ module Howzit
256
277
 
257
278
  private
258
279
 
280
+ ##
281
+ ## Collect all directives in the topic content
282
+ ##
283
+ ## @return [Array] array of Task objects
284
+ ##
259
285
  def gather_tasks
260
286
  runnable = []
261
287
  @prereqs = @content.scan(/(?<=@before\n).*?(?=\n@end)/im).map(&:strip)
@@ -275,7 +301,12 @@ module Howzit
275
301
  default = c[:optional2] =~ /!/ ? false : true
276
302
  title = c[:title2].nil? ? '' : c[:title2].strip
277
303
  block = c[:block]&.strip
278
- runnable << Howzit::Task.new(:block, title, block, optional: optional, default: default)
304
+ runnable << Howzit::Task.new({ type: :block,
305
+ title: title,
306
+ action: block,
307
+ parent: nil },
308
+ optional: optional,
309
+ default: default)
279
310
  else
280
311
  cmd = c[:cmd]
281
312
  optional = c[:optional] =~ /[?!]{1,2}/ ? true : false
@@ -294,15 +325,35 @@ module Howzit
294
325
  # end
295
326
  # runnable.concat(tasks)
296
327
  # end
297
- runnable << Howzit::Task.new(:include, title, obj, optional: optional, default: default)
328
+ runnable << Howzit::Task.new({ type: :include,
329
+ title: title,
330
+ action: obj,
331
+ parent: nil },
332
+ optional: optional,
333
+ default: default)
298
334
  when /run/i
299
335
  # warn "{bg}Running {bw}#{obj}{x}".c if Howzit.options[:log_level] < 2
300
- runnable << Howzit::Task.new(:run, title, obj, optional: optional, default: default)
336
+ runnable << Howzit::Task.new({ type: :run,
337
+ title: title,
338
+ action: obj,
339
+ parent: nil },
340
+ optional: optional,
341
+ default: default)
301
342
  when /copy/i
302
343
  # warn "{bg}Copied {bw}#{obj}{bg} to clipboard{x}".c if Howzit.options[:log_level] < 2
303
- runnable << Howzit::Task.new(:copy, title, Shellwords.escape(obj), optional: optional, default: default)
344
+ runnable << Howzit::Task.new({ type: :copy,
345
+ title: title,
346
+ action: Shellwords.escape(obj),
347
+ parent: nil },
348
+ optional: optional,
349
+ default: default)
304
350
  when /open|url/i
305
- runnable << Howzit::Task.new(:open, title, obj, optional: optional, default: default)
351
+ runnable << Howzit::Task.new({ type: :open,
352
+ title: title,
353
+ action: obj,
354
+ parent: nil },
355
+ optional: optional,
356
+ default: default)
306
357
  end
307
358
  end
308
359
  end
data/lib/howzit/util.rb CHANGED
@@ -4,15 +4,40 @@ module Howzit
4
4
  # Util class
5
5
  module Util
6
6
  class << self
7
+
8
+ ##
9
+ ## Read a file with UTF-8 encoding and
10
+ ## leading/trailing whitespace removed
11
+ ##
12
+ ## @param path [String] The path to read
13
+ ##
14
+ ## @return [String] UTF-8 encoded string
15
+ ##
7
16
  def read_file(path)
8
17
  IO.read(path).force_encoding('utf-8').strip
9
18
  end
10
19
 
20
+ ##
21
+ ## Test if an external command exists and is
22
+ ## executable. Removes additional arguments and passes
23
+ ## just the executable to #command_exist?
24
+ ##
25
+ ## @param command [String] The command
26
+ ##
27
+ ## @return [Boolean] command is valid
28
+ ##
11
29
  def valid_command?(command)
12
30
  cmd = command.split(' ')[0]
13
31
  command_exist?(cmd)
14
32
  end
15
33
 
34
+ ##
35
+ ## Test if external command exists
36
+ ##
37
+ ## @param command [String] The command
38
+ ##
39
+ ## @return [Boolean] command exists
40
+ ##
16
41
  def command_exist?(command)
17
42
  exts = ENV.fetch('PATHEXT', '').split(::File::PATH_SEPARATOR)
18
43
  if Pathname.new(command).absolute?
@@ -3,5 +3,5 @@
3
3
  # Primary module for this gem.
4
4
  module Howzit
5
5
  # Current Howzit version.
6
- VERSION = '2.0.10'
6
+ VERSION = '2.0.13'
7
7
  end
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+
3
+ bundle install
4
+ rake test
data/spec/task_spec.rb CHANGED
@@ -3,7 +3,12 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Howzit::Task do
6
- subject(:task) { Howzit::Task.new(:run, 'List Directory', 'ls') }
6
+ subject(:task) do
7
+ Howzit::Task.new({ type: :run,
8
+ title: 'List Directory',
9
+ action: 'ls',
10
+ parent: nil })
11
+ end
7
12
 
8
13
  describe ".new" do
9
14
  it "makes a new task instance" do
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.10
4
+ version: 2.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -220,6 +220,20 @@ dependencies:
220
220
  - - "~>"
221
221
  - !ruby/object:Gem::Version
222
222
  version: '1.3'
223
+ - !ruby/object:Gem::Dependency
224
+ name: tty-spinner
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - "~>"
228
+ - !ruby/object:Gem::Version
229
+ version: '0.9'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: '0.9'
223
237
  - !ruby/object:Gem::Dependency
224
238
  name: mdless
225
239
  requirement: !ruby/object:Gem::Requirement
@@ -289,6 +303,12 @@ files:
289
303
  - README.md
290
304
  - Rakefile
291
305
  - bin/howzit
306
+ - docker/Dockerfile
307
+ - docker/Dockerfile-2.6
308
+ - docker/Dockerfile-2.7
309
+ - docker/Dockerfile-3.0
310
+ - docker/bash_profile
311
+ - docker/inputrc
292
312
  - fish/completions/bld.fish
293
313
  - fish/completions/fisher.fish
294
314
  - fish/completions/howzit.fish
@@ -307,9 +327,9 @@ files:
307
327
  - lib/howzit/topic.rb
308
328
  - lib/howzit/util.rb
309
329
  - lib/howzit/version.rb
330
+ - scripts/runtests.sh
310
331
  - spec/.rubocop.yml
311
332
  - spec/buildnote_spec.rb
312
- - spec/buildnotes.md.bak
313
333
  - spec/cli_spec.rb
314
334
  - spec/ruby_gem_spec.rb
315
335
  - spec/spec_helper.rb
@@ -343,7 +363,6 @@ summary: Provides a way to access Markdown project notes by topic with query cap
343
363
  test_files:
344
364
  - spec/.rubocop.yml
345
365
  - spec/buildnote_spec.rb
346
- - spec/buildnotes.md.bak
347
366
  - spec/cli_spec.rb
348
367
  - spec/ruby_gem_spec.rb
349
368
  - spec/spec_helper.rb
@@ -1,22 +0,0 @@
1
- # spec
2
-
3
-
4
-
5
- ## File Structure
6
-
7
- Where are the main editable files? Is there a dist/build folder that should be ignored?
8
-
9
- ## Build
10
-
11
- What build system/parameters does this use?
12
-
13
- @run(./build command)
14
-
15
- ## Deploy
16
-
17
- What are the procedures/commands to deploy this project?
18
-
19
- ## Other
20
-
21
- Version control notes, additional gulp/rake/make/etc tasks...
22
-