livetext 0.9.26 → 0.9.31

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.lt3 +3 -2
  3. data/imports/bookish.rb +1 -1
  4. data/lib/livetext/expansion.rb +108 -0
  5. data/lib/livetext/formatter.rb +223 -0
  6. data/lib/livetext/functions.rb +9 -0
  7. data/lib/livetext/helpers.rb +44 -28
  8. data/lib/livetext/html.rb +73 -0
  9. data/lib/livetext/more.rb +24 -4
  10. data/lib/livetext/parser/general.rb +1 -1
  11. data/lib/livetext/parser/set.rb +10 -3
  12. data/lib/livetext/parser/string.rb +14 -5
  13. data/lib/livetext/processor.rb +5 -0
  14. data/lib/livetext/skeleton.rb +0 -4
  15. data/lib/livetext/standard.rb +78 -63
  16. data/lib/livetext/userapi.rb +52 -19
  17. data/lib/livetext/version.rb +1 -1
  18. data/lib/livetext.rb +1 -2
  19. data/plugin/bookish.rb +1 -1
  20. data/plugin/bootstrap_menu.rb +140 -0
  21. data/plugin/misc/navbar.rb +162 -0
  22. data/test/extra/README.txt +149 -0
  23. data/test/extra/bracketed.rb +121 -0
  24. data/test/extra/bracketed.txt +44 -0
  25. data/test/extra/double.rb +121 -0
  26. data/test/extra/double.txt +44 -0
  27. data/test/extra/functions.rb +148 -0
  28. data/test/extra/functions.txt +58 -0
  29. data/test/extra/single.rb +139 -0
  30. data/test/extra/single.txt +52 -0
  31. data/test/extra/testgen.rb +104 -0
  32. data/test/{snapshots/basic_formatting/actual-error.txt → extra/variables..rb} +0 -0
  33. data/test/extra/variables.rb +94 -0
  34. data/test/extra/variables.txt +35 -0
  35. data/test/snapshots/basic_formatting/expected-output.txt +2 -2
  36. data/test/snapshots/simple_vars/source.lt3 +1 -1
  37. data/test/snapshots/subset.txt +49 -49
  38. data/test/unit/all.rb +1 -3
  39. data/test/unit/parser/general.rb +2 -2
  40. data/test/unit/parser/set.rb +0 -9
  41. metadata +19 -27
  42. data/lib/livetext/formatline.rb +0 -408
  43. data/lib/livetext/funcall.rb +0 -168
  44. data/lib/livetext/lineparser.rb +0 -441
  45. data/test/snapshots/basic_formatting/actual-output.txt +0 -13
  46. data/test/snapshots/basic_formatting/err-sdiff.txt +0 -1
  47. data/test/snapshots/basic_formatting/out-sdiff.txt +0 -14
  48. data/test/snapshots/error_inc_line_num/README.txt +0 -20
  49. data/test/snapshots/error_invalid_name/foo +0 -5
  50. data/test/snapshots/more_functions/actual-error.txt +0 -0
  51. data/test/snapshots/more_functions/actual-output.txt +0 -37
  52. data/test/snapshots/more_functions/err-sdiff.txt +0 -1
  53. data/test/snapshots/more_functions/out-sdiff.txt +0 -38
  54. data/test/snapshots/simple_vars/actual-error.txt +0 -0
  55. data/test/snapshots/simple_vars/actual-output.txt +0 -6
  56. data/test/snapshots/simple_vars/err-sdiff.txt +0 -1
  57. data/test/snapshots/simple_vars/out-sdiff.txt +0 -7
  58. data/test/snapshots/var_into_func/actual-error.txt +0 -0
  59. data/test/snapshots/var_into_func/actual-output.txt +0 -16
  60. data/test/snapshots/var_into_func/err-sdiff.txt +0 -1
  61. data/test/snapshots/var_into_func/out-sdiff.txt +0 -17
  62. data/test/testlines.rb +0 -37
  63. data/test/unit/formatline.rb +0 -638
  64. data/test/unit/lineparser.rb +0 -650
  65. data/test/unit/tokenizer.rb +0 -534
@@ -0,0 +1,140 @@
1
+ # https://getbootstrap.com/docs/5.1/components/navs-tabs/#base-nav
2
+
3
+ require 'json'
4
+
5
+ def api_out(indent_level, content)
6
+ api.out "#{(" " * (indent_level * indent_size))}#{content}"
7
+ end
8
+
9
+ def indent_size; 2; end
10
+
11
+ def menu(indent_level: 0, &block)
12
+ api_out indent_level, "<ul class='nav'>"
13
+ read_to_end_of_block(root_indent_level: indent_level)
14
+ yield
15
+ api_out indent_level, "</ul>"
16
+ end
17
+
18
+ def menu_dropdown(title, indent_level: 0, &block)
19
+ api_out indent_level, '<li class="nav-item dropdown">'
20
+ api_out indent_level, " <a class='nav-link dropdown-toggle' data-bs-toggle='dropdown' href='#' role='button' aria-expanded='false'"
21
+ api_out indent_level, " #{title}"
22
+ api_out indent_level, ' </a>'
23
+
24
+ api_out indent_level, ' <ul class="dropdown-menu">'
25
+ yield
26
+ api_out indent_level, ' </ul>'
27
+
28
+ api_out indent_level, '</li>'
29
+ end
30
+
31
+ def menu_link(title, href, active: false, indent_level: 0)
32
+ # Work out if it's possible to determine active state or not
33
+ api_out indent_level, "<a class='nav-link #{"active" if active}' aria-current='page' href='#{href}'"
34
+ api_out indent_level, " #{title}"
35
+ api_out indent_level, '</a>'
36
+ end
37
+
38
+ def menu_dropdown_link(title, href, active: false, indent_level: 0)
39
+ # Work out if it's possible to determine active state or not
40
+ api_out indent_level, "<a class='dropdown-item #{"active" if active}' href='#{href}'"
41
+ api_out indent_level, " #{title}"
42
+ api_out indent_level, '</a>'
43
+ end
44
+
45
+ def next_line
46
+ nextline
47
+ end
48
+
49
+ def peek_next_line
50
+ peek_nextline
51
+ end
52
+
53
+ def line_indent_level(line)
54
+ indent_level = (line.size - line.lstrip.size) / indent_size.to_f
55
+ raise "Unbalanced Indenting: Expecting #{indent_size} indents" if indent_level.to_i != indent_level
56
+ indent_level = indent_level.to_i
57
+ end
58
+
59
+ def line_command(line)
60
+ base_line = line.strip
61
+
62
+ base_command = base_line.split.first
63
+
64
+ if base_command.start_with?(".")
65
+ return base_command.gsub(".")
66
+ else
67
+ raise "Command Expected: #{base_line}"
68
+ end
69
+ end
70
+
71
+ def line_attributes_array(line)
72
+ line_attributes = line.strip.split("", 2).last
73
+ api.out 0, line_attributes.inspect
74
+
75
+ attributes_parsed = JSON.parse(line_attributes)
76
+ attributes_parsed.is_a?(String) ? [attributes_parsed] : attributes_parsed
77
+ end
78
+
79
+ def read_to_end_of_block(root_indent_level: 0)
80
+ expected_indent_level = root_indent_level + 1
81
+ loop do
82
+ peeked_line = peek_next_line
83
+ return if peeked_line.nil? # End of File
84
+ unless peeked_line.strip.size > 0 # Blank line
85
+ next_line
86
+ next
87
+ end
88
+
89
+ line_indent_level = line_indent_level(peeked_line)
90
+
91
+ if expected_indent_level != line_indent_level
92
+ raise "Unexpected Indent Level: Is #{line_indent_level} expected #{expected_indent_level}:\n#{peeked_line}"
93
+ end
94
+
95
+ return if root_indent_level >= line_indent_level # End of Block
96
+
97
+ line = next_line # Actually move to the next line
98
+
99
+ send(line_command(line), *line_attributes_array(line)) do
100
+ # This block doesn't run if command doesn't accept a block
101
+ read_to_end_of_block(root_indent_level: line_indent_level)
102
+ end
103
+ end
104
+ end
105
+
106
+ # Loop should be something like:
107
+ # read `.menu` and log current indent level
108
+ # => send(:menu) do
109
+ # # Read until indent is
110
+ # end
111
+
112
+ def simple_test
113
+ menu do
114
+ menu_link "Home", "http://fake.com/home"
115
+ menu_dropdown("Blog") do
116
+ menu_dropdown_link("Home", "http://fake.com/blog")
117
+ end
118
+ menu_link "About", "http://fake.com/about"
119
+ end
120
+ # This should have the same output as:
121
+ # .menu
122
+ # .menu_link
123
+ # .menu_dropdown ["Blog"]
124
+ # .menu_dropdown_link ["Home", "http://fake.com/blog"]
125
+ # .menu_link ["About", "http://fake.com/about"]
126
+ # or this if we end up sticking with .end
127
+ # .menu
128
+ # .menu_link
129
+ # .menu_dropdown "Blog"
130
+ # .menu_dropdown_link ["Home", "http://fake.com/blog"]
131
+ # .end
132
+ # .menu_link ["About", "http://fake.com/about"]
133
+ # .end
134
+
135
+
136
+ # Really this should probably just re-use Rails ActionView to avoid a ton of duplicate code
137
+ # This would allow us to leverage all the Rails HTML generation logic.
138
+ # Downside of that is that it does break the paradigm a bit in that blocks with errors won't partially render
139
+ # it does however also means we would get sanitization and such for free
140
+ end
@@ -0,0 +1,162 @@
1
+
2
+ private def get_href_title(line)
3
+ *title, href = line[1..-1].split(@space)
4
+ if href[0] == "@"
5
+ blank = true
6
+ href = href[1..-1]
7
+ else
8
+ blank = false
9
+ end
10
+ title = title.join(@space)
11
+ [href, title, blank]
12
+ end
13
+
14
+ private def add_root(url)
15
+ return url if url =~ /^http/ # already has http[s]
16
+ return url unless @root # no root defined
17
+ range1 = 0..-1
18
+ range1 = 0..-2 if @root[-1] == "/"
19
+ root = @root[range1]
20
+ range2 = 0..-1
21
+ range2 = 1..-1 if url[0] == "/"
22
+ url = url[range2]
23
+ root + "/" + url
24
+ end
25
+
26
+ private def get_brand
27
+ peek = @enum.peek
28
+ arg = peek[6..-1]
29
+ href, item, blank = get_href_title(arg)
30
+ # doesn't honor @root... and blank is unused here
31
+ if item =~ /\.(jpg|png|gif)$/i
32
+ item = "<img src='#{item}'></img>"
33
+ end
34
+ details = {class: "navbar-brand mr-auto", href: add_root(href)}
35
+ @brand = html.tag(:a, **details, cdata: item)
36
+ end
37
+
38
+ private def slash_tags
39
+ @root = @brand = nil
40
+ @classes = "navbar-light bg-light"
41
+ loop do # /brand /root ...
42
+ peek = @enum.peek
43
+ break if peek[0] != '/'
44
+ case
45
+ when peek.start_with?("/brand ")
46
+ get_brand
47
+ when peek.start_with?("/root ")
48
+ @root = peek[6..-1]
49
+ when peek.start_with?("/classes ")
50
+ @classes = peek[9..-1]
51
+ end
52
+ line = @enum.next
53
+ end
54
+ end
55
+
56
+ private def handle_children
57
+ value = ""
58
+ loop do
59
+ if @enum.peek.start_with?(@space) # it's a child
60
+ line = @enum.next
61
+ href, title, blank = get_href_title(line)
62
+ details = {class: "dropdown-item", href: add_root(href)}
63
+ details[:target] = "_blank" if blank
64
+ link = html.tag(:a, **details, cdata: title)
65
+ str = html.tag(:li, cdata: link)
66
+ value << str + "\n"
67
+ else
68
+ break
69
+ end
70
+ end
71
+ api.out value
72
+ end
73
+
74
+ private def no_children(href, title)
75
+ html.li(class: "nav-item") do
76
+ api.out html.tag(:a, class: "nav-link", href: add_root(href), cdata: title)
77
+ end
78
+ end
79
+
80
+ private def has_children(href, title)
81
+ css = "nav-item dropdown"
82
+ html.li(class: css) do
83
+ details = {class: "nav-link dropdown-toggle", href: "#", id: "navbarDropdown",
84
+ role: "button", :"data-bs-toggle" => "dropdown", :"aria-expanded" => "false"}
85
+ @dropdowns += 1
86
+ api.out html.tag(:a, **details, cdata: title)
87
+ details = {class: "dropdown-menu", :"aria-labelledby" => "navbarDropdown"}
88
+ html.ul(**details) do # children...
89
+ handle_children
90
+ end
91
+ end
92
+ end
93
+
94
+ private def handle_body
95
+ @dropdowns = 1
96
+ loop do
97
+ line = @enum.next
98
+ href, title, blank = get_href_title(line)
99
+ case line[0]
100
+ when "-"
101
+ no_children(href, title)
102
+ when "="
103
+ has_children(href, title)
104
+ end
105
+ end
106
+ end
107
+
108
+ def bootstrap
109
+ api.out <<~HTML
110
+ <head>
111
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
112
+ rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
113
+ crossorigin="anonymous">
114
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
115
+ integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
116
+ crossorigin="anonymous">
117
+ </script>
118
+ </head>
119
+ HTML
120
+ end
121
+
122
+ private def toggler
123
+ details = {class: "navbar-toggler", type: "button", "data-bs-toggle": "collapse",
124
+ "data-bs-target": "#navbarSupportedContent",
125
+ "aria-controls": "navbarSupportedContent", "aria-expanded": "false",
126
+ "aria-label": "Toggle navigation"}
127
+ str = html.tag(:button, **details, cdata: '<span class="navbar-toggler-icon"></span>')
128
+ api.out str
129
+ end
130
+
131
+ private def branding
132
+ return if @brand.empty? || @brand.nil?
133
+ api.out @brand
134
+ end
135
+
136
+ def html
137
+ @html
138
+ end
139
+
140
+ def navbar
141
+ @html = HTML.new(api)
142
+ # bootstrap
143
+ @space = " "
144
+ @enum = api.body.each
145
+ slash_tags
146
+
147
+ css = "navbar navbar-expand-md " + @classes
148
+ html.nav(class: css) do
149
+ html.div(class: "container-fluid") do
150
+ toggler
151
+ branding
152
+ content = {class: "collapse navbar-collapse", id: "navbarSupportedContent"}
153
+ html.div(**content) do
154
+ css = "navbar-nav me-auto mb-2 mb-md-0"
155
+ html.ul(class: css) do
156
+ handle_body
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+
@@ -0,0 +1,149 @@
1
+ The testgen.rb tool takes a .txt and generates a corresponding file
2
+ of MiniTest code.
3
+
4
+ $ ruby testgen.rb variables.txt # produces variables.rb
5
+
6
+ The tests here include:
7
+
8
+ variables.txt Variable expansion
9
+ functions.txt Function call evaluation
10
+ single.txt Single sigil (see Formatting below)
11
+ double.txt Double sigil (see Formatting below)
12
+ bracketed.txt Bracketed sigil (see Formatting below)
13
+
14
+
15
+ Variables:
16
+ ----------
17
+
18
+ 1. A variable begins with a $ and is followed by an alpha; periods may
19
+ be embedded, but each separate piece must "look like" an identifier
20
+
21
+ $x yes
22
+ $xyz yes
23
+ $xyz.abc yes
24
+ $x123 yes
25
+ $x.123 no
26
+ $345 no
27
+
28
+ 2. Rather than causing an error, invalid variables are rendered "as-is"
29
+ as soon as possible:
30
+ " $ " => " $ "
31
+ " $5 " => " $5 "
32
+ "...$" => "...$" (end of line)
33
+
34
+ 3. Actual variables may be user-defined or predefined. The latter usually
35
+ begin with a capital. This is only a convention so far, nothing that is
36
+ enforced.
37
+
38
+ 4. The $ may be escaped as needed. This is problematic.
39
+
40
+ 5. An unknown variable will not raise an error, but will be replaced with
41
+ a warning string.
42
+
43
+
44
+ Functions:
45
+ ----------
46
+
47
+ 1. A function looks like a variable name, but it has two $ in front.
48
+
49
+ 2. If followed by space, comma, end of line, or similar delimiter, it is
50
+ called with no parameter.
51
+
52
+ 3. Note that a function name may contain periods, but may not end with
53
+ one. "$$func." is parsed as a function call (with no parameter) plus a
54
+ period.
55
+
56
+ 4. Use a colon to pass a single parameter delimited by a space or end of line.
57
+ Colon at end of line is valid but probably pointless.
58
+
59
+ 5. Use brackets to pass a single parameter that contains spaces. The bracketed
60
+ parameter may be terminated by end of line instead of right bracket.
61
+
62
+ 6. Only one parameter (a string) may be passed, but the function may parse it
63
+ however it needs to.
64
+
65
+ 7. There is no enforcement of a parameter being "present or absent" except what
66
+ the function itself may enforce.
67
+
68
+ 8. An unknown function will not raise an error, but will be replaced with a warning
69
+ string.
70
+
71
+
72
+ Formatting:
73
+ -----------
74
+
75
+ 1. My formatting notation would be considered quirky by many people.
76
+ The sigils or markers are:
77
+ * bold
78
+ _ underscore
79
+ ` code/teletype
80
+ ~ strikethrough
81
+
82
+ 1. A single sigil is recognized basically at beginning of line or after a space.
83
+ my_func_name No italics here
84
+ M*A*S*H No boldface here
85
+
86
+ 2. A single sigil is terminated by a space or end of line.
87
+
88
+ 3. A single sigil "by itself" is rendered as-is (asterisk, underscore, whatever).
89
+
90
+ 4. An escaped single sigil is rendered as-is. (This is problematic.)
91
+
92
+ 5. A double sigil is recognized at start of line or after a space
93
+
94
+ 6. A double sigil is terminated by a space OR a comma OR a period. (The comma
95
+ and period cases seem very common to me; they are the whole justification
96
+ for the double sigil.) End of line also terminates it.
97
+
98
+ 7. A double sigil by itself is rendered as-is.
99
+
100
+ 8. A bracketed sigil is in general a sigil followed by: [ data ]
101
+
102
+ 9. An empty bracketed sigil simply "goes away"
103
+ " *[] " => " "
104
+
105
+ 10. End of line can terminate instead of right bracket -- but it may still be empty
106
+ and therefore go away.
107
+
108
+ 11. NOTE: These tests use only asterisks (bold), but the logic "should" be the same
109
+ for all sigils.
110
+
111
+
112
+
113
+ Order of evaluation, etc.:
114
+ --------------------------
115
+
116
+ 1. This logic is always a compromise between syntax and the code that parses it.
117
+ I prefer simplicity whenever possible, though it may introduce complexity in
118
+ other situations. I believe that the acceptable complexity of a workaround
119
+ depends on how commonplace the situation is and how onerous the workaround is.
120
+ These are both highly subjective.
121
+
122
+ 2. For example: Note that the simple formatting sigils may not be nested. However,
123
+ there are functions like $$bits provided (with the silly mnemonic "bold, italic,
124
+ teletype, strikthrough"). Every combination is provided (e.g., $$bi, $$bt).
125
+
126
+ 3. Note also: Formatting is only intra-line; it doesn't span lines. If you need to
127
+ work around this, use a heredoc or make your own .def for this.
128
+
129
+ 4. Finally: HTML or CSS may be inserted at will (possibly with some escaping). This
130
+ can be inline or "a file at a time" via such commands as .copy and .include
131
+
132
+ 5. For these reasons: Parsing is naive and simple. Variables are parsed first.
133
+
134
+ 6. Next, function calls are parsed. I said variables are parsed first; this implies
135
+ that a variable can be embedded in a function parameter. But be aware these are
136
+ "naive" substitutions (like C macros).
137
+ .set alpha = "some value"
138
+ Calling $$myfunc:$alpha (means: Calling $$myfunc:some value)
139
+ Better to say $$myfunc[$alpha] (means: Better to say $$myfunc[some value]
140
+
141
+ 7. Formatting is handled last. The four sigils (* _ ` ~) and their three modes
142
+ (single, double, bracketed) make 12 passes necessary for formatting. As this is
143
+ always single-line, it has not been observed to cause a delay so far.
144
+
145
+ 8. The call api.format(line) essentially expands variables, calls functions, and
146
+ finally does simple formatting. See classes Expansion and Formatter.
147
+
148
+ 9. User code (e.g. inside a .def) may also call expand_variables, expand_functions,
149
+ and Formatter.format separately.
@@ -0,0 +1,121 @@
1
+ require 'minitest/autorun'
2
+
3
+ require 'livetext'
4
+
5
+ # Just another testing class. Chill.
6
+
7
+ class TestingLivetextBracketed < MiniTest::Test
8
+
9
+ def setup
10
+ @live = Livetext.new
11
+ end
12
+
13
+ def check_match(exp, actual)
14
+ if exp.is_a? Regexp
15
+ assert_match(exp, actual, "actual does not match expected")
16
+ else
17
+ assert_equal(exp, actual, "actual != expected")
18
+ end
19
+ end
20
+
21
+ def test_bracketed_001_single_bracketed_item
22
+ # Single bracketed item
23
+ # No special initialization
24
+ src = "*[abc]"
25
+ exp = "<b>abc</b>"
26
+ actual = @live.api.format(src)
27
+ check_match(exp, actual)
28
+ end
29
+
30
+ def test_bracketed_002_end_of_line_can_replace_bracket
31
+ # End of line can replace bracket
32
+ # No special initialization
33
+ src = "*[abc"
34
+ exp = "<b>abc</b>"
35
+ actual = @live.api.format(src)
36
+ check_match(exp, actual)
37
+ end
38
+
39
+ def test_bracketed_003_end_of_line_can_replace_bracket_again
40
+ # End of line can replace bracket again
41
+ # No special initialization
42
+ src = "abc *[d"
43
+ exp = "abc <b>d</b>"
44
+ actual = @live.api.format(src)
45
+ check_match(exp, actual)
46
+ end
47
+
48
+ def test_bracketed_004_missing_right_bracket_ignored_at_eol_if_empty
49
+ # Missing right bracket ignored at eol if empty
50
+ # No special initialization
51
+ src = "abc*["
52
+ exp = "abc*["
53
+ actual = @live.api.format(src)
54
+ check_match(exp, actual)
55
+ end
56
+
57
+ def test_bracketed_005_two_simple_bracketed_items
58
+ # Two simple bracketed items
59
+ # No special initialization
60
+ src = "*[A], *[B], C"
61
+ exp = "<b>A</b>, <b>B</b>, C"
62
+ actual = @live.api.format(src)
63
+ check_match(exp, actual)
64
+ end
65
+
66
+ def test_bracketed_006_simple_bracketed_item
67
+ # Simple bracketed item
68
+ # No special initialization
69
+ src = "Just a *[test]..."
70
+ exp = "Just a <b>test</b>..."
71
+ actual = @live.api.format(src)
72
+ check_match(exp, actual)
73
+ end
74
+
75
+ def test_bracketed_007_bracketed_item_with_space
76
+ # Bracketed item with space
77
+ # No special initialization
78
+ src = "A *[simple test]"
79
+ exp = "A <b>simple test</b>"
80
+ actual = @live.api.format(src)
81
+ check_match(exp, actual)
82
+ end
83
+
84
+ def test_bracketed_008_empty_bracketed_item_results_in_null
85
+ # Empty bracketed item results in null
86
+ # No special initialization
87
+ src = " *[] "
88
+ exp = " "
89
+ actual = @live.api.format(src)
90
+ check_match(exp, actual)
91
+ end
92
+
93
+ def test_bracketed_009_bracketed_item_with_space_again
94
+ # Bracketed item with space again
95
+ # No special initialization
96
+ src = "*[ab c] d"
97
+ exp = "<b>ab c</b> d"
98
+ actual = @live.api.format(src)
99
+ check_match(exp, actual)
100
+ end
101
+
102
+ def test_bracketed_010_two_bracketed_items_with_spaces
103
+ # Two bracketed items with spaces
104
+ # No special initialization
105
+ src = "*[a b] *[c d]"
106
+ exp = "<b>a b</b> <b>c d</b>"
107
+ actual = @live.api.format(src)
108
+ check_match(exp, actual)
109
+ end
110
+
111
+ def test_bracketed_011_solitary_item_missing_right_bracket_ignored_at_eol_if_empty
112
+ # Solitary item, missing right bracket ignored at eol if empty
113
+ # No special initialization
114
+ src = "*["
115
+ exp = ""
116
+ actual = @live.api.format(src)
117
+ check_match(exp, actual)
118
+ end
119
+
120
+
121
+ end
@@ -0,0 +1,44 @@
1
+ Single bracketed item
2
+ "*[abc]"
3
+ "<b>abc</b>"
4
+ --------------------------------
5
+ End of line can replace bracket
6
+ "*[abc"
7
+ "<b>abc</b>"
8
+ --------------------------------
9
+ End of line can replace bracket again
10
+ "abc *[d"
11
+ "abc <b>d</b>"
12
+ --------------------------------
13
+ Missing right bracket ignored at eol if empty
14
+ "abc*["
15
+ "abc*["
16
+ --------------------------------
17
+ Two simple bracketed items
18
+ "*[A], *[B], C"
19
+ "<b>A</b>, <b>B</b>, C"
20
+ --------------------------------
21
+ Simple bracketed item
22
+ "Just a *[test]..."
23
+ "Just a <b>test</b>..."
24
+ --------------------------------
25
+ Bracketed item with space
26
+ "A *[simple test]"
27
+ "A <b>simple test</b>"
28
+ --------------------------------
29
+ Empty bracketed item results in null
30
+ " *[] "
31
+ " "
32
+ --------------------------------
33
+ Bracketed item with space again
34
+ "*[ab c] d"
35
+ "<b>ab c</b> d"
36
+ --------------------------------
37
+ Two bracketed items with spaces
38
+ "*[a b] *[c d]"
39
+ "<b>a b</b> <b>c d</b>"
40
+ --------------------------------
41
+ Solitary item, missing right bracket ignored at eol if empty
42
+ "*["
43
+ ""
44
+ --------------------------------