markdown_exec 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
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