lib/markdown_exec.rb in markdown_exec-2.3.0 vs lib/markdown_exec.rb in markdown_exec-2.4.0
- old
+ new
@@ -113,16 +113,18 @@
end
end
# A class that generates a histogram bar in terminal using xterm-256 color codes.
class Histogram
- # Generates and prints a histogram bar for a given value within a specified range and width, with an option for inverse display.
+ # Generates and prints a histogram bar for a given value within a
+ # specified range and width, with an option for inverse display.
# @param integer_value [Integer] the value to represent in the histogram
# @param min [Integer] the minimum value of the range
# @param max [Integer] the maximum value of the range
# @param width [Integer] the total width of the histogram in characters
- # @param inverse [Boolean] whether the histogram is displayed in inverse order (right to left)
+ # @param inverse [Boolean] whether the histogram is
+ # displayed in inverse order (right to left)
def self.display(integer_value, min, max, width, inverse: false)
return if max <= min # Ensure the range is valid
# Normalize the value within the range 0 to 1
normalized_value = [
@@ -133,11 +135,11 @@
# Calculate how many characters should be filled
filled_length = (normalized_value * width).round
# # Generate the histogram bar using xterm-256 colors (color code 42 is green)
# filled_bar = "\e[48;5;42m" + ' ' * filled_length + "\e[0m"
- filled_bar = ('¤' * filled_length).fg_rgbh_AF_AF_00
+ filled_bar = AnsiString.new('¤' * filled_length).fg_rgbh_AF_AF_00
empty_bar = ' ' * (width - filled_length)
# Determine the order of filled and empty parts based on the inverse flag
inverse ? (empty_bar + filled_bar) : (filled_bar + empty_bar)
end
@@ -148,80 +150,96 @@
@chrome_color = :cyan
@o_color = :red
end
def build_menu(file_names, directory_names, found_in_block_names,
- files_in_directories, vbn)
+ file_name_choices, choices_from_block_names)
choices = []
# Adding section title and data for file names
- choices << { disabled: '',
- name: "in #{file_names[:section_title]}".send(@chrome_color) }
+ choices << {
+ disabled: '',
+ name: AnsiString.new("in #{file_names[:section_title]}")
+ .send(@chrome_color)
+ }
choices += file_names[:data].map { |str| FileInMenu.for_menu(str) }
# Conditionally add directory names if data is present
- unless directory_names[:data].count.zero?
- choices << { disabled: '',
- name: "in #{directory_names[:section_title]}".send(@chrome_color) }
- choices += files_in_directories
+ if directory_names[:data].any?
+ choices << {
+ disabled: '',
+ name: AnsiString.new("in #{directory_names[:section_title]}")
+ .send(@chrome_color)
+ }
+ choices += file_name_choices
end
# Adding found in block names
- choices << { disabled: '',
- name: "in #{found_in_block_names[:section_title]}".send(@chrome_color) }
+ choices << {
+ disabled: '',
+ name: AnsiString.new("in #{found_in_block_names[:section_title]}")
+ .send(@chrome_color)
+ }
+ choices += choices_from_block_names
- choices += vbn
-
choices
end
end
class SearchResultsReport < DirectorySearcher
def directory_names(search_options, highlight_value)
matched_directories = find_directory_names
{
section_title: 'directory names',
data: matched_directories,
- formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(
- matched_directories, highlight: [highlight_value]
- ) }]
+ formatted_text: [{ content:
+ AnsiFormatter.new(search_options).format_and_highlight_array(
+ matched_directories, highlight: [highlight_value]
+ ) }]
}
end
def found_in_block_names(search_options, highlight_value,
formspec: '=%<index>4.d: %<line>s')
- matched_contents = (find_file_contents do |line|
- read_block_name(line, search_options[:fenced_start_and_end_regex],
- search_options[:block_name_match], search_options[:block_name_nick_match])
- end).map.with_index do |(file, contents), index|
+ matched_contents = (
+ find_file_contents do |line|
+ read_block_name(line,
+ search_options[:fenced_start_and_end_regex],
+ search_options[:block_name_match],
+ search_options[:block_name_nick_match])
+ end).map.with_index do |(file, contents), index|
# [file, contents.map { |detail| format(formspec, detail.index, detail.line) }, index]
[file, contents.map do |detail|
format(formspec, { index: detail.index, line: detail.line })
end, index]
end
{
section_title: 'block names',
data: matched_contents.map(&:first),
- formatted_text: matched_contents.map do |(file, details, index)|
- { header: format('- %3.d: %s', index + 1, file),
- content: AnsiFormatter.new(search_options).format_and_highlight_array(
- details,
- highlight: [highlight_value]
- ) }
- end,
+ formatted_text:
+ matched_contents.map do |(file, details, index)|
+ { header: format('- %3.d: %s', index + 1, file),
+ content: AnsiFormatter.new(search_options).format_and_highlight_array(
+ details,
+ highlight: [highlight_value]
+ ) }
+ end,
matched_contents: matched_contents
}
end
def file_names(search_options, highlight_value)
matched_files = find_file_names
{
section_title: 'file names',
data: matched_files,
- formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(
- matched_files, highlight: [highlight_value]
- ).join("\n") }]
+ formatted_text:
+ [{ content:
+ AnsiFormatter.new(search_options).format_and_highlight_array(
+ matched_files,
+ highlight: [highlight_value]
+ ).join("\n") }]
}
end
def read_block_name(line, fenced_start_and_end_regex, block_name_match,
block_name_nick_match)
@@ -266,24 +284,10 @@
"CachedNestedFileReader.#{name} -- #{$!}",
opts
)
end
- # :reek:UtilityFunction
- def list_recent_output(saved_stdout_folder, saved_stdout_glob,
- list_count)
- SavedFilesMatcher.most_recent_list(saved_stdout_folder,
- saved_stdout_glob, list_count)
- end
-
- # :reek:UtilityFunction
- def list_recent_scripts(saved_script_folder, saved_script_glob,
- list_count)
- SavedFilesMatcher.most_recent_list(saved_script_folder,
- saved_script_glob, list_count)
- end
-
def warn_format(name, message, opts = {})
Exceptions.warn_format(
"CachedNestedFileReader.#{name} -- #{message}",
opts
)
@@ -327,26 +331,56 @@
bash: true, # bash block parsing in get_block_summary()
saved_script_filename: nil # calculated
}
end
+ def choices_from_block_names(value, found_in_block_names)
+ found_in_block_names[:matched_contents].map do |matched_contents|
+ filename, details, = matched_contents
+ nexo = AnsiFormatter.new(@options).format_and_highlight_array(
+ details,
+ highlight: [value]
+ )
+ [FileInMenu.for_menu(filename)] +
+ nexo.map do |str|
+ { disabled: '', name: (' ' * 20) + str }
+ end
+ end.flatten
+ end
+
+ def choices_from_file_names(directory_names)
+ directory_names[:data].map do |dn|
+ find_files('*', [dn], exclude_dirs: true)
+ end.flatten(1).map { |str| FileInMenu.for_menu(str) }
+ end
+
public
## Determines the correct filename to use for searching files
#
- def determine_filename(specified_filename: nil, specified_folder: nil, default_filename: nil,
- default_folder: nil, filetree: nil)
- if specified_filename&.present?
- return specified_filename if specified_filename.start_with?('/')
-
- File.join(specified_folder || default_folder, specified_filename)
- elsif specified_folder&.present?
- File.join(specified_folder,
- filetree ? @options[:md_filename_match] : @options[:md_filename_glob])
- else
- File.join(default_folder, default_filename)
- end
+ def determine_filename(
+ specified_filename: nil, specified_folder: nil,
+ default_filename: nil, default_folder: nil, filetree: nil
+ )
+ File.join(
+ *(if specified_filename&.present?
+ if specified_filename.start_with?('/')
+ [specified_filename]
+ else
+ [specified_folder || default_folder, specified_filename]
+ end
+ elsif specified_folder&.present?
+ [specified_folder,
+ if filetree
+ @options[:md_filename_match]
+ else
+ @options[:md_filename_glob]
+ end]
+ else
+ [default_folder, default_filename]
+ end)
+ )
end
private
# def error_handler(name = '', event = nil, backtrace = nil)
@@ -354,86 +388,61 @@
# warn($@.take(4).map.with_index { |line, ind| " * #{ind}: #{line}" })
# binding.pry if $tap_enable
# raise ArgumentError, error
# end
- # Reports and executes block logic
- def execute_block_logic(files)
- @options[:filename] = select_document_if_multiple(files)
- @options.document_inpseq
- rescue StandardError
- error_handler('execute_block_logic')
- # rubocop:disable Style/RescueStandardError
- rescue
- pp $!, $@
- exit 1
- # rubocop:enable Style/RescueStandardError
- end
-
## Executes the block specified in the options
#
def execute_block_with_error_handling
finalize_cli_argument_processing
- execute_code_block_based_on_options(@options)
+ execute_code_block_based_on_options(@options, @options.run_state)
rescue FileMissingError
warn "File missing: #{$!}"
- rescue StandardError
- error_handler('execute_block_with_error_handling')
end
# Main method to execute a block based on options and block_name
- def execute_code_block_based_on_options(options)
+ def execute_code_block_based_on_options(options, run_state)
options = calculated_options.merge(options)
update_options(options, over: false)
+ # recognize commands with an opt_name, no procname
+ return if execute_simple_commands(options)
- simple_commands = {
- doc_glob: -> { @fout.fout options[:md_filename_glob] },
- # list_blocks: -> { list_blocks },
- list_default_env: -> { @fout.fout_list list_default_env },
- list_default_yaml: -> { @fout.fout_list list_default_yaml },
- list_docs: -> { @fout.fout_list files },
- list_recent_output: -> {
- @fout.fout_list list_recent_output(
- @options[:saved_stdout_folder],
- @options[:saved_stdout_glob], @options[:list_count]
- )
- },
- list_recent_scripts: -> {
- @fout.fout_list list_recent_scripts(
- options[:saved_script_folder],
- options[:saved_script_glob], options[:list_count]
- )
- },
- pwd: -> { @fout.fout File.expand_path('..', __dir__) },
- run_last_script: -> { run_last_script },
- tab_completions: -> { @fout.fout tab_completions },
- menu_export: -> { @fout.fout menu_export }
- }
-
- return if execute_simple_commands(simple_commands)
-
- files = opts_prepare_file_list(options)
- execute_block_logic(files)
+ mde_vux_main_loop(opts_prepare_file_list(options))
return unless @options[:output_saved_script_filename]
- @fout.fout "script_block_name: #{@options.run_state.script_block_name}"
- @fout.fout "s_save_filespec: #{@options.run_state.saved_filespec}"
- rescue StandardError
- error_handler('execute_code_block_based_on_options')
+ @fout.fout "script_block_name: #{run_state.script_block_name}"
+ @fout.fout "s_save_filespec: #{run_state.saved_filespec}"
end
# Executes command based on the provided option keys
- def execute_simple_commands(simple_commands)
- simple_commands.each_key do |key|
- if @options[key]
- simple_commands[key].call
+ def execute_simple_commands(options)
+ simple_commands(options).each do |key, proc|
+ if @options[key].is_a?(TrueClass) || @options[key].present?
+ proc.call
return true
end
end
false
end
+ # Extracts all lines matching the given regular expression from a file.
+ #
+ # @param file_path [String] The path to the file to be searched.
+ # @param pattern [Regexp] The regular expression pattern to match.
+ # @return [Array<String>] An array of lines from the file that match the pattern.
+ def extract_lines_matching(file_path, pattern)
+ matching_lines = []
+
+ File.open(file_path, 'r') do |file|
+ file.each_line do |line|
+ matching_lines << line if line =~ pattern
+ end
+ end
+
+ matching_lines
+ end
+
## post-parse options configuration
#
def finalize_cli_argument_processing(rest = @rest)
## position 0: file or folder (optional)
#
@@ -447,13 +456,10 @@
else
raise FileMissingError, pos, caller
end
end
- ## position 1: block name (optional)
- #
- @options[:block_name] = nil
@options[:input_cli_rest] = @rest
rescue FileMissingError
warn_format('finalize_cli_argument_processing',
"File missing -- #{$!}", { abort: true })
exit 1
@@ -472,10 +478,35 @@
found_in_block_names = searcher.found_in_block_names(options, value,
formspec: '%<line>s')
directory_names = searcher.directory_names(options, value)
### search in file contents (block names, chrome, or text)
+ found_report(found_in_block_names, directory_names, file_names)
+
+ return { exit: true } unless execute_chosen_found
+
+ file_name_choices = choices_from_file_names(directory_names)
+
+ unless file_names[:data]&.count.positive? ||
+ file_name_choices&.count.positive? ||
+ found_in_block_names[:data]&.count.positive?
+ return :exit
+ end
+
+ ## pick a document to open
+ #
+ found_files_build_menu_user_select(
+ file_names, directory_names, found_in_block_names,
+ file_name_choices,
+ choices_from_block_names(value, found_in_block_names)
+ )
+ { exit: false }
+ end
+
+ def found_report(found_in_block_names,
+ directory_names,
+ file_names)
[found_in_block_names,
directory_names,
file_names].each do |data|
next if data[:data].count.zero?
next unless data[:formatted_text]
@@ -484,74 +515,159 @@
data[:formatted_text].each do |fi|
@fout.fout fi[:header] if fi[:header]
@fout.fout fi[:content] if fi[:content]
end
end
- return { exit: true } unless execute_chosen_found
+ end
- ## pick a document to open
- #
- files_in_directories = directory_names[:data].map do |dn|
- find_files('*', [dn], exclude_dirs: true)
- end.flatten(1).map { |str| FileInMenu.for_menu(str) }
+ def found_files_build_menu_user_select(
+ file_names, directory_names, found_in_block_names,
+ file_name_choices, choices_from_block_names
+ )
+ choices = MenuBuilder.new.build_menu(
+ file_names, directory_names, found_in_block_names,
+ file_name_choices, choices_from_block_names
+ )
- unless file_names[:data]&.count.positive? || files_in_directories&.count.positive? || found_in_block_names[:data]&.count.positive?
- return { exit: true }
- end
-
- vbn = found_in_block_names[:matched_contents].map do |matched_contents|
- filename, details, = matched_contents
- nexo = AnsiFormatter.new(@options).format_and_highlight_array(
- details,
- highlight: [value]
- )
- [FileInMenu.for_menu(filename)] +
- nexo.map do |str|
- { disabled: '', name: (' ' * 20) + str }
- end
- end.flatten
-
- choices = MenuBuilder.new.build_menu(file_names, directory_names, found_in_block_names,
- files_in_directories, vbn)
-
@options[:filename] = FileInMenu.from_menu(
select_document_if_multiple(
choices,
- prompt: options[:prompt_select_md].to_s + ' ¤ Age in months'.fg_rgbh_AF_AF_00
+ prompt: options[:prompt_select_md].to_s +
+ AnsiString.new(' ¤ Age in months').fg_rgbh_AF_AF_00
)
)
- { exit: false }
end
+ def fout_list(list)
+ if @options[:list_output_format] == :yaml
+ @fout.fout list.to_yaml.sub(/^---\n/, '')
+ elsif @options[:list_output_format] == :json
+ @fout.fout list.to_json
+ else # :text
+ list.each do |item|
+ @fout.fout item
+ end
+ end
+ end
+
+ def history(probe: true)
+ if probe && @options[:probe].present?
+ probe_regexp = Regexp.new(@options[:probe], Regexp::IGNORECASE)
+ end
+
+ if @options[:sift].present?
+ sift_regexp = Regexp.new(@options[:sift], Regexp::IGNORECASE)
+ end
+
+ files_table_rows = @options.read_saved_assets_for_history_table
+
+ if sift_regexp
+ # Filter history to file names matching a pattern
+ files_table_rows.select! { |item| sift_regexp.match(item[:file]) }
+ end
+
+ if probe_regexp
+ # Filter history to files with lines matching a pattern
+ files_table_rows.each do |item|
+ item[:probe] = extract_lines_matching(item[:file], probe_regexp)
+ end
+ files_table_rows.select! { |item| item[:probe].count.positive? }
+ end
+
+ if files_table_rows.count.zero?
+ warn 'Nothing revealed.'
+ elsif probe || probe_regexp
+ if @options[:dig]
+ # Present menu of history
+ @options.register_console_attributes(@options)
+ @options.execute_history_select(
+ files_table_rows,
+ exit_prompt: @options[:prompt_exit],
+ pause_refresh: true,
+ stream: $stderr
+ )
+ elsif @options[:mine]
+ # List lines matched by probe
+ files_table_rows.each do |item|
+ @fout.fout(item[:file])
+ fout_list(item[:probe])
+ end
+ else
+ fout_list(files_table_rows.map(&:file))
+ end
+ else
+ @options.register_console_attributes(@options)
+ @options.execute_history_select(
+ files_table_rows,
+ exit_prompt: @options[:prompt_exit],
+ pause_refresh: true,
+ stream: $stderr
+ )
+ end
+ end
+
## Sets up the options and returns the parsed arguments
#
- def initialize_and_parse_cli_options
- # @options = base_options
+ def initialize_parse_execute_cli
@options = HashDelegator.new(base_options)
-
+ @options.p_all_arguments = ARGV.dup # Duplicate ARGV to track original order
+ ### should not include after '--'
read_configuration_file!(@options,
".#{MarkdownExec::APP_NAME.downcase}.yml")
+ options_parsed = []
@option_parser = OptionParser.new do |opts|
executable_name = File.basename($PROGRAM_NAME)
opts.banner = [
"#{MarkdownExec::APP_NAME}" \
" - #{MarkdownExec::APP_DESC} (#{MarkdownExec::VERSION})",
- "Usage: #{executable_name} [(directory | file [block_name] | search_keyword)] [options]"
+ "Usage: #{executable_name}" \
+ ' [(directory | file [block_name] | search_keyword)] [options]'
].join("\n")
menu_iter do |item|
- opts_menu_option_append opts, @options, item
+ opts_menu_option_append(opts, @options, item, options_parsed)
end
end
@option_parser.load
@option_parser.environment
- @rest = rest = @option_parser.parse!(arguments_for_mde)
- @options.pass_args = ARGV[rest.count + 1..]
+ @options.p_params = {}
+ @rest = @option_parser.parse!(arguments_for_mde, into: @options.p_params)
+ @options.p_options_parsed = options_parsed
+ @options.p_rest = @rest.dup
+
+ # Arguments for script follow ARGV, excluding arguments reserved for MDE, and separator.
+ # Parsed options have already been removed from ARGV.
+ @options.pass_args = ARGV[@rest.count + 1..]
+
@options.merge(@options.run_state.to_h)
+ end
- rest
+ def iter_source_blocks(source, &block)
+ case source
+ when 1
+ HashDelegator.new(@options).blocks_from_nested_files.each(&block)
+ when 2
+ blocks_in_file, menu_blocks, mdoc =
+ HashDelegator.new(@options)
+ .mdoc_menu_and_blocks_from_nested_files(LinkState.new)
+ blocks_in_file.each(&block)
+ when 3
+ blocks_in_file, menu_blocks, mdoc =
+ HashDelegator.new(@options)
+ .mdoc_menu_and_blocks_from_nested_files(LinkState.new)
+ menu_blocks.each(&block)
+ else
+ @options.iter_blocks_from_nested_files do |btype, fcb|
+ case btype
+ when :blocks
+ yield fcb
+ when :filter
+ %i[blocks]
+ end
+ end
+ end
end
##
# Returns a lambda expression based on the given procname.
# @param procname [String] The name of the process to generate a lambda for.
@@ -563,12 +679,13 @@
->(value) { tap_config value: value }
when 'exit'
->(_) { exit }
when 'find', 'open'
->(value) {
- exit if find_value(value, execute_chosen_found: procname == 'open').fetch(:exit,
- false)
+ exit if find_value(
+ value, execute_chosen_found: procname == 'open'
+ ).fetch(:exit, false)
}
when 'help'
->(_) {
@fout.fout menu_help
exit
@@ -593,22 +710,34 @@
}
when 'val_as_int'
lambda(&:to_i)
when 'val_as_str'
lambda(&:to_s)
+ when 'val_as_sym'
+ lambda(&:to_sym)
when 'version'
lambda { |_|
@fout.fout MarkdownExec::VERSION
exit
}
else
procname
end
end
- # def list_blocks; end
+ def list_blocks
+ message = @options[:list_blocks_message]
+ block_eval = @options[:list_blocks_eval]
+ list = []
+ iter_source_blocks(@options[:list_blocks_type]) do |block|
+ list << (block_eval.present? ? eval(block_eval) : block.send(message))
+ end
+
+ fout_list(list)
+ end
+
def list_default_env
menu_iter do |item|
next unless item[:env_var].present?
[
@@ -644,10 +773,39 @@
def list_markdown_files_in_path
Dir.glob(File.join(@options[:path],
@options[:md_filename_glob]))
end
+ # :reek:UtilityFunction
+ def list_recent_output(saved_stdout_folder, saved_stdout_glob,
+ list_count)
+ SavedFilesMatcher.most_recent_list(saved_stdout_folder,
+ saved_stdout_glob, list_count)
+ end
+
+ # :reek:UtilityFunction
+ def list_recent_scripts(saved_script_folder, saved_script_glob,
+ list_count)
+ SavedFilesMatcher.most_recent_list(saved_script_folder,
+ saved_script_glob, list_count)
+ end
+
+ # Reports and executes block logic
+ def mde_vux_main_loop(files)
+ @options[:filename] = select_document_if_multiple(files)
+ @options.vux_main_loop do |type, data|
+ case type
+ when :command_names
+ simple_commands(data).keys
+ when :call_proc
+ simple_commands(data[0])[data[1]].call
+ else
+ raise
+ end
+ end
+ end
+
private
##
# Generates a menu suitable for OptionParser from the menu items defined in YAML format.
# @return [Array<Hash>] The array of option hashes for OptionParser.
@@ -673,41 +831,92 @@
item.delete(:procname)
item
end.to_yaml
end
- def opts_menu_option_append(opts, options, item)
+ def opts_menu_option_append(opts, options, item, options_parsed)
return unless item[:long_name].present? || item[:short_name].present?
- opts.on(*[
+ optname = "-#{item[:short_name]}"
+ switches = [
+ # - argument style = :NONE, :REQUIRED, :OPTIONAL
+ case item[:procname]&.to_s
+ when nil
+ :NONE
+ when *%w[val_as_bool val_as_int val_as_str val_as_sym]
+ :REQUIRED
+ else # debug, exit, find, help, how, open, path, show_config, version
+ nil
+ end,
+
# - long name
if item[:long_name].present?
- "--#{item[:long_name]}#{item[:arg_name].present? ? " #{item[:arg_name]}" : ''}"
+ optname = "--#{item[:long_name]}"
+ "--#{item[:long_name]}" \
+ "#{item[:arg_name].present? ? " #{item[:arg_name]}" : ''}"
end,
# - short name
- item[:short_name].present? ? "-#{item[:short_name]}" : nil,
+ if item[:short_name].present?
+ "-#{item[:short_name]}" \
+ "#{item[:arg_name].present? ? " #{item[:arg_name]}" : ''}"
+ end,
# - description and default
- [item[:description],
- ("[#{value_for_cli item[:default]}]" if item[:default].present?)].compact.join(' '),
+ [
+ item[:description],
+ ("[#{value_for_cli item[:default]}]" if item[:default].present?)
+ ].compact.join(' '),
+ # - type coercion
+ case item[:procname]&.to_s
+ when nil
+ nil
+ when 'val_as_bool'
+ item[:default] ? FalseClass : TrueClass # use sets to desired value
+ when 'val_as_int'
+ Integer
+ else # str, sym
+ # String
+ nil
+ end,
+ # Date – Anything accepted by Date.parse
+ # DateTime – Anything accepted by DateTime.parse
+ # Time – Anything accepted by Time.httpdate or Time.parse
+ # URI – Anything accepted by URI.parse
+ # Shellwords – Anything accepted by Shellwords.shellwords
+ # String – Any non-empty string
+ # Integer – Any integer. Will convert octal. (e.g. 124, -3, 040)
+ # Float – Any float. (e.g. 10, 3.14, -100E+13)
+ # Numeric – Any integer, float, or rational (1, 3.4, 1/3)
+ # DecimalInteger – Like Integer, but no octal format.
+ # OctalInteger – Like Integer, but no decimal format.
+ # DecimalNumeric – Decimal integer or float.
+ # TrueClass – Accepts ‘+, yes, true, -, no, false’ and defaults as true
+ # FalseClass – Same as TrueClass, but defaults to false
+ # Array – Strings separated by ‘,’ (e.g. 1,2,3)
+ # Regexp – Regular expressions. Also includes options.
+
# apply proccode, if present, to value
# save value to options hash if option is named
#
lambda { |value|
+ name = item[:long_name]&.present? ? '--' + item[:long_name].to_s : '-' + item[:short_name].to_s
+ options_parsed << item.merge(name: name, value: value)
(item[:proccode] ? item[:proccode].call(value) : value).tap do |converted|
options[item[:opt_name]] = converted if item[:opt_name]
end
}
- ].compact)
+ ].compact
+ opts.on(*switches)
end
def opts_prepare_file_list(options)
list_files_specified(
determine_filename(
- specified_filename: options[:filename]&.present? ? options[:filename] : nil,
+ specified_filename:
+ options[:filename]&.present? ? options[:filename] : nil,
specified_folder: options[:path],
default_filename: 'README.md',
default_folder: '.'
)
)
@@ -722,16 +931,25 @@
end
public
def run
- initialize_and_parse_cli_options
+ initialize_parse_execute_cli
execute_block_with_error_handling
+ rescue BlockMissing
+ warn 'Block missing'
+ exit 1
+ rescue AppInterrupt, TTY::Reader::InputInterrupt, BlockMissing
+ warn 'Exiting...' if $DEBUG
+ exit 1
rescue StandardError
error_handler('run')
- ensure
- yield if block_given?
+ # rubocop:disable Style/RescueStandardError
+ rescue
+ warn 'Exiting...' if $DEBUG
+ exit 1
+ # rubocop:enable Style/RescueStandardError
end
private
def run_last_script
@@ -739,11 +957,11 @@
@options[:saved_script_glob])
return unless filename
saved_name_split filename
@options[:save_executed_script] = false
- @options.document_inpseq
+ @options.vux_main_loop
rescue StandardError
error_handler('run_last_script')
end
def saved_name_split(name)
@@ -761,21 +979,51 @@
return unless count >= 2
opts = options.dup
select_option_or_exit(
- HashDelegator.new(@options).string_send_color(prompt,
- :prompt_color_after_script_execution),
+ HashDelegator.new(@options)
+ .string_send_color(
+ prompt,
+ :prompt_color_after_script_execution
+ ),
files,
opts.merge(per_page: opts[:select_page_height])
)
end
# Presents a TTY prompt to select an option or exit, returns selected option or nil
def select_option_or_exit(prompt_text, strings, opts = {})
@options.select_option_with_metadata(
prompt_text, strings, opts
)&.fetch(:selected)
+ end
+
+ def simple_commands(options)
+ {
+ doc_glob: -> { @fout.fout options[:md_filename_glob] },
+ history: -> { history },
+ list_blocks: -> { list_blocks },
+ list_default_env: -> { @fout.fout_list list_default_env },
+ list_default_yaml: -> { @fout.fout_list list_default_yaml },
+ list_docs: -> { @fout.fout_list opts_prepare_file_list(options) },
+ list_recent_output: -> {
+ @fout.fout_list list_recent_output(
+ @options[:saved_stdout_folder],
+ @options[:saved_stdout_glob], @options[:list_count]
+ )
+ },
+ list_recent_scripts: -> {
+ @fout.fout_list list_recent_scripts(
+ options[:saved_script_folder],
+ options[:saved_script_glob], options[:list_count]
+ )
+ },
+ menu_export: -> { @fout.fout menu_export },
+ pwd: -> { @fout.fout File.expand_path('..', __dir__) },
+ run_last_script: -> { run_last_script },
+ tab_completions: -> { @fout.fout tab_completions }
+ }
end
public
def tab_completions(data = menu_for_optparse)