Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle Markdown and Tutorial files in textDocument/doccDocumentation #1959

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

matthewbastien
Copy link
Member

@matthewbastien matthewbastien commented Jan 28, 2025

Adds a handler to DocumentationLanguageService for the textDocument/doccDocumentation LSP extension. The request is handled much in the same way as for SwiftLanguageService where the SwiftDocC convert request is assembled and then sent to SwiftDocC for processing. All of the SwiftDocC specific code has been moved into a new DocCDocumentation module to try and keep SourceKit-LSP clean.

SwiftDocC supports four different kinds of Markdown/Tutorial files:

  • Symbol Extensions - Written in markdown with a symbol link surrounded by double backticks as the header. The symbol link must be found in the index and matched to a symbol within a Swift file.
  • Articles - Written in markdown as a standalone page. Can contain links to other Markdown/Tutorial content.
  • Tutorials - Uses the *.tutorial file extension and represents a self-contained tutorial.
  • Tutorial Overviews - Also uses the *.tutorial file extension, but represents a group of tutorials.

In addition to finding the Swift symbol for a symbol extension page, we must do the reverse when generating documentation for a symbol within a Swift file. This involves looking for *.docc documentation catalogs within the target and asking SwiftDocC to build an index for us. We can then retrieve all of the markdown extension pages and their associated symbols to do the lookup.

@matthewbastien
Copy link
Member Author

@swift-ci please test

1 similar comment
@matthewbastien
Copy link
Member Author

@swift-ci please test

@matthewbastien
Copy link
Member Author

@swift-ci please test windows platform

@matthewbastien matthewbastien force-pushed the markdown-documentation branch from aa29b89 to 2530549 Compare March 19, 2025 20:28
@matthewbastien
Copy link
Member Author

@swift-ci please test

@matthewbastien
Copy link
Member Author

@swift-ci please test windows platform

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

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

Thanks for working on this @matthewbastien 🙏🏽

I left some comments inline. On a layering level, I think we should not build the DocCDocumentation module at all if we are building SourceKit-LSP without docc support. That way DocCDocumentation doesn’t need any #if statements and we should be able to eliminate all the protocols. Instead the code in the SourceKitLSP module would need to be guarded by #if statements.

data: try JSONEncoder().encode(
SymbolGraph(
metadata: SymbolGraph.Metadata(
formatVersion: SymbolGraph.SemanticVersion(major: 0, minor: 5, patch: 0),
Copy link
Member

Choose a reason for hiding this comment

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

Why 0.5.0?

Copy link
Member Author

Choose a reason for hiding this comment

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

No reason in particular. It's the version sourcekitd was reporting in its symbol graph. I'm not sure how relevant that version number is to SwiftDocC.

Comment on lines 46 to 53
var doccCatalogURL: URL? {
var pathComponents = self.pathComponents
var result = self
while let lastPathComponent = pathComponents.last {
if lastPathComponent.hasSuffix(".docc") {
return result
}
pathComponents.removeLast()
result.deleteLastPathComponent()
}
return nil
}
Copy link
Member

Choose a reason for hiding this comment

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

This would return incorrect results if the user chose to put their package in a directory that ends with .docc, right? I mean, questionable choice but it would be better to guard against that. What do you think about extending the isHeader filed in SourceKitSourceItemData to a kind filed, which can then be normal source file, header, or docc catalog?

When deserializing, I think it would be good to continue supporting the isHeader field for a while for backwards compatibility but within SourceKit-LSP, we could use that kind.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea. I added the relevant code to SourceKitSourceItemData. LMK if it functions as you expected as I'm not all that familiar with that section of the code base.

Comment on lines 143 to 167
let snapshot = try await self.latestSnapshot(for: uri)
let snapshot = try await self.latestSnapshotOrDisk(for: uri)
Copy link
Member

Choose a reason for hiding this comment

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

This isn’t great because it means that we can send cursor info requests to sourcekitd when the document isn’t open. Today, sourcekitd falls back to reading the file on disk if it isn’t open but I don’t want to add more reliance on this.

Also, this reminds me of an another problem: The location you get from the index will always be from the last time that the index was updated. Even if indexing was instant, this will be the last time the file was saved, but we’re now performing cursor info on the edited version of the document. We need to either:

  • Not rely on the index to find the location of the symbol, which will be tricky
  • Open a separate helper document in sourcekitd that matches the on-disk state, similar to what we used to do here:
    let helperDocumentName = "DocumentSymbols:" + snapshot.document.uri.pseudoPath
    let skreq = SKDRequestDictionary(sourcekitd: self.sourcekitd)
    skreq[keys.request] = self.requests.editor_open
    skreq[keys.name] = helperDocumentName
    skreq[keys.sourcetext] = snapshot.text
    skreq[keys.syntactic_only] = 1

Copy link
Member Author

Choose a reason for hiding this comment

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

I went with the helper document here to ensure that sourcekitd knows of the most recent file contents. We'll have to think a bit more about how we can handle an out of date index. FWIW in my testing I never encountered any issues using the position, though I was using a pretty simple project.

// Do a lookup to find the top level symbol
let topLevelSymbolName = components.removeLast().name
var topLevelSymbolOccurrences = [SymbolOccurrence]()
index.forEachCanonicalSymbolOccurrence(byName: topLevelSymbolName) { symbolOccurrence in
Copy link
Member

Choose a reason for hiding this comment

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

How do we resolve overloads here? Eg. if you have foo(x: Int) and foo(y: Int) in your module, how do we know which one to pick?

@matthewbastien
Copy link
Member Author

@swift-ci please test

@matthewbastien matthewbastien force-pushed the markdown-documentation branch from bbaa6e3 to 6306675 Compare March 28, 2025 19:11
@matthewbastien
Copy link
Member Author

@swift-ci please test

1 similar comment
@matthewbastien
Copy link
Member Author

@swift-ci please test

@matthewbastien matthewbastien force-pushed the markdown-documentation branch from 22842b3 to 2bdc4c8 Compare April 1, 2025 20:25
@matthewbastien
Copy link
Member Author

@swift-ci please test

@matthewbastien
Copy link
Member Author

@swift-ci please test windows platform

@matthewbastien matthewbastien marked this pull request as ready for review April 1, 2025 20:35
@matthewbastien matthewbastien force-pushed the markdown-documentation branch 2 times, most recently from bac8896 to c75e2ab Compare April 2, 2025 21:20
@matthewbastien matthewbastien force-pushed the markdown-documentation branch from c75e2ab to 3da95ef Compare April 2, 2025 21:50
@matthewbastien
Copy link
Member Author

@swift-ci please test

@matthewbastien
Copy link
Member Author

@swift-ci please test windows platform

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.

2 participants