markdown_exec 0.2.4 → 1.0.0

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: 2336bb2091df5bf018181e832fb9c5e7a5b7ee542b6f71cde38a3afb3aa88ab4
4
- data.tar.gz: '072654942ddf5897784c71724a7f9fa6fb5bea9b395d54ca18a3f3fd9b02c5af'
3
+ metadata.gz: 9b40d575297112f5a4bb934475b84251aeeab8f43de5dae963c8d3b1711c3edc
4
+ data.tar.gz: 9e3d3273dc3e3a0deb326fe30e3ff70b15e73f4fd9cea06c42f099f184903fad
5
5
  SHA512:
6
- metadata.gz: 2f28a18aea943a7fec92b16d232b8c3722a30f6960fbd32bf84e95762b4ac322f0fb53f75563118a15136da72e49f16c580a8c7fbf05d2001857e068403834ef
7
- data.tar.gz: 8bb762715d790e37906d61cf8834e86dfe7e4f9611b8f7477da508d2ff658d496c6e1e0c89fc42e9e5f2cf937e29dae3cddf062cabda867f3a5bb1466986f2c3
6
+ metadata.gz: 65654b5af78419591c41e3b50e0697b503ddf429c83312311335dd1b16d0d645c98b4e0c19d6c69446c42823861ca8f384788aeb4c1b5664804e4feecc39a45a
7
+ data.tar.gz: 866360b40733e8925c173582f3c59799e241bf48f52faffa4bfb03cd4c1a11c93f5b27305344b01c4b44a1518ef1c49961c7fbfbdb713638ed8261c8d87401c0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,63 @@
1
1
  # Changelog
2
2
 
3
+ ## [ToDo]
4
+
5
+ - pipe stdin to script
6
+ - present timestamp, result of last exec for each command
7
+ - user settings
8
+ - hidden w , w/o () in names
9
+ - fix regexp in pathnames
10
+ - tab completion from md file
11
+ - read file once to allow for tempdoc stream
12
+ - include md or blocks file
13
+
14
+ - tree display
15
+
16
+ - mde options in md file or included file
17
+
18
+ - include blocks from local md file
19
+
20
+ - chmod a+x logged script
21
+
22
+ - add shebang to saved script
23
+
24
+ - yes/no/write/clipboard/record/edit/history
25
+
26
+ - list, view saved output
27
+
28
+ ## [1.0.0] - 2022-04-26
29
+
30
+ ### Added
31
+
32
+ - Command `--pwd` to print the gem's home folder.
33
+ - Command `--select-recent-output` to select and open a recent output log file.
34
+ e.g. `MDE_OUTPUT_VIEWER_OPTIONS="-a '/Applications/Sublime Text.app'" mde --select-recent-output`
35
+ - Command `--tab-completions` to list the application options.
36
+ - Tab completion script for Bash shell.
37
+
38
+ ### Changed
39
+
40
+ - File names for saved scripts.
41
+ - Hide blocks with empty names, e.g. `:()`.
42
+
43
+ ## [0.2.6] - 2022-04-07
44
+
45
+ ### Changed
46
+
47
+ - Default values for command line options.
48
+
49
+ ## [0.2.5] - 2022-04-03
50
+
51
+ ### Added
52
+
53
+ - Command `--list-default-env` to show default configuration as environment variables.
54
+ - Command `--list-default-yaml` to show default configuration as YAML.
55
+ - Option to exit program when selecting files or blocks.
56
+
57
+ ### Changed
58
+
59
+ - Composition of menu to facilitate reports.
60
+ - List default values in menu help.
3
61
 
4
62
  ## [0.2.4] - 2022-04-01
5
63
 
@@ -11,7 +69,7 @@
11
69
 
12
70
  | YAML Name | Environment Variable | Option Name | Default | Purpose |
13
71
  | :--- | :--- | :--- | :--- | :--- |
14
- | list_count | MDE_LIST_COUNT | `--list_count` | `16` | Max. items to return in list |
72
+ | list_count | MDE_LIST_COUNT | `--list-count` | `16` | Max. items to return in list |
15
73
  | logged_stdout_filename_prefix | MDE_LOGGED_STDOUT_FILENAME_PREFIX | | `mde` | Name prefix for stdout files |
16
74
  | save_execution_output | MDE_SAVE_EXECUTION_OUTPUT | `--save-execution-output` | False | Save standard output of the executed script |
17
75
  | saved_script_filename_prefix | MDE_SAVED_SCRIPT_FILENAME_PREFIX | | `mde` | Name prefix for saved scripts |
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- markdown_exec (0.2.4)
4
+ markdown_exec (1.0.0)
5
5
  open3 (~> 0.1.1)
6
6
  optparse (~> 0.1.1)
7
7
  tty-prompt (~> 0.23.1)
data/README.md CHANGED
@@ -1,30 +1,35 @@
1
1
  # MarkdownExec
2
2
 
3
- Interactively select and execute fenced code blocks in markdown files. Build complex scripts by naming and requiring blocks.
3
+ Interactively select and execute fenced code blocks in markdown files. Build complex scripts by naming and requiring blocks. Log resulting scripts and output. Re-run scripts.
4
4
 
5
- * Code blocks may be named.
5
+ * Code blocks may be named. Named blocks can be required by other blocks.
6
6
 
7
- * Named blocks can be required by other blocks.
7
+ * The user-selected code block, and all required blocks, are arranged into a script in the order they appear in the markdown file. The script can be presented for approval prior to execution.
8
8
 
9
- * The user-selected code block, and all required blocks, are arranged in the order they appear in the markdown file.
9
+ * Executed scripts can be saved. Saved scripts can be listed, selected, and executed.
10
10
 
11
- * The code is presented for approval prior to execution.
11
+ * Output from executed scripts can be saved.
12
12
 
13
13
  ## Screenshots
14
14
 
15
15
  ### Select a file
16
+
16
17
  ![Select a file](/assets/select_a_file.png)
17
18
 
18
19
  ### Select a block
20
+
19
21
  ![Select a block](/assets/select_a_block.png)
20
22
 
21
23
  ### Approve code
24
+
22
25
  ![Approve code](/assets/approve_code.png)
23
26
 
24
27
  ### Output
28
+
25
29
  ![Output of execution](/assets/output_of_execution.png)
26
30
 
27
31
  ### Example blocks
32
+
28
33
  ![Example blocks](/assets/example_blocks.png)
29
34
 
30
35
  ## Installation
@@ -34,39 +39,135 @@ Install:
34
39
 
35
40
  ## Usage
36
41
 
37
- ### `mde --help`
42
+ ### Help
43
+
44
+ #### `mde --help`
45
+
38
46
  Displays help information.
39
47
 
40
- ### `mde`
48
+ ### Basic
49
+
50
+ #### `mde`
51
+
41
52
  Process `README.md` file in the current folder. Displays all the blocks in the file and allows you to select using [up], [down], and [return]. Press [ctrl]-c to abort selection.
42
53
 
43
- ### `mde my.md` or `mde -f my.md`
54
+ #### `mde my.md` or `mde -f my.md`
55
+
44
56
  Select a block to execute from `my.md`.
45
57
 
46
- ### `mde .` or `mde -p .`
58
+ #### `mde my.md myblock`
59
+
60
+ Execute the block named `myblock` from `my.md`.
61
+
62
+ #### `mde .` or `mde -p .`
47
63
 
48
64
  Select a markdown file in the current folder. Select a block to execute from that file.
49
65
 
50
- ### `mde --list-blocks`
66
+ ### Report documents and blocks
67
+
68
+ #### `mde --list-blocks`
69
+
51
70
  List all blocks in the all the markdown documents in the current folder.
52
71
 
53
- ### `mde --list-docs`
72
+ #### `mde --list-docs`
73
+
54
74
  List all markdown documents in the current folder.
55
75
 
76
+ ### Configuration
77
+
78
+ #### `mde --list-default-env` or `mde --list-default-yaml`
79
+
80
+ List default values that can be set in configuration file, environment, and command line.
81
+
82
+ #### `mde -0`
83
+
84
+ Show current configuation values that will be applied to the current run. Does not interrupt processing.
85
+
86
+ ### Save scripts
87
+
88
+ #### `mde --save-executed-script 1`
89
+
90
+ Save executed script in saved script folder.
91
+
92
+ #### `mde --list-recent-scripts`
93
+
94
+ List recent saved scripts in saved script folder.
95
+
96
+ #### `mde --select-recent-script`
97
+
98
+ Select and execute a recently saved script in saved script folder.
99
+
100
+ ### Save output
101
+
102
+ #### `mde --save-execution-output 1`
103
+
104
+ Save execution output in saved output folder.
105
+
56
106
  ## Behavior
107
+
57
108
  * If no file and no folder are specified, blocks within `./README.md` are presented.
58
109
  * If a file is specified, its blocks are presented.
59
110
  * If a folder is specified, its files are presented. When a file is selected, its blocks are presented.
60
111
 
61
112
  ## Configuration
62
- While starting up, reads the YAML configuration file `.mde.yml` in the current folder if it exists.
63
113
 
64
- e.g. Use to set the default file for the current folder.
114
+ ### Environment Variables
115
+
116
+ When executed, `mde` reads the current environment.
117
+ * Configuration in current and children shells, e.g. `export MDE_SAVE_EXECUTED_SCRIPT=1`.
118
+ * Configuration for the current command, e.g. `MDE_SAVE_EXECUTED_SCRIPT=1 mde`.
119
+
120
+ ### Configuration Files
121
+
122
+ * Configuration in all shells, e.g. environment variables set in your user's `~/.bashrc` or `~/.bash_profile` files.
123
+ * Configuration in the optional file `.mde.yml` in the current folder. .e.g. `save_executed_script: true`
124
+ * Configuration in a YAML file and read while parsing the inputs, e.g. `--config my_path/my_file.yml`
125
+
126
+ ### Program Arguments
127
+
128
+ * Configuration in command options, e.g. `mde --save-executed-script 1`
129
+
130
+ ## Representing boolean values
131
+
132
+ Boolean values expressed as strings are interpreted as:
133
+ | String | Boolean |
134
+ | :---: | :---: |
135
+ | *empty string* | False |
136
+ | `0` | False |
137
+ | `1` | True |
138
+ | *anything else* | True |
139
+
140
+ E.g. `opt1=1` will set option `opt1` to True.
141
+
142
+ Boolean options configured with environment variables:
143
+ - Set to `1` or non-empty value to save executed scripts; empty or `0` to disable saving.
144
+ e.g. `export MDE_SAVE_EXECUTED_SCRIPT=1`
145
+ e.g. `export MDE_SAVE_EXECUTED_SCRIPT=`
146
+ - Specify variable on command line.
147
+ e.g. `MDE_SAVE_EXECUTED_SCRIPT=1 mde`
148
+
149
+ ## Tab Completion
150
+
151
+ ### Install tab completion
152
+
153
+ Append a command to load the completion script to your shell configuration file:
154
+
155
+ ```bash :()
156
+ echo "source $(mde --pwd)/bin/tab_completion.sh" >> ~/.bash_profile
157
+ ```
158
+
159
+ ### Example Completions
160
+
161
+ Type tab at end of any of the following commands to see the options.
162
+ - `mde `
163
+ - `mde -`
164
+ - `mde --`
165
+ - `mde --o`
166
+ - `mde --filename my.md -`
167
+ - `mde --filename my.md --`
65
168
 
66
- * `filename: CHANGELOG.md` sets the file to open.
67
- * `folder: documents` sets the folder to search for default or specified files.
169
+ ## Example Blocks
68
170
 
69
- # Example blocks
70
171
  When prompted, select either the `awake` or `asleep` block.
71
172
 
72
173
  ``` :(day)
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+
3
+ __filedirs()
4
+ {
5
+ local IFS=$'\n'
6
+ COMPREPLY=( $(compgen -o plusdirs -f -- "${cur}") )
7
+ }
8
+
9
+ _mde() {
10
+ local cur prev opts
11
+ cur="${COMP_WORDS[COMP_CWORD]}"
12
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
13
+ opts="$(mde --tab-completions)"
14
+
15
+ if [[ ${cur} == -* ]] ; then
16
+ COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
17
+ return 0
18
+ fi
19
+
20
+ __filedirs
21
+ }
22
+
23
+ complete -o filenames -F _mde mde
data/fixtures/exclude2.md CHANGED
@@ -7,3 +7,6 @@ b
7
7
  ``` :three
8
8
  c
9
9
  ```
10
+ ``` :()
11
+ d
12
+ ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  module MarkdownExec
4
4
  APP_NAME = 'MDE'
5
- APP_DESC = 'Markdown block executor'
5
+ APP_DESC = 'Markdown Executor'
6
6
  GEM_NAME = 'markdown_exec'
7
- VERSION = '0.2.4'
7
+ VERSION = '1.0.0'
8
8
  end
data/lib/markdown_exec.rb CHANGED
@@ -14,21 +14,23 @@ require 'yaml'
14
14
  # else true
15
15
 
16
16
  def env_bool(name, default: false)
17
- return default if (val = ENV[name]).nil?
17
+ return default if name.nil? || (val = ENV[name]).nil?
18
18
  return false if val.empty? || val == '0'
19
19
 
20
20
  true
21
21
  end
22
22
 
23
23
  def env_int(name, default: 0)
24
- return default if (val = ENV[name]).nil?
24
+ return default if name.nil? || (val = ENV[name]).nil?
25
25
  return default if val.empty?
26
26
 
27
27
  val.to_i
28
28
  end
29
29
 
30
30
  def env_str(name, default: '')
31
- ENV[name] || default
31
+ return default if name.nil? || (val = ENV[name]).nil?
32
+
33
+ val || default
32
34
  end
33
35
 
34
36
  $pdebug = env_bool 'MDE_DEBUG'
@@ -42,7 +44,12 @@ BLOCK_SIZE = 1024
42
44
 
43
45
  class Object # rubocop:disable Style/Documentation
44
46
  def present?
45
- self && !blank?
47
+ case self.class.to_s
48
+ when 'FalseClass', 'TrueClass'
49
+ true
50
+ else
51
+ self && (!respond_to?(:blank?) || !blank?)
52
+ end
46
53
  end
47
54
  end
48
55
 
@@ -60,16 +67,13 @@ public
60
67
  def tap_inspect(format: nil, name: 'return')
61
68
  return self unless $pdebug
62
69
 
63
- fn = case format
64
- when :json
65
- :to_json
66
- when :string
67
- :to_s
68
- when :yaml
69
- :to_yaml
70
- else
71
- :inspect
72
- end
70
+ cvt = {
71
+ json: :to_json,
72
+ string: :to_s,
73
+ yaml: :to_yaml,
74
+ else: :inspect
75
+ }
76
+ fn = cvt.fetch(format, cvt[:else])
73
77
 
74
78
  puts "-> #{caller[0].scan(/in `?(\S+)'$/)[0][0]}()" \
75
79
  " #{name}: #{method(fn).call}"
@@ -94,44 +98,18 @@ module MarkdownExec
94
98
  # options necessary to start, parse input, defaults for cli options
95
99
 
96
100
  def base_options
97
- {
98
- # commands
99
- list_blocks: false, # command
100
- list_docs: false, # command
101
- list_recent_scripts: false, # command
102
- run_last_script: false, # command
103
- select_recent_script: false, # command
104
-
105
- # command options
106
- filename: env_str('MDE_FILENAME', default: nil), # option Filename to open
107
- list_count: 16,
108
- logged_stdout_filename_prefix: 'mde',
109
- output_execution_summary: env_bool('MDE_OUTPUT_EXECUTION_SUMMARY', default: false), # option
110
- output_script: env_bool('MDE_OUTPUT_SCRIPT', default: false), # option
111
- output_stdout: env_bool('MDE_OUTPUT_STDOUT', default: true), # option
112
- path: env_str('MDE_PATH', default: nil), # option Folder to search for files
113
- save_executed_script: env_bool('MDE_SAVE_EXECUTED_SCRIPT', default: false), # option
114
- save_execution_output: env_bool('MDE_SAVE_EXECUTION_OUTPUT', default: false), # option
115
- saved_script_filename_prefix: 'mde',
116
- saved_script_folder: env_str('MDE_SAVED_SCRIPT_FOLDER', default: 'logs'), # option
117
- saved_script_glob: 'mde_*.sh',
118
- saved_stdout_folder: env_str('MDE_SAVED_STDOUT_FOLDER', default: 'logs'), # option
119
- user_must_approve: env_bool('MDE_USER_MUST_APPROVE', default: true), # option Pause for user to approve script
120
-
121
- # configuration options
122
- block_name_excluded_match: env_str('MDE_BLOCK_NAME_EXCLUDED_MATCH', default: '^\(.+\)$'),
123
- block_name_match: env_str('MDE_BLOCK_NAME_MATCH', default: ':(?<title>\S+)( |$)'),
124
- block_required_scan: env_str('MDE_BLOCK_REQUIRED_SCAN', default: '\+\S+'),
125
- fenced_start_and_end_match: env_str('MDE_FENCED_START_AND_END_MATCH', default: '^`{3,}'),
126
- fenced_start_ex_match: env_str('MDE_FENCED_START_EX_MATCH', default: '^`{3,}(?<shell>[^`\s]*) *(?<name>.*)$'),
127
- heading1_match: env_str('MDE_HEADING1_MATCH', default: '^# *(?<name>[^#]*?) *$'),
128
- heading2_match: env_str('MDE_HEADING2_MATCH', default: '^## *(?<name>[^#]*?) *$'),
129
- heading3_match: env_str('MDE_HEADING3_MATCH', default: '^### *(?<name>.+?) *$'),
130
- md_filename_glob: env_str('MDE_MD_FILENAME_GLOB', default: '*.[Mm][Dd]'),
131
- md_filename_match: env_str('MDE_MD_FILENAME_MATCH', default: '.+\\.md'),
132
- mdheadings: true, # use headings (levels 1,2,3) in block lable
133
- select_page_height: env_int('MDE_SELECT_PAGE_HEIGHT', default: 12)
134
- }
101
+ menu_data
102
+ .map do |_long_name, _short_name, env_var, _arg_name, _description, opt_name, default, proc1| # rubocop:disable Metrics/ParameterLists
103
+ next unless opt_name.present?
104
+
105
+ value = env_str(env_var, default: value_for_hash(default))
106
+ [opt_name, proc1 ? proc1.call(value) : value]
107
+ end.compact.to_h.merge(
108
+ {
109
+ mdheadings: true, # use headings (levels 1,2,3) in block lable
110
+ menu_with_exit: true
111
+ }
112
+ ).tap_inspect format: :yaml
135
113
  end
136
114
 
137
115
  def default_options
@@ -143,6 +121,7 @@ module MarkdownExec
143
121
  prompt_approve_block: 'Process?',
144
122
  prompt_select_block: 'Choose a block:',
145
123
  prompt_select_md: 'Choose a file:',
124
+ prompt_select_output: 'Choose a file:',
146
125
  saved_script_filename: nil, # calculated
147
126
  struct: true # allow get_block_summary()
148
127
  }
@@ -164,8 +143,10 @@ module MarkdownExec
164
143
  selected = get_block_by_name blocks_in_file, opts[:block_name]
165
144
 
166
145
  if opts[:ir_approve]
167
- write_command_file(opts, required_blocks) if opts[:save_executed_script]
146
+ write_command_file opts, required_blocks
168
147
  command_execute opts, required_blocks.flatten.join("\n")
148
+ save_execution_output
149
+ output_execution_summary
169
150
  end
170
151
 
171
152
  selected[:name]
@@ -241,31 +222,30 @@ module MarkdownExec
241
222
  # document and block reports
242
223
  #
243
224
  files = list_files_per_options(options)
244
- if @options[:list_blocks]
245
- fout_list (files.map do |file|
246
- make_block_labels(filename: file, struct: true)
247
- end).flatten(1)
248
- return
249
- end
250
225
 
251
- if @options[:list_docs]
252
- fout_list files
253
- return
254
- end
255
-
256
- if @options[:list_recent_scripts]
257
- fout_list list_recent_scripts
258
- return
259
- end
260
-
261
- if @options[:run_last_script]
262
- run_last_script
263
- return
264
- end
265
-
266
- if @options[:select_recent_script]
267
- select_recent_script
268
- return
226
+ simple_commands = {
227
+ doc_glob: -> { fout options[:md_filename_glob] },
228
+ list_blocks: lambda do
229
+ fout_list (files.map do |file|
230
+ make_block_labels(filename: file, struct: true)
231
+ end).flatten(1)
232
+ end,
233
+ list_default_yaml: -> { fout_list list_default_yaml },
234
+ list_docs: -> { fout_list files },
235
+ list_default_env: -> { fout_list list_default_env },
236
+ list_recent_output: -> { fout_list list_recent_output },
237
+ list_recent_scripts: -> { fout_list list_recent_scripts },
238
+ pwd: -> { fout File.expand_path('..', __dir__) },
239
+ run_last_script: -> { run_last_script },
240
+ select_recent_output: -> { select_recent_output },
241
+ select_recent_script: -> { select_recent_script },
242
+ tab_completions: -> { fout tab_completions }
243
+ }
244
+ simple_commands.each_key do |key|
245
+ if @options[key]
246
+ simple_commands[key].call
247
+ return # rubocop:disable Lint/NonLocalExitFromIterator
248
+ end
269
249
  end
270
250
 
271
251
  # process
@@ -276,8 +256,6 @@ module MarkdownExec
276
256
  struct: true
277
257
  )
278
258
  fout "saved_filespec: #{@execute_script_filespec}" if @options[:output_saved_script_filename]
279
- save_execution_output
280
- output_execution_summary
281
259
  end
282
260
 
283
261
  # standard output; not for debug
@@ -322,6 +300,11 @@ module MarkdownExec
322
300
  exit 1
323
301
  end
324
302
 
303
+ unless File.exist? opts[:filename]
304
+ fout 'Document is missing.'
305
+ exit 1
306
+ end
307
+
325
308
  fenced_start_and_end_match = Regexp.new opts[:fenced_start_and_end_match]
326
309
  fenced_start_ex = Regexp.new opts[:fenced_start_ex_match]
327
310
  block_title = ''
@@ -376,14 +359,37 @@ module MarkdownExec
376
359
  blocks.tap_inspect
377
360
  end
378
361
 
362
+ def list_default_env
363
+ menu_data
364
+ .map do |_long_name, _short_name, env_var, _arg_name, description, _opt_name, default, _proc1| # rubocop:disable Metrics/ParameterLists
365
+ next unless env_var.present?
366
+
367
+ [
368
+ "#{env_var}=#{value_for_cli default}",
369
+ description.present? ? description : nil
370
+ ].compact.join(' # ')
371
+ end.compact.sort
372
+ end
373
+
374
+ def list_default_yaml
375
+ menu_data
376
+ .map do |_long_name, _short_name, _env_var, _arg_name, description, opt_name, default, _proc1| # rubocop:disable Metrics/ParameterLists
377
+ next unless opt_name.present? && default.present?
378
+
379
+ [
380
+ "#{opt_name}: #{value_for_yaml default}",
381
+ description.present? ? description : nil
382
+ ].compact.join(' # ')
383
+ end.compact.sort
384
+ end
385
+
379
386
  def list_files_per_options(options)
380
- default_filename = 'README.md'
381
- default_folder = '.'
382
- if options[:filename]&.present?
383
- list_files_specified(options[:filename], options[:path], default_filename, default_folder)
384
- else
385
- list_files_specified(nil, options[:path], default_filename, default_folder)
386
- end.tap_inspect
387
+ list_files_specified(
388
+ options[:filename]&.present? ? options[:filename] : nil,
389
+ options[:path],
390
+ 'README.md',
391
+ '.'
392
+ ).tap_inspect
387
393
  end
388
394
 
389
395
  def list_files_specified(specified_filename, specified_folder, default_filename, default_folder, filetree = nil)
@@ -438,9 +444,28 @@ module MarkdownExec
438
444
  .tap_inspect
439
445
  end
440
446
 
447
+ def most_recent(arr)
448
+ return unless arr
449
+ return if arr.count < 1
450
+
451
+ arr.max.tap_inspect
452
+ end
453
+
454
+ def most_recent_list(arr)
455
+ return unless arr
456
+ return if (ac = arr.count) < 1
457
+
458
+ arr.sort[-[ac, options[:list_count]].min..].reverse.tap_inspect
459
+ end
460
+
461
+ def list_recent_output
462
+ most_recent_list(Dir.glob(File.join(@options[:saved_stdout_folder],
463
+ @options[:saved_stdout_glob]))).tap_inspect
464
+ end
465
+
441
466
  def list_recent_scripts
442
- Dir.glob(File.join(@options[:saved_script_folder],
443
- @options[:saved_script_glob])).sort[0..(options[:list_count] - 1)].reverse.tap_inspect
467
+ most_recent_list(Dir.glob(File.join(@options[:saved_script_folder],
468
+ @options[:saved_script_glob]))).tap_inspect
444
469
  end
445
470
 
446
471
  def make_block_label(block, call_options = {})
@@ -462,6 +487,89 @@ module MarkdownExec
462
487
  end.compact.tap_inspect
463
488
  end
464
489
 
490
+ def menu_data
491
+ val_as_bool = ->(value) { value.class.to_s == 'String' ? (value.chomp != '0') : value }
492
+ val_as_int = ->(value) { value.to_i }
493
+ val_as_str = ->(value) { value.to_s }
494
+
495
+ summary_head = [
496
+ ['config', nil, nil, 'PATH', 'Read configuration file', nil, '.', lambda { |value|
497
+ read_configuration_file! options, value
498
+ }],
499
+ ['debug', 'd', 'MDE_DEBUG', 'BOOL', 'Debug output', nil, false, ->(value) { $pdebug = value.to_i != 0 }]
500
+ ]
501
+
502
+ # rubocop:disable Layout/LineLength
503
+ summary_body = [
504
+ ['block-name', 'f', 'MDE_BLOCK_NAME', 'RELATIVE', 'Name of block', :block_name, nil, val_as_str],
505
+ ['filename', 'f', 'MDE_FILENAME', 'RELATIVE', 'Name of document', :filename, nil, val_as_str],
506
+ ['list-blocks', nil, nil, nil, 'List blocks', :list_blocks, false, val_as_bool],
507
+ ['list-count', nil, 'MDE_LIST_COUNT', 'NUM', 'Max. items to return in list', :list_count, 16, val_as_int],
508
+ ['list-default-env', nil, nil, nil, 'List default configuration as environment variables', :list_default_env, false, val_as_bool],
509
+ ['list-default-yaml', nil, nil, nil, 'List default configuration as YAML', :list_default_yaml, false, val_as_bool],
510
+ ['list-docs', nil, nil, nil, 'List docs in current folder', :list_docs, false, val_as_bool],
511
+ ['list-recent-output', nil, nil, nil, 'List recent saved output', :list_recent_output, false, val_as_bool],
512
+ ['list-recent-scripts', nil, nil, nil, 'List recent saved scripts', :list_recent_scripts, false, val_as_bool],
513
+ ['logged-stdout-filename-prefix', nil, 'MDE_LOGGED_STDOUT_FILENAME_PREFIX', 'NAME', 'Name prefix for stdout files', :logged_stdout_filename_prefix, 'mde', val_as_str],
514
+ ['output-execution-summary', nil, 'MDE_OUTPUT_EXECUTION_SUMMARY', 'BOOL', 'Display summary for execution', :output_execution_summary, false, val_as_bool],
515
+ ['output-script', nil, 'MDE_OUTPUT_SCRIPT', 'BOOL', 'Display script prior to execution', :output_script, false, val_as_bool],
516
+ ['output-stdout', nil, 'MDE_OUTPUT_STDOUT', 'BOOL', 'Display standard output from execution', :output_stdout, true, val_as_bool],
517
+ ['path', 'p', 'MDE_PATH', 'PATH', 'Path to documents', :path, nil, val_as_str],
518
+ ['pwd', nil, nil, nil, 'Gem home folder', :pwd, false, val_as_bool],
519
+ ['run-last-script', nil, nil, nil, 'Run most recently saved script', :run_last_script, false, val_as_bool],
520
+ ['save-executed-script', nil, 'MDE_SAVE_EXECUTED_SCRIPT', 'BOOL', 'Save executed script', :save_executed_script, false, val_as_bool],
521
+ ['save-execution-output', nil, 'MDE_SAVE_EXECUTION_OUTPUT', 'BOOL', 'Save standard output of the executed script', :save_execution_output, false, val_as_bool],
522
+ ['saved-script-filename-prefix', nil, 'MDE_SAVED_SCRIPT_FILENAME_PREFIX', 'NAME', 'Name prefix for saved scripts', :saved_script_filename_prefix, 'mde', val_as_str],
523
+ ['saved-script-folder', nil, 'MDE_SAVED_SCRIPT_FOLDER', 'SPEC', 'Saved script folder', :saved_script_folder, 'logs', val_as_str],
524
+ ['saved-script-glob', nil, 'MDE_SAVED_SCRIPT_GLOB', 'SPEC', 'Glob matching saved scripts', :saved_script_glob, 'mde_*.sh', val_as_str],
525
+ ['saved-stdout-folder', nil, 'MDE_SAVED_STDOUT_FOLDER', 'SPEC', 'Saved stdout folder', :saved_stdout_folder, 'logs', val_as_str],
526
+ ['saved-stdout-glob', nil, 'MDE_SAVED_STDOUT_GLOB', 'SPEC', 'Glob matching saved outputs', :saved_stdout_glob, 'mde_*.out.txt', val_as_str],
527
+ ['select-recent-output', nil, nil, nil, 'Select and execute a recently saved output', :select_recent_output, false, val_as_bool],
528
+ ['select-recent-script', nil, nil, nil, 'Select and execute a recently saved script', :select_recent_script, false, val_as_bool],
529
+ ['tab-completions', nil, nil, nil, 'List tab completions', :tab_completions, false, val_as_bool],
530
+ ['user-must-approve', nil, 'MDE_USER_MUST_APPROVE', 'BOOL', 'Pause for user to approve script', :user_must_approve, true, val_as_bool]
531
+ ]
532
+ # rubocop:enable Layout/LineLength
533
+
534
+ # rubocop:disable Style/Semicolon
535
+ summary_tail = [
536
+ [nil, '0', nil, nil, 'Show current configuration values',
537
+ nil, nil, ->(_) { options_finalize options; fout sorted_keys(options).to_yaml }],
538
+ ['help', 'h', nil, nil, 'App help',
539
+ nil, nil, ->(_) { fout menu_help; exit }],
540
+ ['version', 'v', nil, nil, "Print the gem's version",
541
+ nil, nil, ->(_) { fout MarkdownExec::VERSION; exit }],
542
+ ['exit', 'x', nil, nil, 'Exit app',
543
+ nil, nil, ->(_) { exit }]
544
+ ]
545
+ # rubocop:enable Style/Semicolon
546
+
547
+ env_vars = [
548
+ [nil, nil, 'MDE_BLOCK_NAME_EXCLUDED_MATCH', nil, 'Pattern for blocks to hide from user-selection',
549
+ :block_name_excluded_match, '^\(.*\)$', val_as_str],
550
+ [nil, nil, 'MDE_BLOCK_NAME_MATCH', nil, '', :block_name_match, ':(?<title>\S+)( |$)', val_as_str],
551
+ [nil, nil, 'MDE_BLOCK_REQUIRED_SCAN', nil, '', :block_required_scan, '\+\S+', val_as_str],
552
+ [nil, nil, 'MDE_FENCED_START_AND_END_MATCH', nil, '', :fenced_start_and_end_match, '^`{3,}', val_as_str],
553
+ [nil, nil, 'MDE_FENCED_START_EX_MATCH', nil, '', :fenced_start_ex_match,
554
+ '^`{3,}(?<shell>[^`\s]*) *(?<name>.*)$', val_as_str],
555
+ [nil, nil, 'MDE_HEADING1_MATCH', nil, '', :heading1_match, '^# *(?<name>[^#]*?) *$', val_as_str],
556
+ [nil, nil, 'MDE_HEADING2_MATCH', nil, '', :heading2_match, '^## *(?<name>[^#]*?) *$', val_as_str],
557
+ [nil, nil, 'MDE_HEADING3_MATCH', nil, '', :heading3_match, '^### *(?<name>.+?) *$', val_as_str],
558
+ [nil, nil, 'MDE_MD_FILENAME_GLOB', nil, '', :md_filename_glob, '*.[Mm][Dd]', val_as_str],
559
+ [nil, nil, 'MDE_MD_FILENAME_MATCH', nil, '', :md_filename_match, '.+\\.md', val_as_str],
560
+ [nil, nil, 'MDE_OUTPUT_VIEWER_OPTIONS', nil, 'Options for viewing saved output file', :output_viewer_options,
561
+ '', val_as_str],
562
+ [nil, nil, 'MDE_SELECT_PAGE_HEIGHT', nil, '', :select_page_height, 12, val_as_int]
563
+ # [nil, nil, 'MDE_', nil, '', nil, '', nil],
564
+ ]
565
+
566
+ summary_head + summary_body + summary_tail + env_vars
567
+ end
568
+
569
+ def menu_help
570
+ @option_parser.help
571
+ end
572
+
465
573
  def option_exclude_blocks(opts, blocks)
466
574
  block_name_excluded_match = Regexp.new opts[:block_name_excluded_match]
467
575
  if opts[:hide_blocks_by_name]
@@ -471,6 +579,27 @@ module MarkdownExec
471
579
  end
472
580
  end
473
581
 
582
+ ## post-parse options configuration
583
+ #
584
+ def options_finalize(rest)
585
+ ## position 0: file or folder (optional)
586
+ #
587
+ if (pos = rest.fetch(0, nil))&.present?
588
+ if Dir.exist?(pos)
589
+ @options[:path] = pos
590
+ elsif File.exist?(pos)
591
+ @options[:filename] = pos
592
+ else
593
+ raise "Invalid parameter: #{pos}"
594
+ end
595
+ end
596
+
597
+ ## position 1: block name (optional)
598
+ #
599
+ block_name = rest.fetch(1, nil)
600
+ @options[:block_name] = block_name if block_name.present?
601
+ end
602
+
474
603
  def optsmerge(call_options = {}, options_block = nil)
475
604
  class_call_options = @options.merge(call_options || {})
476
605
  if options_block
@@ -495,6 +624,14 @@ module MarkdownExec
495
624
  }
496
625
  end
497
626
 
627
+ def prompt_with_quit(prompt_text, items, opts = {})
628
+ exit_option = '* Exit'
629
+ sel = @prompt.select prompt_text,
630
+ items + (@options[:menu_with_exit] ? [exit_option] : []),
631
+ opts
632
+ sel == exit_option ? nil : sel
633
+ end
634
+
498
635
  def read_configuration_file!(options, configuration_path)
499
636
  return unless File.exist?(configuration_path)
500
637
 
@@ -526,87 +663,28 @@ module MarkdownExec
526
663
  #
527
664
  @options = base_options
528
665
 
529
- ## post-parse options configuration
530
- #
531
- options_finalize = ->(_options) {}
532
-
533
- proc_self = ->(value) { value }
534
- proc_to_i = ->(value) { value.to_i != 0 }
535
- proc_true = ->(_) { true }
536
-
537
- # read local configuration file
666
+ ## read local configuration file
538
667
  #
539
668
  read_configuration_file! @options, ".#{MarkdownExec::APP_NAME.downcase}.yml"
540
669
 
541
- option_parser = OptionParser.new do |opts|
670
+ @option_parser = option_parser = OptionParser.new do |opts|
542
671
  executable_name = File.basename($PROGRAM_NAME)
543
672
  opts.banner = [
544
673
  "#{MarkdownExec::APP_NAME}" \
545
674
  " - #{MarkdownExec::APP_DESC} (#{MarkdownExec::VERSION})",
546
- "Usage: #{executable_name} [path] [filename] [options]"
675
+ "Usage: #{executable_name} [(path | filename [block_name])] [options]"
547
676
  ].join("\n")
548
677
 
549
- summary_head = [
550
- ['config', nil, nil, 'PATH', 'Read configuration file',
551
- nil, ->(value) { read_configuration_file! options, value }],
552
- ['debug', 'd', 'MDE_DEBUG', 'BOOL', 'Debug output',
553
- nil, ->(value) { $pdebug = value.to_i != 0 }]
554
- ]
555
-
556
- summary_body = [
557
- ['filename', 'f', 'MDE_FILENAME', 'RELATIVE', 'Name of document',
558
- :filename, proc_self],
559
- ['list-blocks', nil, nil, nil, 'List blocks',
560
- :list_blocks, proc_true],
561
- ['list-docs', nil, nil, nil, 'List docs in current folder',
562
- :list_docs, proc_true],
563
- ['list-recent-scripts', nil, nil, nil, 'List recent saved scripts',
564
- :list_recent_scripts, proc_true],
565
- ['output-execution-summary', nil, 'MDE_OUTPUT_EXECUTION_SUMMARY', 'BOOL', 'Display summary for execution',
566
- :output_execution_summary, proc_to_i],
567
- ['output-script', nil, 'MDE_OUTPUT_SCRIPT', 'BOOL', 'Display script',
568
- :output_script, proc_to_i],
569
- ['output-stdout', nil, 'MDE_OUTPUT_STDOUT', 'BOOL', 'Display standard output from execution',
570
- :output_stdout, proc_to_i],
571
- ['path', 'p', 'MDE_PATH', 'PATH', 'Path to documents',
572
- :path, proc_self],
573
- ['run-last-script', nil, nil, nil, 'Run most recently saved script',
574
- :run_last_script, proc_true],
575
- ['select-recent-script', nil, nil, nil, 'Select and execute a recently saved script',
576
- :select_recent_script, proc_true],
577
- ['save-execution-output', nil, 'MDE_SAVE_EXECUTION_OUTPUT', 'BOOL', 'Save execution output',
578
- :save_execution_output, proc_to_i],
579
- ['save-executed-script', nil, 'MDE_SAVE_EXECUTED_SCRIPT', 'BOOL', 'Save executed script',
580
- :save_executed_script, proc_to_i],
581
- ['saved-script-folder', nil, 'MDE_SAVED_SCRIPT_FOLDER', 'SPEC', 'Saved script folder',
582
- :saved_script_folder, proc_self],
583
- ['saved-stdout-folder', nil, 'MDE_SAVED_STDOUT_FOLDER', 'SPEC', 'Saved stdout folder',
584
- :saved_stdout_folder, proc_self],
585
- ['user-must-approve', nil, 'MDE_USER_MUST_APPROVE', 'BOOL', 'Pause to approve execution',
586
- :user_must_approve, proc_to_i]
587
- ]
588
-
589
- # rubocop:disable Style/Semicolon
590
- summary_tail = [
591
- [nil, '0', nil, nil, 'Show configuration',
592
- nil, ->(_) { options_finalize.call options; fout sorted_keys(options).to_yaml }],
593
- ['help', 'h', nil, nil, 'App help',
594
- nil, ->(_) { fout option_parser.help; exit }],
595
- ['version', 'v', nil, nil, 'App version',
596
- nil, ->(_) { fout MarkdownExec::VERSION; exit }],
597
- ['exit', 'x', nil, nil, 'Exit app',
598
- nil, ->(_) { exit }]
599
- ]
600
- # rubocop:enable Style/Semicolon
601
-
602
- (summary_head + summary_body + summary_tail)
603
- .map do |long_name, short_name, env_var, arg_name, description, opt_name, proc1| # rubocop:disable Metrics/ParameterLists
678
+ menu_data
679
+ .map do |long_name, short_name, _env_var, arg_name, description, opt_name, default, proc1| # rubocop:disable Metrics/ParameterLists
680
+ next unless long_name.present? || short_name.present?
681
+
604
682
  opts.on(*[if long_name.present?
605
683
  "--#{long_name}#{arg_name.present? ? " #{arg_name}" : ''}"
606
684
  end,
607
685
  short_name.present? ? "-#{short_name}" : nil,
608
686
  [description,
609
- env_var.present? ? "env: #{env_var}" : nil].compact.join(' - '),
687
+ default.present? ? "[#{value_for_cli default}]" : nil].compact.join(' '),
610
688
  lambda { |value|
611
689
  ret = proc1.call(value)
612
690
  options[opt_name] = ret if opt_name
@@ -618,47 +696,44 @@ module MarkdownExec
618
696
  option_parser.environment # env defaults to the basename of the program.
619
697
  rest = option_parser.parse! # (into: options)
620
698
 
621
- ## finalize configuration
622
- #
623
- options_finalize.call options
699
+ options_finalize rest
624
700
 
625
- ## position 0: file or folder (optional)
626
- #
627
- if (pos = rest.fetch(0, nil))&.present?
628
- if Dir.exist?(pos)
629
- options[:path] = pos
630
- elsif File.exist?(pos)
631
- options[:filename] = pos
632
- else
633
- raise "Invalid parameter: #{pos}"
634
- end
635
- end
701
+ exec_block options, options[:block_name]
702
+ end
636
703
 
637
- ## position 1: block name (optional)
638
- #
639
- block_name = rest.fetch(1, nil)
704
+ FNR11 = '/'
705
+ FNR12 = ',;'
706
+
707
+ def saved_name_make(opts)
708
+ fne = opts[:filename].gsub(FNR11, FNR12)
709
+ "#{[opts[:saved_script_filename_prefix], Time.now.utc.strftime('%F-%H-%M-%S'), fne,
710
+ ',', opts[:block_name]].join('_')}.sh"
711
+ end
712
+
713
+ def saved_name_split(name)
714
+ mf = name.match(/#{@options[:saved_script_filename_prefix]}_(?<time>[0-9\-]+)_(?<file>.+)_,_(?<block>.+)\.sh/)
715
+ return unless mf
640
716
 
641
- exec_block options, block_name
717
+ @options[:block_name] = mf[:block].tap_inspect name: :options_block_name
718
+ @options[:filename] = mf[:file].gsub(FNR12, FNR11).tap_inspect name: :options_filename
642
719
  end
643
720
 
644
721
  def run_last_script
645
- filename = Dir.glob(File.join(@options[:saved_script_folder],
646
- @options[:saved_script_glob])).sort[0..(options[:list_count] - 1)].last
647
- filename.tap_inspect name: filename
648
- mf = filename.match(/#{@options[:saved_script_filename_prefix]}_(?<time>[0-9\-]+)_(?<file>.+)_(?<block>.+)\.sh/)
722
+ filename = most_recent Dir.glob(File.join(@options[:saved_script_folder],
723
+ @options[:saved_script_glob]))
724
+ return unless filename
649
725
 
650
- @options[:block_name] = mf[:block]
651
- @options[:filename] = "#{mf[:file]}.md" ### other extensions
726
+ filename.tap_inspect name: filename
727
+ saved_name_split filename
652
728
  @options[:save_executed_script] = false
653
729
  select_and_approve_block
654
- save_execution_output
655
- output_execution_summary
656
730
  end
657
731
 
658
732
  def save_execution_output
659
733
  return unless @options[:save_execution_output]
660
734
 
661
735
  fne = File.basename(@options[:filename], '.*')
736
+
662
737
  @options[:logged_stdout_filename] =
663
738
  "#{[@options[:logged_stdout_filename_prefix], Time.now.utc.strftime('%F-%H-%M-%S'), fne,
664
739
  @options[:block_name]].join('_')}.out.txt"
@@ -680,7 +755,9 @@ module MarkdownExec
680
755
 
681
756
  return nil if block_labels.count.zero?
682
757
 
683
- sel = @prompt.select pt, block_labels, per_page: opts[:select_page_height]
758
+ sel = prompt_with_quit pt, block_labels, per_page: opts[:select_page_height]
759
+ return nil if sel.nil?
760
+
684
761
  label_block = blocks_in_file.select { |block| block[:label] == sel }.fetch(0, nil)
685
762
  opts[:block_name] = @options[:block_name] = label_block[:name]
686
763
  end
@@ -694,24 +771,29 @@ module MarkdownExec
694
771
  if files.count == 1
695
772
  files[0]
696
773
  elsif files.count >= 2
697
- @prompt.select opts[:prompt_select_md].to_s, files, per_page: opts[:select_page_height]
774
+ prompt_with_quit opts[:prompt_select_md].to_s, files, per_page: opts[:select_page_height]
698
775
  end
699
776
  end
700
777
 
778
+ def select_recent_output
779
+ filename = prompt_with_quit @options[:prompt_select_output].to_s, list_recent_output,
780
+ per_page: @options[:select_page_height]
781
+ return unless filename.present?
782
+
783
+ `open #{filename} #{options[:output_viewer_options]}`
784
+ end
785
+
701
786
  def select_recent_script
702
- filename = @prompt.select @options[:prompt_select_md].to_s, list_recent_scripts,
703
- per_page: @options[:select_page_height]
704
- mf = filename.match(/#{@options[:saved_script_filename_prefix]}_(?<time>[0-9\-]+)_(?<file>.+)_(?<block>.+)\.sh/)
787
+ filename = prompt_with_quit @options[:prompt_select_md].to_s, list_recent_scripts,
788
+ per_page: @options[:select_page_height]
789
+ return if filename.nil?
705
790
 
706
- @options[:block_name] = mf[:block]
707
- @options[:filename] = "#{mf[:file]}.md" ### other extensions
791
+ saved_name_split filename
708
792
  select_and_approve_block(
709
793
  bash: true,
710
794
  save_executed_script: false,
711
795
  struct: true
712
796
  )
713
- save_execution_output
714
- output_execution_summary
715
797
  end
716
798
 
717
799
  def sorted_keys(hash1)
@@ -722,22 +804,72 @@ module MarkdownExec
722
804
  { headings: headings, name: title, title: title }
723
805
  end
724
806
 
807
+ def tab_completions(data = menu_data)
808
+ data.map do |item|
809
+ "--#{item[0]}" if item[0]
810
+ end.compact
811
+ end
812
+
725
813
  def update_options(opts = {}, over: true)
726
814
  if over
727
815
  @options = @options.merge opts
728
816
  else
729
817
  @options.merge! opts
730
818
  end
731
- @options
819
+ @options.tap_inspect format: :yaml
820
+ end
821
+
822
+ def value_for_cli(value)
823
+ case value.class.to_s
824
+ when 'String'
825
+ "'#{value}'"
826
+ when 'FalseClass', 'TrueClass'
827
+ value ? '1' : '0'
828
+ when 'Integer'
829
+ value
830
+ else
831
+ value.to_s
832
+ end
833
+ end
834
+
835
+ def value_for_hash(value, default = nil)
836
+ return default if value.nil?
837
+
838
+ case value.class.to_s
839
+ when 'String', 'Integer', 'FalseClass', 'TrueClass'
840
+ value
841
+ when value.empty?
842
+ default
843
+ else
844
+ value.to_s
845
+ end
846
+ end
847
+
848
+ def value_for_yaml(value)
849
+ return default if value.nil?
850
+
851
+ case value.class.to_s
852
+ when 'String'
853
+ "'#{value}'"
854
+ when 'Integer'
855
+ value
856
+ when 'FalseClass', 'TrueClass'
857
+ value ? true : false
858
+ when value.empty?
859
+ default
860
+ else
861
+ value.to_s
862
+ end
732
863
  end
733
864
 
734
865
  def write_command_file(opts, required_blocks)
735
- fne = File.basename(opts[:filename], '.*')
736
- opts[:saved_script_filename] =
737
- "#{[opts[:saved_script_filename_prefix], Time.now.utc.strftime('%F-%H-%M-%S'), fne,
738
- opts[:block_name]].join('_')}.sh"
739
- @options[:saved_filespec] = File.join opts[:saved_script_folder], opts[:saved_script_filename]
740
- @execute_script_filespec = @options[:saved_filespec]
866
+ return unless opts[:save_executed_script]
867
+
868
+ opts[:saved_script_filename] = saved_name_make(opts)
869
+ @execute_script_filespec =
870
+ @options[:saved_filespec] =
871
+ File.join opts[:saved_script_folder], opts[:saved_script_filename]
872
+
741
873
  dirname = File.dirname(@options[:saved_filespec])
742
874
  Dir.mkdir dirname unless File.exist?(dirname)
743
875
  File.write(@options[:saved_filespec], "#!/usr/bin/env bash\n" \
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdown_exec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fareed Stevenson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-02 00:00:00.000000000 Z
11
+ date: 2022-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: open3
@@ -67,11 +67,13 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.2.0
69
69
  description: Interactively select and execute fenced code blocks in markdown files.
70
- Build complex scripts by naming and requiring blocks.
70
+ Build complex scripts by naming and requiring blocks. Log resulting scripts and
71
+ output. Re-run scripts.
71
72
  email:
72
73
  - fareed@phomento.com
73
74
  executables:
74
75
  - mde
76
+ - tab_completion.sh
75
77
  extensions: []
76
78
  extra_rdoc_files: []
77
79
  files:
@@ -91,6 +93,7 @@ files:
91
93
  - bin/console
92
94
  - bin/mde
93
95
  - bin/setup
96
+ - bin/tab_completion.sh
94
97
  - fixtures/bash1.md
95
98
  - fixtures/bash2.md
96
99
  - fixtures/exclude1.md
@@ -127,5 +130,5 @@ requirements: []
127
130
  rubygems_version: 3.2.32
128
131
  signing_key:
129
132
  specification_version: 4
130
- summary: Execute shell blocks in markdown files.
133
+ summary: Interactively select and execute fenced code blocks in markdown files.
131
134
  test_files: []