livetext 0.9.52 → 0.9.56

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/imports/bookish.rb +3 -3
  3. data/lib/livetext/ast/show_ast_clean.rb +10 -0
  4. data/lib/livetext/ast/show_ast_result.rb +60 -0
  5. data/lib/livetext/ast/show_raw_arrays.rb +13 -0
  6. data/lib/livetext/ast.rb +464 -0
  7. data/lib/livetext/ast_to_html.rb +32 -0
  8. data/lib/livetext/core.rb +110 -53
  9. data/lib/livetext/errors.rb +1 -0
  10. data/lib/livetext/expansion.rb +21 -21
  11. data/lib/livetext/formatter.rb +70 -200
  12. data/lib/livetext/formatter_component.rb +189 -0
  13. data/lib/livetext/function_registry.rb +163 -0
  14. data/lib/livetext/functions.rb +26 -0
  15. data/lib/livetext/handler/mixin.rb +53 -0
  16. data/lib/livetext/helpers.rb +33 -16
  17. data/lib/livetext/reopen.rb +2 -0
  18. data/lib/livetext/skeleton.rb +0 -3
  19. data/lib/livetext/standard.rb +120 -72
  20. data/lib/livetext/userapi.rb +20 -1
  21. data/lib/livetext/variable_manager.rb +78 -0
  22. data/lib/livetext/variables.rb +9 -1
  23. data/lib/livetext/version.rb +1 -1
  24. data/lib/livetext.rb +9 -3
  25. data/plugin/booktool.rb +14 -14
  26. data/plugin/lt3scriptor.rb +914 -0
  27. data/plugin/mixin_functions_class.rb +33 -0
  28. data/test/snapshots/complex_body/expected-error.txt +0 -0
  29. data/test/snapshots/complex_body/expected-output.txt +8 -0
  30. data/test/snapshots/complex_body/source.lt3 +19 -0
  31. data/test/snapshots/debug_command/expected-error.txt +0 -0
  32. data/test/snapshots/debug_command/expected-output.txt +1 -0
  33. data/test/snapshots/debug_command/source.lt3 +3 -0
  34. data/test/snapshots/def_parameters/expected-error.txt +0 -0
  35. data/test/snapshots/def_parameters/expected-output.txt +21 -0
  36. data/test/snapshots/def_parameters/source.lt3 +44 -0
  37. data/test/snapshots/error_missing_end/match-error.txt +1 -1
  38. data/test/snapshots/functions_reflection/expected-error.txt +0 -0
  39. data/test/snapshots/functions_reflection/expected-output.txt +27 -0
  40. data/test/snapshots/functions_reflection/source.lt3 +5 -0
  41. data/test/snapshots/mixin_functions_class/expected-error.txt +0 -0
  42. data/test/snapshots/mixin_functions_class/expected-output.txt +20 -0
  43. data/test/snapshots/mixin_functions_class/mixin_functions_class.rb +33 -0
  44. data/test/snapshots/mixin_functions_class/source.lt3 +17 -0
  45. data/test/snapshots/multiple_functions/expected-error.txt +0 -0
  46. data/test/snapshots/multiple_functions/expected-output.txt +5 -0
  47. data/test/snapshots/multiple_functions/source.lt3 +16 -0
  48. data/test/snapshots/nested_includes/expected-error.txt +0 -0
  49. data/test/snapshots/nested_includes/expected-output.txt +68 -0
  50. data/test/snapshots/nested_includes/level2.inc +34 -0
  51. data/test/snapshots/nested_includes/level3.inc +20 -0
  52. data/test/snapshots/nested_includes/source.lt3 +49 -0
  53. data/test/snapshots/parameter_handling/expected-error.txt +0 -0
  54. data/test/snapshots/parameter_handling/expected-output.txt +7 -0
  55. data/test/snapshots/parameter_handling/source.lt3 +10 -0
  56. data/test/snapshots/subset.txt +1 -0
  57. data/test/snapshots/system_info/expected-error.txt +0 -0
  58. data/test/snapshots/system_info/match-output.txt +18 -0
  59. data/test/snapshots/system_info/source.lt3 +16 -0
  60. data/test/unit/all.rb +7 -0
  61. data/test/unit/ast.rb +90 -0
  62. data/test/unit/ast_directives.rb +104 -0
  63. data/test/unit/ast_variables.rb +71 -0
  64. data/test/unit/core_methods.rb +317 -0
  65. data/test/unit/formatter.rb +84 -0
  66. data/test/unit/formatter_component.rb +84 -0
  67. data/test/unit/function_registry.rb +132 -0
  68. data/test/unit/mixin_functions_class.rb +131 -0
  69. data/test/unit/stringparser.rb +14 -32
  70. data/test/unit/variable_manager.rb +71 -0
  71. metadata +51 -5
  72. data/imports/markdown.rb +0 -44
  73. data/lib/livetext/processor.rb +0 -88
  74. data/plugin/markdown.rb +0 -43
@@ -0,0 +1,33 @@
1
+ def some_dot_command(args, data)
2
+ # This is a dot command, not a function
3
+ api.out "Dot command called with: #{data}"
4
+ end
5
+
6
+ class Livetext::Functions
7
+ def class_func(param)
8
+ "Class function called with: #{param}"
9
+ end
10
+
11
+ def another_class_func(param)
12
+ "Another class function called with: #{param}"
13
+ end
14
+
15
+ def simple_class_func
16
+ "Simple class function with no parameters"
17
+ end
18
+
19
+ def vars_test(param)
20
+ # New approach: Access variables through the instance variables
21
+ # @live and @vars are now available in Livetext::Functions
22
+
23
+ # Access variables using the new instance-based approach
24
+ view_var = @live.vars.View || "no_view"
25
+
26
+ # Alternative approaches:
27
+ # view_var = @vars.View || "no_view" # Direct access to vars
28
+ # view_var = get_var(:View) || "no_view" # Using helper method
29
+ # view_var = Livetext::Vars[:View] || "no_view" # Fallback to global
30
+
31
+ "Vars test: param=#{param}, View=#{view_var}"
32
+ end
33
+ end
File without changes
@@ -0,0 +1,8 @@
1
+ Testing complex function body logic...
2
+ <p>
3
+
4
+ Testing with nil: nil
5
+ Testing with empty: nil
6
+ Testing with number: number
7
+ Testing with string: HELLO
8
+ Testing explicit empty: empty
@@ -0,0 +1,19 @@
1
+ Testing complex function body logic...
2
+
3
+ .func complex
4
+ if param.empty?
5
+ 'nil'
6
+ elsif param == "\"\""
7
+ 'empty'
8
+ elsif param.to_i.to_s == param
9
+ 'number'
10
+ else
11
+ param.upcase
12
+ end
13
+ .end
14
+
15
+ Testing with nil: $$complex[]
16
+ Testing with empty: $$complex[]
17
+ Testing with number: $$complex[42]
18
+ Testing with string: $$complex[hello]
19
+ Testing explicit empty: $$complex[""]
File without changes
@@ -0,0 +1,3 @@
1
+ .debug on
2
+ .debug off
3
+ .debug on
File without changes
@@ -0,0 +1,21 @@
1
+ Testing no parameters:
2
+ No parameters method called
3
+ <p>
4
+
5
+ Testing with args and data:
6
+ Args: ["arg1", "arg2"], Data: "arg1 arg2"
7
+ This is the data line
8
+ <p>
9
+
10
+ Testing with body:
11
+ Args: ["arg1", "arg2"], Data: "arg1 arg2"
12
+ Body has 3 lines:
13
+ 1: This is line 1 of the body
14
+ 2: This is line 2 of the body
15
+ 3: This is line 3 of the body
16
+ Testing with raw body:
17
+ Args: ["arg1", "arg2"], Data: "arg1 arg2"
18
+ Raw body has 3 lines:
19
+ 1: This is line 1 of the raw body
20
+ 2: This is line 2 of the raw body
21
+ 3: This is line 3 of the raw body
@@ -0,0 +1,44 @@
1
+ .def no_params
2
+ api.out "No parameters method called"
3
+ .end
4
+
5
+ .def with_args args
6
+ api.out "Args: #{args.inspect}, Data: #{data.inspect}"
7
+ .end
8
+
9
+ .def with_body body
10
+ api.out "Args: #{args.inspect}, Data: #{data.inspect}"
11
+ api.out "Body has #{body.length} lines:"
12
+ body.each_with_index do |line, i|
13
+ api.out " #{i+1}: #{line}"
14
+ end
15
+ .end
16
+
17
+ .def with_body_raw body raw
18
+ api.out "Args: #{args.inspect}, Data: #{data.inspect}"
19
+ api.out "Raw body has #{body.length} lines:"
20
+ body.each_with_index do |line, i|
21
+ api.out " #{i+1}: #{line}"
22
+ end
23
+ .end
24
+
25
+ Testing no parameters:
26
+ .no_params
27
+
28
+ Testing with args and data:
29
+ .with_args arg1 arg2
30
+ This is the data line
31
+
32
+ Testing with body:
33
+ .with_body arg1 arg2
34
+ This is line 1 of the body
35
+ This is line 2 of the body
36
+ This is line 3 of the body
37
+ .end
38
+
39
+ Testing with raw body:
40
+ .with_body_raw arg1 arg2
41
+ This is line 1 of the raw body
42
+ This is line 2 of the raw body
43
+ This is line 3 of the raw body
44
+ .end
@@ -1 +1 @@
1
- 1 /Expected .end, found end of file/
1
+ 1 /expected .end but found end of file/
@@ -0,0 +1,27 @@
1
+ <h3>Available Functions</h3>
2
+ <ul>
3
+ <li><strong>$$br</strong> - builtin</li>
4
+ <li><strong>$$date</strong> - builtin</li>
5
+ <li><strong>$$day</strong> - builtin</li>
6
+ <li><strong>$$days_ago</strong> - builtin</li>
7
+ <li><strong>$$days_from_now</strong> - builtin</li>
8
+ <li><strong>$$format_date</strong> - builtin</li>
9
+ <li><strong>$$hostname</strong> - builtin</li>
10
+ <li><strong>$$hour</strong> - builtin</li>
11
+ <li><strong>$$isqrt</strong> - builtin</li>
12
+ <li><strong>$$link</strong> - builtin</li>
13
+ <li><strong>$$livetext_version</strong> - builtin</li>
14
+ <li><strong>$$minute</strong> - builtin</li>
15
+ <li><strong>$$month</strong> - builtin</li>
16
+ <li><strong>$$platform</strong> - builtin</li>
17
+ <li><strong>$$pwd</strong> - builtin</li>
18
+ <li><strong>$$rand</strong> - builtin</li>
19
+ <li><strong>$$reverse</strong> - builtin</li>
20
+ <li><strong>$$ruby_version</strong> - builtin</li>
21
+ <li><strong>$$second</strong> - builtin</li>
22
+ <li><strong>$$testfunc</strong> - inline (unknown)</li>
23
+ <li><strong>$$time</strong> - builtin</li>
24
+ <li><strong>$$week</strong> - builtin</li>
25
+ <li><strong>$$weekday</strong> - builtin</li>
26
+ <li><strong>$$year</strong> - builtin</li>
27
+ </ul>
@@ -0,0 +1,5 @@
1
+ .func testfunc
2
+ "Test function: #{param}"
3
+ .end
4
+
5
+ .functions
@@ -0,0 +1,20 @@
1
+ Testing functions defined in Livetext::Functions class
2
+ <p>
3
+
4
+ Now calling functions defined in Livetext::Functions class:
5
+ Class function called with: test_parameter
6
+ <p>
7
+
8
+ And another one with brackets:
9
+ Another class function called with: with brackets
10
+ <p>
11
+
12
+ And a function with no parameters:
13
+ Simple class function with no parameters
14
+ <p>
15
+
16
+ Testing that the functions have access to Livetext::Vars:
17
+ Vars test: param=some_value, View=[View is undefined]
18
+ <p>
19
+
20
+ That's all.
@@ -0,0 +1,33 @@
1
+ def some_dot_command(args, data)
2
+ # This is a dot command, not a function
3
+ api.out "Dot command called with: #{data}"
4
+ end
5
+
6
+ class Livetext::Functions
7
+ def class_func(param)
8
+ "Class function called with: #{param}"
9
+ end
10
+
11
+ def another_class_func(param)
12
+ "Another class function called with: #{param}"
13
+ end
14
+
15
+ def simple_class_func
16
+ "Simple class function with no parameters"
17
+ end
18
+
19
+ def vars_test(param)
20
+ # New approach: Access variables through the instance variables
21
+ # @live and @vars are now available in Livetext::Functions
22
+
23
+ # Access variables using the new instance-based approach
24
+ view_var = @live.vars.View || "no_view"
25
+
26
+ # Alternative approaches:
27
+ # view_var = @vars.View || "no_view" # Direct access to vars
28
+ # view_var = get_var(:View) || "no_view" # Using helper method
29
+ # view_var = Livetext::Vars[:View] || "no_view" # Fallback to global
30
+
31
+ "Vars test: param=#{param}, View=#{view_var}"
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ Testing functions defined in Livetext::Functions class
2
+
3
+ .mixin mixin_functions_class
4
+
5
+ Now calling functions defined in Livetext::Functions class:
6
+ $$class_func:test_parameter
7
+
8
+ And another one with brackets:
9
+ $$another_class_func[with brackets]
10
+
11
+ And a function with no parameters:
12
+ $$simple_class_func
13
+
14
+ Testing that the functions have access to Livetext::Vars:
15
+ $$vars_test:some_value
16
+
17
+ That's all.
@@ -0,0 +1,5 @@
1
+ Testing multiple user functions in the same document...
2
+ <p>
3
+
4
+ Calling all functions:
5
+ one
@@ -0,0 +1,16 @@
1
+ Testing multiple user functions in the same document...
2
+
3
+ .func func1
4
+ 'one'
5
+ .end
6
+
7
+ .func func2
8
+ 'two'
9
+ .end
10
+
11
+ .func func3
12
+ 'three'
13
+ .end
14
+
15
+ Calling all functions:
16
+ $$func1[] and $$func2[] and $$func3[]
File without changes
@@ -0,0 +1,68 @@
1
+ # Main file - Level 1
2
+ # This file includes level2 and tests all variables and functions from all levels
3
+ <p>
4
+
5
+ # Include level2 (which includes level3)
6
+ # Level 2 - Middle level
7
+ # This file includes level3 and defines its own variables and functions
8
+ <p>
9
+
10
+ # Level 3 - Deepest level
11
+ # This file defines variables and functions that should be accessible to all higher levels
12
+ <p>
13
+
14
+ This is content from level 3.
15
+ The deep variable is: I am from level 3
16
+ The deep number is: 42
17
+ This is content from level 2.
18
+ The middle variable is: I am from level 2
19
+ The middle number is: 100
20
+ <p>
21
+
22
+ # Test using deep level variables and functions
23
+ Deep variable from level 2: I am from level 3
24
+ Deep function call: Deep function called with: from level 2
25
+ Deep math: 42 = 42
26
+ <p>
27
+
28
+ # Test our own variables and functions
29
+ Middle function call: Middle function called with: from level 2
30
+ Combined variables: Deep: I am from level 3, Middle: I am from level 2
31
+ <p>
32
+
33
+ # Test deep method from level 3
34
+ Deep method called from level 3
35
+ # Test all variables from all levels
36
+ Main variable: I am from main level
37
+ Middle variable: I am from level 2
38
+ Deep variable: I am from level 3
39
+ <p>
40
+
41
+ # Test all functions from all levels
42
+ Main function: Main function called with: from main
43
+ Middle function: Middle function called with: from main
44
+ Deep function: Deep function called with: from main
45
+ <p>
46
+
47
+ # Test math functions
48
+ Deep math: 20 = 20
49
+ Main number: 999
50
+ Middle number: 100
51
+ Deep number: 42
52
+ <p>
53
+
54
+ # Test combined functions
55
+ Combine vars: Deep: I am from level 3, Middle: I am from level 2
56
+ Test all levels: Main: I am from main level, Middle: I am from level 2, Deep: I am from level 3
57
+ <p>
58
+
59
+ # Test methods from all levels
60
+ Main method called from main level
61
+ Middle method called from level 2
62
+ Deep method called from level 3
63
+ <p>
64
+
65
+ # Test that we can still access everything after the include
66
+ Final test - deep function: Deep function called with: final call
67
+ Final test - middle function: Middle function called with: final call
68
+ Final test - main function: Main function called with: final call
@@ -0,0 +1,34 @@
1
+ # Level 2 - Middle level
2
+ # This file includes level3 and defines its own variables and functions
3
+
4
+ .include level3.inc
5
+
6
+ .set middle_var="I am from level 2", middle_number=100
7
+
8
+ .func middle_function
9
+ "Middle function called with: " + param
10
+ .end
11
+
12
+ .func combine_vars
13
+ "Deep: " + "I am from level 3" + ", Middle: " + "I am from level 2"
14
+ .end
15
+
16
+ This is content from level 2.
17
+ The middle variable is: $middle_var
18
+ The middle number is: $middle_number
19
+
20
+ # Test using deep level variables and functions
21
+ Deep variable from level 2: $deep_var
22
+ Deep function call: $$deep_function[from level 2]
23
+ Deep math: $$deep_math[21] = 42
24
+
25
+ # Test our own variables and functions
26
+ Middle function call: $$middle_function[from level 2]
27
+ Combined variables: $$combine_vars[]
28
+
29
+ .def middle_method
30
+ api.out "Middle method called from level 2"
31
+ .end
32
+
33
+ # Test deep method from level 3
34
+ .deep_method
@@ -0,0 +1,20 @@
1
+ # Level 3 - Deepest level
2
+ # This file defines variables and functions that should be accessible to all higher levels
3
+
4
+ .set deep_var="I am from level 3", deep_number=42
5
+
6
+ .func deep_function
7
+ "Deep function called with: " + param
8
+ .end
9
+
10
+ .func deep_math
11
+ (param.to_i * 2).to_s
12
+ .end
13
+
14
+ .def deep_method
15
+ api.out "Deep method called from level 3"
16
+ .end
17
+
18
+ This is content from level 3.
19
+ The deep variable is: $deep_var
20
+ The deep number is: $deep_number
@@ -0,0 +1,49 @@
1
+ # Main file - Level 1
2
+ # This file includes level2 and tests all variables and functions from all levels
3
+
4
+ .set main_var="I am from main level", main_number=999
5
+
6
+ .func main_function
7
+ "Main function called with: " + param
8
+ .end
9
+
10
+ .func test_all_levels
11
+ "Main: " + "I am from main level" + ", Middle: " + "I am from level 2" + ", Deep: " + "I am from level 3"
12
+ .end
13
+
14
+ # Include level2 (which includes level3)
15
+ .include level2.inc
16
+
17
+ # Test all variables from all levels
18
+ Main variable: $main_var
19
+ Middle variable: $middle_var
20
+ Deep variable: $deep_var
21
+
22
+ # Test all functions from all levels
23
+ Main function: $$main_function[from main]
24
+ Middle function: $$middle_function[from main]
25
+ Deep function: $$deep_function[from main]
26
+
27
+ # Test math functions
28
+ Deep math: $$deep_math[10] = 20
29
+ Main number: $main_number
30
+ Middle number: $middle_number
31
+ Deep number: $deep_number
32
+
33
+ # Test combined functions
34
+ Combine vars: $$combine_vars[]
35
+ Test all levels: $$test_all_levels[]
36
+
37
+ .def main_method
38
+ api.out "Main method called from main level"
39
+ .end
40
+
41
+ # Test methods from all levels
42
+ .main_method
43
+ .middle_method
44
+ .deep_method
45
+
46
+ # Test that we can still access everything after the include
47
+ Final test - deep function: $$deep_function[final call]
48
+ Final test - middle function: $$middle_function[final call]
49
+ Final test - main function: $$main_function[final call]
@@ -0,0 +1,7 @@
1
+ Testing function parameter handling...
2
+ <p>
3
+
4
+ Testing with no parameter: nil
5
+ Testing with empty parameter: nil
6
+ Testing with string parameter: hello
7
+ Testing with number parameter: 42
@@ -0,0 +1,10 @@
1
+ Testing function parameter handling...
2
+
3
+ .func testparam
4
+ param.empty? ? 'nil' : param
5
+ .end
6
+
7
+ Testing with no parameter: $$testparam[]
8
+ Testing with empty parameter: $$testparam[]
9
+ Testing with string parameter: $$testparam[hello]
10
+ Testing with number parameter: $$testparam[42]
@@ -23,6 +23,7 @@ h This file specifies which snapshots will/won't be run.
23
23
 
24
24
  # import/include/mixin, others...
25
25
 
26
+
26
27
  error_no_such_inc # Output BEFORE error doesn't get passed through ("leading" output)
27
28
  error_no_such_copy # ^ Same behavior as error_no_such_inc
28
29
  error_no_such_mixin # ^ Same behavior as error_missing_end
File without changes
@@ -0,0 +1,18 @@
1
+ 1 System Info Variables:
2
+ 2 Hostname: HAL9000
3
+ 3 /Platform: .*x86_64-darwin\d+/
4
+ 4 /Ruby Version: \d+\.\d+\.\d+/
5
+ 5 Livetext Version: 0.9.55
6
+ 6 <p>
7
+ 7
8
+ 8 System Info Functions:
9
+ 9 Hostname: HAL9000
10
+ 10 /Platform: .*x86_64-darwin\d+/
11
+ 11 /Ruby Version: \d+\.\d+\.\d+/
12
+ 12 Livetext Version: 0.9.55
13
+ 13 <p>
14
+ 14
15
+ 15 Date Formatting Functions:
16
+ 16 /Default: \d{4}-\d{2}-\d{2}/
17
+ 17 /Days ago: \d{4}-\d{2}-\d{2}/
18
+ 18 /Days from now: \d{4}-\d{2}-\d{2}/
@@ -0,0 +1,16 @@
1
+ System Info Variables:
2
+ Hostname: $Hostname
3
+ Platform: $Platform
4
+ Ruby Version: $RubyVersion
5
+ Livetext Version: $LivetextVersion
6
+
7
+ System Info Functions:
8
+ Hostname: $$hostname
9
+ Platform: $$platform
10
+ Ruby Version: $$ruby_version
11
+ Livetext Version: $$livetext_version
12
+
13
+ Date Formatting Functions:
14
+ Default: $$format_date
15
+ Days ago: $$days_ago[7]
16
+ Days from now: $$days_from_now[7]
data/test/unit/all.rb CHANGED
@@ -7,5 +7,12 @@ require_relative 'single'
7
7
  require_relative 'double'
8
8
  require_relative 'bracketed'
9
9
  require_relative 'variables'
10
+ require_relative 'variable_manager'
10
11
  require_relative 'functions'
12
+ require_relative 'function_registry'
13
+ require_relative 'mixin_functions_class'
14
+ require_relative 'formatter'
15
+ require_relative 'formatter_component'
16
+ require_relative 'core_methods'
17
+ require_relative 'stringparser'
11
18
 
data/test/unit/ast.rb ADDED
@@ -0,0 +1,90 @@
1
+ require 'minitest/autorun'
2
+
3
+ require_relative '../../lib/livetext/ast'
4
+
5
+ class TestingLivetextAST < Minitest::Test
6
+ def setup
7
+ @ast = LivetextAST.new
8
+ end
9
+
10
+ def test_ast_initialization
11
+ # Test that AST is properly initialized
12
+ assert(@ast, "AST should be initialized")
13
+ assert_instance_of(LivetextAST, @ast)
14
+ end
15
+
16
+ def test_basic_formatting
17
+ # Test basic text formatting
18
+ assert_equal([:bold, "bold"], @ast.parse_inline_formatting("*bold"))
19
+ assert_equal([:italic, "italic"], @ast.parse_inline_formatting("_italic"))
20
+ assert_equal([:code, "code"], @ast.parse_inline_formatting("`code"))
21
+ assert_equal([:strike, "strike"], @ast.parse_inline_formatting("~strike"))
22
+ end
23
+
24
+ def test_double_markers
25
+ # Test double markers
26
+ assert_equal([:bold, "bold"], @ast.parse_inline_formatting("**bold"))
27
+ assert_equal("**", @ast.parse_inline_formatting("**")) # standalone
28
+ assert_equal(" ** ", @ast.parse_inline_formatting(" ** ")) # surrounded by spaces
29
+ end
30
+
31
+ def test_bracketed_markers
32
+ # Test bracketed markers
33
+ assert_equal([:bold, "content"], @ast.parse_inline_formatting("*[content]"))
34
+ assert_equal([:italic, "content"], @ast.parse_inline_formatting("_[content]"))
35
+ assert_equal([:code, "content"], @ast.parse_inline_formatting("`[content]"))
36
+ assert_equal([], @ast.parse_inline_formatting("*[]")) # empty brackets disappear
37
+ end
38
+
39
+ def test_escaped_markers
40
+ # Test escaped markers
41
+ assert_equal("*literal", @ast.parse_inline_formatting("\\*literal"))
42
+ assert_equal("_literal", @ast.parse_inline_formatting("\\_literal"))
43
+ assert_equal("`literal", @ast.parse_inline_formatting("\\`literal"))
44
+ assert_equal("~literal", @ast.parse_inline_formatting("\\~literal"))
45
+ end
46
+
47
+ def test_mixed_content
48
+ # Test mixed content with text and formatting
49
+ assert_equal([:text, [:bold, "bold"], " text"], @ast.parse_inline_formatting("*bold text"))
50
+ assert_equal([:text, "Hello ", [:bold, "world!"]], @ast.parse_inline_formatting("Hello *world!"))
51
+ assert_equal([:text, [:bold, "bold,"], " ", [:italic, "italic,"], " and ", [:code, "code"]],
52
+ @ast.parse_inline_formatting("*bold, _italic, and `code"))
53
+ end
54
+
55
+ def test_double_marker_termination
56
+ # Test double markers ending at comma and period
57
+ assert_equal([:text, [:bold, "word"], ", text"], @ast.parse_inline_formatting("**word, text"))
58
+ assert_equal([:text, [:bold, "word"], ". text"], @ast.parse_inline_formatting("**word. text"))
59
+ end
60
+
61
+ def test_edge_cases
62
+ # Test edge cases
63
+ assert_equal([], @ast.parse_inline_formatting(nil))
64
+ assert_equal([], @ast.parse_inline_formatting(""))
65
+ assert_equal("plain text", @ast.parse_inline_formatting("plain text"))
66
+ end
67
+
68
+ def test_complex_examples
69
+ # Test more complex examples
70
+ input = "Hello *world and **bold, text with _italic and `code"
71
+ expected = [:text, "Hello ", [:bold, "world"], " and ", [:bold, "bold"], ", text with ", [:italic, "italic"], " and ", [:code, "code"]]
72
+ assert_equal(expected, @ast.parse_inline_formatting(input))
73
+ end
74
+
75
+ def test_bracketed_with_spaces
76
+ # Test bracketed markers with spaces
77
+ assert_equal([:text, [:bold, "This whole thing"], " is bold"],
78
+ @ast.parse_inline_formatting("*[This whole thing] is bold"))
79
+ assert_equal([:text, [:italic, "Important note"], " here"],
80
+ @ast.parse_inline_formatting("_[Important note] here"))
81
+ end
82
+
83
+ def test_escaped_in_context
84
+ # Test escaped markers in context
85
+ assert_equal("Literal *asterisks* here",
86
+ @ast.parse_inline_formatting("Literal \\*asterisks\\* here"))
87
+ assert_equal([:text, "Mixed ", [:bold, "bold"], " and *literal* text"],
88
+ @ast.parse_inline_formatting("Mixed *bold and \\*literal\\* text"))
89
+ end
90
+ end