[RFC] A more flexible and feature-rich code block diff support in tutorials


Swift-DocC Tutorials will automatically highlight new code that is added in each tutorial step that has a code block in it. I was working on an issue to add full diff (insertions and deletions) support to Tutorial code blocks, and realized I'm not sure how we should approach this, and whether working on this is the right use of our time.

This RFC outlines the ways Swift-DocC allows authors use code and diff snippets in their documentation, focusing on Tutorial steps and @Code, but also including code blocks in Markdown, and Snippets.

It turned out to be monstrously long, but I wanted to share it out before I dove deeper into the rabbit holes.

Acknowledgements and disclaimers

The context and the problem

Let's consider the context at two abstraction levels:

  • If we take a step back, there are a number of ways to provide code examples in Swift-DocC, ranging from code blocks in Articles, to code blocks backed by Snippets, to code blocks in @Code directives in Tutorial steps. Each of the ways to embed a code listing behaves slightly differently and has a different purpose. They can share implementation details, though. Improvement to one can bring improvements to others.
  • In swift-docc/#515 we're considering adding a way to highlight deleted lines (or parts of lines) of code in Tutorial steps.
  • Using Snippets in DocC is not fully documented (or perhaps I could not find docs and examples for it). That's a separate concern that I hope to address in the coming week, but I'm adding it here as an important piece of context: we have multiple ways to showcase code.
  • In a related pitch on supporting code diffs in Article code blocks, we're talking about supporting unidiff format in code blocks as-is (comment). I agree with @ethankusters that supporting diff syntax highlighting similar to the way GitHub does it would be beneficial to our users, and make it feel like Swift-DocC diff highlighting just works.

With this context, here are a few problems / questions

  • Should we add insertions + deletions support to tutorial steps at all? Maybe Tutorial code steps are highlights for a reason, and we should not try to show them as diffs?
    • Instead, should we provide documentation of using diff-based Markdown code blocks everywhere, including Articles and Tutorial steps, without using @Code directive? I.e. leave code steps to @Code, but if the authors want to show a diff — document how an arbitrary code block can be used, and make sure diffs are supported.
    • And should we improve the way Markdown code blocks support diffs, and improve the UX for that flow? I.e. continue work in this pitch.
  • If we support deletions, should we make the diff behavior optional? Should we allow authors to decide whether they want to just show a plain code block, a block that highlights the new code compared to the last step (current behavior), or a full diff?
  • If we support deletions, and/or make them optional, how should we change FileReference code API, and RenderNode spec?
    • For FileReference code, we can make it backwards-compatible by providing highlights computed property and deprecating it.
    • But, for the RenderNode spec, we could add the new fields to it, and bump its version from 0.3.0 to 0.4.0? There's no easy way to provide backwards compatibility that I know of.
  • If we choose to implement deletions support, what should the right scope of the improvement be? Should we collect inline diffs, and detect moved chunks of code?
    • Currently, LineHighlighter grabs just the insertions. Kyle already added some code that grabs deletions as well. That could be already good enough.
    • We could add support for detecting moved chunks of code.
    • We could also detect inline changes and try and highlight them within a single line, closer to how GitHub shows diffs.
  • If we show insertions and deletions in diffs, what should the UX be?
    • The @Code directive shows the code block in the right column in desktop resolution, or in single-column on mobile.
    • By default, the step code block shows only the new lines in mobile layout to save space, and 2 lines above and beyond the changed chunk. If you tap on the file name, we show the full file (with new lines highlighted).
    • Should we show side by side diff, or unified diff? GitHub shows unified by default, but you can switch to split view. Should that be in-scope, or not? That feels like a separate improvement to me.

Proposed solution

After spending some time researching how @Code directive and other ways to showcase code blocks, I was not convinced that adding deletion support into @Code directive is the right area to spend our time:

  1. Don't implement insertion+deletion support in @Code Tutorial steps. Instead, improve how diffs show in inline Markdown code blocks, and document that behavior.
    1. I think that highlighting full diffs in the tutorial code steps would accent too much attention on the specifics of the diff, instead of the tutorial steps and API usage.
  2. So, no changes to RenderNode API and spec.
  3. Instead of working on this immediately, we should experiment and improve diff syntax highlighting, like @omathews suggests, and make sure diff blocks have syntax highlighting.
    1. Once we verify and document that diff blocks work in Tutorial steps, we can close the original issue (swift-docc/#515).
  4. Make sure that the ability to put diffs in code blocks is documented for both Articles and Tutorials. I can experiment with that, and document my findings.
  5. Improve our documentation around @Snippet. Document when it's a good idea to use a snippet (since it's a source code that builds, there's guarantee that it's valid and it won't go stale) versus a code block (supports diffs).

As a follow-up, if this direction sounds more or less fine, we could also research and discuss:

  • Whether we could support using Snippets in Tutorials as code blocks, so that we have the guarantee that tutorial steps actually build successfully?
  • Whether we should support diff blocks in Snippets with a new //command (i.e. similar to slice). That would break Swift syntax in Snippets though, so I'm not sure how we could go around this. We could have before and after, and generate the diff on the fly perhaps.


  • Why shouldn't we implement the insertions and deletions support in tutorial steps?
    • The problem I see with that is that we will have to make them optional (or the currently existing tutorials will suddenly render differently, not good), and then the diff format that we currently provide in our FileReference is not compatible with git-diff. That would mean that we'd have to work on diff support twice — for tutorial steps and for code blocks.
    • The time spent on this would be better spent in documenting the approaches that are already there and available to authors.

Next steps

  • Let's discuss and align on whether we should support deletions in @code directive or not. Once that is clear:
  • @omathews, I don't want to be a nuisance there, but happy to help if you will need an extra pair of hands in working in code diffs support across docs.
  • I would love to focus on checking how Snippets work across all types of content, and whether inline markdown code blocks work in Tutorials, and documenting that. If I can help bring clarity on how to show code diffs or code blocks depending on the author's intent, I would be happy with that for now.

UPD: Did some research around Snippets!

1 Like