markdown_exec 2.0.8.3 → 2.1.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: 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