Skip to content

Add position tracking when normalizing doc comments - #3430

Open
Padmashree06 wants to merge 1 commit into
swiftlang:mainfrom
Padmashree06:go-to-def
Open

Padmashree06 wants to merge 1 commit into
swiftlang:mainfrom
Padmashree06:go-to-def

Conversation

@Padmashree06

Copy link
Copy Markdown
Contributor

Motivation

Trivia.docCommentValue normalizes documentation comments, but does not preserve the source position of each resulting line.

DocumentationLanguageService needs this mapping for features such as symbol-link diagnostics and go-to-definition, and currently maintains its own comment parsing and position tracking.

This change exposes the existing normalization logic with per-line source positions so DocumentationLanguageService can reuse it instead of duplicating it.

Changes

  • Added Trivia.DocCommentLine to represent a normalized documentation line and its source position.
  • Added Trivia.docCommentLines(startingAt:) to expose normalized lines with source positions.
  • Refactored docCommentValue to share the same extraction logic, preserving its existing behavior.

Testing

  • Added testDocCommentLinePositions to TriviaCommentValueTests.
  • All tests pass.

Follow-up

Update DocumentationLanguageService to use docCommentLines(startingAt:) instead of maintaining its own comment normalization and position mapping.

Comment thread Sources/SwiftSyntax/Trivia+commentValue.swift
@rintaro

rintaro commented Sep 11, 2026

Copy link
Copy Markdown
Member

@swift-ci Please test

@ktoso

ktoso commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

@swift-ci please test Windows

@ahoppen ahoppen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delayed review, @Padmashree06.

public var docCommentValue: String? {
var comments: [Substring] = []
var currentLineComments: [String] = [] // Reset line comments when encountering a block comment
extractDocCommentLines(tokenStart: AbsolutePosition(utf8Offset: 0))?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is passing in AbsolutePosition(utf8Offset: 0) a valid option here? Is it just because we don’t inspect DocCommentLine.position here? If so, could you explain that in a short comment?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I have added a comment explaining it!

var currentLineComments: [CommentEntry] = []
var isInsideDocLineCommentSection = false
var consecutiveNewlines = 0
var offset = 0 // UTF-8 byte offset from tokenStart, i.e. from the start of `pieces`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all we do with offset is to add it to tokenStart. Would it be clearer to have this be something like

Suggested change
var offset = 0 // UTF-8 byte offset from tokenStart, i.e. from the start of `pieces`
var currentPiecePosition = tokenStart
…
defer { currentPiecePosition += piece.sourceLength.utf8Length }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup! Modified the variable!

// very start of `wholeText` — lines 1..n of a block are always passed through unchanged.
return comments.flatMap { entry -> [DocCommentLine] in
guard entry.wholeText.hasPrefix(prefix) else {
return entry.lines.map { DocCommentLine(text: $0.text, position: $0.position) }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the map doing anything here? Isn’t it just mapping identity?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the map was just rebuilding the same values, it now returns entry.lines directly.

return DocCommentLine(text: line.text, position: line.position)
}
return DocCommentLine(
text: line.text.dropFirst(prefix.count),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we know that eg. the 2nd line here starts with prefix if we only check entry.wholeText above? I’m generally surprised that we need a distinction between the first element and any subsequent ones if we didn't need it before.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the per line loop, now we only check wholeText.hasPrefix(prefix) once and only strip lines[0], which is the start of the entry. Later lines are never checked or modified, same as before.

@Padmashree06

Copy link
Copy Markdown
Contributor Author

Sorry for the delayed review, @Padmashree06.

That's okay!! Thank you for the review!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants