lib/hash_delegator.rb in markdown_exec-2.5.0 vs lib/hash_delegator.rb in markdown_exec-2.6.0
- old
+ new
@@ -18,11 +18,10 @@
require 'yaml'
require_relative 'ansi_string'
require_relative 'array'
require_relative 'array_util'
-# require_relative 'block_label'
require_relative 'block_types'
require_relative 'cached_nested_file_reader'
require_relative 'constants'
require_relative 'directory_searcher'
require_relative 'evaluate_shell_expressions'
@@ -163,10 +162,11 @@
body.lines.map { |line| indent + line.chomp }.join("\n")
end
def initialize_fcb_names(fcb)
fcb.oname = fcb.dname = fcb.title || ''
+ fcb.s2title = fcb.oname
end
def join_code_lines(lines)
((lines || []) + ['']).join("\n")
end
@@ -265,10 +265,11 @@
# find tables in multiple lines and format horizontally
def tables_into_columns!(blocks_menu, delegate_object)
return unless delegate_object[:tables_into_columns]
+ screenwidth = delegate_object[:console_width]
lines = blocks_menu.map(&:oname)
text_tables = TableExtractor.extract_tables(
lines,
regexp: delegate_object[:table_parse_regexp]
)
@@ -277,36 +278,52 @@
text_tables.each do |table|
next unless table[:columns].positive?
range = table[:start_index]..(table[:start_index] + table[:rows] - 1)
lines = blocks_menu[range].map(&:dname)
- formatted = MarkdownTableFormatter.format_table(
+ table__hs = MarkdownTableFormatter.format_table__hs(
column_count: table[:columns],
decorate: {
border: delegate_object[:table_border_color],
header_row: delegate_object[:table_header_row_color],
row: delegate_object[:table_row_color],
separator_line: delegate_object[:table_separator_line_color]
},
lines: lines
)
- unless formatted.count == range.size
- # warn [__LINE__, range, lines, formatted].inspect
+ unless table__hs.count == range.size
raise 'Invalid result from MarkdownTableFormatter.format_table()'
end
# read indentation from first line
indent = blocks_menu[range.first].oname.split('|', 2).first
# replace text in each block
range.each.with_index do |block_ind, ind|
- ### format oname to dname
- blocks_menu[block_ind].dname = indent + formatted[ind]
+ fcb = blocks_menu[block_ind]
+ fcb.s3formatted_table_row = fcb.padded = table__hs[ind] ####
+ fcb.padded_width = table__hs[ind].padded_width
+ if fcb.center
+ cw = (screenwidth - table__hs[ind].padded_width) / 2
+ if cw.positive?
+ indent = ' ' * cw
+ fcb.s3indent = fcb.indent = indent
+ end
+ else
+ fcb.s3indent = fcb.indent
+ end
+ fcb.s3indent ||= ''
+ fcb.dname = fcb.indented_decorated = fcb.s3indent + fcb.s3formatted_table_row.decorate
end
end
end
+ # s0indent: indent,
+ # s0printable: line_obj[:text],
+ # s1decorated: decorated,
+ # s2title = fcb.oname
+ # s3formatted_table_row = fcb.padded = table__hs[ind]####
# Creates a TTY prompt with custom settings. Specifically,
# it disables the default 'cross' symbol and
# defines a lambda function to handle interrupts.
# @return [TTY::Prompt] A new TTY::Prompt instance
@@ -343,14 +360,14 @@
# Yields a line as a new block if the selected message type includes :line.
# @param [String] line The line to be processed.
# @param [Array<Symbol>] selected_types A list of message types to check.
# @param [Proc] block The block to be called with the line data.
- def yield_line_if_selected(line, selected_types, &block)
+ def yield_line_if_selected(line, selected_types, id: '', &block)
return unless block && block_type_selected?(selected_types, :line)
- block.call(:line, MarkdownExec::FCB.new(body: [line]))
+ block.call(:line, MarkdownExec::FCB.new(body: [line], id: id))
end
end
# This module provides methods for compacting and converting data structures.
module CompactionHelpers
@@ -530,22 +547,23 @@
@@printed_messages.add(str)
end
end
class HashDelegatorParent
- attr_accessor :most_recent_loaded_filename, :pass_args, :run_state,
+ attr_accessor :pass_args, :run_state,
:p_all_arguments, :p_options_parsed, :p_params, :p_rest
extend HashDelegatorSelf
include CompactionHelpers
include TextAnalyzer
def initialize(delegate_object = {})
@delegate_object = delegate_object
@prompt = HashDelegator.tty_prompt_without_disabled_symbol
- @most_recent_loaded_filename = nil
+ @opts_most_recent_filename = nil
+ @vars_most_recent_filename = nil
@pass_args = []
@run_state = OpenStruct.new(
link_history: [],
source: OpenStruct.new
)
@@ -557,22 +575,14 @@
@p_all_arguments = []
@p_options_parsed = []
@p_params = {}
@p_rest = []
+
+ @compressed_ids = nil
end
- # private
-
- # def [](key)
- # @delegate_object[key]
- # end
-
- # def []=(key, value)
- # @delegate_object[key] = value
- # end
-
##
# Returns the absolute path of the given file path.
# If the provided path is already absolute, it returns it as is.
# Otherwise, it prefixes the path with the current working directory.
#
@@ -589,115 +599,58 @@
else
File.join(Dir.getwd, file_path)
end
end
+ def add_back_option(id: '', menu_blocks:)
+ append_chrome_block(id: id, menu_blocks: menu_blocks,
+ menu_state: MenuState::BACK)
+ end
+
+ def add_exit_option(id: '', menu_blocks:)
+ append_chrome_block(id: id, menu_blocks: menu_blocks,
+ menu_state: MenuState::EXIT)
+ end
+
+ def add_inherited_lines(menu_blocks:, link_state:)
+ append_inherited_lines(menu_blocks: menu_blocks, link_state: link_state)
+ end
+
# Modifies the provided menu blocks array by adding 'Back' and 'Exit' options,
# along with initial and final dividers, based on the delegate object's configuration.
#
# @param menu_blocks [Array] The array of menu block elements to be modified.
- def add_menu_chrome_blocks!(menu_blocks:, link_state:)
+ def add_menu_chrome_blocks!(id: '', menu_blocks:, link_state:)
return unless @delegate_object[:menu_link_format].present?
if @delegate_object[:menu_with_inherited_lines]
add_inherited_lines(menu_blocks: menu_blocks,
link_state: link_state)
end
# back before exit
- add_back_option(menu_blocks: menu_blocks) if should_add_back_option?
+ add_back_option(id: "#{id}.back",
+ menu_blocks: menu_blocks) if should_add_back_option?
# exit after other options
if @delegate_object[:menu_with_exit]
- add_exit_option(menu_blocks: menu_blocks)
+ add_exit_option(id: "#{id}.exit", menu_blocks: menu_blocks)
end
- append_divider(menu_blocks: menu_blocks, position: :initial)
- append_divider(menu_blocks: menu_blocks, position: :final)
+ append_divider(id: "#{id}.init", menu_blocks: menu_blocks,
+ position: :initial)
+ append_divider(id: "#{id}.final", menu_blocks: menu_blocks,
+ position: :final)
end
- def variable_expansions!(
- echo_command_form: 'echo "$%s"',
- link_state:,
- menu_blocks:,
- regexp: Regexp.new(@delegate_object[:variable_expression_regexp])
- )
- # !!v link_state.inherited_lines_block
- # collect variables in menu_blocks
- #
- variables_count = Hash.new(0)
- menu_blocks.each do |fcb|
- next if fcb.type == BlockType::SHELL
-
- fcb.oname.scan(regexp) do |(expression, variable)|
- expression.match(regexp)
- variables_count[$LAST_MATCH_INFO[:variable]] += 1
- end
- end
- # !!v variables_count
-
- # commands to echo variables
- #
- commands = {}
- variables_count.each do |variable, count|
- command = format(echo_command_form, variable)
- commands[variable] = command
- end
- # !!v commands
-
- # replacement dictionary from evaluated commands
- #
- replacement_dictionary = evaluate_shell_expressions(
- link_state.inherited_lines_block, commands,
- key_format: "${%s}" # no need to escape variable name for regexp
- ) # !!t
- return if replacement_dictionary.nil?
-
- # update blocks
- #
- Regexp.union(replacement_dictionary.keys).tap do |pattern|
- menu_blocks.each do |fcb|
- next if fcb.type == BlockType::SHELL
-
- fcb.variable_expansion!(pattern, replacement_dictionary)
- end
- end
- end
-
- private
-
- def replace_keys_in_lines(replacement_dictionary, lines)
- # Create a regex pattern that matches any key in the replacement dictionary
- pattern = Regexp.union(replacement_dictionary.keys.map { |key|
- "%<#{key}>"
- })
-
- # Iterate over each line and apply gsub with the replacement hash
- lines.map do |line|
- line.gsub(pattern) { |match| replacement_dictionary[match] }
- end
- end
-
- def add_back_option(menu_blocks:)
- append_chrome_block(menu_blocks: menu_blocks, menu_state: MenuState::BACK)
- end
-
- def add_exit_option(menu_blocks:)
- append_chrome_block(menu_blocks: menu_blocks, menu_state: MenuState::EXIT)
- end
-
- def add_inherited_lines(menu_blocks:, link_state:)
- append_inherited_lines(menu_blocks: menu_blocks, link_state: link_state)
- end
-
public
# Appends a chrome block, which is a menu option for Back or Exit
#
# @param all_blocks [Array] The current blocks in the menu
# @param type [Symbol] The type of chrome block to add (:back or :exit)
- def append_chrome_block(menu_blocks:, menu_state:)
+ def append_chrome_block(menu_blocks:, menu_state:, id: '')
case menu_state
when MenuState::BACK
history_state_partition
option_name = @delegate_object[:menu_option_back_name]
insert_at_top = @delegate_object[:menu_back_at_top]
@@ -731,10 +684,12 @@
chrome_block = FCB.new(
chrome: true,
dname: HashDelegator.new(@delegate_object).string_send_color(
formatted_name, :menu_chrome_color
),
+ id: id,
+ type: BlockType::CHROME,
nickname: formatted_name,
oname: formatted_name
)
if insert_at_top
@@ -749,14 +704,14 @@
# Appends a formatted divider to the specified position in a menu block array.
# The method checks for the presence of formatting options before appending.
#
# @param menu_blocks [Array] The array of menu block elements.
# @param position [Symbol] The position to insert the divider (:initial or :final).
- def append_divider(menu_blocks:, position:)
+ def append_divider(id: '', menu_blocks:, position:)
return unless divider_formatting_present?(position)
- divider = create_divider(position)
+ divider = create_divider(position, id: id)
position == :initial ? menu_blocks.unshift(divider) : menu_blocks.push(divider)
end
# Appends a formatted divider to the specified position in a menu block array.
# The method checks for the presence of formatting options before appending.
@@ -770,11 +725,11 @@
chrome_blocks = link_state.inherited_lines_map do |line|
formatted = format(@delegate_object[:menu_inherited_lines_format],
{ line: line })
FCB.new(
chrome: true,
- disabled: '',
+ disabled: TtyMenu::DISABLE,
dname: HashDelegator.new(@delegate_object).string_send_color(
formatted, :menu_inherited_lines_color
),
oname: formatted
)
@@ -838,29 +793,33 @@
# @return [Array<FCB>] An array of FCB objects representing the blocks.
def blocks_from_nested_files
register_console_attributes(@delegate_object)
@decor_patterns_from_delegate_object_for_block_create = collect_line_decor_patterns(@delegate_object)
+ count = 0
blocks = []
iter_blocks_from_nested_files do |btype, fcb|
+ count += 1
case btype
when :blocks
if @delegate_object[:bash]
fcb.for_menu!(
block_calls_scan: @delegate_object[:block_calls_scan],
block_name_match: @delegate_object[:block_name_match],
block_name_nick_match: @delegate_object[:block_name_nick_match],
+ id: "*#{count}",
) do |oname, color|
apply_block_type_color_option(oname, color)
end
end
blocks << fcb
when :filter # types accepted
%i[blocks line]
when :line
unless @delegate_object[:no_chrome]
- create_and_add_chrome_blocks(blocks, fcb)
+ create_and_add_chrome_blocks(blocks, fcb, id: "*#{count}",
+ init_ids: init_ids)
end
end
end
# !!t blocks.count
blocks
@@ -892,10 +851,17 @@
end
end
# private
+ def build_replacement_dictionary(commands, link_state)
+ evaluate_shell_expressions(
+ link_state.inherited_lines_block, commands,
+ key_format: "${%s}" # no need to escape variable name for regexp
+ ) # !!t
+ end
+
def calc_logged_stdout_filename(block_name:)
return unless @delegate_object[:saved_stdout_folder]
@delegate_object[:logged_stdout_filename] =
SavedAsset.new(
@@ -935,18 +901,44 @@
return false
end
true
end
+ def chrome_block_criteria
+ [
+ { center: :table_center, format: :menu_note_format,
+ match: :menu_table_rows_match, type: BlockType::TEXT },
+ { case_conversion: :upcase, center: :heading1_center,
+ collapse: :heading1_collapse, collapsible: :heading1_collapsible,
+ color: :menu_heading1_color, format: :menu_heading1_format, level: 1,
+ match: :heading1_match, type: BlockType::HEADING, wrap: true },
+ { center: :heading2_center,
+ collapse: :heading2_collapse, collapsible: :heading2_collapsible,
+ color: :menu_heading2_color, format: :menu_heading2_format, level: 2,
+ match: :heading2_match, type: BlockType::HEADING, wrap: true },
+ { case_conversion: :downcase, center: :heading3_center,
+ collapse: :heading3_collapse, collapsible: :heading3_collapsible,
+ color: :menu_heading3_color, format: :menu_heading3_format, level: 3,
+ match: :heading3_match, type: BlockType::HEADING, wrap: true },
+ { center: :divider4_center,
+ collapse: :divider4_collapse, collapsible: :divider4_collapsible,
+ color: :menu_divider_color, format: :menu_divider_format, level: 4,
+ match: :divider_match, type: BlockType::DIVIDER },
+ { color: :menu_note_color, format: :menu_note_format,
+ match: :menu_note_match, type: BlockType::TEXT, wrap: true },
+ { color: :menu_task_color, format: :menu_task_format,
+ match: :menu_task_match, type: BlockType::TEXT, wrap: true }
+ ]
+ end
+
+ # sets ENV
def code_from_vars_block_to_set_environment_variables(selected)
code_lines = []
YAML.load(selected.body.join("\n"))&.each do |key, value|
ENV[key] = value.to_s
+ code_lines.push "#{key}=#{Shellwords.escape(value)}"
- require 'shellwords'
- code_lines.push "#{key}=\"#{Shellwords.escape(value)}\""
-
next unless @delegate_object[:menu_vars_set_format].present?
formatted_string = format(@delegate_object[:menu_vars_set_format],
{ key: key, value: value })
print string_send_color(formatted_string, :menu_vars_set_color)
@@ -969,11 +961,15 @@
%i[line_decor_pre line_decor_main line_decor_post].flat_map do |key|
extract_patterns.call(key)
end
end
- def command_execute(command, shell:, args: [])
+ def command_execute(
+ command,
+ erls:,
+ shell:, args: []
+ )
@run_state.files = StreamsOut.new
@run_state.options = @delegate_object
@run_state.started_at = Time.now.utc
if @delegate_object[:execute_in_own_window] &&
@@ -981,17 +977,19 @@
@run_state.saved_filespec.present?
@run_state.in_own_window = true
command_execute_in_own_window(
args: args,
+ erls: erls,
script: @delegate_object[:execute_command_format]
)
else
@run_state.in_own_window = false
command_execute_in_process(
args: args, command: command,
+ erls: erls,
filename: @delegate_object[:filename], shell: shell
)
end
@run_state.completed_at = Time.now.utc
@@ -1005,22 +1003,31 @@
@run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
@run_state.error_message)
@fout.fout "Error ENOENT: #{err.inspect}"
end
- def command_execute_in_own_window(args:, script:)
+ def command_execute_in_own_window(
+ args:,
+ erls:,
+ script:
+ )
system(
format(
script,
command_execute_in_own_window_format_arguments(
+ erls: erls,
rest: args ? args.join(' ') : ''
)
)
)
end
- def command_execute_in_own_window_format_arguments(home: Dir.pwd, rest: '')
+ def command_execute_in_own_window_format_arguments(
+ home: Dir.pwd,
+ erls:,
+ rest: ''
+ )
{
batch_index: @run_state.batch_index,
batch_random: @run_state.batch_random,
block_name: @delegate_object[:block_name],
document_filename: File.basename(@delegate_object[:filename]),
@@ -1028,20 +1035,25 @@
home: home,
output_filename: File.basename(
@delegate_object[:logged_stdout_filespec]
),
output_filespec: @delegate_object[:logged_stdout_filespec],
+ play_command: erls[:play_bin],
rest: rest,
script_filename: @run_state.saved_filespec,
script_filespec: File.join(home, @run_state.saved_filespec),
started_at: @run_state.started_at.strftime(
@delegate_object[:execute_command_title_time_format]
)
}
end
- def command_execute_in_process(args:, command:, filename:, shell:)
+ def command_execute_in_process(
+ args:, command:,
+ erls:,
+ filename:, shell:
+ )
execute_command_with_streams(
[shell, '-c', command,
@delegate_object[:filename], *args]
)
end
@@ -1059,10 +1071,23 @@
# @return [LoadFileLinkState] An object indicating whether to load
# the next block or reuse the current one.
def compile_execute_and_trigger_reuse(
mdoc:, selected:, block_source:, link_state:
)
+ # play_bin matches the name in mde.applescript, called by .mde.macos.yml
+ bim = @delegate_object[:block_interactive_match]
+ play_bin = if bim.present? && selected.start_line =~ Regexp.new(bim)
+ @delegate_object[:play_bin_interactive]
+ else
+ bbm = @delegate_object[:block_batch_match]
+ if bbm.present? && selected.start_line =~ Regexp.new(bbm)
+ @delegate_object[:play_bin_batch]
+ else
+ @delegate_object[:document_play_bin]
+ end
+ end
+
required_lines = execute_block_type_port_code_lines(
mdoc: mdoc, selected: selected,
link_state: link_state, block_source: block_source
)
output_or_approval = @delegate_object[:output_script] ||
@@ -1078,12 +1103,14 @@
else
true
end
if allow_execution
- execute_required_lines(required_lines: required_lines,
- selected: selected,
+ execute_required_lines(blockname: selected.pub_name,
+ erls: { play_bin: play_bin,
+ shell: selected.shell },
+ required_lines: required_lines,
shell: selected.shell)
end
link_state.block_name = nil
end
@@ -1115,10 +1142,30 @@
import_paths: @delegate_object[:import_paths]&.split(':')
)
HashDelegator.count_matches_in_lines(lines, regex) / 2
end
+ def count_named_group_occurrences(
+ blocks, pattern, exclude_types: [BlockType::SHELL]
+ )
+ # Initialize a counter for named group occurrences
+ occurrence_count = Hash.new(0)
+
+ blocks.each do |block|
+ # Skip processing for shell-type blocks
+ next if exclude_types.include?(block.type)
+
+ # Scan each block name for matches of the pattern
+ block.oname.scan(pattern) do |(_, variable_name)|
+ pattern.match($LAST_MATCH_INFO.to_s) # Reapply match for named groups
+ occurrence_count[$LAST_MATCH_INFO[:variable]] += 1
+ end
+ end
+
+ occurrence_count
+ end
+
##
# Creates and adds a formatted block to the blocks array
# based on the provided match and format options.
# @param blocks [Array] The array of blocks to add the new block to.
# @param match_data [MatchData] The match data containing named captures
@@ -1127,43 +1174,43 @@
# for the new block.
# @param color_method [Symbol] The color method to apply
# to the block's display name.
# return number of lines added
def create_and_add_chrome_block(blocks:, match_data:,
+ collapse: nil,
format_option:, color_method:,
case_conversion: nil,
center: nil,
decor_patterns: [],
+ disabled: true,
+ id: '',
+ level: 0,
+ type: '',
wrap: nil)
line_cap = NamedCaptureExtractor::extract_named_group2(match_data)
-
# replace tabs in indent
line_cap[:indent] ||= ''
line_cap[:indent] = line_cap[:indent].dup if line_cap[:indent].frozen?
line_cap[:indent].gsub!("\t", ' ')
# replace tabs in text
line_cap[:text] ||= ''
line_cap[:text] = line_cap[:text].dup if line_cap[:text].frozen?
line_cap[:text].gsub!("\t", ' ')
# missing capture
+ line_cap[:collapse] ||= ''
line_cap[:line] ||= ''
accepted_width = @delegate_object[:console_width] - 2
- line_caps = if wrap
- if line_cap[:text].length > accepted_width
- wrapper = StringWrapper.new(
- width: accepted_width - line_cap[:indent].length
- )
- wrapper.wrap(line_cap[:text]).map do |line|
- line_cap.dup.merge(text: line)
- end
- else
- [line_cap]
- end
- else
- [line_cap]
- end
+
+ line_caps = [line_cap]
+ if wrap && line_cap[:text].length > accepted_width
+ wrapper = StringWrapper.new(width: accepted_width - line_cap[:indent].length)
+ line_caps = wrapper.wrap(line_cap[:text]).map do |wrapped_line|
+ line_cap.dup.merge(text: wrapped_line)
+ end
+ end
+
if center
line_caps.each do |line_obj|
line_obj[:indent] =
if line_obj[:text].length < accepted_width
' ' * ((accepted_width - line_obj[:text].length) / 2)
@@ -1171,11 +1218,11 @@
''
end
end
end
- line_caps.each do |line_obj|
+ line_caps.each_with_index do |line_obj, index|
next if line_obj[:text].nil?
case case_conversion
when :upcase
line_obj[:text].upcase!
@@ -1194,14 +1241,27 @@
oname, color_method, decor_patterns
)
line_obj[:line] = line_obj[:indent] + line_obj[:text]
blocks.push FCB.new(
+ center: center,
chrome: true,
- disabled: '',
+ collapse: collapse.nil? ? (line_obj[:collapse] == COLLAPSIBLE_TOKEN_COLLAPSE) : collapse,
+ token: line_obj[:collapse],
+ disabled: disabled ? TtyMenu::DISABLE : nil,
+ ####
+ # id: "#{@delegate_object[:filename]}:#{index}",
+ id: "#{id}.#{index}",
+ level: level,
+ s0indent: indent,
+ s0printable: line_obj[:text],
+ s1decorated: decorated,
dname: line_obj[:indent] + decorated,
- oname: line_obj[:text]
+ indent: line_obj[:indent],
+ oname: line_obj[:text],
+ text: line_obj[:text],
+ type: type
)
end
line_caps.count
end
@@ -1211,60 +1271,63 @@
# @param blocks [Array] The array to append new blocks to.
# @param fcb [FCB] The file control block being processed.
# @param opts [Hash] Options containing configuration for line processing.
# @param use_chrome [Boolean] Indicates if the chrome styling should
# be applied.
- def create_and_add_chrome_blocks(blocks, fcb)
- # rubocop:disable Layout/LineLength
- match_criteria = [
- { format: :menu_note_format, match: :menu_table_rows_match },
- { color: :menu_heading1_color, format: :menu_heading1_format, match: :heading1_match, wrap: true, center: :heading1_center, case_conversion: :upcase },
- { color: :menu_heading2_color, format: :menu_heading2_format, match: :heading2_match, wrap: true, center: :heading2_center },
- { color: :menu_heading3_color, format: :menu_heading3_format, match: :heading3_match, wrap: true, center: :heading3_center, case_conversion: :downcase },
- { color: :menu_divider_color, format: :menu_divider_format, match: :menu_divider_match },
- { color: :menu_note_color, format: :menu_note_format, match: :menu_note_match, wrap: true },
- { color: :menu_task_color, format: :menu_task_format, match: :menu_task_match, wrap: true }
- ]
- # rubocop:enable Layout/LineLength
- # rubocop:enable Style/UnlessElse
- match_criteria.each do |criteria|
+ def create_and_add_chrome_blocks(blocks, fcb, id: '', init_ids: false)
+ chrome_block_criteria.each_with_index do |criteria, index|
unless @delegate_object[criteria[:match]].present? &&
(mbody = fcb.body[0].match @delegate_object[criteria[:match]])
next
end
create_and_add_chrome_block(
blocks: blocks,
case_conversion: criteria[:case_conversion],
center: criteria[:center] &&
@delegate_object[criteria[:center]],
+
+ collapse: case fcb.collapse_token
+ when COLLAPSIBLE_TOKEN_COLLAPSE
+ true
+ when COLLAPSIBLE_TOKEN_EXPAND
+ false
+ else
+ false####
+ end,
+
color_method: criteria[:color] &&
@delegate_object[criteria[:color]].to_sym,
decor_patterns:
@decor_patterns_from_delegate_object_for_block_create,
+ disabled: !(criteria[:collapsible] && @delegate_object[criteria[:collapsible]]),
+ id: "#{id}.#{index}",
format_option: criteria[:format] &&
@delegate_object[criteria[:format]],
+ level: criteria[:level],
match_data: mbody,
+ type: criteria[:type],
wrap: criteria[:wrap]
)
break
end
end
- def create_divider(position)
+ def create_divider(position, id: '')
divider_key = if position == :initial
:menu_initial_divider
else
:menu_final_divider
end
oname = format(@delegate_object[:menu_divider_format],
HashDelegator.safeval(@delegate_object[divider_key]))
FCB.new(
chrome: true,
- disabled: '',
+ disabled: TtyMenu::DISABLE,
dname: string_send_color(oname, :menu_divider_color),
+ id: id,
oname: oname
)
end
# Prompts user if named block is the same as the prior execution.
@@ -1342,22 +1405,25 @@
@delegate_object[:menu_divider_format].present? &&
@delegate_object[divider_key].present?
end
def dml_menu_append_chrome_item(
- name, count, type, menu_state: MenuState::LOAD,
+ name, count, type,
+ id: '',
+ menu_state: MenuState::LOAD,
always_create: true, always_enable: true
)
raise unless name.present?
raise if @dml_menu_blocks.nil?
item = @dml_menu_blocks.find { |block| block.oname == name }
# create menu item when it is needed (count > 0)
#
if item.nil? && (always_create || count.positive?)
- item = append_chrome_block(menu_blocks: @dml_menu_blocks,
+ item = append_chrome_block(id: id,
+ menu_blocks: @dml_menu_blocks,
menu_state: menu_state)
end
# update item if it exists
#
@@ -1365,11 +1431,11 @@
item.dname = type.present? ? "#{name} (#{count} #{type})" : name
if always_enable || count.positive?
item.delete(:disabled)
else
- item[:disabled] = ''
+ item[:disabled] = TtyMenu::DISABLE
end
end
def do_save_execution_output
return unless @delegate_object[:save_execution_output]
@@ -1519,22 +1585,19 @@
# @param mdoc [YourMDocClass] An instance of the MDoc class.
#
def execute_block_by_type_for_lfls(
selected:, mdoc:, block_source:, link_state: LinkState.new
)
- # !!v selected
# order should not be important other than else clause
if selected.type == BlockType::EDIT
debounce_reset
- # !!v link_state.inherited_lines_block
vux_edit_inherited
return :break if pause_user_exit
next_state_append_code(selected, link_state, [])
elsif selected.type == BlockType::HISTORY
- # !!b
debounce_reset
return :break if execute_block_type_history_ux(
selected: selected,
link_state: link_state
) == :no_history
@@ -1542,14 +1605,14 @@
LoadFileLinkState.new(LoadFile::REUSE, link_state)
elsif selected.type == BlockType::LINK
debounce_reset
execute_block_type_link_with_state(link_block_body: selected.body,
- mdoc: mdoc,
- selected: selected,
- link_state: link_state,
- block_source: block_source)
+ mdoc: mdoc,
+ selected: selected,
+ link_state: link_state,
+ block_source: block_source)
elsif selected.type == BlockType::LOAD
debounce_reset
code_lines = execute_block_type_load_code_lines(selected)
next_state_append_code(selected, link_state, code_lines)
@@ -1609,10 +1672,19 @@
elsif selected.type == BlockType::VARS
debounce_reset
next_state_append_code(selected, link_state,
code_from_vars_block_to_set_environment_variables(selected))
+ elsif COLLAPSIBLE_TYPES.include?(selected.type)
+ debounce_reset
+ if @compressed_ids.keys.include?(selected.id)
+ @compressed_ids.delete(selected.id)
+ else
+ @compressed_ids.merge!(selected.id => selected.level)
+ end
+ LoadFileLinkState.new(LoadFile::REUSE, link_state)
+
elsif debounce_allows
compile_execute_and_trigger_reuse(mdoc: mdoc,
selected: selected,
link_state: link_state,
block_source: block_source)
@@ -1693,13 +1765,11 @@
# action for file loading.
def execute_block_type_link_with_state(
link_block_body: [], mdoc: nil, selected: FCB.new,
link_state: LinkState.new, block_source: {}
)
- # !!p link_block_body selected
link_block_data = HashDelegator.parse_yaml_data_from_body(link_block_body)
- # !!v link_block_data
## collect blocks specified by block
#
if mdoc
code_info = mdoc.collect_recursively_required_code(
anyname: selected.pub_name,
@@ -1789,40 +1859,41 @@
end
def execute_block_type_load_code_lines(
selected,
directory: @delegate_object[:document_configurations_directory],
+ exit_prompt: @delegate_object[:prompt_filespec_back],
filename_pattern: @delegate_object[:vars_block_filename_pattern],
glob: @delegate_object[:document_configurations_glob],
view: @delegate_object[:vars_block_filename_view]
)
- # !!p selected
block_data = HashDelegator.parse_yaml_data_from_body(selected.body)
- # !!v block_data
if selected_option = select_option_with_metadata(
prompt_title,
- Dir.glob(
+ [exit_prompt] + Dir.glob(
File.join(
Dir.pwd,
block_data['directory'] || directory,
block_data['glob'] || glob
)
).sort.map do |file|
{ name: format(
- block_data['view'] || view,
- NamedCaptureExtractor::extract_named_group2(
- file.match(
- Regexp.new(block_data['filename_pattern'] ||
- filename_pattern)
- )
+ block_data['view'] || view,
+ NamedCaptureExtractor::extract_named_group2(
+ file.match(
+ Regexp.new(block_data['filename_pattern'] ||
+ filename_pattern)
)
- ),
+ )
+ ),
oname: file }
end,
simple_menu_options
)
- File.readlines(selected_option.oname, chomp: true)
+ if selected_option.dname != exit_prompt
+ File.readlines(selected_option.oname, chomp: true)
+ end
else
warn "No matching files found" ###
end
end
@@ -1833,11 +1904,11 @@
#
# @param mdoc [YourMDocClass] An instance of the MDoc class.
# @param selected [Hash] The selected block.
# @return [Array<String>] Required code blocks as an array of lines.
def execute_block_type_port_code_lines(mdoc:, selected:, block_source:,
- link_state: LinkState.new)
+ link_state: LinkState.new)
required = mdoc.collect_recursively_required_code(
anyname: selected.pub_name,
label_format_above: @delegate_object[:shell_code_label_format_above],
label_format_below: @delegate_object[:shell_code_label_format_below],
block_source: block_source
@@ -1879,28 +1950,23 @@
required[:code] + code_lines)
end
end
def execute_block_type_save(code_lines:, selected:)
- # !!p code_lines, selected
block_data = HashDelegator.parse_yaml_data_from_body(selected.body)
- # !!v block_data
directory_glob = if block_data['directory']
- # !!b
File.join(
block_data['directory'],
block_data['glob'] ||
@delegate_object[:document_saved_lines_glob].split('/').last
)
else
- # !!b
@delegate_object[:document_saved_lines_glob]
end
- # !!v directory_glob
save_filespec_from_expression(directory_glob).tap do |save_filespec|
- if save_filespec
+ if save_filespec && save != exit_prompt
begin
File.write(save_filespec,
HashDelegator.join_code_lines(code_lines))
rescue Errno::ENOENT
report_error($ERROR_INFO)
@@ -2047,24 +2113,67 @@
# including output formatting and summarization.
#
# @param required_lines [Array<String>] The lines of code to be executed.
# @param selected [FCB] The selected functional code block object.
def execute_required_lines(
- required_lines: [], selected: FCB.new, shell:
+ blockname: '',
+ erls: {},
+ required_lines: [], shell:
)
if @delegate_object[:save_executed_script]
- write_command_file(required_lines: required_lines,
- selected: selected,
+ write_command_file(blockname: blockname,
+ required_lines: required_lines,
shell: shell)
end
if @dml_block_state
calc_logged_stdout_filename(block_name: @dml_block_state.block.oname)
end
- format_and_execute_command(code_lines: required_lines, shell: shell)
+ format_and_execute_command(
+ code_lines: required_lines,
+ erls: erls,
+ shell: shell
+ )
post_execution_process
end
+ def expand_blocks_with_replacements(
+ menu_blocks, replacements, exclude_types: [BlockType::SHELL]
+ )
+ # update blocks
+ #
+ Regexp.union(replacements.keys).tap do |pattern|
+ menu_blocks.each do |block|
+ next if exclude_types.include?(block.type)
+
+ block.expand_variables_in_attributes!(pattern, replacements)
+ end
+ end
+ end
+
+ def expand_variable_references!(
+ # echo_format: 'echo "$%s"',
+ echo_format: 'echo $%s',
+ link_state:,
+ blocks:,
+ pattern: Regexp.new(@delegate_object[:variable_expression_regexp])
+ )
+ # Count occurrences of named groups in each block
+ variable_counts = count_named_group_occurrences(blocks, pattern)
+
+ # Generate echo commands for each variable based on its count
+ echo_commands = generate_echo_commands(variable_counts, echo_format)
+
+ # Build a dictionary to replace variables with the corresponding commands
+ replacements = build_replacement_dictionary(echo_commands, link_state)
+
+ # Exit early if no replacements are needed
+ return if replacements.nil?
+
+ # Expand each block with replacements from the dictionary
+ expand_blocks_with_replacements(blocks, replacements)
+ end
+
# Retrieves a specific data symbol from the delegate object,
# converts it to a string, and applies a color style
# based on the specified color symbol.
#
# @param default [String] The default value
@@ -2094,15 +2203,24 @@
end
{ size: file_size, lines: line_count }
end
- def format_and_execute_command(code_lines:, shell:)
+ def format_and_execute_command(
+ code_lines:,
+ erls:,
+ shell:
+ )
formatted_command = code_lines.flatten.join("\n")
@fout.fout fetch_color(data_sym: :script_execution_head,
color_sym: :script_execution_frame_color)
- command_execute(formatted_command, args: @pass_args, shell: shell)
+ command_execute(
+ formatted_command,
+ args: @pass_args,
+ erls: erls,
+ shell: shell
+ )
@fout.fout fetch_color(data_sym: :script_execution_tail,
color_sym: :script_execution_frame_color)
end
# Format expression using environment variables and run state
@@ -2160,10 +2278,21 @@
end
@fout.fout fetch_color(data_sym: :execution_report_preview_tail,
color_sym: :execution_report_preview_frame_color)
end
+ def generate_echo_commands(variable_counts, echo_format)
+ # commands to echo variables
+ #
+ commands = {}
+ variable_counts.each do |variable, count|
+ command = format(echo_format, variable)
+ commands[variable] = command
+ end
+ commands
+ end
+
def generate_temp_filename(ext = '.sh')
filename = begin
Dir::Tmpname.make_tmpname(['x', ext], nil)
rescue NoMethodError
require 'securerandom'
@@ -2217,16 +2346,13 @@
filename: nil,
home: Dir.pwd,
order: :chronological,
path: ''
)
- # !!v filename, 'path', path
- # !!v File.join(home, path, filename)
files = Dir.glob(
File.join(home, path, filename)
)
- # !!v files
sorted_files = case order
when :alphabetical
files.sort
when :chronological
files.sort_by { |file| File.mtime(file) }
@@ -2265,20 +2391,22 @@
state = initial_state
selected_types = yield :filter
cfile.readlines(
@delegate_object[:filename],
import_paths: @delegate_object[:import_paths]&.split(':')
- ).each do |nested_line|
+ ).each_with_index do |nested_line, index|
if nested_line
- update_line_and_block_state(nested_line, state, selected_types,
- &block)
+ update_line_and_block_state(
+ nested_line, state, selected_types,
+ id: "#{@delegate_object[:filename]}:#{index}",
+ &block
+ )
end
end
end
def iter_source_blocks(source, &block)
- # !!v source
case source
when 1
blocks_from_nested_files.each(&block)
when 2
@dml_blocks_in_file.each(&block)
@@ -2398,35 +2526,31 @@
)
}
end
def list_blocks
- # !!b
message = @delegate_object[:list_blocks_message]
block_eval = @delegate_object[:list_blocks_eval]
- # !!v message block_eval
list = []
iter_source_blocks(@delegate_object[:list_blocks_type]) do |block|
- # !!v block
list << (block_eval.present? ? eval(block_eval) : block.send(message))
end
list.compact!
- # !!v list
@fout.fout_list(list)
end
# Loads auto blocks based on delegate object settings and updates
# if new filename is detected.
# Executes a specified block once per filename.
# @param all_blocks [Array] Array of all block elements.
# @return [Boolean, nil] True if values were modified, nil otherwise.
- def load_auto_opts_block(all_blocks, mdoc:)
+ def load_auto_opts_block(all_blocks, id: '', mdoc:)
block_name = @delegate_object[:document_load_opts_block_name]
unless block_name.present? &&
- @most_recent_loaded_filename != @delegate_object[:filename]
+ @opts_most_recent_filename != @delegate_object[:filename]
return
end
block = HashDelegator.block_find(all_blocks, :oname, block_name)
return unless block
@@ -2435,17 +2559,30 @@
mdoc: mdoc,
selected: block
)
update_menu_base(options_state.options)
- @most_recent_loaded_filename = @delegate_object[:filename]
+ @opts_most_recent_filename = @delegate_object[:filename]
true
end
+ def load_auto_vars_block(all_blocks,
+ block_name: @delegate_object[:document_load_vars_block_name])
+ unless block_name.present? &&
+ @vars_most_recent_filename != @delegate_object[:filename]
+ return
+ end
+
+ block = HashDelegator.block_find(all_blocks, :oname, block_name)
+ return unless block
+
+ @vars_most_recent_filename = @delegate_object[:filename]
+ code_from_vars_block_to_set_environment_variables(block)
+ end
+
def load_cli_or_user_selected_block(all_blocks: [], menu_blocks: [],
default: nil)
- # !!b
if @delegate_object[:block_name].present?
block = all_blocks.find do |item|
item.pub_name == @delegate_object[:block_name]
end
source = OpenStruct.new(block_name_from_ui: false)
@@ -2503,11 +2640,10 @@
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
link_state:)
if block_name_from_cli &&
@cli_block_name == @menu_base_options[:menu_persist_block_name]
- # !!b 'pause cli control, allow user to select block'
block_name_from_cli = false
now_using_cli = false
@menu_base_options[:block_name] =
@delegate_object[:block_name] = \
link_state.block_name =
@@ -2528,28 +2664,52 @@
end
## Handles the file loading and returns the blocks
# in the file and MDoc instance
#
- def mdoc_menu_and_blocks_from_nested_files(link_state)
+ def mdoc_menu_and_blocks_from_nested_files(link_state, id: '')
+ # read blocks, load document opts block, and re-process blocks
+ #
all_blocks, mdoc = mdoc_and_blocks_from_nested_files
+ if load_auto_opts_block(all_blocks, id: id, mdoc: mdoc)
+ all_blocks, mdoc = mdoc_and_blocks_from_nested_files
+ end
- # recreate menu with new options
+ # load document vars block
#
- if load_auto_opts_block(all_blocks, mdoc: mdoc)
- all_blocks, mdoc = mdoc_and_blocks_from_nested_files
+ if code_lines = load_auto_vars_block(all_blocks)
+ new_code = HashDelegator.code_merge(link_state.inherited_lines,
+ code_lines)
+ next_state_set_code(
+ nil,
+ link_state,
+ new_code
+ )
+ link_state.inherited_lines = new_code
end
- menu_blocks = mdoc.fcbs_per_options(@delegate_object)
+ # filter by name, collapsed
+ #
+ menu_blocks, @compressed_ids = mdoc.fcbs_per_options(
+ @delegate_object.merge!(compressed_ids: @compressed_ids)
+ )
- variable_expansions!(menu_blocks: menu_blocks, link_state: link_state)
- add_menu_chrome_blocks!(menu_blocks: menu_blocks, link_state: link_state)
+ # text substitution in menu
+ #
+ expand_variable_references!(blocks: menu_blocks, link_state: link_state)
+ # expand_command_substition!(blocks: menu_blocks, link_state: link_state)
+ # chrome for menu
+ #
+ add_menu_chrome_blocks!(id: id, menu_blocks: menu_blocks,
+ link_state: link_state)
+
### compress empty lines
HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
- HashDelegator.tables_into_columns!(menu_blocks, @delegate_object)
- [all_blocks, menu_blocks, mdoc] # !!r
+ HashDelegator.tables_into_columns!(menu_blocks, @delegate_object) ####
+
+ [all_blocks, menu_blocks, mdoc]
end
def menu_add_disabled_option(document_glob)
raise unless document_glob.present?
raise if @dml_menu_blocks.nil?
@@ -2560,11 +2720,11 @@
#
return unless block.nil?
chrome_block = FCB.new(
chrome: true,
- disabled: '',
+ disabled: TtyMenu::DISABLE,
dname: HashDelegator.new(@delegate_object).string_send_color(
document_glob, :menu_inherited_lines_color
),
oname: formatted_name
)
@@ -2628,11 +2788,11 @@
def next_state_set_code(selected, link_state, code_lines)
block_names = []
dependencies = {}
link_history_push_and_next(
- curr_block_name: selected.pub_name,
+ curr_block_name: selected&.pub_name,
curr_document_filename: @delegate_object[:filename],
inherited_block_names:
((link_state&.inherited_block_names || []) + block_names).sort.uniq,
inherited_dependencies:
(link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
@@ -2745,36 +2905,35 @@
do_save_execution_output
output_execution_summary
fout_execution_report if @delegate_object[:output_execution_report]
end
- # Prepare the blocks menu by adding labels and other necessary details.
- # Remove filtered blocks.
+ # Filter blocks per block_name_include_match, block_name_wrapper_match.
#
# @param all_blocks [Array<Hash>] The list of blocks from the file.
- # @param opts [Hash] The options hash.
- # @return [Array<Hash>] The updated blocks menu.
- def prepare_blocks_menu(menu_blocks)
- menu_blocks.map do |fcb|
- next if Filter.prepared_not_in_menu?(
+ def select_blocks(menu_blocks)
+ menu_blocks.select do |fcb|
+ !Filter.prepared_not_in_menu?(
@delegate_object,
fcb,
%i[block_name_include_match block_name_wrapper_match]
)
+ end
+ end
- fcb.name = fcb.dname
- # fcb.label = BlockLabel.make(
- # body: fcb.body,
- # filename: @delegate_object[:filename],
- # headings: fcb.headings,
- # menu_blocks_with_docname: @delegate_object[:menu_blocks_with_docname],
- # menu_blocks_with_headings: @delegate_object[:menu_blocks_with_headings],
- # text: fcb.text,
- # title: fcb.title
- # )
+ # Filter blocks per block_name_include_match, block_name_wrapper_match.
+ # Set name displayed by tty-prompt.
+ #
+ # @param all_blocks [Array<Hash>] The list of blocks from the file.
+ # @param opts [Hash] The options hash.
+ # @return [Array<Hash>] The updated blocks menu.
+ def blocks_as_menu_items(menu_blocks)
+ select_blocks(menu_blocks).map do |fcb|
+ fcb.name = fcb.indented_decorated || (fcb.indent + (fcb.s1decorated || fcb.dname))
+ fcb.value = fcb.id || fcb.name
fcb.to_h
- end.compact
+ end
end
def print_formatted_option(key, value)
formatted_str = format(@delegate_object[:menu_opts_set_format],
{ key: key, value: value })
@@ -3140,10 +3299,24 @@
rescue StandardError
HashDelegator.error_handler('register_console_attributes',
{ abort: true })
end
+ # private
+
+ def replace_keys_in_lines(replacement_dictionary, lines)
+ # Create a regex pattern that matches any key in the replacement dictionary
+ pattern = Regexp.union(replacement_dictionary.keys.map { |key|
+ "%<#{key}>"
+ })
+
+ # Iterate over each line and apply gsub with the replacement hash
+ lines.map do |line|
+ line.gsub(pattern) { |match| replacement_dictionary[match] }
+ end
+ end
+
def report_error(err)
# Handle ENOENT error
@run_state.aborted_at = Time.now.utc
@run_state.error_message = err.message
@run_state.error = err
@@ -3233,13 +3406,16 @@
name
end
end
end
- def save_to_file(required_lines:, selected:, shell:)
+ def save_to_file(
+ erls:,
+ required_lines:, selected:, shell:
+ )
write_command_file(
- required_lines: required_lines, selected: selected, shell: shell
+ required_lines: required_lines, blockname: selected.pub_name, shell: shell
)
@fout.fout "File saved: #{@run_state.saved_filespec}"
end
def saved_asset_filename(filename, link_state = LinkState.new)
@@ -3252,11 +3428,10 @@
)
).generate_name
end
def select_document_if_multiple(options, files, prompt:)
- # binding.irb
return files if files.class == String ###
return files[0] if (count = files.count) == 1
return unless count >= 2
@@ -3272,31 +3447,49 @@
end
# Presents a TTY prompt to select an option or exit,
# returns metadata including option and selected
def select_option_with_metadata(prompt_text, menu_items, opts = {})
- # !!v prompt_text menu_items
## configure to environment
#
register_console_attributes(opts)
- # crashes if all menu options are disabled
+ active_color_pastel = Pastel.new
+ active_color_pastel = opts[:menu_active_color_pastel_messages]
+ .inject(active_color_pastel) do |p, message|
+ p.send(message)
+ end
+
begin
+ props = {
+ active_color: active_color_pastel.detach,
+ # activate dynamic list searching on letter/number key presses
+ filter: true,
+ }.freeze
+
+ # crashes if all menu options are disabled
+ # crashes if default is not an existing item
+ #
selection = @prompt.select(prompt_text,
menu_items,
- opts.merge(filter: true))
- # !!v selection
+ opts.merge(props))
+ rescue TTY::Prompt::ConfigurationError
+ # prompt fails when collapsible block name has changed; clear default
+ selection = @prompt.select(prompt_text,
+ menu_items,
+ opts.merge(props).merge(default: nil))
rescue NoMethodError
# no enabled options in page
return
end
selected = menu_items.find do |item|
if item.instance_of?(Hash)
- (item[:name] || item[:dname]) == selection
+ # (item[:id] || item[:name] || item[:dname]) == selection
+ [item[:id], item[:name], item[:dname]].include?(selection)
elsif item.instance_of?(MarkdownExec::FCB)
- item.dname == selection
+ item.dname == selection || item.id == selection
else
item == selection
end
end
if selected.instance_of?(String)
@@ -3351,11 +3544,11 @@
File.write filespec, HashDelegator.join_code_lines(code)
File.chmod 0o755, filespec
out = `#{cmd}`.sub(/.*?#{marker}/m, '')
File.delete filespec
- out # !!r
+ out
end
def should_add_back_option?(
menu_with_back: @delegate_object[:menu_with_back]
)
@@ -3485,12 +3678,15 @@
# indicating whether to update headings while processing.
#
# @return [Void] The function modifies the `state`
# and `selected_types` arguments in place.
##
- def update_line_and_block_state(nested_line, state, selected_types,
- &block)
+ def update_line_and_block_state(
+ nested_line, state, selected_types,
+ id:,
+ &block
+ )
line = nested_line.to_s
if line.match(@delegate_object[:fenced_start_and_end_regex])
if state[:in_fenced_block]
## end of code block
#
@@ -3521,15 +3717,12 @@
]
elsif nested_line[:depth].zero? ||
@delegate_object[:menu_include_imported_notes]
# add line if it is depth 0 or option allows it
#
- HashDelegator.yield_line_if_selected(line, selected_types, &block)
-
- else
- # !!b 'line is not recognized for block state'
-
+ HashDelegator.yield_line_if_selected(line, selected_types, id: id,
+ &block)
end
end
## apply options to current state
#
@@ -3543,17 +3736,15 @@
@dml_block_state = load_cli_or_user_selected_block(
all_blocks: @dml_blocks_in_file,
menu_blocks: @dml_menu_blocks,
default: @dml_menu_default_dname
)
- # !!b '@run_state.source.block_name_from_cli:',@run_state.source.block_name_from_cli
if !@dml_block_state
# HashDelegator.error_handler('block_state missing', { abort: true })
# document has no enabled items
:break
elsif @dml_block_state.state == MenuState::EXIT
- # !!b 'load_cli_or_user_selected_block -> break'
:break
end
end
def vux_clear_menu_state
@@ -3602,11 +3793,10 @@
block_name_from_cli: @dml_now_using_cli,
block_state: @dml_block_state,
was_using_cli: @dml_now_using_cli
)
- # !!b '!block_name_from_ui + cli_break -> break'
!@dml_block_state.source.block_name_from_ui && cli_break && :break
end
def vux_execute_block_per_type(block_name, formatted_choice_ostructs)
case block_name
@@ -3808,27 +3998,25 @@
InputSequencer.new(
@delegate_object[:filename],
block_list
).run do |msg, data|
- # !!v msg data
- # !!t msg
case msg
when :parse_document # once for each menu
- vux_parse_document
- vux_menu_append_history_files(formatted_choice_ostructs)
+ vux_parse_document(id: 'vux_parse_document')
+ vux_menu_append_history_files(formatted_choice_ostructs,
+ id: "vux_menu_append_history_files",)
vux_publish_document_file_name_for_external_automation
when :display_menu
+ # does not display
vux_clear_menu_state
when :end_of_cli
- # !!b
# yield :end_of_cli, @delegate_object
if @delegate_object[:list_blocks]
- # !!b
list_blocks
:exit
end
when :user_choice
@@ -3860,22 +4048,25 @@
end
end
end
- def vux_menu_append_history_files(formatted_choice_ostructs)
+ def vux_menu_append_history_files(formatted_choice_ostructs,
+ id: '')
if @delegate_object[:menu_for_history]
history_files(
@dml_link_state,
filename: saved_asset_filename(@delegate_object[:filename],
@dml_link_state),
path: @delegate_object[:saved_script_folder]
).tap do |files|
if files.count.positive?
dml_menu_append_chrome_item(
formatted_choice_ostructs[:history].oname, files.count,
- 'files', menu_state: MenuState::HISTORY
+ 'files',
+ id: id,
+ menu_state: MenuState::HISTORY
)
end
end
end
@@ -3893,36 +4084,41 @@
menu_add_disabled_option(document_glob)
end
if files.count.positive?
dml_menu_append_chrome_item(
formatted_choice_ostructs[:load].dname, files.count, 'files',
+ id: "#{id}.load",
menu_state: MenuState::LOAD
)
end
if @delegate_object[:menu_inherited_lines_edit_always] ||
lines_count.positive?
dml_menu_append_chrome_item(
formatted_choice_ostructs[:edit].dname, lines_count, 'lines',
+ id: "#{id}.edit",
menu_state: MenuState::EDIT
)
end
if lines_count.positive?
dml_menu_append_chrome_item(
formatted_choice_ostructs[:save].dname, 1, '',
+ id: "#{id}.save",
menu_state: MenuState::SAVE
)
end
if lines_count.positive?
dml_menu_append_chrome_item(
formatted_choice_ostructs[:view].dname, 1, '',
+ id: "#{id}.view",
menu_state: MenuState::VIEW
)
end
# rubocop:disable Style/GuardClause
if @delegate_object[:menu_with_shell]
dml_menu_append_chrome_item(
formatted_choice_ostructs[:shell].dname, 1, '',
+ id: "#{id}.shell",
menu_state: MenuState::SHELL
)
end
# rubocop:enable Style/GuardClause
@@ -3937,11 +4133,11 @@
**execute_navigate_back.merge(prior_block_was_link: true)
)
)
end
- def vux_parse_document
+ def vux_parse_document(id: '')
@run_state.batch_index += 1
@run_state.in_own_window = false
@run_state.source.block_name_from_cli, @dml_now_using_cli =
manage_cli_selection_state(
@@ -3958,14 +4154,14 @@
@dml_link_state.block_name
end
# update @delegate_object and @menu_base_options in auto_load
#
+ # @dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc, @dml_link_state =
@dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
- mdoc_menu_and_blocks_from_nested_files(@dml_link_state)
+ mdoc_menu_and_blocks_from_nested_files(@dml_link_state, id: id)
dump_delobj(@dml_blocks_in_file, @dml_menu_blocks, @dml_link_state)
- # !!b 'loop', @run_state.source.block_name_from_cli, @cli_block_name
end
def vux_publish_block_name_for_external_automation(block_name)
publish_for_external_automation(
message: format(
@@ -3993,11 +4189,10 @@
)
end
# return :break to break from loop
def vux_user_selected_block_name
- # !!b
if @dml_link_state.block_name.present?
# @prior_block_was_link = true
@dml_block_state.block = blocks_find_by_block_name(
@dml_blocks_in_file,
@dml_link_state.block_name
@@ -4024,64 +4219,57 @@
rescue Interrupt
# user interrupts process
end
def wait_for_user_selected_block(all_blocks, menu_blocks, default)
- # !!b
block_state = wait_for_user_selection(all_blocks, menu_blocks, default)
handle_back_or_continue(block_state)
block_state
end
def wait_for_user_selection(_all_blocks, menu_blocks, default)
- # !!b
if @delegate_object[:clear_screen_for_select_block]
printf("\e[1;1H\e[2J")
end
- # !!b
prompt_title = string_send_color(
@delegate_object[:prompt_select_block].to_s,
:prompt_color_after_script_execution
)
- # !!b
- menu_items = prepare_blocks_menu(menu_blocks)
+ menu_items = blocks_as_menu_items(menu_blocks)
if menu_items.empty?
return SelectedBlockMenuState.new(nil, OpenStruct.new,
MenuState::EXIT)
end
- # !!b
# default value may not match if color is different from
# originating menu (opts changed while processing)
selection_opts = if default && menu_blocks.map(&:dname).include?(default)
@delegate_object.merge(default: default)
else
@delegate_object
end
- # !!b
selection_opts.merge!(
{ cycle: @delegate_object[:select_page_cycle],
per_page: @delegate_object[:select_page_height] }
)
selected_option = select_option_with_metadata(prompt_title, menu_items,
selection_opts)
- # !!b
determine_block_state(selected_option)
end
# Handles the core logic for generating the command
# file's metadata and content.
- def write_command_file(required_lines:, selected:, shell: nil)
+ def write_command_file(required_lines:, blockname:, shell: nil)
return unless @delegate_object[:save_executed_script]
time_now = Time.now.utc
@run_state.saved_script_filename =
SavedAsset.new(
- blockname: selected.pub_name,
+ blockname: blockname,
exts: '.sh',
filename: @delegate_object[:filename],
prefix: @delegate_object[:saved_script_filename_prefix],
saved_asset_format:
shell_escape_asset_format(
@@ -4271,10 +4459,11 @@
# Expect that method opts_command_execute is
# called with argument args having value pigeon
c.expects(:command_execute).with(
'',
args: pigeon,
+ erls: {},
shell: ShellType::BASH
)
# Call method opts_execute_required_lines
c.execute_required_lines(shell: ShellType::BASH)
@@ -4485,11 +4674,11 @@
.returns({ code: ['code line'] })
result = @hd.execute_block_type_port_code_lines(
mdoc: @mdoc, selected: @selected, block_source: {}
)
- assert_equal ['code line', 'key="value"'], result
+ assert_equal ['code line', 'key=value'], result
end
end
class TestHashDelegatorCommandOrUserSelectedBlock < Minitest::Test
def setup
@@ -4812,10 +5001,10 @@
# )
def test_call
@hd.expects(:history_files).with(nil, filename: '*', path: nil).once
@hd.execute_block_type_history_ux(filename: '*', link_state: LinkState.new,
- selected: FCB.new(body: []))
+ selected: FCB.new(body: []))
end
end
class TestHashDelegatorHandleBlockState < Minitest::Test
def setup