Class: Jsonc::Merge::CommentTracker

Inherits:
Object
  • Object
show all
Defined in:
lib/jsonc/merge/comment_tracker.rb

Overview

Extracts and tracks comments with their line numbers from JSONC source.
JSONC supports both single-line (//) and multi-line (/* */) comments.

Examples:

Basic usage

tracker = CommentTracker.new(jsonc_source)
tracker.comments # => [{line: 1, indent: 0, text: "This is a comment"}]
tracker.comment_at(1) # => {line: 1, indent: 0, text: "This is a comment"}

Comment types

// Single-line comment
/* Block comment */
"key": "value" // Inline comment

Constant Summary collapse

SINGLE_LINE_COMMENT_REGEX =

Regex to match full-line single-line comments

%r{\A(\s*)//\s?(.*)\z}
BLOCK_COMMENT_SINGLE_REGEX =

Regex to match full-line block comments (single line)

%r{\A(\s*)/\*\s?(.*?)\s?\*/\s*\z}
INLINE_COMMENT_REGEX =

Regex to match inline single-line comments

%r{\s+//\s?(.*)$}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source) ⇒ CommentTracker

Initialize comment tracker by scanning the source

Parameters:

  • source (String)

    JSONC source code



36
37
38
39
40
41
# File 'lib/jsonc/merge/comment_tracker.rb', line 36

def initialize(source)
  @source = source
  @lines = source.lines.map(&:chomp)
  @comments = extract_comments
  @comments_by_line = @comments.group_by { |c| c[:line] }
end

Instance Attribute Details

#commentsArray<Hash> (readonly)

Returns All extracted comments with metadata.

Returns:

  • (Array<Hash>)

    All extracted comments with metadata



28
29
30
# File 'lib/jsonc/merge/comment_tracker.rb', line 28

def comments
  @comments
end

#linesArray<String> (readonly)

Returns Source lines.

Returns:

  • (Array<String>)

    Source lines



31
32
33
# File 'lib/jsonc/merge/comment_tracker.rb', line 31

def lines
  @lines
end

Instance Method Details

#blank_line?(line_num) ⇒ Boolean

Check if a line is blank

Parameters:

  • line_num (Integer)

    1-based line number

Returns:

  • (Boolean)


100
101
102
103
104
# File 'lib/jsonc/merge/comment_tracker.rb', line 100

def blank_line?(line_num)
  return false if line_num < 1 || line_num > @lines.length

  @lines[line_num - 1].strip.empty?
end

#comment_at(line_num) ⇒ Hash?

Get comment at a specific line

Parameters:

  • line_num (Integer)

    1-based line number

Returns:

  • (Hash, nil)

    Comment info or nil



47
48
49
# File 'lib/jsonc/merge/comment_tracker.rb', line 47

def comment_at(line_num)
  @comments_by_line[line_num]&.first
end

#comments_in_range(range) ⇒ Array<Hash>

Get all comments in a line range

Parameters:

  • range (Range)

    Range of 1-based line numbers

Returns:

  • (Array<Hash>)

    Comments in the range



55
56
57
# File 'lib/jsonc/merge/comment_tracker.rb', line 55

def comments_in_range(range)
  @comments.select { |c| range.cover?(c[:line]) }
end

#full_line_comment?(line_num) ⇒ Boolean

Check if a line is a full-line comment

Parameters:

  • line_num (Integer)

    1-based line number

Returns:

  • (Boolean)


91
92
93
94
# File 'lib/jsonc/merge/comment_tracker.rb', line 91

def full_line_comment?(line_num)
  comment = comment_at(line_num)
  comment&.dig(:full_line) || false
end

#inline_comment_at(line_num) ⇒ Hash?

Get trailing comment on the same line (inline comment)

Parameters:

  • line_num (Integer)

    1-based line number

Returns:

  • (Hash, nil)

    Inline comment or nil



82
83
84
85
# File 'lib/jsonc/merge/comment_tracker.rb', line 82

def inline_comment_at(line_num)
  comment = comment_at(line_num)
  comment if comment && !comment[:full_line]
end

#leading_comments_before(line_num) ⇒ Array<Hash>

Get leading comments before a line (consecutive comment lines immediately above)

Parameters:

  • line_num (Integer)

    1-based line number

Returns:

  • (Array<Hash>)

    Leading comments



63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/jsonc/merge/comment_tracker.rb', line 63

def leading_comments_before(line_num)
  leading = []
  current = line_num - 1

  while current >= 1
    comment = comment_at(current)
    break unless comment && comment[:full_line]

    leading.unshift(comment)
    current -= 1
  end

  leading
end