docyard 0.5.0 → 0.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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +20 -1
  4. data/lib/docyard/build/static_generator.rb +1 -1
  5. data/lib/docyard/components/base_processor.rb +6 -0
  6. data/lib/docyard/components/code_block_diff_preprocessor.rb +104 -0
  7. data/lib/docyard/components/code_block_feature_extractor.rb +113 -0
  8. data/lib/docyard/components/code_block_focus_preprocessor.rb +77 -0
  9. data/lib/docyard/components/code_block_icon_detector.rb +40 -0
  10. data/lib/docyard/components/code_block_line_wrapper.rb +46 -0
  11. data/lib/docyard/components/code_block_options_preprocessor.rb +76 -0
  12. data/lib/docyard/components/code_block_patterns.rb +51 -0
  13. data/lib/docyard/components/code_block_processor.rb +135 -14
  14. data/lib/docyard/components/code_line_parser.rb +80 -0
  15. data/lib/docyard/components/code_snippet_import_preprocessor.rb +125 -0
  16. data/lib/docyard/components/registry.rb +4 -4
  17. data/lib/docyard/components/table_of_contents_processor.rb +1 -1
  18. data/lib/docyard/components/tabs_parser.rb +135 -4
  19. data/lib/docyard/components/tabs_range_finder.rb +42 -0
  20. data/lib/docyard/config/validator.rb +8 -0
  21. data/lib/docyard/config.rb +7 -0
  22. data/lib/docyard/icons/file_types.rb +0 -13
  23. data/lib/docyard/markdown.rb +13 -5
  24. data/lib/docyard/rack_application.rb +1 -1
  25. data/lib/docyard/renderer.rb +4 -3
  26. data/lib/docyard/templates/assets/css/code.css +12 -4
  27. data/lib/docyard/templates/assets/css/components/code-block.css +427 -24
  28. data/lib/docyard/templates/assets/css/components/navigation.css +12 -9
  29. data/lib/docyard/templates/assets/css/components/tabs.css +50 -44
  30. data/lib/docyard/templates/assets/css/variables.css +44 -0
  31. data/lib/docyard/templates/partials/_code_block.html.erb +50 -2
  32. data/lib/docyard/version.rb +1 -1
  33. metadata +11 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52a27eaf396879abae3d0b091e5a488629022bb777838fafc17c0bb07e15d65b
4
- data.tar.gz: ec6a567e2e411800f67f96351a80f7e60823ac399df8cac7e82b7c3a9a1370b5
3
+ metadata.gz: 34bf24256bf638697dbc86f9d146667a4f17594c365070313eb19e74f8ab5134
4
+ data.tar.gz: 357ea04d8822f47c98e2aee4e09ca9e8cc69b7e4d11cbdce1491874aaa459b5c
5
5
  SHA512:
6
- metadata.gz: 6e0d9995254e35e291250db40c9572d3bde1a76d86433d0fbcfb6e2a8602d6e68ad6be910d7a03036814dcdc0fba64289d68c760d9d6f1336376e2f709ec35b8
7
- data.tar.gz: 127131f44ea7140f227a8e95468b96e09256b5fced9ef557e37e5a1caefc888adb902a8b2d164474cb1f31ae0ddd4b76b1c4684b39e5125ddce17c84bbd754e7
6
+ metadata.gz: bc5c55d5ad69b73ef286eec3b4e202c7312b3dfd7d67b79d491612357e1c4e6e03d255a78af97706bdd5fe19353b7dd96f2da046a4c17f2e363342fc265b5628
7
+ data.tar.gz: 855fdcdcf312f3778fd204ca1fd36825c183b1abf0a5c938edff599693930d519668b8fdb0770f65ae00f9db43d1b794e31ca922b29dbde73143caf6abd2f64c
data/.rubocop.yml CHANGED
@@ -16,7 +16,7 @@ Metrics/BlockLength:
16
16
  - '*.gemspec'
17
17
 
18
18
  RSpec/ExampleLength:
19
- Max: 10
19
+ Max: 15
20
20
 
21
21
  Metrics/ClassLength:
22
22
  Max: 150
data/CHANGELOG.md CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2025-12-25
11
+
12
+ ### Added
13
+ - **Line Numbers** - Display line numbers on code blocks with `:line-numbers` or `:line-numbers=N` syntax, plus global config option (#33)
14
+ - **Line Highlighting** - Highlight specific lines in code blocks with `{1,3,5-7}` syntax (#34)
15
+ - **Diff Markers** - Show additions/deletions with `// [!code ++]` and `// [!code --]` comments, supports all major comment styles (#35)
16
+ - **Code Block Titles** - Add filename titles to code blocks with `[filename.js]` syntax, auto-detects file icons (#36)
17
+ - **Focus Mode** - Dim surrounding code with `// [!code focus]` to highlight important lines (#37)
18
+ - **Error/Warning Markers** - Highlight problematic lines with `// [!code error]` and `// [!code warning]` (#38)
19
+ - **Code Snippet Imports** - Import code from external files with `<<< @/filepath` syntax (#39)
20
+ - **VS Code Regions** - Import specific code sections with `<<< @/filepath#region-name` (#39)
21
+ - **Line Range Extraction** - Extract specific lines from imports with `<<< @/filepath{2-10}` (#39)
22
+ - **Language Override** - Override auto-detected language in imports with `<<< @/filepath{js}` (#39)
23
+
24
+ ### Changed
25
+ - Code block processor refactored for better maintainability with shared patterns module
26
+ - Improved code block CSS with support for all new marker types
27
+
10
28
  ## [0.5.0] - 2025-11-18
11
29
 
12
30
  ### Added
@@ -91,7 +109,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
91
109
  - Initial gem structure
92
110
  - Project scaffolding
93
111
 
94
- [Unreleased]: https://github.com/sanifhimani/docyard/compare/v0.5.0...HEAD
112
+ [Unreleased]: https://github.com/sanifhimani/docyard/compare/v0.6.0...HEAD
113
+ [0.6.0]: https://github.com/sanifhimani/docyard/compare/v0.5.0...v0.6.0
95
114
  [0.5.0]: https://github.com/sanifhimani/docyard/compare/v0.4.0...v0.5.0
96
115
  [0.4.0]: https://github.com/sanifhimani/docyard/compare/v0.3.0...v0.4.0
97
116
  [0.3.0]: https://github.com/sanifhimani/docyard/compare/v0.2.0...v0.3.0
@@ -10,7 +10,7 @@ module Docyard
10
10
  def initialize(config, verbose: false)
11
11
  @config = config
12
12
  @verbose = verbose
13
- @renderer = Renderer.new(base_url: config.build.base_url)
13
+ @renderer = Renderer.new(base_url: config.build.base_url, config: config)
14
14
  end
15
15
 
16
16
  def generate
@@ -12,6 +12,12 @@ module Docyard
12
12
  end
13
13
  end
14
14
 
15
+ attr_reader :context
16
+
17
+ def initialize(context = {})
18
+ @context = context
19
+ end
20
+
15
21
  def preprocess(content)
16
22
  content
17
23
  end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_processor"
4
+ require_relative "code_block_patterns"
5
+
6
+ module Docyard
7
+ module Components
8
+ class CodeBlockDiffPreprocessor < BaseProcessor
9
+ include CodeBlockPatterns
10
+
11
+ self.priority = 6
12
+
13
+ CODE_BLOCK_REGEX = /^```(\w*).*?\n(.*?)^```/m
14
+ TABS_BLOCK_REGEX = /^:::[ \t]*tabs[ \t]*\n.*?^:::[ \t]*$/m
15
+
16
+ def preprocess(content)
17
+ context[:code_block_diff_lines] ||= []
18
+ context[:code_block_error_lines] ||= []
19
+ context[:code_block_warning_lines] ||= []
20
+ @block_index = 0
21
+ @tabs_ranges = find_tabs_ranges(content)
22
+
23
+ content.gsub(CODE_BLOCK_REGEX) { |_| process_code_block(Regexp.last_match) }
24
+ end
25
+
26
+ private
27
+
28
+ def process_code_block(match)
29
+ return match[0] if inside_tabs?(match.begin(0))
30
+
31
+ result = extract_all_markers(match[2])
32
+ store_extracted_markers(result)
33
+ @block_index += 1
34
+ match[0].sub(match[2], result[:cleaned_content])
35
+ end
36
+
37
+ def store_extracted_markers(result)
38
+ context[:code_block_diff_lines][@block_index] = result[:diff_lines]
39
+ context[:code_block_error_lines][@block_index] = result[:error_lines]
40
+ context[:code_block_warning_lines][@block_index] = result[:warning_lines]
41
+ end
42
+
43
+ def extract_all_markers(code_content)
44
+ diff_info = extract_diff_lines(code_content)
45
+ error_info = extract_error_lines(diff_info[:cleaned_content])
46
+ warning_info = extract_warning_lines(error_info[:cleaned_content])
47
+
48
+ {
49
+ diff_lines: diff_info[:lines],
50
+ error_lines: error_info[:lines],
51
+ warning_lines: warning_info[:lines],
52
+ cleaned_content: warning_info[:cleaned_content]
53
+ }
54
+ end
55
+
56
+ def extract_diff_lines(code_content)
57
+ extract_marker_lines(code_content, DIFF_MARKER_PATTERN) do |match|
58
+ diff_type = match.captures.compact.first
59
+ diff_type == "++" ? :addition : :deletion
60
+ end
61
+ end
62
+
63
+ def extract_error_lines(code_content)
64
+ extract_marker_lines(code_content, ERROR_MARKER_PATTERN) { true }
65
+ end
66
+
67
+ def extract_warning_lines(code_content)
68
+ extract_marker_lines(code_content, WARNING_MARKER_PATTERN) { true }
69
+ end
70
+
71
+ def extract_marker_lines(code_content, pattern)
72
+ lines = code_content.lines
73
+ marker_lines = {}
74
+ cleaned_lines = []
75
+
76
+ lines.each_with_index do |line, index|
77
+ line_num = index + 1
78
+
79
+ if (match = line.match(pattern))
80
+ marker_lines[line_num] = yield(match)
81
+ cleaned_lines << line.gsub(pattern, "")
82
+ else
83
+ cleaned_lines << line
84
+ end
85
+ end
86
+
87
+ { lines: marker_lines, cleaned_content: cleaned_lines.join }
88
+ end
89
+
90
+ def inside_tabs?(position)
91
+ @tabs_ranges.any? { |range| range.cover?(position) }
92
+ end
93
+
94
+ def find_tabs_ranges(content)
95
+ ranges = []
96
+ content.scan(TABS_BLOCK_REGEX) do
97
+ match = Regexp.last_match
98
+ ranges << (match.begin(0)...match.end(0))
99
+ end
100
+ ranges
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "code_block_patterns"
4
+
5
+ module Docyard
6
+ module Components
7
+ module CodeBlockFeatureExtractor
8
+ include CodeBlockPatterns
9
+
10
+ CODE_FENCE_REGEX = /^```(\w+)(?:\s*\[([^\]]+)\])?(:\S+)?(?:\s*\{([^}\n]+)\})?[ \t]*\n(.*?)^```/m
11
+
12
+ module_function
13
+
14
+ def process_markdown(markdown)
15
+ blocks = []
16
+ cleaned = markdown.gsub(CODE_FENCE_REGEX) do
17
+ block_data = extract_block_data(Regexp.last_match)
18
+ blocks << block_data
19
+ "```#{block_data[:lang]}\n#{block_data[:cleaned_content]}```"
20
+ end
21
+ { cleaned_markdown: cleaned, blocks: blocks }
22
+ end
23
+
24
+ def extract_block_data(match)
25
+ code_content = match[5]
26
+ diff_info = extract_diff_lines(code_content)
27
+ focus_info = extract_focus_lines(diff_info[:cleaned_content])
28
+ error_info = extract_error_lines(focus_info[:cleaned_content])
29
+ warning_info = extract_warning_lines(error_info[:cleaned_content])
30
+
31
+ build_block_result(match, diff_info, focus_info, error_info, warning_info)
32
+ end
33
+
34
+ def build_block_result(match, diff_info, focus_info, error_info, warning_info)
35
+ {
36
+ lang: match[1],
37
+ title: match[2],
38
+ option: match[3],
39
+ highlights: parse_highlights(match[4]),
40
+ diff_lines: diff_info[:lines],
41
+ focus_lines: focus_info[:lines],
42
+ error_lines: error_info[:lines],
43
+ warning_lines: warning_info[:lines],
44
+ cleaned_content: warning_info[:cleaned_content]
45
+ }
46
+ end
47
+
48
+ def parse_highlights(highlights_str)
49
+ return [] if highlights_str.nil? || highlights_str.strip.empty?
50
+
51
+ highlights_str.split(",").flat_map { |part| parse_highlight_part(part.strip) }.uniq.sort
52
+ end
53
+
54
+ def parse_highlight_part(part)
55
+ return (part.split("-")[0].to_i..part.split("-")[1].to_i).to_a if part.include?("-")
56
+
57
+ [part.to_i]
58
+ end
59
+
60
+ def extract_diff_lines(code_content)
61
+ lines = code_content.lines
62
+ diff_lines = {}
63
+ cleaned_lines = []
64
+
65
+ lines.each_with_index do |line, index|
66
+ line_num = index + 1
67
+
68
+ if (match = line.match(DIFF_MARKER_PATTERN))
69
+ diff_type = match.captures.compact.first
70
+ diff_lines[line_num] = diff_type == "++" ? :addition : :deletion
71
+ cleaned_line = line.gsub(DIFF_MARKER_PATTERN, "")
72
+ cleaned_lines << cleaned_line
73
+ else
74
+ cleaned_lines << line
75
+ end
76
+ end
77
+
78
+ { lines: diff_lines, cleaned_content: cleaned_lines.join }
79
+ end
80
+
81
+ def extract_focus_lines(code_content)
82
+ extract_marker_lines(code_content, FOCUS_MARKER_PATTERN)
83
+ end
84
+
85
+ def extract_error_lines(code_content)
86
+ extract_marker_lines(code_content, ERROR_MARKER_PATTERN)
87
+ end
88
+
89
+ def extract_warning_lines(code_content)
90
+ extract_marker_lines(code_content, WARNING_MARKER_PATTERN)
91
+ end
92
+
93
+ def extract_marker_lines(code_content, pattern)
94
+ lines = code_content.lines
95
+ marker_lines = {}
96
+ cleaned_lines = []
97
+
98
+ lines.each_with_index do |line, index|
99
+ line_num = index + 1
100
+
101
+ if line.match?(pattern)
102
+ marker_lines[line_num] = true
103
+ cleaned_lines << line.gsub(pattern, "")
104
+ else
105
+ cleaned_lines << line
106
+ end
107
+ end
108
+
109
+ { lines: marker_lines, cleaned_content: cleaned_lines.join }
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_processor"
4
+
5
+ module Docyard
6
+ module Components
7
+ class CodeBlockFocusPreprocessor < BaseProcessor
8
+ self.priority = 7
9
+
10
+ FOCUS_MARKER_PATTERN = %r{
11
+ (?:
12
+ //\s*\[!code\s+focus\] |
13
+ \#\s*\[!code\s+focus\] |
14
+ /\*\s*\[!code\s+focus\]\s*\*/ |
15
+ --\s*\[!code\s+focus\] |
16
+ <!--\s*\[!code\s+focus\]\s*--> |
17
+ ;\s*\[!code\s+focus\]
18
+ )[^\S\n]*
19
+ }x
20
+
21
+ CODE_BLOCK_REGEX = /^```(\w*).*?\n(.*?)^```/m
22
+ TABS_BLOCK_REGEX = /^:::[ \t]*tabs[ \t]*\n.*?^:::[ \t]*$/m
23
+
24
+ def preprocess(content)
25
+ context[:code_block_focus_lines] ||= []
26
+ @block_index = 0
27
+ @tabs_ranges = find_tabs_ranges(content)
28
+
29
+ content.gsub(CODE_BLOCK_REGEX) { |_| process_code_block(Regexp.last_match) }
30
+ end
31
+
32
+ private
33
+
34
+ def process_code_block(match)
35
+ return match[0] if inside_tabs?(match.begin(0))
36
+
37
+ focus_info = extract_focus_lines(match[2])
38
+ context[:code_block_focus_lines][@block_index] = focus_info[:lines]
39
+ @block_index += 1
40
+ match[0].sub(match[2], focus_info[:cleaned_content])
41
+ end
42
+
43
+ def extract_focus_lines(code_content)
44
+ lines = code_content.lines
45
+ focus_lines = {}
46
+ cleaned_lines = []
47
+
48
+ lines.each_with_index do |line, index|
49
+ line_num = index + 1
50
+
51
+ if line.match?(FOCUS_MARKER_PATTERN)
52
+ focus_lines[line_num] = true
53
+ cleaned_line = line.gsub(FOCUS_MARKER_PATTERN, "")
54
+ cleaned_lines << cleaned_line
55
+ else
56
+ cleaned_lines << line
57
+ end
58
+ end
59
+
60
+ { lines: focus_lines, cleaned_content: cleaned_lines.join }
61
+ end
62
+
63
+ def inside_tabs?(position)
64
+ @tabs_ranges.any? { |range| range.cover?(position) }
65
+ end
66
+
67
+ def find_tabs_ranges(content)
68
+ ranges = []
69
+ content.scan(TABS_BLOCK_REGEX) do
70
+ match = Regexp.last_match
71
+ ranges << (match.begin(0)...match.end(0))
72
+ end
73
+ ranges
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../language_mapping"
4
+
5
+ module Docyard
6
+ module Components
7
+ module CodeBlockIconDetector
8
+ MANUAL_ICON_PATTERN = /^:([a-z0-9-]+):\s*(.+)$/i
9
+
10
+ module_function
11
+
12
+ def detect(title, language)
13
+ return { title: nil, icon: nil, icon_source: nil } if title.nil?
14
+
15
+ if (match = title.match(MANUAL_ICON_PATTERN))
16
+ return {
17
+ title: match[2].strip,
18
+ icon: match[1],
19
+ icon_source: "phosphor"
20
+ }
21
+ end
22
+
23
+ icon, icon_source = auto_detect_icon(language)
24
+ { title: title, icon: icon, icon_source: icon_source }
25
+ end
26
+
27
+ def auto_detect_icon(language)
28
+ return [nil, nil] if language.nil?
29
+
30
+ if LanguageMapping.terminal_language?(language)
31
+ %w[terminal-window phosphor]
32
+ elsif (ext = LanguageMapping.extension_for(language))
33
+ [ext, "file-extension"]
34
+ else
35
+ %w[file phosphor]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "code_line_parser"
4
+
5
+ module Docyard
6
+ module Components
7
+ module CodeBlockLineWrapper
8
+ module_function
9
+
10
+ def wrap_code_block(html, block_data)
11
+ html.gsub(%r{<pre[^>]*><code[^>]*>(.*?)</code></pre>}m) do
12
+ pre_match = Regexp.last_match(0)
13
+ code_content = Regexp.last_match(1)
14
+ lines = CodeLineParser.new(code_content).parse
15
+ wrapped_lines = wrap_lines_with_classes(lines, block_data)
16
+ pre_match.sub(code_content, wrapped_lines.join)
17
+ end
18
+ end
19
+
20
+ def wrap_lines_with_classes(lines, block_data)
21
+ lines.each_with_index.map do |line, index|
22
+ source_line = index + 1
23
+ display_line = block_data[:start_line] + index
24
+ classes = build_line_classes(source_line, display_line, block_data)
25
+ %(<span class="#{classes}">#{line}</span>)
26
+ end
27
+ end
28
+
29
+ DIFF_CLASSES = { addition: "docyard-code-line--diff-add", deletion: "docyard-code-line--diff-remove" }.freeze
30
+
31
+ def build_line_classes(source_line, display_line, block_data)
32
+ (["docyard-code-line"] + feature_classes(source_line, display_line, block_data)).join(" ")
33
+ end
34
+
35
+ def feature_classes(source_line, display_line, block_data)
36
+ [
37
+ ("docyard-code-line--highlighted" if block_data[:highlights].include?(display_line)),
38
+ DIFF_CLASSES[block_data[:diff_lines][source_line]],
39
+ ("docyard-code-line--focus" if block_data[:focus_lines][source_line]),
40
+ ("docyard-code-line--error" if block_data[:error_lines][source_line]),
41
+ ("docyard-code-line--warning" if block_data[:warning_lines][source_line])
42
+ ].compact
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_processor"
4
+
5
+ module Docyard
6
+ module Components
7
+ class CodeBlockOptionsPreprocessor < BaseProcessor
8
+ self.priority = 5
9
+
10
+ CODE_FENCE_REGEX = /^```(\w+)(?:\s*\[([^\]]+)\])?(:\S+)?(?:\s*\{([^}\n]+)\})?/
11
+ TABS_BLOCK_REGEX = /^:::[ \t]*tabs[ \t]*\n.*?^:::[ \t]*$/m
12
+
13
+ def preprocess(content)
14
+ context[:code_block_options] ||= []
15
+ @tabs_ranges = find_tabs_ranges(content)
16
+
17
+ process_code_fences(content)
18
+ end
19
+
20
+ private
21
+
22
+ def process_code_fences(content)
23
+ result = +""
24
+ last_end = 0
25
+
26
+ content.scan(CODE_FENCE_REGEX) do
27
+ match = Regexp.last_match
28
+ result << content[last_end...match.begin(0)]
29
+ result << process_fence_match(match)
30
+ last_end = match.end(0)
31
+ end
32
+
33
+ result << content[last_end..]
34
+ end
35
+
36
+ def process_fence_match(match)
37
+ store_code_block_options(match) unless inside_tabs?(match.begin(0))
38
+ "```#{match[1]}"
39
+ end
40
+
41
+ def store_code_block_options(match)
42
+ context[:code_block_options] << {
43
+ lang: match[1],
44
+ title: match[2],
45
+ option: match[3],
46
+ highlights: parse_highlights(match[4])
47
+ }
48
+ end
49
+
50
+ def inside_tabs?(position)
51
+ @tabs_ranges.any? { |range| range.cover?(position) }
52
+ end
53
+
54
+ def find_tabs_ranges(content)
55
+ ranges = []
56
+ content.scan(TABS_BLOCK_REGEX) do
57
+ match = Regexp.last_match
58
+ ranges << (match.begin(0)...match.end(0))
59
+ end
60
+ ranges
61
+ end
62
+
63
+ def parse_highlights(highlights_str)
64
+ return [] if highlights_str.nil? || highlights_str.strip.empty?
65
+
66
+ highlights_str.split(",").flat_map { |part| parse_highlight_part(part.strip) }.uniq.sort
67
+ end
68
+
69
+ def parse_highlight_part(part)
70
+ return (part.split("-")[0].to_i..part.split("-")[1].to_i).to_a if part.include?("-")
71
+
72
+ [part.to_i]
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docyard
4
+ module Components
5
+ module CodeBlockPatterns
6
+ DIFF_MARKER_PATTERN = %r{
7
+ (?:
8
+ //\s*\[!code\s*([+-]{2})\] |
9
+ \#\s*\[!code\s*([+-]{2})\] |
10
+ /\*\s*\[!code\s*([+-]{2})\]\s*\*/ |
11
+ --\s*\[!code\s*([+-]{2})\] |
12
+ <!--\s*\[!code\s*([+-]{2})\]\s*--> |
13
+ ;\s*\[!code\s*([+-]{2})\]
14
+ )[^\S\n]*
15
+ }x
16
+
17
+ FOCUS_MARKER_PATTERN = %r{
18
+ (?:
19
+ //\s*\[!code\s+focus\] |
20
+ \#\s*\[!code\s+focus\] |
21
+ /\*\s*\[!code\s+focus\]\s*\*/ |
22
+ --\s*\[!code\s+focus\] |
23
+ <!--\s*\[!code\s+focus\]\s*--> |
24
+ ;\s*\[!code\s+focus\]
25
+ )[^\S\n]*
26
+ }x
27
+
28
+ ERROR_MARKER_PATTERN = %r{
29
+ (?:
30
+ //\s*\[!code\s+error\] |
31
+ \#\s*\[!code\s+error\] |
32
+ /\*\s*\[!code\s+error\]\s*\*/ |
33
+ --\s*\[!code\s+error\] |
34
+ <!--\s*\[!code\s+error\]\s*--> |
35
+ ;\s*\[!code\s+error\]
36
+ )[^\S\n]*
37
+ }x
38
+
39
+ WARNING_MARKER_PATTERN = %r{
40
+ (?:
41
+ //\s*\[!code\s+warning\] |
42
+ \#\s*\[!code\s+warning\] |
43
+ /\*\s*\[!code\s+warning\]\s*\*/ |
44
+ --\s*\[!code\s+warning\] |
45
+ <!--\s*\[!code\s+warning\]\s*--> |
46
+ ;\s*\[!code\s+warning\]
47
+ )[^\S\n]*
48
+ }x
49
+ end
50
+ end
51
+ end