markdown_exec 2.5.0 → 2.6.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/Gemfile.lock +2 -2
  4. data/Rakefile +3 -3
  5. data/bats/block-types.bats +13 -7
  6. data/bats/import.bats +6 -0
  7. data/bats/markup.bats +6 -15
  8. data/bats/options-collapse.bats +26 -0
  9. data/bats/options.bats +1 -1
  10. data/bats/table.bats +8 -0
  11. data/bats/test_helper.bash +74 -49
  12. data/bats/variable-expansion.bats +46 -0
  13. data/bin/tab_completion.sh +1 -1
  14. data/docs/dev/bats-document-configuration.md +8 -1
  15. data/docs/dev/block-type-bash.md +1 -1
  16. data/docs/dev/block-type-opts.md +1 -5
  17. data/docs/dev/block-type-vars.md +4 -0
  18. data/docs/dev/import-missing.md +2 -0
  19. data/docs/dev/menu-cli.md +1 -1
  20. data/docs/dev/options-collapse.md +47 -0
  21. data/docs/dev/requiring-blocks.md +3 -0
  22. data/docs/dev/specs.md +2 -1
  23. data/docs/dev/table-crash.md +39 -0
  24. data/docs/dev/table-indent.md +26 -0
  25. data/docs/dev/text-decoration.md +2 -5
  26. data/docs/dev/variable-expansion.md +2 -4
  27. data/examples/bash-blocks.md +1 -1
  28. data/examples/block-names.md +1 -1
  29. data/examples/block-types.md +1 -1
  30. data/examples/data-files.md +1 -1
  31. data/examples/document_options.md +2 -2
  32. data/examples/indent.md +1 -1
  33. data/examples/interrupt.md +1 -1
  34. data/examples/link-blocks-vars.md +1 -1
  35. data/examples/linked.md +1 -1
  36. data/examples/linked1.md +1 -1
  37. data/examples/nickname.md +1 -1
  38. data/examples/opts-blocks-require.md +1 -1
  39. data/examples/opts-blocks.md +1 -1
  40. data/examples/opts_output_execution.md +1 -1
  41. data/examples/pass-through-arguments.md +1 -1
  42. data/examples/pause-after-execution.md +1 -1
  43. data/examples/port-blocks.md +1 -1
  44. data/examples/save.md +1 -1
  45. data/examples/text-markup.md +1 -1
  46. data/examples/variable-expansion.md +6 -2
  47. data/examples/vars-blocks.md +1 -1
  48. data/examples/wrap.md +1 -1
  49. data/lib/block_types.rb +4 -0
  50. data/lib/cached_nested_file_reader.rb +7 -4
  51. data/lib/collapser.rb +302 -0
  52. data/lib/constants.rb +10 -0
  53. data/lib/evaluate_shell_expressions.rb +0 -3
  54. data/lib/fcb.rb +13 -17
  55. data/lib/format_table.rb +11 -7
  56. data/lib/hash_delegator.rb +461 -272
  57. data/lib/hierarchy_string.rb +5 -1
  58. data/lib/markdown_exec/version.rb +1 -1
  59. data/lib/markdown_exec.rb +16 -32
  60. data/lib/mdoc.rb +100 -35
  61. data/lib/menu.src.yml +124 -17
  62. data/lib/menu.yml +102 -16
  63. data/lib/ww.rb +75 -22
  64. metadata +12 -9
  65. data/lib/append_to_bash_history.rb +0 -303
  66. data/lib/ce_get_cost_and_usage.rb +0 -23
  67. data/lib/doh.rb +0 -190
  68. data/lib/layered_hash.rb +0 -143
  69. data/lib/poly.rb +0 -171
@@ -50,7 +50,7 @@ The hidden block "(taxonomy)" prints "taxonomy".
50
50
  echo "taxonomy"
51
51
  ```
52
52
 
53
- ```opts :(document_options)
53
+ ```opts :(document_opts)
54
54
  execute_in_own_window: false
55
55
  output_execution_report: false
56
56
  output_execution_summary: false
@@ -54,7 +54,7 @@ The hidden block "(success)" is required above. It prints "Success".
54
54
  echo "Success"
55
55
  ```
56
56
 
57
- ```opts :(document_options)
57
+ ```opts :(document_opts)
58
58
  execute_in_own_window: false
59
59
  output_execution_report: false
60
60
  output_execution_summary: false
@@ -16,7 +16,7 @@ Specified block types.
16
16
  a: 1
17
17
  ```
18
18
 
19
- ```opts :(document_options)
19
+ ```opts :(document_opts)
20
20
  bash_only: false
21
21
  execute_in_own_window: false
22
22
  output_execution_report: false
@@ -1,6 +1,6 @@
1
1
  # Demonstrate data blocks
2
2
 
3
- ```opts :(document_options)
3
+ ```opts :(document_opts)
4
4
  pause_after_script_execution: true
5
5
  ```
6
6
 
@@ -1,8 +1,8 @@
1
1
  This document demonstrates the automatic loading of options in a block with a reserved name.
2
2
 
3
- ```opts :(document_options)
3
+ ```opts :(document_opts)
4
4
  menu_divider_format: "=> %{line} == %{line} <="
5
5
  ```
6
6
 
7
- The divider below is named "Divider #1". Notice the "(document_options)" block sets the "menu_divider_format" option to duplicate the divider name when it is displayed.
7
+ The divider below is named "Divider #1". Notice the "(document_opts)" block sets the "menu_divider_format" option to duplicate the divider name when it is displayed.
8
8
  ::: Divider #1
data/examples/indent.md CHANGED
@@ -4,12 +4,12 @@ Indentation is either leading spaces or tabs.
4
4
  Tabs are interpreted as 4 spaces.
5
5
 
6
6
  ## Related MDE options
7
+ divider_match | Pattern for topics/dividers in block selection menu
7
8
  fenced_start_and_end_regex | Matches the start and end of a fenced code block
8
9
  fenced_start_extended_regex | Match the start of a fenced block
9
10
  heading1_match | MDE_HEADING1_MATCH
10
11
  heading2_match | MDE_HEADING2_MATCH
11
12
  heading3_match | MDE_HEADING3_MATCH
12
- menu_divider_match | Pattern for topics/dividers in block selection menu
13
13
  menu_note_match | Pattern for notes in block selection menu
14
14
  menu_task_match | Pattern for tasks
15
15
 
@@ -1,4 +1,4 @@
1
- ```opts :(document_options)
1
+ ```opts :(document_opts)
2
2
  pause_after_script_execution: false
3
3
  ```
4
4
  ```bash
@@ -1,5 +1,5 @@
1
1
  # Demonstrate link blocks set variables
2
- ```opts :(document_options)
2
+ ```opts :(document_opts)
3
3
  execute_in_own_window: false
4
4
  menu_with_inherited_lines: true
5
5
  output_execution_report: false
data/examples/linked.md CHANGED
@@ -1,6 +1,6 @@
1
1
  Demonstrate setting variable values interactively for use in generated scripts.
2
2
 
3
- ```opts :(document_options)
3
+ ```opts :(document_opts)
4
4
  menu_with_inherited_lines: true
5
5
  pause_after_script_execution: false
6
6
  user_must_approve: false
data/examples/linked1.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Demo document linking
2
2
 
3
- ```opts :(document_options)
3
+ ```opts :(document_opts)
4
4
  dump_inherited_lines: true
5
5
  pause_after_script_execution: false
6
6
  user_must_approve: false
data/examples/nickname.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Demo block nicknames
2
2
 
3
- ```opts :(document_options)
3
+ ```opts :(document_opts)
4
4
  pause_after_script_execution: true
5
5
  ```
6
6
 
@@ -1,5 +1,5 @@
1
1
  # Demonstrate requiring blocks
2
- ```opts :(document_options) +(custom) +[custom]
2
+ ```opts :(document_opts) +(custom) +[custom]
3
3
  menu_divider_color: red # color to indicate failure
4
4
  ```
5
5
  ```opts :(custom)
@@ -19,7 +19,7 @@ menu_note_match: "^\\+ +(?<line>.+?) *$"
19
19
  menu_task_color: fg_rgb_127_127_255
20
20
  ```
21
21
 
22
- ```opts :(document_options)
22
+ ```opts :(document_opts)
23
23
  menu_divider_color: green
24
24
  menu_link_color: fg_rgbh_88_cc_66
25
25
  menu_note_color: yellow
@@ -1,7 +1,7 @@
1
1
  # Demo options: output_execution_report, output_execution_summary
2
2
 
3
3
  ::: Options are initially True
4
- ```opts :(document_options) +[document_options]
4
+ ```opts :(document_opts) +[document_options]
5
5
  output_execution_report: true
6
6
  output_execution_summary: true
7
7
  pause_after_script_execution: true
@@ -1,4 +1,4 @@
1
- ```opts :(document_options)
1
+ ```opts :(document_opts)
2
2
  execute_in_own_window: false
3
3
  pause_after_script_execution: true
4
4
  save_executed_script: true
@@ -1,6 +1,6 @@
1
1
  # Demonstrate pause for user approval before returning to the menu
2
2
  Controlled by option `pause_after_script_execution`.
3
- ```opts :(document_options)
3
+ ```opts :(document_opts)
4
4
  pause_after_script_execution: true
5
5
  ```
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Demo variable porting
2
2
 
3
- ```opts :(document_options)
3
+ ```opts :(document_opts)
4
4
  dump_inherited_lines: true
5
5
  execute_in_own_window: false
6
6
  output_execution_report: false
data/examples/save.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Demonstrate custom file names
2
- ```opts :(document_options) +[custom]
2
+ ```opts :(document_opts) +[custom]
3
3
  pause_after_script_execution: true # for interactive demos
4
4
  save_executed_script: true # demonstrate saved scripts
5
5
  save_execution_output: true # demonstrate saved output
@@ -47,7 +47,7 @@ line_decor_post | Line-oriented text decoration (Post)
47
47
  line_decor_pre | Line-oriented text decoration (Pre)
48
48
  menu_note_match | Pattern for notes in block selection menu
49
49
 
50
- ```opts :(document_options)
50
+ ```opts :(document_opts)
51
51
  line_decor_post:
52
52
  - :color_method: blue
53
53
  :pattern: '!([^!]{0,64})!blue!'
@@ -68,7 +68,9 @@ glob: ${ALPHA}.sh
68
68
  ```
69
69
 
70
70
  / import bats-document-configuration.md
71
- ```opts :(document_options)
71
+ ```opts :(document_opts)
72
+ divider4_center: false
73
+
72
74
  dump_blocks_in_file: false # Dump BlocksInFile (stage 1)
73
75
  dump_delegate_object: false # Dump @delegate_object
74
76
  dump_inherited_lines: false # Dump inherited lines
@@ -88,7 +90,9 @@ script_execution_frame_color: plain
88
90
  script_execution_head:
89
91
  script_execution_tail:
90
92
 
93
+ table_center: false
94
+
91
95
  user_must_approve: false
92
96
 
93
97
  clear_screen_for_select_block: false
94
- ```
98
+ ```
@@ -1,4 +1,4 @@
1
- ```opts :(document_options)
1
+ ```opts :(document_opts)
2
2
  execute_in_own_window: false
3
3
  output_execution_report: false
4
4
  output_execution_summary: false
data/examples/wrap.md CHANGED
@@ -100,7 +100,7 @@ shell_code_label_format_above: "# -^-"
100
100
  shell_code_label_format_below: "# -v- +%{block_name} -o- %{document_filename} -o- %{time_now_date} -v-"
101
101
  ```
102
102
 
103
- ```opts :(document_options)
103
+ ```opts :(document_opts)
104
104
  execute_in_own_window: false
105
105
  output_execution_report: false
106
106
  output_execution_summary: false
data/lib/block_types.rb CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  class BlockType
4
4
  ALL = [
5
+ CHROME = 'chrome',
6
+ DIVIDER = 'divider',
5
7
  EDIT = 'edit',
8
+ HEADING = 'heading',
6
9
  HISTORY = 'history',
7
10
  LINK = 'link',
8
11
  LOAD = 'load',
@@ -10,6 +13,7 @@ class BlockType
10
13
  PORT = 'port',
11
14
  SAVE = 'save',
12
15
  SHELL = 'shell',
16
+ TEXT = 'text',
13
17
  VARS = 'vars',
14
18
  VIEW = 'view',
15
19
  YAML = 'yaml'
@@ -39,11 +39,13 @@ class CachedNestedFileReader
39
39
  )
40
40
  end
41
41
 
42
- def readlines(filename, depth = 0, context: '', import_paths: nil, indention: '', &block)
42
+ def readlines(filename, depth = 0, context: '', import_paths: nil,
43
+ indention: '', &block)
43
44
  if @file_cache.key?(filename)
44
45
  @file_cache[filename].each(&block) if block
45
46
  return @file_cache[filename]
46
47
  end
48
+ raise Errno::ENOENT, filename unless filename
47
49
 
48
50
  directory_path = File.dirname(filename)
49
51
  processed_lines = []
@@ -60,6 +62,8 @@ class CachedNestedFileReader
60
62
  File.join(directory_path, name_strip)
61
63
  end
62
64
 
65
+ raise Errno::ENOENT, name_strip unless included_file_path
66
+
63
67
  processed_lines += readlines(included_file_path, depth + 1,
64
68
  context: "#{filename}:#{ind + 1}",
65
69
  import_paths: import_paths,
@@ -73,9 +77,8 @@ class CachedNestedFileReader
73
77
  end
74
78
 
75
79
  @file_cache[filename] = processed_lines
76
- rescue Errno::ENOENT
77
- # Exceptions.error_handler('readlines', { abort: true })
78
- warn_format('readlines', "No such file -- #{filename} @@ #{context}",
80
+ rescue Errno::ENOENT => err_filename
81
+ warn_format('readlines', "#{err_filename} @@ #{context}",
79
82
  { abort: true })
80
83
  end
81
84
  end
data/lib/collapser.rb ADDED
@@ -0,0 +1,302 @@
1
+ require_relative 'constants'
2
+
3
+ class Collapser
4
+ attr_accessor :options, :state
5
+
6
+ def initialize(
7
+ collapsed_level: nil,
8
+ collapsible_types: COLLAPSIBLE_TYPES,
9
+ options: {},
10
+ state: Hash.new(nil)
11
+ )
12
+ @collapsed_level = collapsed_level
13
+ @collapsible_types = collapsible_types.dup
14
+ @options = options.dup
15
+ @state = state.dup # do not dup
16
+ end
17
+
18
+ def collapse_per_options?(fcb, options: @options)
19
+ criteria = options["#{fcb.type}#{fcb.level}_collapse".to_sym]
20
+ return false if criteria.nil?
21
+
22
+ criteria
23
+ end
24
+
25
+ def collapse_per_state?(fcb, default: false, state: @state)
26
+ return false if state.nil?
27
+
28
+ criteria = state[fcb.id]
29
+ return default if criteria.nil?
30
+
31
+ !!criteria
32
+ end
33
+
34
+ def collapse_per_token?(fcb)
35
+ fcb.token == COLLAPSIBLE_TOKEN_COLLAPSE
36
+ end
37
+
38
+ def collapsible_per_options?(fcb, options: @options)
39
+ criteria = options["#{fcb.type}#{fcb.level}_collapsible".to_sym]
40
+ return false if criteria.nil?
41
+
42
+ criteria
43
+ end
44
+
45
+ def collapsible_per_type?(fcb, collapsible_types: @collapsible_types)
46
+ @collapsible_types.nil? || @collapsible_types.include?(fcb.type)
47
+ end
48
+
49
+ def collapse?(fcb, initialize: false)
50
+ if initialize
51
+ (collapse_per_options?(fcb) || collapse_per_token?(fcb)) && !expand_per_token?(fcb)
52
+ else
53
+ collapse_per_state?(fcb, default: false)
54
+ end
55
+ end
56
+
57
+ def collapsible?(fcb)
58
+ collapsible_per_options?(fcb)
59
+ end
60
+
61
+ def expand_per_token?(fcb)
62
+ fcb.token == COLLAPSIBLE_TOKEN_EXPAND
63
+ end
64
+
65
+ def hide?(fcb,
66
+ collapsed_level: @collapsed_level,
67
+ collapsible_types: @collapsible_types,
68
+ initialize: true)
69
+
70
+ fcb.collapsible = collapsible?(fcb)
71
+ if collapsed_level.nil?
72
+ # is not collapsing
73
+ fcb.collapse = collapse?(fcb, initialize: initialize)
74
+ collapsed_level = fcb.level if fcb.collapse
75
+ fcb.hide = false
76
+
77
+ elsif fcb.level.nil?
78
+ fcb.hide = true
79
+
80
+ elsif fcb.level > collapsed_level
81
+ # Currently collapsed; evaluate for the current block
82
+ fcb.collapse = collapse?(fcb, initialize: initialize)
83
+ collapsed_level = fcb.level if fcb.collapse
84
+ fcb.hide = true # block is at a deeper level thus hidden
85
+
86
+ else
87
+ # Currently expanded; evaluate for the current block
88
+ if fcb.collapsible
89
+ fcb.collapse = collapse?(fcb, initialize: initialize)
90
+ collapsed_level = fcb.collapse ? fcb.level : nil
91
+ fcb.hide = false
92
+ elsif collapsible_per_type?(fcb)
93
+ fcb.collapsible = false
94
+ fcb.collapse = false
95
+ fcb.hide = false
96
+ else
97
+ fcb.hide = true
98
+ end
99
+ end
100
+ @state[fcb.id] = fcb.level if fcb.collapse
101
+
102
+ collapsed_level
103
+ end
104
+
105
+ # Reject rows that should be hidden based on the hierarchy
106
+ def reject(fcbs, initialize: true, &block)
107
+ analyze(fcbs, initialize: initialize, reject: true, &block)
108
+ end
109
+
110
+ # Reject rows that should be hidden based on the hierarchy
111
+ def analyze(fcbs, initialize: true, reject: false, &block)
112
+ fcbs.reject do |fcb|
113
+ @collapsed_level = hide?(fcb, initialize: initialize)
114
+ block.call fcb, @collapsed_level if block
115
+
116
+ reject && fcb.hide
117
+ end
118
+ end
119
+ end
120
+
121
+ return if $PROGRAM_NAME != __FILE__
122
+
123
+ require 'minitest/autorun'
124
+ require_relative 'ww'
125
+
126
+ # Updated FCB struct with an id for testing
127
+ FCB = Struct.new(:id, :type, :level, :token, :collapse, :collapsible, :hide)
128
+ OPTIONS = {
129
+ divider4_collapse: false,
130
+ divider4_collapsible: true,
131
+ heading1_collapse: false,
132
+ heading1_collapsible: false,
133
+ heading2_collapse: true,
134
+ heading2_collapsible: true,
135
+ heading3_collapse: false,
136
+ heading3_collapsible: true
137
+ }.freeze
138
+ STATE = {}.freeze
139
+
140
+ class CollapserTest < Minitest::Test
141
+ def setup
142
+ @collapser = Collapser.new(collapsible_types: COLLAPSIBLE_TYPES.dup,
143
+ options: OPTIONS.dup,
144
+ state: STATE).dup
145
+ end
146
+
147
+ def test_analyze
148
+ # Define test scenarios as arrays of FCB objects and expected filtered results
149
+
150
+ # :id, :type, :level, :token
151
+ ff_h1a = ['h1a', 'heading', 1, '']
152
+ ff_h1b = ['h1b', 'heading', 1, '']
153
+ ff_h2a = ['h2a', 'heading', 2, '']
154
+ ff_h2b = ['h2b', 'heading', 2, '']
155
+ ff_t1 = ['t1', 'text', nil, '']
156
+ ff_t2 = ['t2', 'text', nil, '']
157
+ ff_t3 = ['t3', 'text', nil, '']
158
+ ff_t4 = ['t4', 'text', nil, '']
159
+ ff_h1a_collapse = ['h1a', 'heading', 1, COLLAPSIBLE_TOKEN_COLLAPSE]
160
+ ff_h1a_expand = ['h1a', 'heading', 1, COLLAPSIBLE_TOKEN_EXPAND]
161
+ ff_h1b_expand = ['h1b', 'heading', 1, COLLAPSIBLE_TOKEN_EXPAND]
162
+
163
+ # :collapse, :collapsible, :hide
164
+ cc_init = [false, false, false]
165
+ cc_collapse = [true, false, false]
166
+ cc_collapse_collapsible = [true, true, false]
167
+ cc_collapsible = [false, true, false]
168
+ cc_collapsible_hide = [false, true, true]
169
+ cc_hide = [false, false, true]
170
+ cc_undefined_hide = [nil, nil, false]
171
+
172
+ fc_h1a__init = FCB.new(*ff_h1a, *cc_init)
173
+ fc_h1b__init = FCB.new(*ff_h1b, *cc_init)
174
+ fc_h2a__init = FCB.new(*ff_h2a, *cc_init)
175
+ fc_h2b__init = FCB.new(*ff_h2b, *cc_init)
176
+ fc_t1__init = FCB.new(*ff_t1, *cc_init)
177
+ fc_t2__init = FCB.new(*ff_t2, *cc_init)
178
+ fc_t3__init = FCB.new(*ff_t3, *cc_init)
179
+ fc_t4__init = FCB.new(*ff_t4, *cc_init)
180
+
181
+ fc_h1a__collapse = FCB.new(*ff_h1a, *cc_collapse)
182
+
183
+ fc_h1a__collapse_collapsible = FCB.new(*ff_h1a, *cc_collapse_collapsible)
184
+ fc_h2a__collapse_collapsible = FCB.new(*ff_h2a, *cc_collapse_collapsible)
185
+
186
+ fc_h1a__collapsible = FCB.new(*ff_h1a, *cc_collapsible)
187
+ fc_h2a__collapsible = FCB.new(*ff_h2a, *cc_collapsible)
188
+
189
+ fc_h1a__collapsible_hide = FCB.new(*ff_h1a, *cc_collapsible_hide)
190
+
191
+ fc_t1__hide = FCB.new(*ff_t1, *cc_hide)
192
+ fc_t2__hide = FCB.new(*ff_t2, *cc_hide)
193
+ fc_h2a__hide = FCB.new(*ff_h2a, *cc_hide)
194
+
195
+ analyze_cases = {
196
+ with_token: [
197
+ { name: 'collapse',
198
+ fcbs: [FCB.new(*ff_h1a_collapse, *cc_init)],
199
+ expected: [FCB.new(*ff_h1a_collapse, *cc_collapse)] },
200
+ { name: 'expand',
201
+ fcbs: [FCB.new(*ff_h1a_expand, *cc_init)],
202
+ expected: [FCB.new(*ff_h1a_expand, *cc_init)] },
203
+ { name: 'collapse, against options',
204
+ fcbs: [FCB.new(*ff_h1a_collapse, *cc_init)],
205
+ options: { heading1_collapse: false },
206
+ expected: [FCB.new(*ff_h1a_collapse, *cc_collapse)] },
207
+ { name: 'expand, against options',
208
+ fcbs: [FCB.new(*ff_h1a_expand, *cc_init)],
209
+ options: { heading1_collapse: true },
210
+ expected: [FCB.new(*ff_h1a_expand, *cc_init)] },
211
+ ],
212
+
213
+ with_no_state: [
214
+ { name: ' ',
215
+ fcbs: [FCB.new(*ff_h1a_expand, *cc_init), fc_t1__init, fc_h2a__init, fc_t2__init, FCB.new(*ff_h1b_expand, *cc_init), fc_t3__init, fc_h2b__init, fc_t4__init],
216
+ options: { heading2_collapse: true },
217
+ expected: [FCB.new(*ff_h1a_expand, *cc_init), fc_t1__init, FCB.new(*ff_h2a, *cc_collapse), FCB.new(*ff_h1b_expand, *cc_init), fc_t3__init, FCB.new(*ff_h2b, *cc_collapse)] },
218
+
219
+ { name: 'not collapsible',
220
+ fcbs: [fc_h1a__init],
221
+ options: {},
222
+ expected: [fc_h1a__init] },
223
+
224
+ { name: 'collapse, not collapsible',
225
+ fcbs: [fc_h1a__init],
226
+ options: { heading1_collapse: true },
227
+ expected: [fc_h1a__collapse] },
228
+
229
+ { name: 'collapsible, not collapsed',
230
+ fcbs: [fc_h1a__init],
231
+ options: { heading1_collapsible: true },
232
+ expected: [fc_h1a__collapsible] },
233
+
234
+ { name: 'collapsible, not collapsed, with a dependent',
235
+ fcbs: [fc_h1a__init, fc_t1__init],
236
+ options: { heading1_collapsible: true },
237
+ expected: [fc_h1a__init, fc_t1__init] },
238
+
239
+ { name: 'collapsible, collapsed',
240
+ fcbs: [fc_h1a__init],
241
+ options: { heading1_collapse: true,
242
+ heading1_collapsible: true },
243
+ expected: [fc_h1a__collapse_collapsible] },
244
+
245
+ { name: 'collapsible, collapsed, with a dependent',
246
+ fcbs: [fc_h1a__init, fc_t1__init],
247
+ options: { heading1_collapse: true,
248
+ heading1_collapsible: true },
249
+ expected: [fc_h1a__collapse_collapsible,
250
+ fc_t1__hide] },
251
+
252
+ { name: 'collapsible, collapsed, with a dependent and lower levels',
253
+ fcbs: [fc_h1a__init, fc_t1__init, fc_h2a__init],
254
+ options: { heading1_collapse: true,
255
+ heading1_collapsible: true },
256
+ expected: [fc_h1a__collapse_collapsible,
257
+ fc_t1__hide,
258
+ fc_h2a__hide] },
259
+
260
+ { name: 'collapsible, collapsed, with a dependent and higher levels',
261
+ fcbs: [fc_h1a__init, fc_h2a__init, fc_t1__init],
262
+ options: { heading2_collapse: true,
263
+ heading2_collapsible: true },
264
+ expected: [fc_h1a__init,
265
+ fc_h2a__collapse_collapsible,
266
+ fc_t1__hide] },
267
+ ],
268
+
269
+ with_empty_state: [
270
+ { name: 'expanded remains expanded',
271
+ fcbs: [fc_h1a__init],
272
+ options: { heading1_collapsible: true },
273
+ initialize: false,
274
+ expected: [fc_h1a__collapsible] },
275
+ ],
276
+
277
+ with_collapsed_state: [
278
+ { name: 'collapsed remains collapsed',
279
+ fcbs: [fc_h1a__collapse_collapsible],
280
+ options: { heading1_collapsible: true },
281
+ state: { "h1a" => 1 },
282
+ initialize: false,
283
+ expected: [fc_h1a__collapse_collapsible] },
284
+ ]
285
+ }
286
+
287
+ analyze_cases.each do |name, test_cases|
288
+ test_cases.each_with_index do |test_case, index|
289
+ @collapser = Collapser.new(collapsed_level: test_case[:collapsed_level],
290
+ collapsible_types: test_case[:collapsible_types] || COLLAPSIBLE_TYPES,
291
+ options: (test_case[:options] || OPTIONS).dup,
292
+ state: (test_case[:state] || STATE).dup)
293
+ analysis = @collapser.analyze(
294
+ test_case[:fcbs],
295
+ initialize: test_case[:initialize].nil? ? true : test_case[:initialize]
296
+ )
297
+ assert_equal test_case[:expected], analysis,
298
+ "Failed on test case #{index + 1} #{name} #{test_case[:name]}"
299
+ end
300
+ end
301
+ end
302
+ end
data/lib/constants.rb CHANGED
@@ -30,6 +30,16 @@ BLOCK_TYPE_COLOR_OPTIONS = {
30
30
  BlockType::VARS => :menu_vars_color
31
31
  }.freeze
32
32
 
33
+
34
+ COLLAPSIBLE_SYMBOL_COLLAPSED = '⬢' # '<+>' # '∆'
35
+ COLLAPSIBLE_SYMBOL_EXPANDED = '⬡' # '< >' # '…'
36
+
37
+ # in regexp (?<collapse>[+-~]?)
38
+ COLLAPSIBLE_TOKEN_COLLAPSE = '+'
39
+ COLLAPSIBLE_TOKEN_EXPAND = '-'
40
+
41
+ COLLAPSIBLE_TYPES = [BlockType::DIVIDER, BlockType::HEADING].freeze
42
+
33
43
  class ExecutionStreams
34
44
  STD_ERR = :stderr
35
45
  STD_IN = :stdin
@@ -21,11 +21,9 @@ def evaluate_shell_expressions(initial_code, expressions, shell: '/bin/bash',
21
21
  script << "\necho #{token}#{index}\n"
22
22
  script << expression << "\n"
23
23
  end
24
- # !!v script
25
24
 
26
25
  # Execute
27
26
  stdout_str, stderr_str, status = Open3.capture3(shell, "-c", script)
28
- # !!v stdout_str, stderr_str, status
29
27
 
30
28
  unless status.success?
31
29
  raise "Shell script execution failed: #{stderr_str}"
@@ -38,7 +36,6 @@ def evaluate_shell_expressions(initial_code, expressions, shell: '/bin/bash',
38
36
  result_hash[sprintf(key_format, key)] = output_parts[index].chomp
39
37
  end
40
38
  end
41
- # !!v result_hash
42
39
 
43
40
  result_hash
44
41
  end