-
Notifications
You must be signed in to change notification settings - Fork 146
memoize the common fragments calculation for diffing overloads #992
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,8 +11,57 @@ | |
import Foundation | ||
import SymbolKit | ||
|
||
typealias OverloadDeclaration = ( | ||
declaration: [SymbolGraph.Symbol.DeclarationFragments.Fragment], | ||
reference: ResolvedTopicReference | ||
) | ||
|
||
/// Translates a symbol's declaration into a render node's Declarations section. | ||
struct DeclarationsSectionTranslator: RenderSectionTranslator { | ||
/// A mapping from a symbol reference to the "common fragments" in its overload group. | ||
static let commonFragmentsMap: Synchronized<[ResolvedTopicReference: [SymbolGraph.Symbol.DeclarationFragments.Fragment]]> = .init([:]) | ||
|
||
/// Set the common fragments for the given symbol references. | ||
func setCommonFragments( | ||
references: [ResolvedTopicReference], | ||
fragments: [SymbolGraph.Symbol.DeclarationFragments.Fragment]) | ||
{ | ||
Self.commonFragmentsMap.sync({ map in | ||
for reference in references { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why bother saving the fragments for all of the overload declarations? Reading this, I'm surprised that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The entire trick being relied upon here is that the list of common fragments is going to be the same for a group of overloaded declarations. So the first one in a group that gets rendered can save the calculation for the whole group. So when we check for a cached result, we only need to check for one of the declarations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the |
||
map[reference] = fragments | ||
} | ||
}) | ||
} | ||
|
||
/// Fetch the common fragments for the given reference, if present. | ||
func commonFragments( | ||
for reference: ResolvedTopicReference | ||
) -> [SymbolGraph.Symbol.DeclarationFragments.Fragment]? { | ||
Self.commonFragmentsMap.sync({ $0[reference] }) | ||
} | ||
|
||
/// Fetch the common fragments for the given references, or compute it if necessary. | ||
func commonFragments( | ||
for mainDeclaration: OverloadDeclaration, | ||
overloadDeclarations: [OverloadDeclaration] | ||
) -> [SymbolGraph.Symbol.DeclarationFragments.Fragment] { | ||
if let fragments = commonFragments(for: mainDeclaration.reference) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See the previous comment. If you only ever query the fragments for the main declaration here, then why save the same fragments for each of the overloads in the call to |
||
return fragments | ||
} | ||
|
||
let preProcessedDeclarations = [mainDeclaration.declaration] + overloadDeclarations.map(\.declaration) | ||
|
||
// Collect the "common fragments" so we can highlight the ones that are different | ||
// in each declaration | ||
let commonFragments = longestCommonSubsequence(preProcessedDeclarations) | ||
|
||
setCommonFragments( | ||
references: [mainDeclaration.reference] + overloadDeclarations.map(\.reference), | ||
fragments: commonFragments) | ||
|
||
return commonFragments | ||
} | ||
|
||
func translateSection( | ||
for symbol: Symbol, | ||
renderNode: inout RenderNode, | ||
|
@@ -79,11 +128,6 @@ struct DeclarationsSectionTranslator: RenderSectionTranslator { | |
return postProcessTokens(translatedDeclaration) | ||
} | ||
|
||
typealias OverloadDeclaration = ( | ||
declaration: [SymbolGraph.Symbol.DeclarationFragments.Fragment], | ||
reference: ResolvedTopicReference | ||
) | ||
|
||
func renderOtherDeclarationsTokens( | ||
from overloadDeclarations: [OverloadDeclaration], | ||
displayIndex: Int, | ||
|
@@ -162,11 +206,12 @@ struct DeclarationsSectionTranslator: RenderSectionTranslator { | |
let processedOverloadDeclarations = overloadDeclarations.map({ | ||
OverloadDeclaration($0.declaration.flatMap(preProcessFragment(_:)), $0.reference) | ||
}) | ||
let preProcessedDeclarations = [mainDeclaration] + processedOverloadDeclarations.map(\.declaration) | ||
|
||
// Collect the "common fragments" so we can highlight the ones that are different | ||
// in each declaration | ||
let commonFragments = longestCommonSubsequence(preProcessedDeclarations) | ||
let commonFragments = commonFragments( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, it appears you only ever need the common fragments for the main declaration, and not for the individual overloads. |
||
for: (mainDeclaration, renderNode.identifier), | ||
overloadDeclarations: processedOverloadDeclarations) | ||
|
||
renderedTokens = translateDeclaration( | ||
mainDeclaration, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why define this as static? It will work fine, I think, but would fail someday if we ever compile more than one documentation project in the same DocC process, because we would have values left over from previous runs. As written, we assume the map will only ever be used once. Might be safer to just make this a normal attribute of
DeclarationsSectionTranslator
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DeclarationsSectionTranslator
instances are not shared between different symbols' renderings. If i made this an instance property, the saved results would not be available the next time around.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, ok makes sense.