@@ -20,6 +20,7 @@ import Markdown
2020import SKUtilities
2121import SourceKitLSP
2222import SemanticIndex
23+ import SKLogging
2324
2425extension DocumentationLanguageService {
2526 package func doccDocumentation( _ req: DoccDocumentationRequest ) async throws -> DoccDocumentationResponse {
@@ -29,7 +30,6 @@ extension DocumentationLanguageService {
2930 guard let workspace = await sourceKitLSPServer. workspaceForDocument ( uri: req. textDocument. uri) else {
3031 throw ResponseError . workspaceNotOpen ( req. textDocument. uri)
3132 }
32- let documentationManager = workspace. doccDocumentationManager
3333 let snapshot = try documentManager. latestSnapshot ( req. textDocument. uri)
3434 var moduleName : String ? = nil
3535 var catalogURL : URL ? = nil
@@ -40,81 +40,197 @@ extension DocumentationLanguageService {
4040
4141 switch snapshot. language {
4242 case . tutorial:
43- return try await documentationManager. renderDocCDocumentation (
44- tutorialFile: snapshot. text,
43+ return try await tutorialDocumentation (
44+ for: snapshot,
45+ in: workspace,
4546 moduleName: moduleName,
4647 catalogURL: catalogURL
4748 )
4849 case . markdown:
49- guard case . symbol( let symbolName) = MarkdownTitleFinder . find ( parsing: snapshot. text) else {
50- // This is an article that can be rendered on its own
51- return try await documentationManager. renderDocCDocumentation (
52- markupFile: snapshot. text,
53- moduleName: moduleName,
54- catalogURL: catalogURL
55- )
50+ return try await markdownDocumentation (
51+ for: snapshot,
52+ in: workspace,
53+ moduleName: moduleName,
54+ catalogURL: catalogURL
55+ )
56+ case . swift:
57+ guard let position = req. position else {
58+ throw ResponseError . invalidParams ( " A position must be provided for Swift files " )
5659 }
57- guard let moduleName, symbolName == moduleName else {
58- // This is a symbol extension page. Find the symbol so that we can include it in the request.
59- guard let index = workspace. index ( checkedFor: . deletedFiles) else {
60- throw ResponseError . requestFailed ( doccDocumentationError: . indexNotAvailable)
61- }
62- guard let symbolLink = DocCSymbolLink ( linkString: symbolName) ,
63- let symbolOccurrence = try await index. primaryDefinitionOrDeclarationOccurrence (
64- ofDocCSymbolLink: symbolLink,
65- fetchSymbolGraph: { location in
66- guard let symbolWorkspace = try await workspaceForDocument ( uri: location. documentUri) else {
67- throw ResponseError . internalError ( " Unable to find language service for \( location. documentUri) " )
68- }
69- let languageService = try await sourceKitLSPServer. primaryLanguageService (
70- for: location. documentUri,
71- . swift,
72- in: symbolWorkspace
73- )
74- return try await languageService. symbolGraph ( forOnDiskContentsOf: location. documentUri, at: location)
60+
61+ return try await swiftDocumentation (
62+ for: snapshot,
63+ at: position,
64+ in: workspace,
65+ moduleName: moduleName,
66+ catalogURL: catalogURL
67+ )
68+ default :
69+ throw ResponseError . requestFailed ( doccDocumentationError: . unsupportedLanguage( snapshot. language) )
70+ }
71+ }
72+
73+ private func tutorialDocumentation(
74+ for snapshot: DocumentSnapshot ,
75+ in workspace: Workspace ,
76+ moduleName: String ? ,
77+ catalogURL: URL ?
78+ ) async throws -> DoccDocumentationResponse {
79+ return try await workspace. doccDocumentationManager. renderDocCDocumentation (
80+ tutorialFile: snapshot. text,
81+ moduleName: moduleName,
82+ catalogURL: catalogURL
83+ )
84+ }
85+
86+ private func markdownDocumentation(
87+ for snapshot: DocumentSnapshot ,
88+ in workspace: Workspace ,
89+ moduleName: String ? ,
90+ catalogURL: URL ?
91+ ) async throws -> DoccDocumentationResponse {
92+ guard let sourceKitLSPServer else {
93+ throw ResponseError . internalError ( " SourceKit-LSP is shutting down " )
94+ }
95+ let documentationManager = workspace. doccDocumentationManager
96+ guard case . symbol( let symbolName) = MarkdownTitleFinder . find ( parsing: snapshot. text) else {
97+ // This is an article that can be rendered on its own
98+ return try await documentationManager. renderDocCDocumentation (
99+ markupFile: snapshot. text,
100+ moduleName: moduleName,
101+ catalogURL: catalogURL
102+ )
103+ }
104+ guard let moduleName, symbolName == moduleName else {
105+ // This is a symbol extension page. Find the symbol so that we can include it in the request.
106+ guard let index = workspace. index ( checkedFor: . deletedFiles) else {
107+ throw ResponseError . requestFailed ( doccDocumentationError: . indexNotAvailable)
108+ }
109+ guard let symbolLink = DocCSymbolLink ( linkString: symbolName) ,
110+ let symbolOccurrence = try await index. primaryDefinitionOrDeclarationOccurrence (
111+ ofDocCSymbolLink: symbolLink,
112+ fetchSymbolGraph: { location in
113+ guard let symbolWorkspace = try await workspaceForDocument ( uri: location. documentUri) else {
114+ throw ResponseError . internalError ( " Unable to find language service for \( location. documentUri) " )
75115 }
76- )
77- else {
78- throw ResponseError . requestFailed ( doccDocumentationError: . symbolNotFound( symbolName) )
79- }
80- let symbolDocumentUri = symbolOccurrence. location. documentUri
81- guard let symbolWorkspace = try await workspaceForDocument ( uri: symbolDocumentUri) else {
82- throw ResponseError . internalError ( " Unable to find language service for \( symbolDocumentUri) " )
83- }
84- let languageService = try await sourceKitLSPServer. primaryLanguageService (
85- for: symbolDocumentUri,
86- . swift,
87- in: symbolWorkspace
88- )
89- let symbolGraph = try await languageService. symbolGraph (
90- forOnDiskContentsOf: symbolDocumentUri,
91- at: symbolOccurrence. location
92- )
93- guard let symbolGraph else {
94- throw ResponseError . internalError ( " Unable to retrieve symbol graph for \( symbolOccurrence. symbol. name) " )
95- }
96- return try await documentationManager. renderDocCDocumentation (
97- symbolUSR: symbolOccurrence. symbol. usr,
98- symbolGraph: symbolGraph,
99- markupFile: snapshot. text,
100- moduleName: moduleName,
101- catalogURL: catalogURL
116+ let languageService = try await sourceKitLSPServer. primaryLanguageService (
117+ for: location. documentUri,
118+ . swift,
119+ in: symbolWorkspace
120+ )
121+ return try await languageService. symbolGraph (
122+ forOnDiskContentsOf: location. documentUri,
123+ at: location
124+ )
125+ }
102126 )
127+ else {
128+ throw ResponseError . requestFailed ( doccDocumentationError: . symbolNotFound( symbolName) )
103129 }
104- // This is a page representing the module itself.
105- // Create a dummy symbol graph and tell SwiftDocC to convert the module name.
106- // The version information isn't really all that important since we're creating
107- // what is essentially an empty symbol graph.
130+ let symbolDocumentUri = symbolOccurrence. location. documentUri
131+ guard let symbolWorkspace = try await workspaceForDocument ( uri: symbolDocumentUri) else {
132+ throw ResponseError . internalError ( " Unable to find language service for \( symbolDocumentUri) " )
133+ }
134+ let languageService = try await sourceKitLSPServer. primaryLanguageService (
135+ for: symbolDocumentUri,
136+ . swift,
137+ in: symbolWorkspace
138+ )
139+ let symbolGraph = try await languageService. symbolGraph (
140+ forOnDiskContentsOf: symbolDocumentUri,
141+ at: symbolOccurrence. location
142+ )
108143 return try await documentationManager. renderDocCDocumentation (
109- symbolUSR: moduleName ,
110- symbolGraph: emptySymbolGraph ( forModule : moduleName ) ,
144+ symbolUSR: symbolOccurrence . symbol . usr ,
145+ symbolGraph: symbolGraph ,
111146 markupFile: snapshot. text,
112147 moduleName: moduleName,
113148 catalogURL: catalogURL
114149 )
115- default :
116- throw ResponseError . requestFailed ( doccDocumentationError: . unsupportedLanguage( snapshot. language) )
117150 }
151+ // This is a page representing the module itself.
152+ // Create a dummy symbol graph and tell SwiftDocC to convert the module name.
153+ // The version information isn't really all that important since we're creating
154+ // what is essentially an empty symbol graph.
155+ return try await documentationManager. renderDocCDocumentation (
156+ symbolUSR: moduleName,
157+ symbolGraph: emptySymbolGraph ( forModule: moduleName) ,
158+ markupFile: snapshot. text,
159+ moduleName: moduleName,
160+ catalogURL: catalogURL
161+ )
162+ }
163+
164+ private func swiftDocumentation(
165+ for snapshot: DocumentSnapshot ,
166+ at position: Position ,
167+ in workspace: Workspace ,
168+ moduleName: String ? ,
169+ catalogURL: URL ?
170+ ) async throws -> DoccDocumentationResponse {
171+ guard let sourceKitLSPServer else {
172+ throw ResponseError . internalError ( " SourceKit-LSP is shutting down " )
173+ }
174+ let documentationManager = workspace. doccDocumentationManager
175+ let ( symbolGraph, symbolUSR, overrideDocComments) = try await sourceKitLSPServer. primaryLanguageService (
176+ for: snapshot. uri,
177+ snapshot. language,
178+ in: workspace
179+ ) . symbolGraph ( for: snapshot, at: position)
180+ // Locate the documentation extension and include it in the request if one exists
181+ let markupExtensionFile = await orLog ( " Finding markup extension file for symbol \( symbolUSR) " ) {
182+ try await findMarkupExtensionFile (
183+ workspace: workspace,
184+ documentationManager: documentationManager,
185+ catalogURL: catalogURL,
186+ for: symbolUSR,
187+ fetchSymbolGraph: { location in
188+ guard let symbolWorkspace = try await workspaceForDocument ( uri: location. documentUri) else {
189+ throw ResponseError . internalError ( " Unable to find language service for \( location. documentUri) " )
190+ }
191+ let languageService = try await sourceKitLSPServer. primaryLanguageService (
192+ for: location. documentUri,
193+ . swift,
194+ in: symbolWorkspace
195+ )
196+ return try await languageService. symbolGraph ( forOnDiskContentsOf: location. documentUri, at: location)
197+ }
198+ )
199+ }
200+ return try await documentationManager. renderDocCDocumentation (
201+ symbolUSR: symbolUSR,
202+ symbolGraph: symbolGraph,
203+ overrideDocComments: overrideDocComments,
204+ markupFile: markupExtensionFile,
205+ moduleName: moduleName,
206+ catalogURL: catalogURL
207+ )
208+ }
209+
210+ private func findMarkupExtensionFile(
211+ workspace: Workspace ,
212+ documentationManager: DocCDocumentationManager ,
213+ catalogURL: URL ? ,
214+ for symbolUSR: String ,
215+ fetchSymbolGraph: @Sendable ( SymbolLocation) async throws -> String ?
216+ ) async throws -> String ? {
217+ guard let catalogURL else {
218+ return nil
219+ }
220+ let catalogIndex = try await documentationManager. catalogIndex ( for: catalogURL)
221+ guard let index = workspace. index ( checkedFor: . deletedFiles) ,
222+ let symbolInformation = try await index. doccSymbolInformation (
223+ ofUSR: symbolUSR,
224+ fetchSymbolGraph: fetchSymbolGraph
225+ ) ,
226+ let markupExtensionFileURL = catalogIndex. documentationExtension ( for: symbolInformation)
227+ else {
228+ return nil
229+ }
230+ return try ? documentManager. latestSnapshotOrDisk (
231+ DocumentURI ( markupExtensionFileURL) ,
232+ language: . markdown
233+ ) ? . text
118234 }
119235}
120236
0 commit comments