-
Notifications
You must be signed in to change notification settings - Fork 162
Add base support for converting catalogs that include multiple languages #47
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
Add base support for converting catalogs that include multiple languages #47
Conversation
7c02a1e to
3a592dc
Compare
|
@swift-ci please test |
franklinsch
left a comment
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.
This is looking great! Appreciate the refactoring along the way and testing!
| } else { | ||
| value = try! encoder.encode(CodableRenderReference.init(reference)) | ||
| referenceCache.sync({ $0[cacheKeyWithConformance] = value }) | ||
| encoderUserInfoVariantOverrides.values.removeAll() |
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.
Not sure I follow this, why do we need to clear this property?
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.
Ah right- so we only want the variant overrides that are collected while encoding that single reference. Since the cache contains the reference and the overrides for that reference.
But this is definitely confusing- I'll add a comment.
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.
Addressed here: 5ef11e1.
Thanks!
|
|
||
| // We only want to check for an objective-c variant | ||
| // if we're currently indexing a swift variant. | ||
| guard language == .swift else { |
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.
Is this guard needed? In what cases do we have render nodes that have Objective-C variants that we don't want to index?
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.
It is... I'd definitely be open to suggestions for how to improve this but otherwise we end up in an infinite loop since an objective-c symbol has an objective-c variant.
| return directive | ||
| } | ||
| diagnosticEngine.emit(Problem(diagnostic: Diagnostic(source: found.source, severity: .warning, range: directive?.range, identifier: "org.swift.docc.DeprecationSummaryForAvailableSymbol", summary: "\(symbol.absolutePath.singleQuoted) isn't unconditionally deprecated"), possibleSolutions: [])) | ||
| let symbol = updatedNode.symbol |
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.
Should we be using the unifiedSymbol here instead?
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.
Addressed here: e03f383.
Thanks!
| let moduleInterfaceLanguages: Set<SourceLanguage> | ||
| if FeatureFlags.current.isExperimentalObjectiveCSupportEnabled { | ||
| // Infer the module's interface languages from the interface | ||
| // languages of the first symbol in the symbol graph. |
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.
Is this comment accurate? Also, do we have a ticket we can link to for SymbolKit to provide an API that gives this information?
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.
You're right- that's out-of-date. I've updated it and linked a ticket.
Resolved here: beb3a14.
| /// | ||
| /// - Parameters: | ||
| /// - pathComponents: The relative path components from the module or framework to the symbol. | ||
| /// - interfaceLanguage: The source language of the symbol. |
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.
This needs updating
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.
Thanks!
Addressed here: 5c8b48d.
| } | ||
|
|
||
| /// Returns the primary symbol to use as documentation source. | ||
| var documentedSymbol: SymbolGraph.Symbol? { |
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.
This API seems a bit odd to me. If it's the doc comment we're interested in, should the API not just return the doc comment rather than the whole symbol?
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.
Generally this was just what caused the least code churn. But I think this API makes sense long-term as well. There are cases when we need all the metadata that comes with a symbol, but specifically the documented one.
For example, we might want to attach a diagnostic to the documented version of a symbol and needs it source location.
| "minor" : 5, | ||
| "patch" : 3 | ||
| }, | ||
| "generator" : "SymbolKit" |
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.
Nit: this should probably be clang
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.
|
|
||
| /// A test case that enables the experimental Objective-C support feature flag | ||
| /// before running. | ||
| class ExperimentalObjectiveCTestCase: XCTestCase { |
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.
Nice!
| /// before running. | ||
| class ExperimentalObjectiveCTestCase: XCTestCase { | ||
| override func setUp() { | ||
| enableFeatureFlag(\.isExperimentalObjectiveCSupportEnabled) |
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.
We should generally call super.setUp() here in case the base class has some set up behavior (although I doubt XCTestCase does)
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.
Good call- 92935cb.
| "minor" : 5, | ||
| "patch" : 3 | ||
| }, | ||
| "generator" : "SymbolKit" |
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.
clang
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.
|
@swift-ci please test |
f23071b to
a2212c2
Compare
|
We were initially seeing a significant performance regression in this PR but @franklinsch and I have found some other areas of the codebase that could be optimized to negate the regression. We're now seeing a significant performance improvement of around 20%. The majority of this benefit comes from implementing copy-on-write semantics for the Lazy initialization of the expensive URL property within |
|
@swift-ci please test |
franklinsch
left a comment
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.
This looks great! I think we should reconsider making ResolvedTopicReference immutable though, and split out further changes in a separate PR.
| /// The identifier of the bundle that owns this documentation topic. | ||
| public var bundleIdentifier: String { | ||
| didSet { updateURL() } | ||
| return _storage.bundleIdentifier |
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.
What performance gain do we get by making this type immutable? Thinking about this more, I prefer the flexibility of keeping this type mutable. It allows for clients to easily mutate properties of this type without calling the initializer and configure all the properties again. If making it immutable provides a significant performance gain, I think we should look at alternatives to improve performance in the future. What do you think?
Also, as it stands, this type is not copy-on-write, because it cannot be written to.
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.
Thinking about this more, I prefer the flexibility of keeping this type mutable. It allows for clients to easily mutate properties of this type without calling the initializer and configure all the properties again.
I think this is a good point and in general I agree. However, because ResolvedTopicReference is effectively a wrapper around URL I think it's important to expose a similar API to URL and, thus, communicate with clients that doing something like adding a path component is an expensive operation.
Prior to this, we exposed the pathComponents property directly and then monitored it to recalculate the URL when it was modified. This is likely surprising to clients who would not expect the direct modification of an array of strings to be an expensive operation. Limiting clients to using the addingPathComponent(_:) style api communicates better what's actually happening here.
I've added further documentation to ResolvedTopicReference in a commit here (5c78d85) to clarify this.
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.
That's a great argument. The fact that it's a wrapper around URL, it makes sense to convey similar characteristics to URL, which includes immutability. The alternative here would've been to keep it mutable and document the properties that incur a performance cost when written to, but I do prefer the immutable approach now. Thanks for adding the doc comment!
| } | ||
|
|
||
| public init(bundleIdentifier: String, path: String, fragment: String? = nil, sourceLanguages: Set<SourceLanguage>) { | ||
| self.init( |
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.
Beyond the copy-on-write change, which by itself provides a net gain in performance, I would suggest keeping the changes in this type to separate PR to manage risk (e.g., if we need to revert this PR for whatever reason, the additional improvements would not be reverted).
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.
This is a good point. I've rebased the commits in this PR to 5 isolated commits and will plan on a rebase and merge instead of a squash so that we can revert individual parts of this PR if necessary.
Adds base support for converting DocC Catalogs that include multiple languages by expanding symbol semantic models to hold language-specific variants. Resolves rdar://84169407 Co-authored-by: Franklin Schrans <[email protected]>
We add ResolvedTopicReferences to the cache when initializing a new reference so we don't need these additional calls.
Co-authored-by: Franklin Schrans <[email protected]>
We don't always use the URL property on a resolvedtopicreference and its expensive to compute, so it makes sense to make this a lazy initialization.
Because `urlReadablePath` was always call in the topic reference's initializer, when adding and removing path components, we were performing duplicate work. This adds a new private initializer that skips the `urlReadablePath` call when we know we already have an escaped path.
5c78d85 to
50a3564
Compare
|
@swift-ci please test |
Bug/issue #, if applicable: rdar://84169407
Summary
Adds base support for converting DocC Catalogs that include multiple languages by expanding symbol semantic models to hold language-specific variants.
This builds on previous work landed in:
Dependencies
None.
Testing
Build documentation for the included test bundle at
Tests/SwiftDocCTests/Test\ Bundles/MixedLanguageFramework.doccand confirm that output is expected.Steps:
swift run docc preview Tests/SwiftDocCTests/Test\ Bundles/MixedLanguageFramework.docc \ --enable-experimental-objective-c-support \ --indexChecklist
Make sure you check off the following items. If they cannot be completed, provide a reason.
./bin/testscript and it succeeded