SE-0356: Swift Snippets

That's correct. In the near term we will definitely want to give snippets access to the libraries from package dependencies as well though.

Closing the loop here on some of my concerns:

I think if we moved the goal of being able to add "parts" of a snippet into text into future directions and explore it soon enough that'd help.

Specifically, pages like https://vapor.codes and similar ones in style (got a few of my own which would want such style of "page"/"docs") would then be able to be pretty much written almost entirely in docc, which I think is a noble goal :slight_smile:

Done. Thanks for the feedback, @ktoso!

1 Like

Thank you! From my perspective we're all good here then :slight_smile:

I also like the "slice" wording you used there in the update, very nice.

I like the general direction, but feels the current implementation too limiting and somewhat incomplete even as the first stage of this feature. One of the limits is rather arbitrary: only 1-level of subdirectory.

I like everything in the future directions, although some of them feel more like temporarily cut features, rather than additional features. However, I understand the consideration for implementation difficulties and the sentiment for shipping something that's usable right now.


Currently "snippets embedded in Markdown" and "snippets in documentation comments" are listed as alternatives. Will acceptance of this proposal preclude these 2 alternatives? Because they sound like very good features to me (snippets are a lot easier to author and read this way imo), and don't seem to conflict with what's proposed here from what I can tell.


I think the line between a tutorial and a snippet is blurry. Sometimes a piece of documentation can contain snippets that follow logical progression. For example, the documentation for String starts with a snippet that demonstrates the instantiation of a string:

let greeting = "Welcome!"

then 2 snippets later refers back to it to demonstrate concatenation:

let longerGreeting = greeting + " We're glad you're here!"

then refers back to it again in another section:

var otherGreeting = greeting

It would be rather inconvenient to both the snippet author and code readers if each snippet is a new file, instead of sections of a single file.


I can imagine a situation where a few snippets all need the same lengthy initialization/setup code. (E.g. a few snippets that demonstrate some TaskGroup operations, and they all need the same hidden setup of some collections of actor instances.) Has there been discussions about giving some snippet files the shared access to some common code, so they can stay short and simple?


Coining the term snippet is an intentional move to differentiate them from existing “sample” or “example” code, which have historically been larger projects contextualized by more complex scenarios.

Just want to point out that "snippet" is also already used by Xcode to mean something similar to code template.


The proposal has both // MARK: HIDE and // MARK: Hide. Is it intended to be case-insensitive?

Are the // MARK: - variants (with separator above or below) supported?

I think instead of "Hide" and "Show", it might read better to use the past participles "Hidden" and "Shown". The reasoning is that "hide" and "show" read like instructions to the snippet builder which are addressed to the machine, whereas "hidden" and "shown" read like descriptions of the property of parts of a snippet file which are addressed to the human.

In general, I don't really like overloading // MARK for denoting visibility. It feels like a misuse and can lead to a lot of noise in some editors' jump bar, minimap, or their equivalent. Instead, maybe we can just drop the "MARK" part and go directly with // HIDDEN and // SHOWN?


One more backshed: I don't like using //! for comments, and think maybe something like //# is better. Both "!" and "#" are used as comments in some languages, but "#" is probably more suited for the job in this case. Because "!" looks more like "/" than "#" does, which makes it less easy to tell apart //! and /// from a glance. Also I think "!" used as a punctuation for expressing strong emotions in many natural languages, which might make it less suitable as part of a delimiter.


In addition to the feedback above, I concur with everything posted by Konrad upthread.

8 Likes

I relate very much to this. DocC has support for stellar tutorials, but we're talking about something different here, more akin to light literate programming: we walk with the user along a short path made of code and prose.

DocC interactive tutorials are not always something a user wants to engage into, especially when she's chosen to read an article, or the reference of a particular api (in which the doc author has decided that a few literate programming paragraphs are the way to go).

I'm thus very interested with "sections of code" as mentioned upthread by @ktoso, and I'm not very thrilled by SHOW/HIDE.

4 Likes

I don't have a proper review to provide on this, but the proposal sounds very much like an open-source (and SwiftPM compatible) version of Playgrounds, which I like in principle.

The show/hide syntax seems to be a common callout for a variety of reasons. Does the // MARK: syntax have precedent as a Swift open-source feature?
This is would be my main query with the proposal. I always imagined this as Xcode feature, and a syntactical carry-over Obj-C, due to the very un-Swifty use of all caps.

2 Likes

Regardless of the “sections” discussion which it seems we’ll need to defer to a future revision (though really I think it is simple to implement so might as well do the right thing right away), I wanted to bump the concern about abusing // MARK — it does the wrong thing, making the mini map in such files very confused.

There’d have to be some other syntax for it I strongly suggest…

2 Likes

I also think that snippet slices or sections should be discussed now to ensure that the hide syntax does work well together with the slice syntax.

2 Likes

You make good points and there was some discussion on this previously as well. I would propose we just use regular // comments for this–it doesn't require any new syntax and rendered explanations will always be at the top of the file, so they won't interfere with // comments throughout the rest of the file if they are present. The snippet-build tool can take the first contiguous // comments as the snippet explanation.

// Use `foo` to...

func foo() {

bar()

}
3 Likes

All fair points. I would suggest we use the following markers instead:

// snippet.hide
// snippet.show

To be consistent with the new hide/show syntax, slicing could be done as follows:

// snippet.IDENTIFIER
// snippet.IDENTIFIER.end

Where IDENTIFIER consists of URL-compatible characters, as it needs to be for DocC symbol references to work correctly. The end version is not needed if slices are back-to-back: starting a new slice also terminates the previous slice if needed. Given what we have been discussing here, I don't think slices should nest or overlap.

The implementations for slices will have to come at a later date, as it will require changes to three of the four projects of the implementation: Swift-DocC-Plugin, Swift-DocC, and SymbolKit.

This approach hopefully addresses the // MARK issue while giving a consistent approach for future slice support.

10 Likes

That looks very pleasant and works well with the sections as well -- very nice! Thanks for considering it @bitjammer !

Cool, makes sense for impl of that to arrive later but we have a clear path towards it now -- thank you!

4 Likes

This looks promising. Thank you!

Interesting. I wonder if you've seen my attempt last September at adding literate programming support to Swift:

https://github.com/apple/swift-package-manager/pull/3747
https://github.com/apple/swift-driver/pull/837
https://github.com/apple/swift/pull/39305

It was quite experimental, but at some point I should probably turn it in an SE proposal.

IMO both of these ideas are useful and somewhat orthogonal to one another. Literate programming isn't really much use for snippets of code in documentation (in a general sense) because it's intended for writing complete programs, whereas snippets in documentation tend to want to stand alone (or maybe with adjacent snippets, but certainly not with every snippet in a single document).

Sorry for the late feedback.

It feels to me like this is a very unsatisfying solution for something that stands alone as an example or other lightweight documentation, and that a literate approach that embeds the code directly into a document while leaving it easily extractable is going to be better on essentially every axis. I don't think such a thing would be terribly difficult to write. However, the more I look at what's being proposed here, the more I think that you're not really interested in occupying that part of the design space, and instead you'd like to build a pool of code that can be cross-referenced into heavyweight docs tools that are unlikely to ever be made literate. It is undoubtedly true that a literate approach is going to be inherently tied to the limitations of the surrounding document format, in terms of both the quality of document that can be produced from that format and the development tools for editing that format. An external cross-reference approach will always be much easier to fit into an existing docs ecosystem. I think as long as you're honest about that, you'll be able to deliver a satisfying solution without drawing all the air away from a literate approach for lightweight docs.

I do think that the ability to run and test that the snippet does what it's expected to do is at least as important as the ability to verify that it compiles. There are contexts where that can't really be done automatically — I can't imagine a snippet verifying that e.g. a SwiftUI example produces the expected visual results — but you shouldn't allow that to dominate your thinking enough to not consider how to test in the simple cases.

9 Likes

Hi John, and thanks for the feedback! Agreed, ultimately this is the main motivation to go this route, rather than the literate approach. The literate approach is certainly appealing in many ways, but ultimately it is a big move to jump off the train of all the tooling that comes with using standard .swift files. Tools could add special support for snippets, and we could imagine documentation editing tools, but even if they don't, snippets are always usable anywhere Swift is available.

Additionally, a major goal / benefit of this approach is to provide packages or other bundles of code with little programs that are super easy to open, remix, and run across every platform that Swift runs on -- on day one. Having that code be a regular .swift file is nice, beyond documentation value. Also great for indexes of sample code that may accompany docs.

And also agreed the importance of testing. This is an interesting area that will impact not just snippets, but likely other tooling that we'd like to support snippets well.

1 Like

Another late review--sorry. (I blame the button for clearing new threads being placed where it's inevitable to hit by accident.)

Like others I agree that the problem being addressed is significant: for me, the point that hits home most is that code listings often go stale/don't build, but I also very much agree that sample projects are a lot of effort. However, this proposal states upfront that it is not planning to supplant these existing "vehicles" for sample code (nor, by extension, to tackle the identified drawbacks of those "vehicles") but rather it proposes to offer a third "vehicle" for sample code within the Swift ecosystem.

As has been noted in the preceding discussion here, many have naturally drawn comparisons with literate programming, tutorials (such as the excellent ones developed to teach SwiftUI in its earliest days), and Playgrounds. I would consider all of these to be viable "vehicles" that are middle ways along a spectrum between the two poles noted above. Having seen materials delivered with good effect in the form of tutorials and Playgrounds, which already have mindshare in the Swift ecosystem, I'm struck with the question as to whether a new "vehicle" is the answer here.

I am not convinced by the discussion under "Alternatives considered" that snippets hold their weight because they are "much simpler" than Playgrounds: I can write an arbitrarily simple Playground with minimal ceremony, and it all just works. Additionally, the premise that snippets stand on their own because they are for simpler examples than Playgrounds is undercut with future directions to support multi-file snippets, snippet dependencies, and so forth—once snippets have taken on all of these future directions, what remains to distinguish them from Playgrounds?

While I appreciate the great effort that has gone into taking this proposal to review, with the multiple components that need to be updated for the MVP, I wonder if the answer doesn't lie in a different direction. Consider if we'd aim instead to build tooling (as part of SwiftPM or otherwise) which can extract code listings (with an understanding of some syntax for hidden setup and teardown code) and run and test these listings, much like we can already extract synthesized interface "headers" in Xcode for viewing. Yes, it is a totally orthogonal direction, but if such a direction answers the most salient problems identified here (namely, all three of the drawbacks listed under "Code listings with documentation") without introducing a new concept to the ecosystem, I think it behooves us to go in that direction first and see what remains. Put another way, I think the bar for justifying such an involved addition to the Swift ecosystem as snippets should be that it can hold its weight as a go-to "vehicle" for authors even if we improve the tooling story around code listings and build out open-source support for Playgrounds.

7 Likes

Thanks for the feedback, and interest in this domain. I'll try to answer some of these points, and how they would fit with snippets. Starting with playgrounds...

I think it is fair to start by thinking of snippets like playgrounds, but with the primary focus being to make them a straight-forward language feature, not an IDE feature. And, therefore, to ensure snippets work everywhere that Swift works. But the goals diverge a bit from there...

For snippets, we wanted to ensure that the "little programs" you write are a great fit for including in generated documentation, that the programs inherit code from their containing package, and each program is very simple to edit and fully understand (no hidden files). Notably, the most requested features for snippets through this review -- especially named sections -- are well suited to these use cases, while those same features aren't all that interesting for playgrounds.

Playgrounds, in contrast, are self-contained and often not tied to the other code in a package. They tend to stand-alone, and are not imported into other docs. This is also why each has its own resources and additional source files. Playgrounds have unique runtime behaviors ("run to here" or "always live".) And generally, playgrounds are becoming more advanced with powerful custom tools that would be hard to make available everywhere.

Most importantly, snippets aren't really a new type of file -- they are just a convention for working with a regular .swift file.

Consider if we'd aim instead to build tooling (as part of SwiftPM or otherwise) which can extract code listings (with an understanding of some syntax for hidden setup and teardown code) and run and test these listings, much like we can already extract synthesized interface "headers" in Xcode for viewing. Yes, it is a totally orthogonal direction, but if such a direction answers the most salient problems identified here (namely, all three of the drawbacks listed under "Code listings with documentation") without introducing a new concept to the ecosystem

First, this does feel like it adds a new concept to the ecosystem. We'd have to support a new type of documentation + code in a single file. Presumably this would be something like Markdown with code listings, but would then need to add syntax to show and hide bits of code, introduce dependencies / imports, and more. We'd have to support all these features we already support for regular Swift files. Snippets are really just .swift files.

Secondly, we've used this extraction approach in other documentation projects, and our experience is what led us to try something simpler with snippets. The extraction workflow tends to result in fairly complex tooling configs that are hard to setup and maintain. As you write code within your docs, you have to run this machinery to see if the code works. A snippet is always just a simple, runnable program.

I hope that helps explain a little how we ended up with this simple convention for snippets.

3 Likes

SE-0356 has been accepted with modification. If you'd like to continue discussing this, please do so in that thread.