markdown_exec 2.0.8.3 → 2.1.0

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: 942e4a005f42e2f3c2bd4542ba1b61e666c7379c0a2d81a5427f14ec76590a7d
4
- data.tar.gz: c38976432cd43684654663876cd6ee5fa35b40339d5217bae00e282c29cd7415
3
+ metadata.gz: f49177c09809e5635f448181166f2fe60dffe9b942f1c23eeb29b2ecd43b55a8
4
+ data.tar.gz: 43488e384c44c757e1c7b95ad68e755a60a5bd617f3532c4e3213604984097e5
5
5
  SHA512:
6
- metadata.gz: e02a78e7a564273392203583c1b7d9502647c3379fa0894bf3b2a4b858127cfb8196a493d43e404395762449f267f8e68df48567585aa39d66d804c6c11e2edc
7
- data.tar.gz: 1bf94ff09d5248dd54853eca88dbc8ebce6ce5c17e9e5110103a2bc91250fa099120a81c7de0f060afdc0d08c607e5fbb7096735aea9ffcb4c7a4ee9bb33220f
6
+ metadata.gz: 9c923456c84259f80f53a839d48d4ed54c91f3bdc3ddb06fb96a1014b79a4640f21eed0bdb1f58635869fa70a5fb786259745a6e2a46cba2780a0093b81bc3e9
7
+ data.tar.gz: fbccdda04147b4a9ac0c9dd31bff501a626c60112fd70d0554aa89d2e005494e755abc0f63714c417e25461da23de3a960478cc1dd65fef9c126e927b6bbf037
data/CHANGELOG.md CHANGED
@@ -1,14 +1,23 @@
1
1
  # Changelog
2
2
 
3
- Pass-through arguments after "--" to the script
4
- M Gemfile.lock
5
- M bin/tab_completion.sh
6
- M examples/pass-through.md
7
- M lib/hash_delegator.rb
8
- M lib/markdown_exec.rb
9
- M lib/markdown_exec/version.rb
10
- M lib/menu.src.yml
11
- M lib/menu.yml
3
+ ## [2.1.0] - 2024-07-15
4
+
5
+ ### Added
6
+
7
+ - Option to toggle the output of an execution report.
8
+ - Option to toggle a menu entry to view saved script and output files.
9
+ - Options for formatting and parsing saved script and output file names.
10
+
11
+ ### Changed
12
+
13
+ - Fix handling of output streams for executed scripts to improve logging out output and addition to inherited code.
14
+ - YAML blocks are not executable from the menu.
15
+ - Fix collection of output of Link blocks.
16
+ - Pass-through arguments after "--" to the script.
17
+ - Remove app info from menu.yml.
18
+ - Remove test for unwanted arguments.
19
+ - Fix Link next_block_name when filename is used.
20
+ - In example doc, use nicknames to allow block content to be displayed.
12
21
 
13
22
  ## [2.0.8] - 2024-06-05
14
23
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- markdown_exec (2.0.8.3)
4
+ markdown_exec (2.1.0)
5
5
  clipboard (~> 1.3.6)
6
6
  open3 (~> 0.1.1)
7
7
  optparse (~> 0.1.1)
@@ -13,7 +13,7 @@ __filedirs_all()
13
13
  }
14
14
 
15
15
  _mde_echo_version() {
16
- echo "2.0.8.3"
16
+ echo "2.1.0"
17
17
  }
18
18
 
19
19
  _mde() {
@@ -178,4 +178,4 @@ _mde() {
178
178
 
179
179
  complete -o filenames -o nospace -F _mde mde
180
180
  # _mde_echo_version
181
- # echo "Updated: 2024-06-15 14:50:18 UTC"
181
+ # echo "Updated: 2024-07-16 20:54:59 UTC"
@@ -0,0 +1,50 @@
1
+ # Demonstrate data blocks
2
+
3
+ ```opts :(document_options)
4
+ pause_after_script_execution: true
5
+ ```
6
+
7
+ ## Create a data file by requiring a YAML block.
8
+ ::: YAML into a file
9
+ / YAML block that loads data into a file.
10
+ ```yaml :(test1) >test1.yml
11
+ a: species
12
+ b: genus
13
+ ```
14
+ This is a Bash block that
15
+ - requires a hidden YAML block (that creates a file), and
16
+ - displays the file.
17
+ ```bash +(test1)
18
+ echo "- The data file created"
19
+ ls -al *.yml
20
+ echo -e "\n- Contents of the file"
21
+ cat -n test1.yml
22
+ echo -e "\n- Remove the data file"
23
+ rm test1.yml
24
+ ```
25
+
26
+ ## Load data by requiring a yaml block.
27
+ ::: YAML into a shell variable
28
+ / YAML block that loads data into a variable.
29
+ ```yaml :(test2) >$test2
30
+ c: family
31
+ d: order
32
+ ```
33
+ This is a Bash block that
34
+ - requires a hidden YAML block (that sets a variable), and
35
+ - displays the variable.
36
+ ```bash +(test2)
37
+ echo 'data:'
38
+ echo "$test2"
39
+ ```
40
+
41
+ ## Visible YAML block that is not executable.
42
+ ::: Non-interactive data
43
+ ```yaml
44
+ e: class
45
+ f: phylum
46
+ ```
47
+
48
+ # Related MDE options
49
+ block_stdin_scan | Match to place block body into a file or a variable
50
+ block_stdout_scan | Match to place block body into a file or a variable
@@ -0,0 +1,46 @@
1
+ # Demo options: output_execution_report, output_execution_summary
2
+
3
+ ::: Options are initially True
4
+ ```opts :(document_options) +[document_options]
5
+ output_execution_report: true
6
+ output_execution_summary: true
7
+ pause_after_script_execution: true
8
+ ```
9
+ ```bash
10
+ whoami
11
+ pwd >&2
12
+ date >&1 1>&2
13
+ ```
14
+
15
+ ## output_execution_report
16
+ ### Example
17
+ -^-
18
+ Command: mde ./examples/opt_output_execution_summary.md
19
+ StdOut: logs/mde_2024-07-08-03-21-59_opt_output_execution_summary_,_whoami___pwd__&2___date__&1_1_&2_.out.txt
20
+ -v-
21
+ ### Toggle
22
+ ```opts
23
+ output_execution_report: false
24
+ ```
25
+ ```opts
26
+ output_execution_report: true
27
+ ```
28
+
29
+ ## output_execution_summary
30
+ ### Example (edited)
31
+ :execute_aborted_at:
32
+ :execute_completed_at: 2024-07-08 03:21:59.988451000 Z
33
+ :execute_error:
34
+ :execute_error_message:
35
+ :execute_options: { ... }
36
+ :execute_started_at: 2024-07-08 03:21:59.864442000 Z
37
+ :saved_filespec:
38
+ :script_block_name:
39
+ :streamed_lines: { ... }
40
+ ### Toggle
41
+ ```opts
42
+ output_execution_summary: false
43
+ ```
44
+ ```opts
45
+ output_execution_summary: true
46
+ ```
data/examples/save.md ADDED
@@ -0,0 +1,7 @@
1
+ ```opts :(document_options)
2
+ execute_in_own_window: false
3
+ save_execution_output: true
4
+ ```
5
+ ```bash :test
6
+ echo "$(date -u)"
7
+ ```
data/lib/constants.rb CHANGED
@@ -48,6 +48,7 @@ class MenuState
48
48
  CONTINUE = :continue
49
49
  EDIT = :edit
50
50
  EXIT = :exit
51
+ HISTORY = :history
51
52
  LOAD = :load
52
53
  SAVE = :save
53
54
  SHELL = :shell
@@ -34,6 +34,7 @@ require_relative 'mdoc'
34
34
  require_relative 'regexp'
35
35
  require_relative 'resize_terminal'
36
36
  require_relative 'std_out_err_logger'
37
+ require_relative 'streams_out'
37
38
  require_relative 'string_util'
38
39
 
39
40
  class String
@@ -163,15 +164,6 @@ module HashDelegatorSelf
163
164
  # end.join("\n")
164
165
  # end
165
166
 
166
- # Formats and returns the execution streams (like stdin, stdout, stderr) for a given key.
167
- # It concatenates the array of strings found under the specified key in the run_state's files.
168
- #
169
- # @param key [Symbol] The key corresponding to the desired execution stream.
170
- # @return [String] A concatenated string of the execution stream's contents.
171
- def format_execution_streams(key, files = {})
172
- (files || {}).fetch(key, []).join
173
- end
174
-
175
167
  # Indents all lines in a given string with a specified indentation string.
176
168
  # @param body [String] A multi-line string to be indented.
177
169
  # @param indent [String] The string used for indentation (default is an empty string).
@@ -266,16 +258,6 @@ module HashDelegatorSelf
266
258
  exit 1
267
259
  end
268
260
 
269
- # # Evaluates the given string as Ruby code and rescues any StandardErrors.
270
- # # If an error occurs, it calls the error_handler method with 'safeval'.
271
- # # @param str [String] The string to be evaluated.
272
- # # @return [Object] The result of evaluating the string.
273
- # def safeval(str)
274
- # eval(str)
275
- # rescue StandardError # catches NameError, StandardError
276
- # error_handler('safeval')
277
- # end
278
-
279
261
  def set_file_permissions(file_path, chmod_value)
280
262
  File.chmod(chmod_value, file_path)
281
263
  end
@@ -309,21 +291,6 @@ module HashDelegatorSelf
309
291
  &block)
310
292
  end
311
293
 
312
- def write_execution_output_to_file(files, filespec)
313
- FileUtils.mkdir_p File.dirname(filespec)
314
-
315
- File.write(
316
- filespec,
317
- ["-STDOUT-\n",
318
- format_execution_streams(ExecutionStreams::STD_OUT, files),
319
- "-STDERR-\n",
320
- format_execution_streams(ExecutionStreams::STD_ERR, files),
321
- "-STDIN-\n",
322
- format_execution_streams(ExecutionStreams::STD_IN, files),
323
- "\n"].join
324
- )
325
- end
326
-
327
294
  # Yields a line as a new block if the selected message type includes :line.
328
295
  # @param [String] line The line to be processed.
329
296
  # @param [Array<Symbol>] selected_messages A list of message types to check.
@@ -587,6 +554,9 @@ module MarkdownExec
587
554
  when MenuState::EXIT
588
555
  option_name = @delegate_object[:menu_option_exit_name]
589
556
  insert_at_top = @delegate_object[:menu_exit_at_top]
557
+ when MenuState::HISTORY
558
+ option_name = @delegate_object[:menu_option_history_name]
559
+ insert_at_top = @delegate_object[:menu_load_at_top]
590
560
  when MenuState::LOAD
591
561
  option_name = @delegate_object[:menu_option_load_name]
592
562
  insert_at_top = @delegate_object[:menu_load_at_top]
@@ -599,6 +569,8 @@ module MarkdownExec
599
569
  when MenuState::VIEW
600
570
  option_name = @delegate_object[:menu_option_view_name]
601
571
  insert_at_top = @delegate_object[:menu_load_at_top]
572
+ else
573
+ raise "Missing MenuState: #{menu_state}"
602
574
  end
603
575
 
604
576
  formatted_name = format(@delegate_object[:menu_link_format],
@@ -726,11 +698,12 @@ module MarkdownExec
726
698
  return unless @delegate_object[:saved_stdout_folder]
727
699
 
728
700
  @delegate_object[:logged_stdout_filename] =
729
- SavedAsset.stdout_name(blockname: block_name,
730
- filename: File.basename(@delegate_object[:filename],
731
- '.*'),
732
- prefix: @delegate_object[:logged_stdout_filename_prefix],
733
- time: Time.now.utc)
701
+ SavedAsset.new(blockname: block_name,
702
+ filename: @delegate_object[:filename],
703
+ prefix: @delegate_object[:logged_stdout_filename_prefix],
704
+ time: Time.now.utc,
705
+ exts: '.out.txt',
706
+ saved_asset_format: @delegate_object[:saved_asset_format]).generate_name
734
707
 
735
708
  @logged_stdout_filespec =
736
709
  @delegate_object[:logged_stdout_filespec] =
@@ -793,7 +766,7 @@ module MarkdownExec
793
766
  end
794
767
 
795
768
  def command_execute(command, args: [])
796
- @run_state.files = Hash.new([])
769
+ @run_state.files = StreamsOut.new
797
770
  @run_state.options = @delegate_object
798
771
  @run_state.started_at = Time.now.utc
799
772
 
@@ -807,7 +780,6 @@ module MarkdownExec
807
780
  command_execute_in_own_window_format_arguments(rest: args ? args.join(' ') : '')
808
781
  )
809
782
  )
810
-
811
783
  else
812
784
  @run_state.in_own_window = false
813
785
  execute_command_with_streams(
@@ -821,14 +793,14 @@ module MarkdownExec
821
793
  @run_state.aborted_at = Time.now.utc
822
794
  @run_state.error_message = err.message
823
795
  @run_state.error = err
824
- @run_state.files[ExecutionStreams::STD_ERR] += [@run_state.error_message]
796
+ @run_state.files.append_stream_line(ExecutionStreams::STD_ERR, @run_state.error_message)
825
797
  @fout.fout "Error ENOENT: #{err.inspect}"
826
798
  rescue SignalException => err
827
799
  # Handle SignalException
828
800
  @run_state.aborted_at = Time.now.utc
829
801
  @run_state.error_message = 'SIGTERM'
830
802
  @run_state.error = err
831
- @run_state.files[ExecutionStreams::STD_ERR] += [@run_state.error_message]
803
+ @run_state.files.append_stream_line(ExecutionStreams::STD_ERR, @run_state.error_message)
832
804
  @fout.fout "Error ENOENT: #{err.inspect}"
833
805
  end
834
806
 
@@ -1081,8 +1053,7 @@ module MarkdownExec
1081
1053
  return unless @delegate_object[:save_execution_output]
1082
1054
  return if @run_state.in_own_window
1083
1055
 
1084
- HashDelegator.write_execution_output_to_file(@run_state.files,
1085
- @delegate_object[:logged_stdout_filespec])
1056
+ @run_state.files.write_execution_output_to_file(@delegate_object[:logged_stdout_filespec])
1086
1057
  end
1087
1058
 
1088
1059
  # Select and execute a code block from a Markdown document.
@@ -1097,7 +1068,6 @@ module MarkdownExec
1097
1068
  block_name: @delegate_object[:block_name],
1098
1069
  document_filename: @delegate_object[:filename]
1099
1070
  )
1100
- # @dml_link_state_block_name_from_cli = @dml_link_state.block_name.present? ###
1101
1071
  @run_state.block_name_from_cli = @dml_link_state.block_name.present?
1102
1072
  @cli_block_name = @dml_link_state.block_name
1103
1073
  @dml_now_using_cli = @run_state.block_name_from_cli
@@ -1119,12 +1089,14 @@ module MarkdownExec
1119
1089
  pop_add_current_code_to_head_and_trigger_load(@dml_link_state, inherited_block_names, code_lines, inherited_dependencies, selected)
1120
1090
  end
1121
1091
 
1122
- item_back = format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_back_name]))
1123
- item_edit = format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_edit_name]))
1124
- item_load = format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_load_name]))
1125
- item_save = format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_save_name]))
1126
- item_shell = format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_shell_name]))
1127
- item_view = format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_view_name]))
1092
+ fdo = ->(mo) { format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[mo])) }
1093
+ item_back = fdo.call(:menu_option_back_name)
1094
+ item_edit = fdo.call(:menu_option_edit_name)
1095
+ item_history = fdo.call(:menu_option_history_name)
1096
+ item_load = fdo.call(:menu_option_load_name)
1097
+ item_save = fdo.call(:menu_option_save_name)
1098
+ item_shell = fdo.call(:menu_option_shell_name)
1099
+ item_view = fdo.call(:menu_option_view_name)
1128
1100
 
1129
1101
  @run_state.batch_random = Random.new.rand
1130
1102
  @run_state.batch_index = 0
@@ -1138,6 +1110,12 @@ module MarkdownExec
1138
1110
  # puts "@ - parse document #{data}"
1139
1111
  inpseq_parse_document(data)
1140
1112
 
1113
+ if @delegate_object[:menu_for_history]
1114
+ history_files.tap do |files|
1115
+ menu_enable_option(item_history, files.count, 'files', menu_state: MenuState::HISTORY) if files.count.positive?
1116
+ end
1117
+ end
1118
+
1141
1119
  if @delegate_object[:menu_for_saved_lines] && @delegate_object[:document_saved_lines_glob].present?
1142
1120
 
1143
1121
  sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename, @delegate_object[:document_saved_lines_glob])
@@ -1148,11 +1126,11 @@ module MarkdownExec
1148
1126
 
1149
1127
  # add menu items (glob, load, save) and enable selectively
1150
1128
  menu_add_disabled_option(sf) if files.count.positive? || lines_count.positive?
1151
- menu_enable_option(format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_load_name])), files.count, 'files', menu_state: MenuState::LOAD) if files.count.positive?
1152
- menu_enable_option(format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_edit_name])), lines_count, 'lines', menu_state: MenuState::EDIT) if lines_count.positive?
1153
- menu_enable_option(format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_save_name])), 1, '', menu_state: MenuState::SAVE) if lines_count.positive?
1154
- menu_enable_option(format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_view_name])), 1, '', menu_state: MenuState::VIEW) if lines_count.positive?
1155
- menu_enable_option(format(@delegate_object[:menu_link_format], HashDelegator.safeval(@delegate_object[:menu_option_shell_name])), 1, '', menu_state: MenuState::SHELL) if @delegate_object[:menu_with_shell]
1129
+ menu_enable_option(item_load, files.count, 'files', menu_state: MenuState::LOAD) if files.count.positive?
1130
+ menu_enable_option(item_edit, lines_count, 'lines', menu_state: MenuState::EDIT) if lines_count.positive?
1131
+ menu_enable_option(item_save, 1, '', menu_state: MenuState::SAVE) if lines_count.positive?
1132
+ menu_enable_option(item_view, 1, '', menu_state: MenuState::VIEW) if lines_count.positive?
1133
+ menu_enable_option(item_shell, 1, '', menu_state: MenuState::SHELL) if @delegate_object[:menu_with_shell]
1156
1134
  end
1157
1135
 
1158
1136
  when :display_menu
@@ -1197,6 +1175,42 @@ module MarkdownExec
1197
1175
  debounce_reset
1198
1176
  edited = edit_text(@dml_link_state.inherited_lines.join("\n"))
1199
1177
  @dml_link_state.inherited_lines = edited.split("\n") if edited
1178
+
1179
+ return :break if pause_user_exit
1180
+
1181
+ InputSequencer.next_link_state(prior_block_was_link: true)
1182
+
1183
+ when item_history
1184
+ debounce_reset
1185
+ files = history_files
1186
+ files_table_rows = files.map do |file|
1187
+ if Regexp.new(@delegate_object[:saved_asset_match]) =~ file
1188
+ OpenStruct.new(file: file, row: [$~[:time], $~[:blockname], $~[:exts]].join(' '))
1189
+ else
1190
+ warn "Cannot parse name: #{file}"
1191
+ next
1192
+ end
1193
+ end.compact
1194
+
1195
+ case (name = prompt_select_code_filename(
1196
+ [@delegate_object[:prompt_filespec_back]] +
1197
+ files_table_rows.map(&:row),
1198
+ string: @delegate_object[:prompt_select_history_file],
1199
+ color_sym: :prompt_color_after_script_execution
1200
+ ))
1201
+ when @delegate_object[:prompt_filespec_back]
1202
+ # do nothing
1203
+ else
1204
+ file = files_table_rows.select { |ftr| ftr.row == name }&.first
1205
+ info = file_info(file.file)
1206
+ warn "#{file.file} - #{info[:lines]} lines / #{info[:size]} bytes"
1207
+ warn(File.readlines(file.file, chomp: false).map.with_index do |line, ind|
1208
+ format(' %s. %s', format('% 4d', ind).violet, line)
1209
+ end)
1210
+ end
1211
+
1212
+ return :break if pause_user_exit
1213
+
1200
1214
  InputSequencer.next_link_state(prior_block_was_link: true)
1201
1215
 
1202
1216
  when item_load
@@ -1207,6 +1221,9 @@ module MarkdownExec
1207
1221
  @dml_link_state.inherited_lines ||= []
1208
1222
  @dml_link_state.inherited_lines += File.readlines(load_filespec, chomp: true)
1209
1223
  end
1224
+
1225
+ return :break if pause_user_exit
1226
+
1210
1227
  InputSequencer.next_link_state(prior_block_was_link: true)
1211
1228
 
1212
1229
  when item_save
@@ -1220,6 +1237,7 @@ module MarkdownExec
1220
1237
  return :break
1221
1238
 
1222
1239
  end
1240
+
1223
1241
  InputSequencer.next_link_state(prior_block_was_link: true)
1224
1242
 
1225
1243
  when item_shell
@@ -1238,11 +1256,17 @@ module MarkdownExec
1238
1256
  warn "#{'ERR'.bred} #{exit_status}"
1239
1257
  end
1240
1258
  end
1259
+
1260
+ return :break if pause_user_exit
1261
+
1241
1262
  InputSequencer.next_link_state(prior_block_was_link: true)
1242
1263
 
1243
1264
  when item_view
1244
1265
  debounce_reset
1245
1266
  warn @dml_link_state.inherited_lines.join("\n")
1267
+
1268
+ return :break if pause_user_exit
1269
+
1246
1270
  InputSequencer.next_link_state(prior_block_was_link: true)
1247
1271
 
1248
1272
  else
@@ -1482,7 +1506,6 @@ module MarkdownExec
1482
1506
  # @param mdoc [YourMDocClass] An instance of the MDoc class.
1483
1507
  #
1484
1508
  def execute_shell_type(selected:, mdoc:, block_source:, link_state: LinkState.new)
1485
- # binding.irb
1486
1509
  if selected.fetch(:shell, '') == BlockType::LINK
1487
1510
  debounce_reset
1488
1511
  push_link_history_and_trigger_load(link_block_body: selected.fetch(:body, ''),
@@ -1561,6 +1584,21 @@ module MarkdownExec
1561
1584
  string_send_color(data_string, color_sym)
1562
1585
  end
1563
1586
 
1587
+ # size of a file in bytes and the number of lines
1588
+ def file_info(file_path)
1589
+ file_size = 0
1590
+ line_count = 0
1591
+
1592
+ File.open(file_path, 'r') do |file|
1593
+ file.each_line do |_line|
1594
+ line_count += 1
1595
+ end
1596
+ file_size = file.size
1597
+ end
1598
+
1599
+ { size: file_size, lines: line_count }
1600
+ end
1601
+
1564
1602
  def format_and_execute_command(code_lines:)
1565
1603
  formatted_command = code_lines.flatten.join("\n")
1566
1604
  @fout.fout fetch_color(data_sym: :script_execution_head,
@@ -1621,11 +1659,6 @@ module MarkdownExec
1621
1659
  bm = extract_named_captures_from_option(titlexcall,
1622
1660
  @delegate_object[:block_name_match])
1623
1661
 
1624
- fcb.stdin = extract_named_captures_from_option(titlexcall,
1625
- @delegate_object[:block_stdin_scan])
1626
- fcb.stdout = extract_named_captures_from_option(titlexcall,
1627
- @delegate_object[:block_stdout_scan])
1628
-
1629
1662
  shell_color_option = SHELL_COLOR_OPTIONS[fcb[:shell]]
1630
1663
 
1631
1664
  if @delegate_object[:block_name_nick_match].present? && fcb.oname =~ Regexp.new(@delegate_object[:block_name_nick_match])
@@ -1662,7 +1695,7 @@ module MarkdownExec
1662
1695
  Thread.new do
1663
1696
  stream.each_line do |line|
1664
1697
  line.strip!
1665
- @run_state.files[file_type] << line if @run_state.files
1698
+ @run_state.files.append_stream_line(file_type, line) if @run_state.files.streams
1666
1699
 
1667
1700
  if @delegate_object[:output_stdout]
1668
1701
  # print line
@@ -1679,6 +1712,16 @@ module MarkdownExec
1679
1712
  end
1680
1713
  end
1681
1714
 
1715
+ def history_files
1716
+ Dir.glob(
1717
+ File.join(
1718
+ @delegate_object[:saved_script_folder],
1719
+ SavedAsset.new(filename: @delegate_object[:filename],
1720
+ saved_asset_format: @delegate_object[:saved_asset_format]).generate_name
1721
+ )
1722
+ )
1723
+ end
1724
+
1682
1725
  # Initializes variables for regex and other states
1683
1726
  def initial_state
1684
1727
  {
@@ -1760,7 +1803,7 @@ module MarkdownExec
1760
1803
  file.rewind
1761
1804
 
1762
1805
  if link_block_data.fetch(LinkKeys::EXEC, false)
1763
- @run_state.files = Hash.new([])
1806
+ @run_state.files = StreamsOut.new
1764
1807
  execute_command_with_streams([cmd]) do |_stdin, stdout, stderr, _thread|
1765
1808
  line = stdout || stderr
1766
1809
  output_lines.push(line) if line
@@ -1831,7 +1874,6 @@ module MarkdownExec
1831
1874
  document_filename: File.basename(@delegate_object[:filename]),
1832
1875
  document_filespec: @delegate_object[:filename],
1833
1876
  home: Dir.pwd,
1834
- # rest: '',
1835
1877
  started_at: Time.now.utc.strftime(@delegate_object[:execute_command_title_time_format])
1836
1878
  }
1837
1879
  end
@@ -1886,43 +1928,6 @@ module MarkdownExec
1886
1928
  end
1887
1929
  end
1888
1930
 
1889
- # private
1890
-
1891
- # def read_block_name(line)
1892
- # bm = extract_named_captures_from_option(line, @delegate_object[:block_name_match])
1893
- # name = bm[:title]
1894
-
1895
- # if @delegate_object[:block_name_nick_match].present? && line =~ Regexp.new(@delegate_object[:block_name_nick_match])
1896
- # name = $~[0]
1897
- # else
1898
- # name = bm && bm[1] ? bm[:title] : name
1899
- # end
1900
- # name
1901
- # end
1902
-
1903
- # # Loads auto link block.
1904
- # def load_auto_link_block(all_blocks, link_state, mdoc, block_source:)
1905
- # block_name = @delegate_object[:document_load_link_block_name]
1906
- # return unless block_name.present? && @most_recent_loaded_filename != @delegate_object[:filename]
1907
-
1908
- # block = HashDelegator.block_find(all_blocks, :oname, block_name)
1909
- # return unless block
1910
-
1911
- # if block.fetch(:shell, '') != BlockType::LINK
1912
- # HashDelegator.error_handler('must be Link block type', { abort: true })
1913
-
1914
- # else
1915
- # # debounce_reset
1916
- # push_link_history_and_trigger_load(
1917
- # link_block_body: block.fetch(:body, ''),
1918
- # mdoc: mdoc,
1919
- # selected: block,
1920
- # link_state: link_state,
1921
- # block_source: block_source
1922
- # )
1923
- # end
1924
- # end
1925
-
1926
1931
  # Handle expression with wildcard characters
1927
1932
  def load_filespec_wildcard_expansion(expr, auto_load_single: false)
1928
1933
  files = find_files(expr)
@@ -1933,7 +1938,11 @@ module MarkdownExec
1933
1938
  else
1934
1939
  ## user selects from existing files or other
1935
1940
  #
1936
- case (name = prompt_select_code_filename([@delegate_object[:prompt_filespec_back]] + files))
1941
+ case (name = prompt_select_code_filename(
1942
+ [@delegate_object[:prompt_filespec_back]] + files,
1943
+ string: @delegate_object[:prompt_select_code_file],
1944
+ color_sym: :prompt_color_after_script_execution
1945
+ ))
1937
1946
  when @delegate_object[:prompt_filespec_back]
1938
1947
  # do nothing
1939
1948
  else
@@ -1958,7 +1967,6 @@ module MarkdownExec
1958
1967
  # recreate menu with new options
1959
1968
  #
1960
1969
  all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_opts_block(all_blocks)
1961
- # load_auto_link_block(all_blocks, link_state, mdoc, block_source: {})
1962
1970
 
1963
1971
  menu_blocks = mdoc.fcbs_per_options(@delegate_object)
1964
1972
  add_menu_chrome_blocks!(menu_blocks: menu_blocks, link_state: link_state)
@@ -2077,7 +2085,7 @@ module MarkdownExec
2077
2085
  @fout.fout formatted_string
2078
2086
  end
2079
2087
 
2080
- def output_execution_result
2088
+ def fout_execution_report
2081
2089
  @fout.fout fetch_color(data_sym: :execution_report_preview_head,
2082
2090
  color_sym: :execution_report_preview_frame_color)
2083
2091
  [
@@ -2101,16 +2109,16 @@ module MarkdownExec
2101
2109
  def output_execution_summary
2102
2110
  return unless @delegate_object[:output_execution_summary]
2103
2111
 
2104
- fout_section 'summary', {
2112
+ @fout.fout_section 'summary', {
2105
2113
  execute_aborted_at: @run_state.aborted_at,
2106
2114
  execute_completed_at: @run_state.completed_at,
2107
2115
  execute_error: @run_state.error,
2108
2116
  execute_error_message: @run_state.error_message,
2109
- execute_files: @run_state.files,
2110
2117
  execute_options: @run_state.options,
2111
2118
  execute_started_at: @run_state.started_at,
2119
+ saved_filespec: @run_state.saved_filespec,
2112
2120
  script_block_name: @run_state.script_block_name,
2113
- saved_filespec: @run_state.saved_filespec
2121
+ streamed_lines: @run_state.files.streams
2114
2122
  }
2115
2123
  end
2116
2124
 
@@ -2123,6 +2131,11 @@ module MarkdownExec
2123
2131
  ), level: level
2124
2132
  end
2125
2133
 
2134
+ def pause_user_exit
2135
+ @delegate_object[:pause_after_script_execution] &&
2136
+ prompt_select_continue == MenuState::EXIT
2137
+ end
2138
+
2126
2139
  def pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines,
2127
2140
  dependencies, selected, next_block_name: nil)
2128
2141
  pop = @link_history.pop # updatable
@@ -2175,7 +2188,7 @@ module MarkdownExec
2175
2188
  def post_execution_process
2176
2189
  do_save_execution_output
2177
2190
  output_execution_summary
2178
- output_execution_result
2191
+ fout_execution_report if @delegate_object[:output_execution_report]
2179
2192
  end
2180
2193
 
2181
2194
  # Prepare the blocks menu by adding labels and other necessary details.
@@ -2349,10 +2362,9 @@ module MarkdownExec
2349
2362
 
2350
2363
  # public
2351
2364
 
2352
- def prompt_select_code_filename(filenames)
2365
+ def prompt_select_code_filename(filenames, string: @delegate_object[:prompt_select_code_file], color_sym: :prompt_color_after_script_execution)
2353
2366
  @prompt.select(
2354
- string_send_color(@delegate_object[:prompt_select_code_file],
2355
- :prompt_color_after_script_execution),
2367
+ string_send_color(string, color_sym),
2356
2368
  filter: true,
2357
2369
  quiet: true
2358
2370
  ) do |menu|
@@ -2493,7 +2505,7 @@ module MarkdownExec
2493
2505
  end
2494
2506
 
2495
2507
  # Registers console attributes by modifying the options hash.
2496
- # This method handles terminal resizing and adjusts the console dimensions
2508
+ # This method handles terminal resizing and adjusts the console dimensions
2497
2509
  # and pagination settings based on the current terminal size.
2498
2510
  #
2499
2511
  # @param opts [Hash] a hash containing various options for the console settings.
@@ -2510,19 +2522,15 @@ module MarkdownExec
2510
2522
  # register_console_attributes(opts)
2511
2523
  # # opts will be updated with the current console dimensions and pagination settings.
2512
2524
  def register_console_attributes(opts)
2513
- begin
2514
- if (resized = @delegate_object[:menu_resize_terminal])
2515
- resize_terminal
2516
- end
2525
+ if (resized = @delegate_object[:menu_resize_terminal])
2526
+ resize_terminal
2527
+ end
2517
2528
 
2518
- if resized || !opts[:console_width]
2519
- opts[:console_height], opts[:console_width] = opts[:console_winsize] = IO.console.winsize
2520
- end
2529
+ opts[:console_height], opts[:console_width] = opts[:console_winsize] = IO.console.winsize if resized || !opts[:console_width]
2521
2530
 
2522
- opts[:per_page] = opts[:select_page_height] = [opts[:console_height] - 3, 4].max unless opts[:select_page_height]&.positive?
2523
- rescue StandardError
2524
- HashDelegator.error_handler('register_console_attributes', { abort: true })
2525
- end
2531
+ opts[:per_page] = opts[:select_page_height] = [opts[:console_height] - 3, 4].max unless opts[:select_page_height]&.positive?
2532
+ rescue StandardError
2533
+ HashDelegator.error_handler('register_console_attributes', { abort: true })
2526
2534
  end
2527
2535
 
2528
2536
  # Check if the delegate object responds to a given method.
@@ -2584,8 +2592,11 @@ module MarkdownExec
2584
2592
  ## user selects from existing files or other
2585
2593
  # input into path with wildcard for easy entry
2586
2594
  #
2587
- name = prompt_select_code_filename([@delegate_object[:prompt_filespec_back], @delegate_object[:prompt_filespec_other]] + files)
2588
- case name
2595
+ case (name = prompt_select_code_filename(
2596
+ [@delegate_object[:prompt_filespec_back], @delegate_object[:prompt_filespec_other]] + files,
2597
+ string: @delegate_object[:prompt_select_code_file],
2598
+ color_sym: :prompt_color_after_script_execution
2599
+ ))
2589
2600
  when @delegate_object[:prompt_filespec_back]
2590
2601
  # do nothing
2591
2602
  when @delegate_object[:prompt_filespec_other]
@@ -2712,9 +2723,13 @@ module MarkdownExec
2712
2723
  dname = oname = title = fcb_title_groups.fetch(:name, '')
2713
2724
  end
2714
2725
 
2726
+ # disable fcb for data blocks
2727
+ disabled = fcb_title_groups.fetch(:shell, '') == 'yaml' ? '' : nil
2728
+
2715
2729
  MarkdownExec::FCB.new(
2716
2730
  body: [],
2717
2731
  call: rest.match(Regexp.new(@delegate_object[:block_calls_scan]))&.to_a&.first,
2732
+ disabled: disabled,
2718
2733
  dname: dname,
2719
2734
  headings: headings,
2720
2735
  indent: fcb_title_groups.fetch(:indent, ''),
@@ -2848,12 +2863,12 @@ module MarkdownExec
2848
2863
 
2849
2864
  time_now = Time.now.utc
2850
2865
  @run_state.saved_script_filename =
2851
- SavedAsset.script_name(
2852
- blockname: selected.pub_name,
2853
- filename: @delegate_object[:filename],
2854
- prefix: @delegate_object[:saved_script_filename_prefix],
2855
- time: time_now
2856
- )
2866
+ SavedAsset.new(blockname: selected.pub_name,
2867
+ exts: '.sh',
2868
+ filename: @delegate_object[:filename],
2869
+ prefix: @delegate_object[:saved_script_filename_prefix],
2870
+ saved_asset_format: @delegate_object[:saved_asset_format],
2871
+ time: time_now).generate_name
2857
2872
  @run_state.saved_filespec =
2858
2873
  File.join(@delegate_object[:saved_script_folder],
2859
2874
  @run_state.saved_script_filename)
@@ -2942,21 +2957,6 @@ module MarkdownExec
2942
2957
 
2943
2958
  def self.next_link_state(*args, **kwargs, &block)
2944
2959
  super
2945
- # result = super
2946
-
2947
- # @logger ||= StdOutErrLogger.new
2948
- # @logger.unknown(
2949
- # HashDelegator.clean_hash_recursively(
2950
- # { "HashDelegator.next_link_state":
2951
- # { 'args': args,
2952
- # 'at': Time.now.strftime('%FT%TZ'),
2953
- # 'for': /[^\/]+:\d+/.match(caller.first)[0],
2954
- # 'kwargs': kwargs,
2955
- # 'return': result } }
2956
- # )
2957
- # )
2958
-
2959
- # result
2960
2960
  end
2961
2961
  end
2962
2962
  end
@@ -3468,25 +3468,27 @@ module MarkdownExec
3468
3468
  @hd.instance_variable_set(:@run_state, mock('run_state'))
3469
3469
  end
3470
3470
 
3471
- def test_format_execution_streams_with_valid_key
3472
- result = HashDelegator.format_execution_streams(:stdout,
3473
- { stdout: %w[output1 output2] })
3471
+ def test_format_execution_stream_with_valid_key
3472
+ result = HashDelegator.format_execution_stream(
3473
+ { stdout: %w[output1 output2] },
3474
+ ExecutionStreams::STD_OUT
3475
+ )
3474
3476
 
3475
- assert_equal 'output1output2', result
3477
+ assert_equal "output1\noutput2", result
3476
3478
  end
3477
3479
 
3478
- def test_format_execution_streams_with_empty_key
3480
+ def test_format_execution_stream_with_empty_key
3479
3481
  @hd.instance_variable_get(:@run_state).stubs(:files).returns({})
3480
3482
 
3481
- result = HashDelegator.format_execution_streams(:stderr)
3483
+ result = HashDelegator.format_execution_stream(nil, ExecutionStreams::STD_ERR)
3482
3484
 
3483
3485
  assert_equal '', result
3484
3486
  end
3485
3487
 
3486
- def test_format_execution_streams_with_nil_files
3488
+ def test_format_execution_stream_with_nil_files
3487
3489
  @hd.instance_variable_get(:@run_state).stubs(:files).returns(nil)
3488
3490
 
3489
- result = HashDelegator.format_execution_streams(:stdin)
3491
+ result = HashDelegator.format_execution_stream(nil, :stdin)
3490
3492
 
3491
3493
  assert_equal '', result
3492
3494
  end
@@ -3601,19 +3603,19 @@ module MarkdownExec
3601
3603
 
3602
3604
  def test_handle_stream
3603
3605
  stream = StringIO.new("line 1\nline 2\n")
3604
- file_type = :stdout
3606
+ file_type = ExecutionStreams::STD_OUT
3605
3607
 
3606
3608
  Thread.new { @hd.handle_stream(stream: stream, file_type: file_type) }
3607
3609
 
3608
3610
  @hd.wait_for_stream_processing
3609
3611
 
3610
3612
  assert_equal ['line 1', 'line 2'],
3611
- @hd.instance_variable_get(:@run_state).files[:stdout]
3613
+ @hd.instance_variable_get(:@run_state).files[ExecutionStreams::STD_OUT]
3612
3614
  end
3613
3615
 
3614
3616
  def test_handle_stream_with_io_error
3615
3617
  stream = StringIO.new("line 1\nline 2\n")
3616
- file_type = :stdout
3618
+ file_type = ExecutionStreams::STD_OUT
3617
3619
  stream.stubs(:each_line).raises(IOError)
3618
3620
 
3619
3621
  Thread.new { @hd.handle_stream(stream: stream, file_type: file_type) }
@@ -3621,7 +3623,7 @@ module MarkdownExec
3621
3623
  @hd.wait_for_stream_processing
3622
3624
 
3623
3625
  assert_equal [],
3624
- @hd.instance_variable_get(:@run_state).files[:stdout]
3626
+ @hd.instance_variable_get(:@run_state).files[ExecutionStreams::STD_OUT]
3625
3627
  end
3626
3628
  end
3627
3629
 
@@ -7,5 +7,5 @@ module MarkdownExec
7
7
  BIN_NAME = 'mde'
8
8
  GEM_NAME = 'markdown_exec'
9
9
  TAP_DEBUG = 'MDE_DEBUG'
10
- VERSION = '2.0.8.3'
10
+ VERSION = '2.1.0'
11
11
  end
data/lib/mdoc.rb CHANGED
@@ -53,6 +53,17 @@ module MarkdownExec
53
53
  end
54
54
  end
55
55
 
56
+ # Collects and formats the shell command output to redirect script block code to a file or a variable.
57
+ #
58
+ # @param [Hash] fcb A hash containing information about the script block's stdout and body.
59
+ # @option fcb [Hash] :stdout A hash specifying the stdout details.
60
+ # @option stdout [Boolean] :type Indicates whether to export to a variable (true) or to write to a file (false).
61
+ # @option stdout [String] :name The name of the variable or file to which the body will be output.
62
+ # @option fcb [Array<String>] :body An array of strings representing the lines of the script block's body.
63
+ #
64
+ # @return [String] A string containing the formatted shell command to output the script block's body.
65
+ # If stdout[:type] is true, the command will export the body to a shell variable.
66
+ # If stdout[:type] is false, the command will write the body to a file.
56
67
  def collect_block_code_stdout(fcb)
57
68
  stdout = fcb[:stdout]
58
69
  body = fcb[:body].join("\n")
data/lib/menu.src.yml CHANGED
@@ -64,6 +64,7 @@
64
64
  :procname: val_as_str
65
65
 
66
66
  - :default: ">(?<full>(?<type>\\$)?(?<name>[A-Za-z_\\-\\.\\w]+))"
67
+ :description: Match to place block body into a file or a variable
67
68
  :env_var: MDE_BLOCK_STDOUT_SCAN
68
69
  :opt_name: block_stdout_scan
69
70
  :procname: val_as_str
@@ -106,12 +107,6 @@
106
107
  :opt_name: display_level_xbase_prefix
107
108
  :procname: val_as_str
108
109
 
109
- # - :default: "(document_link)"
110
- # :description: Name of Link block to load with the document
111
- # :env_var: MDE_DOCUMENT_LOAD_LINK_BLOCK_NAME
112
- # :opt_name: document_load_link_block_name
113
- # :procname: val_as_str
114
-
115
110
  - :default: "(document_options)"
116
111
  :description: Name of Opts block to load with the document
117
112
  :env_var: MDE_DOCUMENT_LOAD_OPTS_BLOCK_NAME
@@ -132,6 +127,13 @@
132
127
  :opt_name: menu_for_saved_lines
133
128
  :procname: val_as_bool
134
129
 
130
+ - :arg_name: BOOL
131
+ :default: true
132
+ :description: Add menu options for history
133
+ :env_var: MDE_menu_for_history
134
+ :opt_name: menu_for_history
135
+ :procname: val_as_bool
136
+
135
137
  - :arg_name: BOOL
136
138
  :default: false
137
139
  :description: Dump @delegate_object
@@ -648,8 +650,8 @@
648
650
  :opt_name: menu_note_format
649
651
  :procname: val_as_str
650
652
 
651
- ## all lines that do not start with "/ " are notes
652
- - :default: "^(?<line>(?!/ )(?<indent>[ \t]*)(?<text>.*?)(?<trailing>[ \t]*))?$"
653
+ ## lines that start with "/" are comments (hidden), not notes (visible)
654
+ - :default: "^(?<line>(?!/)(?<indent>[ \t]*)(?<text>.*?)(?<trailing>[ \t]*))?$"
653
655
  :description: Pattern for notes in block selection menu
654
656
  :env_var: MDE_MENU_NOTE_MATCH
655
657
  :opt_name: menu_note_match
@@ -676,6 +678,13 @@
676
678
  :opt_name: menu_option_exit_name
677
679
  :procname: val_as_str
678
680
 
681
+ - :default:
682
+ :line: "* History"
683
+ :description: Text for History option
684
+ :env_var: MDE_MENU_OPTION_HISTORY_NAME
685
+ :opt_name: menu_option_history_name
686
+ :procname: val_as_str
687
+
679
688
  - :default:
680
689
  :line: "* Load"
681
690
  :description: Text for Load option
@@ -868,6 +877,13 @@
868
877
  :opt_name: output_execution_label_value_color
869
878
  :procname: val_as_str
870
879
 
880
+ - :arg_name: BOOL
881
+ :default: true
882
+ :description: Output execution report at end of execution
883
+ :env_var: MDE_OUTPUT_EXECUTION_REPORT
884
+ :opt_name: output_execution_report
885
+ :procname: val_as_bool
886
+
871
887
  - :arg_name: BOOL
872
888
  :default: false
873
889
  :description: Output saved script filename at end of execution
@@ -990,6 +1006,12 @@
990
1006
  :opt_name: prompt_select_code_file
991
1007
  :procname: val_as_str
992
1008
 
1009
+ - :default: "\nView file:"
1010
+ :description: Prompt to select a saved asset
1011
+ :env_var: MDE_PROMPT_SELECT_HISTORY_FILE
1012
+ :opt_name: prompt_select_history_file
1013
+ :procname: val_as_str
1014
+
993
1015
  - :default: "\nChoose a file:"
994
1016
  :description: Prompt to select a markdown document
995
1017
  :env_var: MDE_PROMPT_SELECT_MD
@@ -1036,6 +1058,18 @@
1036
1058
  :opt_name: runtime_exception_error_level
1037
1059
  :procname: val_as_int
1038
1060
 
1061
+ - :default: '%{prefix}%{join}%{time}%{join}%{filename}%{join}%{mark}%{join}%{blockname}%{join}%{exts}'
1062
+ :description: saved_asset_format
1063
+ :env_var: MDE_SAVED_ASSET_FORMAT
1064
+ :opt_name: saved_asset_format
1065
+ :procname: val_as_str
1066
+
1067
+ - :default: "^(?<prefix>.+)(?<join>_)(?<time>[0-9\\-]+)\\g'join'(?<filename>.+)\\g'join'(?<mark>~)\\g'join'(?<blockname>.+)\\g'join'(?<exts>\\..+)$"
1068
+ :description: saved_asset_match
1069
+ :env_var: MDE_SAVED_ASSET_MATCH
1070
+ :opt_name: saved_asset_match
1071
+ :procname: val_as_str
1072
+
1039
1073
  - :arg_name: BOOL
1040
1074
  :default: false
1041
1075
  :description: Whether to save an executed script
data/lib/menu.yml CHANGED
@@ -53,6 +53,7 @@
53
53
  :opt_name: block_stdin_scan
54
54
  :procname: val_as_str
55
55
  - :default: ">(?<full>(?<type>\\$)?(?<name>[A-Za-z_\\-\\.\\w]+))"
56
+ :description: Match to place block body into a file or a variable
56
57
  :env_var: MDE_BLOCK_STDOUT_SCAN
57
58
  :opt_name: block_stdout_scan
58
59
  :procname: val_as_str
@@ -105,6 +106,12 @@
105
106
  :env_var: MDE_MENU_FOR_SAVED_LINES
106
107
  :opt_name: menu_for_saved_lines
107
108
  :procname: val_as_bool
109
+ - :arg_name: BOOL
110
+ :default: true
111
+ :description: Add menu options for history
112
+ :env_var: MDE_menu_for_history
113
+ :opt_name: menu_for_history
114
+ :procname: val_as_bool
108
115
  - :arg_name: BOOL
109
116
  :default: false
110
117
  :description: Dump @delegate_object
@@ -541,7 +548,7 @@
541
548
  :env_var: MDE_MENU_NOTE_FORMAT
542
549
  :opt_name: menu_note_format
543
550
  :procname: val_as_str
544
- - :default: "^(?<line>(?!/ )(?<indent>[ \t]*)(?<text>.*?)(?<trailing>[ \t]*))?$"
551
+ - :default: "^(?<line>(?!/)(?<indent>[ \t]*)(?<text>.*?)(?<trailing>[ \t]*))?$"
545
552
  :description: Pattern for notes in block selection menu
546
553
  :env_var: MDE_MENU_NOTE_MATCH
547
554
  :opt_name: menu_note_match
@@ -564,6 +571,12 @@
564
571
  :env_var: MDE_MENU_OPTION_EXIT_NAME
565
572
  :opt_name: menu_option_exit_name
566
573
  :procname: val_as_str
574
+ - :default:
575
+ :line: "* History"
576
+ :description: Text for History option
577
+ :env_var: MDE_MENU_OPTION_HISTORY_NAME
578
+ :opt_name: menu_option_history_name
579
+ :procname: val_as_str
567
580
  - :default:
568
581
  :line: "* Load"
569
582
  :description: Text for Load option
@@ -726,6 +739,12 @@
726
739
  :env_var: MDE_OUTPUT_EXECUTION_LABEL_VALUE_COLOR
727
740
  :opt_name: output_execution_label_value_color
728
741
  :procname: val_as_str
742
+ - :arg_name: BOOL
743
+ :default: true
744
+ :description: Output execution report at end of execution
745
+ :env_var: MDE_OUTPUT_EXECUTION_REPORT
746
+ :opt_name: output_execution_report
747
+ :procname: val_as_bool
729
748
  - :arg_name: BOOL
730
749
  :default: false
731
750
  :description: Output saved script filename at end of execution
@@ -841,6 +860,13 @@
841
860
  :procname: val_as_str
842
861
  - :default: |2-
843
862
 
863
+ View file:
864
+ :description: Prompt to select a saved asset
865
+ :env_var: MDE_PROMPT_SELECT_HISTORY_FILE
866
+ :opt_name: prompt_select_history_file
867
+ :procname: val_as_str
868
+ - :default: |2-
869
+
844
870
  Choose a file:
845
871
  :description: Prompt to select a markdown document
846
872
  :env_var: MDE_PROMPT_SELECT_MD
@@ -882,6 +908,16 @@
882
908
  :env_var: MDE_RUNTIME_EXCEPTION_ERROR_LEVEL
883
909
  :opt_name: runtime_exception_error_level
884
910
  :procname: val_as_int
911
+ - :default: "%{prefix}%{join}%{time}%{join}%{filename}%{join}%{mark}%{join}%{blockname}%{join}%{exts}"
912
+ :description: saved_asset_format
913
+ :env_var: MDE_SAVED_ASSET_FORMAT
914
+ :opt_name: saved_asset_format
915
+ :procname: val_as_str
916
+ - :default: "^(?<prefix>.+)(?<join>_)(?<time>[0-9\\-]+)\\g'join'(?<filename>.+)\\g'join'(?<mark>~)\\g'join'(?<blockname>.+)\\g'join'(?<exts>\\..+)$"
917
+ :description: saved_asset_match
918
+ :env_var: MDE_SAVED_ASSET_MATCH
919
+ :opt_name: saved_asset_match
920
+ :procname: val_as_str
885
921
  - :arg_name: BOOL
886
922
  :default: false
887
923
  :description: Whether to save an executed script
data/lib/saved_assets.rb CHANGED
@@ -12,23 +12,57 @@ module MarkdownExec
12
12
  # method derives a name for stdout redirection.
13
13
  #
14
14
  class SavedAsset
15
- FNR11 = %r{[^!#%&()\+,\-0-9=A-Z_a-z~]}.freeze
15
+ FNR11 = %r{[^!#%\+\-0-9=@A-Z_a-z]}.freeze # characters than can be used in a file name without quotes or escaping
16
+ # except '.', ',', '~' reserved for tokenization
16
17
  # / !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
17
18
  FNR12 = '_'
18
19
  DEFAULT_FTIME = '%F-%H-%M-%S'
20
+ FILE_BLOCK_SEP = ','
21
+ JOIN_STR = '_'
22
+ MARK_STR = '~'
19
23
 
20
- # Generates a formatted script name based on the provided parameters.
21
- def self.script_name(filename:, prefix:, time:, blockname:, ftime: DEFAULT_FTIME, join_str: '_', pattern: FNR11, replace: FNR12, exts: '.sh')
22
- fne = filename.gsub(pattern, replace)
23
- bne = blockname.gsub(pattern, replace)
24
- "#{[prefix, time.strftime(ftime), fne, ',', bne].join(join_str)}#{exts}"
24
+ # @param filename [String] the name of the file
25
+ # @param prefix [String] the prefix to use
26
+ # @param time [Time] the time object for formatting
27
+ # @param blockname [String] the block name to include
28
+ # @param ftime [String] the time format (default: DEFAULT_FTIME)
29
+ # @param pattern [Regexp] the pattern to search (default: FNR11)
30
+ # @param replace [String] the string to replace the pattern (default: FNR12)
31
+ # @param exts [String] the extension to append (default: '.sh')
32
+ def initialize(
33
+ saved_asset_format:, filename: nil, prefix: nil, time: nil, blockname: nil,
34
+ ftime: DEFAULT_FTIME, pattern: FNR11, replace: FNR12, exts: nil,
35
+ mark: nil, join_str: nil
36
+ )
37
+ @filename = filename ? filename.gsub(pattern, replace) : '*' # [String] the name of the file
38
+ @prefix = prefix || '*' # [String] the prefix to use
39
+ @time = time ? time.strftime(ftime) : '*' # [Time] the time object for formatting
40
+ @blockname = blockname ? blockname.gsub(pattern, replace) : '*' # [String] the block name to include
41
+ # @ftime = ftime # [String] the time format (default: DEFAULT_FTIME)
42
+ # @pattern = pattern # [Regexp] the pattern to search (default: FNR11)
43
+ # @replace = replace # [String] the string to replace the pattern (default: FNR12)
44
+ @exts = exts || '.*' # [String] the extension to append (default: '.sh')
45
+ @mark = mark || MARK_STR
46
+ @join_str = join_str || JOIN_STR
47
+ @saved_asset_format = saved_asset_format
25
48
  end
26
49
 
27
- # Generates a formatted stdout name based on the provided parameters.
28
- def self.stdout_name(filename:, prefix:, time:, blockname:, ftime: DEFAULT_FTIME, join_str: '_', pattern: FNR11, replace: FNR12, exts: '.out.txt')
29
- fne = filename.gsub(pattern, replace)
30
- bne = blockname.gsub(pattern, replace)
31
- "#{[prefix, time.strftime(ftime), fne, ',', bne].join(join_str)}#{exts}"
50
+ # Generates a formatted name based on the provided parameters.
51
+ #
52
+ # @return [String] the generated formatted name
53
+ def generate_name
54
+ format(
55
+ @saved_asset_format,
56
+ {
57
+ blockname: @blockname,
58
+ exts: @exts,
59
+ filename: @filename,
60
+ join: @join_str,
61
+ mark: @mark,
62
+ prefix: @prefix,
63
+ time: @time
64
+ }
65
+ )
32
66
  end
33
67
  end
34
68
  end
@@ -61,4 +95,76 @@ class SavedAssetTest < Minitest::Test
61
95
  filename: filename, prefix: prefix, time: time, blockname: blockname
62
96
  )
63
97
  end
98
+
99
+ def test_wildcard_name_with_all_parameters
100
+ filename = 'sample.txt'
101
+ prefix = 'test'
102
+ time = Time.new(2023, 1, 1, 12, 0, 0)
103
+ blockname = 'block/1:2'
104
+ expected_wildcard = 'test_2023-01-01-12-00-00_sample_txt_,_block_1_2.sh'
105
+
106
+ assert_equal expected_wildcard, MarkdownExec::SavedAsset.wildcard_name(
107
+ filename: filename, prefix: prefix, time: time, blockname: blockname
108
+ )
109
+ end
110
+
111
+ def test_wildcard_name_with_missing_time
112
+ filename = 'sample.txt'
113
+ prefix = 'test'
114
+ time = nil
115
+ blockname = 'block/1:2'
116
+ expected_wildcard = 'test_*_sample_txt_,_block_1_2.sh'
117
+
118
+ assert_equal expected_wildcard, MarkdownExec::SavedAsset.wildcard_name(
119
+ filename: filename, prefix: prefix, time: time, blockname: blockname
120
+ )
121
+ end
122
+
123
+ def test_wildcard_name_with_missing_filename
124
+ filename = nil
125
+ prefix = 'test'
126
+ time = Time.new(2023, 1, 1, 12, 0, 0)
127
+ blockname = 'block/1:2'
128
+ expected_wildcard = 'test_2023-01-01-12-00-00_*_,_block_1_2.sh'
129
+
130
+ assert_equal expected_wildcard, MarkdownExec::SavedAsset.wildcard_name(
131
+ filename: filename, prefix: prefix, time: time, blockname: blockname
132
+ )
133
+ end
134
+
135
+ def test_wildcard_name_with_missing_prefix
136
+ filename = 'sample.txt'
137
+ prefix = nil
138
+ time = Time.new(2023, 1, 1, 12, 0, 0)
139
+ blockname = 'block/1:2'
140
+ expected_wildcard = '*_2023-01-01-12-00-00_sample_txt_,_block_1_2.sh'
141
+
142
+ assert_equal expected_wildcard, MarkdownExec::SavedAsset.wildcard_name(
143
+ filename: filename, prefix: prefix, time: time, blockname: blockname
144
+ )
145
+ end
146
+
147
+ def test_wildcard_name_with_missing_blockname
148
+ filename = 'sample.txt'
149
+ prefix = 'test'
150
+ time = Time.new(2023, 1, 1, 12, 0, 0)
151
+ blockname = nil
152
+ expected_wildcard = 'test_2023-01-01-12-00-00_sample_txt_,_*.sh'
153
+
154
+ assert_equal expected_wildcard, MarkdownExec::SavedAsset.wildcard_name(
155
+ filename: filename, prefix: prefix, time: time, blockname: blockname
156
+ )
157
+ end
158
+
159
+ def test_wildcard_name_with_all_missing
160
+ filename = nil
161
+ prefix = nil
162
+ time = nil
163
+ blockname = nil
164
+ expected_wildcard = '*_*_*_,_*.sh'
165
+
166
+ assert_equal expected_wildcard, MarkdownExec::SavedAsset.wildcard_name(
167
+ filename: filename, prefix: prefix, time: time, blockname: blockname
168
+ )
169
+ end
64
170
  end
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # encoding=utf-8
5
+
6
+ class StreamsOut
7
+ attr_accessor :streams
8
+
9
+ def initialize
10
+ @streams = []
11
+ end
12
+
13
+ def append_stream_line(stream, line)
14
+ @streams << { stream: stream, line: line, timestamp: Time.now }
15
+ end
16
+
17
+ def write_execution_output_to_file(filespec)
18
+ FileUtils.mkdir_p File.dirname(filespec)
19
+
20
+ output = @streams.map do |entry|
21
+ case entry[:stream]
22
+ when ExecutionStreams::STD_OUT
23
+ entry[:line]
24
+ # "OUT: #{entry[:line]}"
25
+ when ExecutionStreams::STD_ERR
26
+ entry[:line]
27
+ # "ERR: #{entry[:line]}"
28
+ when ExecutionStreams::STD_IN
29
+ entry[:line]
30
+ # " IN: #{entry[:line]}"
31
+ end
32
+ end.join("\n")
33
+
34
+ File.write(filespec, output)
35
+ end
36
+ end
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: 2.0.8.3
4
+ version: 2.1.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: 2024-06-15 00:00:00.000000000 Z
11
+ date: 2024-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: clipboard
@@ -115,6 +115,7 @@ files:
115
115
  - bin/tab_completion.sh.erb
116
116
  - examples/block_names.md
117
117
  - examples/colors.md
118
+ - examples/data-files.md
118
119
  - examples/document_options.md
119
120
  - examples/duplicate_block.md
120
121
  - examples/import0.md
@@ -135,10 +136,12 @@ files:
135
136
  - examples/load_code.md
136
137
  - examples/nickname.md
137
138
  - examples/opts.md
139
+ - examples/opts_output_execution.md
138
140
  - examples/pass-through.md
139
141
  - examples/pause-after-execution.md
140
142
  - examples/plant.md
141
143
  - examples/port.md
144
+ - examples/save.md
142
145
  - examples/search.md
143
146
  - examples/vars.md
144
147
  - examples/wrap.md
@@ -179,6 +182,7 @@ files:
179
182
  - lib/saved_files_matcher.rb
180
183
  - lib/shared.rb
181
184
  - lib/std_out_err_logger.rb
185
+ - lib/streams_out.rb
182
186
  - lib/string_util.rb
183
187
  - lib/tap.rb
184
188
  homepage: https://rubygems.org/gems/markdown_exec