Skip to content

Fix "missing technology root" warning for tutorials #812

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

Merged

Conversation

sofiaromorales
Copy link
Contributor

@sofiaromorales sofiaromorales commented Jan 31, 2024

Bug/issue #, if applicable: rdar://117866037

Summary

When either previewing or converting a tutorial, the diagnostic engine was emitting a warning about a missing technology-root, even when the tutorial was well structured and with the appropriate root. This fix resolved the problem by also checking the context root technology.

Testing

Steps:

  1. Preview the attached tutorial.
  2. Assert that is not showing a No TechnologyRoot to organize article-only documentation.warning.

Tutorial.docc.zip

Checklist

Make sure you check off the following items. If they cannot be completed, provide a reason.

  • Added tests
  • Ran the ./bin/test script and it succeeded
  • Updated documentation if necessary (N/A)

Co-authored-by: mayaepps <[email protected]>
@@ -3071,31 +3071,71 @@ class ConvertActionTests: XCTestCase {

// Tests that when converting a catalog with no technology root a warning is raised (r93371988)
func testConvertWithNoTechnologyRoot() throws {
let bundle = Folder(name: "unit-test.docc", content: [
func convert(_ bundle: Folder) throws -> ConvertAction {
Copy link
Contributor

Choose a reason for hiding this comment

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

There's more common code in these 3 example catalogs that could moved into this test helper. Doing so would help focus the test on only the inputs and the expected problems. For example:

func problemsFromConverting(_ catalogContent: [File]) throws -> [Problem] {
    let catalog = Folder(name: "unit-test.docc", content: catalogContent)
    let testDataProvider = try TestFileSystem(folders: [catalog, Folder.emptyHTMLTemplateDirectory])
    let engine = DiagnosticEngine()
    var action = try ConvertAction(
        documentationBundleURL: catalog.absoluteURL,
        outOfProcessResolver: nil,
        analyze: false,
        targetDirectory: URL(fileURLWithPath: "/output"),
        htmlTemplateDirectory: Folder.emptyHTMLTemplateDirectory.absoluteURL,
        emitDigest: false,
        currentPlatforms: nil,
        dataProvider: testDataProvider,
        fileManager: testDataProvider,
        temporaryDirectory: URL(fileURLWithPath: "/tmp"),
        diagnosticEngine: engine
    )
    _ = try action.perform(logHandle: .none)
    return engine.problems
}

could be used like

let onlyTutorialArticleProblems = try problemsFromConverting([
    InfoPlist(displayName: "TestBundle", identifier: "com.test.example"),
    TextFile(name: "Article.tutorial", utf8Content: """
        @Article(time: 20) {
           @Intro(title: "Slothy Tutorials") {
              This is an abstract for the intro.
           }
        }
        """
    ),
])
XCTAssert(onlyTutorialArticleProblems.contains(where: { $0.diagnostic.identifier == "org.swift.docc.MissingTechnologyRoot" }))

postConversionProblems.append(
Problem(
diagnostic: Diagnostic(
severity: .warning,
identifier: "org.swift.docc.MissingTechnologyRoot",
summary: "No TechnologyRoot to organize article-only documentation.",
explanation: "Article-only documentation needs a TechnologyRoot page (indicated by a `TechnologyRoot` directive within a `Metadata` directive) to define the root of the documentation hierarchy."
summary: "There was no root found for this documentation catalog.",
Copy link
Contributor

Choose a reason for hiding this comment

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

Considering recent changes to @TechnologyRoot, I feel like this diagnostic is more general than it would more actionable if it focused on what's wrong and how to fix it.

Currently, there are a two different groups of cases that could result in this warning:

  • The catalog could be "empty" (context.knownPages.isEmpty)
  • The catalog could have @Tutorial or @Article files but no tutorial table of contents page (@Tutorials)

The action that the developer would take to fix these two groups is different, so I think that it could make sense make one tailored diagnostic for each group of cases.


The "empty" catalog case has 3 different possibilities could happen if the catalog

  • didn't contain any markdown files or tutorial files at all
  • contains a markdown file that doesn't start with a level one heading, for example
    Some content without a level one heading
  • contains a documentation extension file but no input for symbol graph files

If the catalog truly has no files and no symbol graph input is provided to DocC, I don't know if it makes much sense to warn about it. We can, but it may not be all that informative and helpful to the developer.

If the article starts with anything other than a level one heading we already raise a "org.swift.docc.Article.Title.NotFound" warning with a fixit. If the article is completely empty, we don't seem to raise this warning. Maybe we should?

If the link in the documentation extension file doesn't resolve to a symbol, we already warn about the unresolved link.

So, I don't feel that it's necessary to raise a warning about the "empty" catalog cases but it's probably fine if we do.


The case where the catalog does contain one ore more @Tutorial or @Article files but no tutorial table of contents page (@Tutorials) is a bit different from the "empty" catalog cases. The issue is very specific and there's a very specific way that the developer can fix it; add a @Tutorials file1.

Given this, I feel like the issue would be clearer to the developer if this diagnostic focused on the missing table of contents issue.

postConversionProblems.append(
    Problem(
        diagnostic: Diagnostic(
            severity: .warning,
            identifier: "org.swift.docc.MissingTableOfContents",
            summary: "Missing tutorial table of contents page.",
            explanation: "`@Tutorial` and `@Article` pages require a `@Tutorials` table of content page to define the documentation hierarchy"
        ),
        possibleSolutions: Solution(summary: "Create a `@Tutorials` table of content page", replacements: [])
    )
)

Footnotes

  1. A future enhancement could be to synthesize a minimal table of contents page if the developer doesn't create one—just like we do with the technology root—which would remove the need for this warning.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know if fixits/solutions support defining new files or if they can only modify existing files. If it's possible to define a new file in a fixit/solution then maybe we could reuse the init command code to fill in most of the necessary information for the developer

let tableOfContentsFilename = "table-of-contents.tutorial"
let source = rootURL?.appendingPathComponent(tableOfContentsFilename)

Solution(
    summary: "Create a `@Tutorials` table of content page",
    replacements: [
        Replacement(
            range: .init(line: 1, column: 1, source: source) ..< .init(line: 1, column: 1, source: source),
            replacement: CatalogTemplateKind.tutorialTemplateFiles(converter.firstAvailableBundle()?.displayName ?? "Project Name")[tableOfContentsFilename]! // this file name is known to exist in the dictionary
        )
    ]
)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added your suggestion and tweaked how formattedDiagnosticSource works in order to be able to suggest addition of new content files without having to refactor the Replacement struct.

guard let diagnosticRange = diagnostic.range
      else {
          // If the replacement operation involves adding new files,
          // emit the file content as an addition instead of a replacement.
          //
          // Example:
          // --> /path/to/new/file.md
          // Summary
          // suggestion:
          // 0 + Addition file and
          // 1 + multiline file content.
          var addition = ""
          solutions.forEach { solution in
              addition.append("\n" + solution.summary)
              solution.replacements.forEach { replacement in
                  let solutionFragments = replacement.replacement.split(separator: "\n")
                  addition += "\nsuggestion:\n" + solutionFragments.enumerated().map {
                      "\($0.offset) + \($0.element)"
                  }.joined(separator: "\n")
              }
          }
          return "\n--> \(formattedSourcePath(url))\(addition)"
      }

Since the diagnostic does not belongs to an existing range in the source code the diagnostic.range will be nil and we log the suggestion.

This allows us to use fixits/solutions for defining new files instead of only modify existing files.

The other possible improvement to do here is making range in Replacement optional, but I don't know if that might be a breaking API change.

Implemented here d7720d9.

@sofiaromorales
Copy link
Contributor Author

@swift-ci please test

1 similar comment
@sofiaromorales
Copy link
Contributor Author

@swift-ci please test

@sofiaromorales
Copy link
Contributor Author

@swift-ci please test

Copy link
Contributor

@d-ronnqvist d-ronnqvist left a comment

Choose a reason for hiding this comment

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

This looks good to me.

One comment about potentially adding one more test but I don't consider that blocking.

@sofiaromorales
Copy link
Contributor Author

@swift-ci please test

@sofiaromorales
Copy link
Contributor Author

@swift-ci please test

@sofiaromorales
Copy link
Contributor Author

@swift-ci please test

1 similar comment
@sofiaromorales
Copy link
Contributor Author

@swift-ci please test

@sofiaromorales sofiaromorales merged commit e353808 into swiftlang:main Feb 16, 2024
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