Class: Kotoshu::Documents::MarkdownDocument

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

Overview

Markdown document implementation.

Handles Markdown files with AST parsing for structured navigation.

Examples:

Creating a markdown document

doc = MarkdownDocument.new("# Title\n\nParagraph text")
doc.text_nodes.each { |node| puts node.text }

Constant Summary

Constants inherited from Document

Document::FORMATS

Instance Attribute Summary

Attributes inherited from Document

#content, #format, #language_code

Instance Method Summary collapse

Methods inherited from Document

detect_format, detect_language_from_path, from_file, from_string, #line_count, #word_count

Constructor Details

#initialize(content, format: :markdown, language_code: 'en') ⇒ MarkdownDocument

Create a new markdown document.

Parameters:

  • content (String)

    The document content

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

    Document format (must be :markdown)

  • language_code (String) (defaults to: 'en')

    Language code

Raises:

  • (ArgumentError)


23
24
25
26
27
28
29
# File 'lib/kotoshu/documents/markdown_document.rb', line 23

def initialize(content, format: :markdown, language_code: 'en')
  raise ArgumentError, "Format must be :markdown" unless format == :markdown

  super(content, format: format, language_code: language_code)
  @parsed = false
  @ast = nil
end

Instance Method Details

#apply(corrections) ⇒ MarkdownDocument

Apply corrections and return new document.

Parameters:

Returns:



139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/kotoshu/documents/markdown_document.rb', line 139

def apply(corrections)
  return self if corrections.empty?

  # Apply corrections one by one
  result = self
  corrections.each do |error|
    suggestion = error.recommended_suggestion
    result = result.replace_node(error.location, suggestion.word)
  end

  result
end

#context_for(location, window: 2) ⇒ Models::Context

Get context around a location.

For markdown, navigates the AST to find surrounding context.

Parameters:

  • location (Location)

    The error location

  • window (Integer) (defaults to: 2)

    Number of sibling elements before/after

Returns:



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/kotoshu/documents/markdown_document.rb', line 76

def context_for(location, window: 2)
  return plain_text_context(location, window: 5) if location.line_column?

  parse unless @parsed

  # For node-based locations, find parent and siblings
  parent_path = location.node_path[0..-2]
  current_type = location.node_path.last

  parent = navigate_ast(@ast, parent_path)
  return Models::Context.new(before: "", current: "", after: "", location: location, window: window) unless parent

  # Find siblings around current element
  siblings = extract_siblings(parent)
  current_idx = siblings.find_index { |s| s[:type] == current_type }

  return Models::Context.new(before: "", current: "", after: "", location: location, window: window) unless current_idx

  before_sibs = siblings[[0, current_idx - window].max..current_idx - 1]
  after_sibs = siblings[(current_idx + 1)..(current_idx + window)]

  before = before_sibs.map { |s| text_from_node(s) }.join("\n")
  current = text_from_node(parent)
  after = after_sibs.map { |s| text_from_node(s) }.join("\n")

  Models::Context.new(
    before: before,
    current: current,
    after: after,
    location: location,
    window: window
  )
end

#get_node(path) ⇒ Object?

Get node at a specific path in the AST.

Parameters:

  • path (Array)

    Node path (e.g., [:document, :p, 1])

Returns:

  • (Object, nil)

    The node or nil



63
64
65
66
67
# File 'lib/kotoshu/documents/markdown_document.rb', line 63

def get_node(path)
  parse unless @parsed

  navigate_ast(@ast, path)
end

#nameString

Document name for display.

Returns:

  • (String)

    Document name



155
156
157
# File 'lib/kotoshu/documents/markdown_document.rb', line 155

def name
  "markdown"
end

#parseHash

Parse the markdown document into an AST.

Returns:

  • (Hash)

    The parsed AST



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/kotoshu/documents/markdown_document.rb', line 34

def parse
  return @ast if @parsed

  begin
    require 'kramdown'
  rescue LoadError
    raise "Kramdown gem not available. Add 'kramdown' to Gemfile"
  end

  kd = Kramdown::Document.new(content)
  @ast = kd.to_hash
  @parsed = true

  @ast
end

#replace_node(location, new_text) ⇒ Object

Replace text at a specific location.

Navigates the AST to find the text node and replaces it, then regenerates markdown.

return [MarkdownDocument] New document with replacement

Parameters:

  • location (Location)

    The location to replace

  • new_text (String)

    The new text



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/kotoshu/documents/markdown_document.rb', line 118

def replace_node(location, new_text)
  parse unless @parsed

  # Navigate to the node and replace its text
  modified_ast = replace_in_ast(@ast, location.node_path, new_text)

  # Regenerate markdown from modified AST
  begin
    require 'kramdown'
    new_content = Kramdown::Converter.new(modified_ast).to_kramdown
  rescue LoadError
    raise "Kramdown gem not available. Add 'kramdown' to Gemfile"
  end

  MarkdownDocument.new(new_content, @format, @language_code)
end

#text_nodesArray<TextNode>

Get all text nodes for spell checking.

Extracts text from the AST, skipping code blocks.

Returns:

  • (Array<TextNode>)

    Text nodes in the document



55
56
57
# File 'lib/kotoshu/documents/markdown_document.rb', line 55

def text_nodes
  extract_text_nodes
end