Class: Kotoshu::Documents::Document

Inherits:
Object
  • Object
show all
Defined in:
lib/kotoshu/documents/document.rb

Overview

Document abstraction for structure-aware spell and grammar checking. Pairs the flattened text a checker scans with the source positions of each TextNode, so errors can be reported against the original markup-bearing source.

Subclasses (or plugins) populate text_nodes with one entry per contiguous text run, each carrying the SourceRange it occupies in the source. The base class provides the bidirectional offset-mapping logic — it walks the nodes to recover a source range from a flattened offset.

Kotoshu ships only PlainTextDocument; format-specific parsers (Markdown, AsciiDoc, etc.) live in plugins.

Direct Known Subclasses

PlainTextDocument

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(text_nodes:, source: nil, format: :plain, language_code: nil) ⇒ Document

Returns a new instance of Document.

Parameters:

  • text_nodes (Array<TextNode>)

    one entry per contiguous text run, in source order

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

    the original markup-bearing source (kept around so consumers can slice it for context display)

  • format (Symbol) (defaults to: :plain)

    e.g. :plain, :markdown, :asciidoc

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

    ISO 639-1 code when known

Raises:

  • (ArgumentError)


27
28
29
30
31
32
33
34
35
# File 'lib/kotoshu/documents/document.rb', line 27

def initialize(text_nodes:, source: nil, format: :plain, language_code: nil)
  raise ArgumentError, "text_nodes cannot be empty" if text_nodes.nil? || text_nodes.empty?

  @text_nodes = text_nodes.freeze
  @source = source
  @format = format
  @language_code = language_code
  freeze
end

Instance Attribute Details

#formatObject (readonly)

Returns the value of attribute format.



19
20
21
# File 'lib/kotoshu/documents/document.rb', line 19

def format
  @format
end

#language_codeObject (readonly)

Returns the value of attribute language_code.



19
20
21
# File 'lib/kotoshu/documents/document.rb', line 19

def language_code
  @language_code
end

#sourceObject (readonly)

Returns the value of attribute source.



19
20
21
# File 'lib/kotoshu/documents/document.rb', line 19

def source
  @source
end

#text_nodesObject (readonly)

Returns the value of attribute text_nodes.



19
20
21
# File 'lib/kotoshu/documents/document.rb', line 19

def text_nodes
  @text_nodes
end

Instance Method Details

#each_node(&block) ⇒ Object

Iterate text nodes. Returns an Enumerator when no block given.



96
97
98
99
100
# File 'lib/kotoshu/documents/document.rb', line 96

def each_node(&block)
  return enum_for(:each_node) unless block

  @text_nodes.each(&block)
end

#flattened_lengthObject

Total character length of the flattened text.



46
47
48
# File 'lib/kotoshu/documents/document.rb', line 46

def flattened_length
  @text_nodes.sum(&:length)
end

#flattened_offset?(offset) ⇒ Boolean

True when offset falls within the flattened text.

Returns:

  • (Boolean)


51
52
53
# File 'lib/kotoshu/documents/document.rb', line 51

def flattened_offset?(offset)
  offset >= 0 && offset < flattened_length
end

#flattened_textString

The flattened text — concatenation of every node's text. This is what the checker actually scans.

Returns:

  • (String)


41
42
43
# File 'lib/kotoshu/documents/document.rb', line 41

def flattened_text
  @text_nodes.map(&:text).join
end

#source_range_at(flattened_offset) ⇒ SourceRange?

Recover the SourceRange of the TextNode that contains the given flattened offset. Returns nil for out-of-range offsets.

Note: this returns the node's source range, not a one-char slice. For a tighter range, use #source_range_for with a flattened sub-range.

Parameters:

  • flattened_offset (Integer)

    0-based offset in flattened text

Returns:



64
65
66
67
68
69
# File 'lib/kotoshu/documents/document.rb', line 64

def source_range_at(flattened_offset)
  return nil unless flattened_offset?(flattened_offset)

  node = node_at(flattened_offset)
  node&.source_range
end

#source_range_for(flattened_start, flattened_end = nil) ⇒ SourceRange?

Recover the SourceRange spanning a flattened sub-range. The start of the result is the start of the node containing flattened_start; the end is the end of the node containing flattened_end - 1. Useful for errors that span multiple text nodes (e.g. a grammar error like "an friend" that crosses a markup boundary).

Parameters:

  • flattened_start (Integer)

    inclusive

  • flattened_end (Integer) (defaults to: nil)

    exclusive; pass nil for "to end"

Returns:



81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/kotoshu/documents/document.rb', line 81

def source_range_for(flattened_start, flattened_end = nil)
  return nil unless flattened_offset?(flattened_start)

  end_offset = flattened_end ? flattened_end - 1 : flattened_length - 1
  end_offset = [end_offset, flattened_length - 1].min
  return nil unless flattened_offset?(end_offset)

  start_node = node_at(flattened_start)
  end_node = node_at(end_offset)
  return nil unless start_node && end_node

  start_node.source_range.union(end_node.source_range)
end