Class: Coradoc::AsciiDoc::ParseError

Inherits:
Error
  • Object
show all
Defined in:
lib/coradoc/asciidoc/parse_error.rb

Overview

Custom error class for parsing failures

Provides detailed context about where parsing failed, including line number, column position, and source snippet.

Examples:

Raising a parse error

raise Coradoc::AsciiDoc::ParseError.new(
  "Unexpected token",
  line: 42,
  column: 10,
  source: "line with error",
  suggestion: "Did you mean X?"
)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(message, line: nil, column: nil, source_line: nil, source: nil, suggestion: nil, cause: nil) ⇒ ParseError

Create a new ParseError

Parameters:

  • message (String)

    Error description

  • line (Integer, nil) (defaults to: nil)

    Line number (1-indexed)

  • column (Integer, nil) (defaults to: nil)

    Column position (1-indexed)

  • source_line (String, nil) (defaults to: nil)

    The source line containing the error

  • source (String, nil) (defaults to: nil)

    Full source content

  • suggestion (String, nil) (defaults to: nil)

    Suggestion for fixing the error

  • cause (Exception, nil) (defaults to: nil)

    Original exception that caused this error



47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/coradoc/asciidoc/parse_error.rb', line 47

def initialize(message, line: nil, column: nil, source_line: nil, source: nil, suggestion: nil, cause: nil)
  @line = line
  @column = column
  @source_line = source_line
  @source = source
  @suggestion = suggestion
  @cause = cause

  full_message = build_full_message(message)
  super(full_message)

  set_backtrace(cause.backtrace) if cause&.backtrace
end

Instance Attribute Details

#causeException? (readonly)

Returns Original exception that caused this error.

Returns:

  • (Exception, nil)

    Original exception that caused this error



36
37
38
# File 'lib/coradoc/asciidoc/parse_error.rb', line 36

def cause
  @cause
end

#columnInteger? (readonly)

Returns Column position where error occurred.

Returns:

  • (Integer, nil)

    Column position where error occurred



24
25
26
# File 'lib/coradoc/asciidoc/parse_error.rb', line 24

def column
  @column
end

#lineInteger? (readonly)

Returns Line number where error occurred.

Returns:

  • (Integer, nil)

    Line number where error occurred



21
22
23
# File 'lib/coradoc/asciidoc/parse_error.rb', line 21

def line
  @line
end

#sourceString? (readonly)

Returns Full source content (for multi-line context).

Returns:

  • (String, nil)

    Full source content (for multi-line context)



33
34
35
# File 'lib/coradoc/asciidoc/parse_error.rb', line 33

def source
  @source
end

#source_lineString? (readonly)

Returns Source line containing the error.

Returns:

  • (String, nil)

    Source line containing the error



27
28
29
# File 'lib/coradoc/asciidoc/parse_error.rb', line 27

def source_line
  @source_line
end

#suggestionString? (readonly)

Returns Helpful suggestion for fixing the error.

Returns:

  • (String, nil)

    Helpful suggestion for fixing the error



30
31
32
# File 'lib/coradoc/asciidoc/parse_error.rb', line 30

def suggestion
  @suggestion
end

Class Method Details

.extract_location(exception) ⇒ Array<Integer, Integer>

Extract line and column from Parslet error

Parameters:

  • exception (Parslet::ParseFailed)

    The Parslet exception

Returns:

  • (Array<Integer, Integer>)

    Line and column numbers



114
115
116
117
118
119
120
121
122
123
# File 'lib/coradoc/asciidoc/parse_error.rb', line 114

def self.extract_location(exception)
  return [nil, nil] unless exception.is_a?(Parslet::ParseFailed)

  cause = exception.cause
  return [nil, nil] unless cause

  [cause.source_line, cause.source_column]
rescue NoMethodError
  [nil, nil]
end

.extract_source_line(source, line) ⇒ String?

Extract the relevant source line

Parameters:

  • source (String, nil)

    Full source text

  • line (Integer, nil)

    Line number

Returns:

  • (String, nil)

    The source line



130
131
132
133
134
135
# File 'lib/coradoc/asciidoc/parse_error.rb', line 130

def self.extract_source_line(source, line)
  return nil if source.nil? || line.nil?

  lines = source.split("\n")
  lines[line - 1] if line.positive? && line <= lines.length
end

.from_parslet(exception, source = nil) ⇒ ParseError

Create a ParseError from a Parslet exception

Parameters:

  • exception (Parslet::ParseFailed)

    The Parslet exception

  • source (String) (defaults to: nil)

    The original source text

Returns:

  • (ParseError)

    A new ParseError with extracted context



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/coradoc/asciidoc/parse_error.rb', line 89

def self.from_parslet(exception, source = nil)
  return exception if exception.is_a?(ParseError)

  line, column = extract_location(exception)
  source_line = extract_source_line(source, line)
  suggestion = generate_suggestion(exception)

  new(
    exception.message,
    line: line,
    column: column,
    source_line: source_line,
    source: source,
    suggestion: suggestion,
    cause: exception
  )
rescue StandardError
  # If we can't extract details, wrap the original exception
  new(exception.message, cause: exception)
end

.generate_suggestion(exception) ⇒ String?

Generate a helpful suggestion based on the error

Parameters:

  • exception (Exception)

    The parsing exception

Returns:

  • (String, nil)

    A suggestion string



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/coradoc/asciidoc/parse_error.rb', line 141

def self.generate_suggestion(exception)
  message = exception.message.to_s.downcase

  case message
  when /expected.*heading/
    "Make sure headings start with '=' followed by a space"
  when /expected.*list/
    "List items should start with '*', '.', or a term followed by '::'"
  when /expected.*table/
    "Tables require '|===' delimiters and '|' for cell separators"
  when /expected.*block/
    'Block delimiters should be 4 identical characters (----, ====, etc.)'
  when /expected.*attribute/
    "Attributes should be in the format ':name: value'"
  when /unexpected.*end/
    'Check for missing closing delimiters or incomplete syntax'
  end
end

Instance Method Details

#build_full_message(base_message) ⇒ String

Build a formatted error message with context

Parameters:

  • base_message (String)

    The base error message

Returns:

  • (String)

    Formatted error message with location context



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/coradoc/asciidoc/parse_error.rb', line 65

def build_full_message(base_message)
  parts = [base_message]

  if line && column
    parts << "at line #{line}, column #{column}"
  elsif line
    parts << "at line #{line}"
  end

  if source_line
    parts << "\n  > #{source_line}"
    parts << "  > #{' ' * (column - 1)}^" if column&.positive?
  end

  parts << "\n  Suggestion: #{suggestion}" if suggestion

  parts.join("\n")
end