One thing that I could see happening in the not too distant future is modules emitting some of their own snippets, for those that don't pertain to any other libraries. If that happens, then there would indeed be at least two places to look for snippets. If snippets never appeared one level up from libraries, I think this would be a much stronger need.
this is true. but sometimes updating all the snippets would take time that would block a critical change, especially if the project has a lot of documentation. one thing that happens sometimes is people start overriding the CI bot and it becomes a habit to skip CI for all but the biggest changes, which is bad.
I wouldn't expect it to. If CI is just running swift build
at the end of the day, and NIOWebSocket
is one of the libraries in the package, won't the library need to build anyway? Or are you referring to just the fact that importing NIOWebSocket
when compiling a file might be expensive?
the second. for example, swift-json
might want to demonstrate a use case that involves NIOWebSocket
, even though the library does not depend on SwiftNIO.
Okay, right, so this is what you were mentioning before. That is an interesting use case that I am tracking for the next iteration. It'll be an interesting situation to have dependencies that only snippets need. I wouldn't want it to require folks to end up having to build snippets explicitly as a separate step.
Wrong documentation is multiple times worse than no documentation.
That's a terrible way to look at documentation that "it slows you down". It is a crucial part of the process and project. As important as any functionality itself.
many open source projects can afford to take it slow and keep all the project peripherals in sync with the core. thereās no doubt that thereās value in that. but a lot of code is written in environments where the incentives are different, and developers may have no choice but to push ahead with changes that suffer from the exact problems you mention.
Seeing as these are compiled as regular executables, they can be tested like executables too, and there are many existing options to help with that. At the end of the day, it's the package authors who are in charge of how much testing code to write, no matter the context, and I'd encourage them to do what they think is right.
For this first iteration, I think we can get plenty of coverage with precondition
or XCTest
. Test code can be easily hidden with //MARK: Hide
.
Hi @bitjammer, this is looking really great. I love the idea of declaring your sample code somewhere once and being able to refer to it in multiple places. (cc @Joseph_Heck who had also expressed interest for this in the past)
I'd be interested in hearing more of your rationale for the one-to-one mapping between snippets and files. Do you see a use case where you'd want to define multiple related small snippets in a file to showcase different aspects of my library's API. Looking at this documentation page for Swift-Markdown, it contains docs for three related concepts, so I might expect being able to declare these three snippets in the same file. That being said, the simplicity of just being able to create a file to create a snippet without much additional syntax is compelling, so the approach you've chosen is probably betterājust interested in hearing your thought process on this.
Each snippet is a single file, but it is also a fully valid program. It can access the features of the rest of the package
Can you elaborate on the semantics here? What modules does a snippet import implicitly? How do you deal with name collisions if multiple targets of your package define APIs of the same name? If my target declares a function that refers to an API from external dependencies (e.g., in parameter position), is my snippet able to import that external dependency so that I can call my function?
To help organizing a growing number of snippets, you can also create one additional level of subdirectories under
Snippets
. This does not affect snippet links as shown below.
What is the purpose of groups from a user (consumer) perspective? Are they just a way for package authors to organize their snippets? Do these need to be explicitly treated in SwiftPM or can they just be "folders"?
Finally, about the snippet description syntax:
//! Line comments prefixed with ! will
//! serve as the snippet's short description.
99.9% sure about this, but just checking: this is Markdown content, right?
Amazing work!
This looks awesome, and I think that having small code samples that are actually compiled and checked to prevent them from going stale is a killer feature for the Swift documentation ecosystem.
I have a question on future directions, do you think there could be demand for allowing package adopters to use snippets as autocompletion templates in their source code? Specifically this would require some editor support and certain bits of the snippets to be marked as "to be filled in". I am not sure it's a good idea myself since it might encourage people to not simplify their APIs and going for an approach of "this is complex to do but we can just provide a snippet for it" instead of doing the work to improve the ergonomics of that particular use case. I am looking forward to hearing everyone's thoughts on this.
I'm on the fence about these.
On the one hand, I agree that sample code today is awful (and I especially dislike when it is distributed as an Xcode project), and I think we all know the struggle about keeping code snippets in documentation accurate.
On the other hand, I'm not sure about the shape of this solution. I can only think of examples that would demonstrate the most basic functionality, and I struggle to see the tangible gains this would bring.
Looking at some of the examples in the markdown package, like MaximumWidth. This is what the user would see:
import Markdown
let source = """
This is a really, really, really, really, really, really, really, really, really, really, really long line.
"""
let document = Document(parsing: source)
let lineLimit = MarkupFormatter.Options.PreferredLineLimit(maxLength: 80, breakWith: .softBreak)
let formattingOptions = MarkupFormatter.Options(preferredLineLimit: lineLimit)
let newSource = document.format(options: formattingOptions)
And if they executed it, they'd see the line of text, followed by a version with a line break.
I feel like that doesn't really call for building and executing code. You could just tell them what the result is - and it would be faster, it would work on the web, computers without Swift installed, phones, tablets, etc. Maintenance is an issue, sure, but it's also an issue if you just run code without checking that it shows the user what you expected to communicate.
Perhaps this is just a poor example, but I get the same feeling from the other examples in the repository. I'm not seeing any cases where I think "Wow! The ability to run this code really adds something that just telling me the result couldn't do!"
Perhaps there just isn't enough of a window. The snippet needs to be: (a) small enough to fit in a single, reasonably-sized file, but also (b) complex enough that executing it adds insight, but also (c) not complex enough for an actual DocC tutorial, and (d) not editable - because Playgrounds, with their greater ability to mix code and prose, are better for that.
There might be something here, but so far I'm not convinced. More compelling examples could change my mind.
For me, a huge advantage of having these snippets be buildable is not that the documentation readers would actually run them, but that authors can be confident their snippets still compile with the current version of their framework.
I often come across small code examples in docs that no longer compile so I think there's significant value in having your documentation snippets run in CI, even if you wouldn't expect readers of the docs to be running them.
I don't see how the proposed snippets are incompatible with this workflow. Using the @Snippets
directive in your Swift-DocC docs would allow folks to read the snippet in the same way they read markdown fenced code blocks today- just with the benefit of having the confidence that the snippet will actually run.
to me this sounds more like a project example than a snippet. if itās important enough to halt a PR if it breaks, itās probably important enough to have a target descriptor in the Package.swift
, with an explicit path and dependencies.
along a similar vein, i would probably want to view such a snippet/example in a text editor, or something thatās meant to be a code window, like the GitHub file viewer.
So, when I look at that documentation, I see three related concepts but they don't always go together. For example, I can parse a Markdown file, format it, and not do any modification. Someone may want to build up some Markdown from structured data and then print it out, so no parsing needs to be done. I don't think I would have combined those concepts into one article if I were writing that documentation, unless I were putting everything about the library on one page. I'm wondering if that article was basically pulled from the repo's README? I think when I wrote the README, it was basically just a "here's everything, I guess" article (which READMEs tend to be).
The goal of snippets is one block of code is one high-level task. Putting that in one file means that an author or a reader doesn't have to do the extra work to sort a growing wall of text, which starts to get back into one of the problems with sample code projects.
If sharing demo code (code that executes but is not shown) among snippets becomes an issue, a package can define a helper library. If sharing presentation code (the code that is actually shown in the rendered code block) becomes an issue, I think that perhaps indicates a potential need for an API. That is another benefit that I see for shorter example codeāit highlights the usability (or lack thereof) of libraries very well, so I expect that the more that folks write snippets, the more informed they will be about the design of their API.
A module can import any of the libraries created in its host package. This was a compromise to allow for some flexibility without having to configure each and every snippet. It's possible that we might want to broaden that to libraries pulled in with dependenciesāI think someone brought that up above.
I'm not sure I understand what you mean regarding name collisions. These are basically just executable targets. You still need to import libraries to get their APIs, so if each library has their own foo
, you need to use A.foo
or B.foo
to differentiate, same as any Swift code.
This started out as a formal thing that would create semantic groupings, but I made a change to just allow subdirectories without affecting the outgoing Symbol Graph data. Basically, the extra level of subdirectories is purely for organization, and the limit of depth one allows folks to put snippet-related resources in deeper directories without having to resort to fancy naming.
You know it!
Great question! I think it could be really interesting for folks to be able to "template-ize" their snippets by using automatically expanding placeholder tokens. I rely on autocomplete on a daily basis but, as much as we all like to argue about naming, sometimes you just can't anticipate exactly how someone might have named their API. Being able to autocomplete by descriptive names that expand out to one or more lines of code could be a really powerful shortcut to going and googling a phrase on the web.
I can understand your concern about folks ignoring the shape of their own APIs and throwing a huge recipe at you. I still think snippets will more often than not encourage more succinct APIs. Some things really are multi-step, dependent tasks, and remembering all of the steps is really difficult at times. It's a bit of a gray area. For example, consider reading all of the output from a process's pipes without locking or leaving full buffers, hanging your program: I can never remember how to do it properly and the solution is usually quite long, if I remember correctly. It definitely needs a better convenience API, but I also would love to have that at my fingertips when I really do need to handle indefinite or super long outputs from a launched process.
Improving APIs has always been driven more by social pressure, and it would be beneficial to think of ways to allow for feedback about a given snippet that makes its way back to the community. For example, if there is an incredibly popular snippet around 15-20 lines long, a package author should be able to get that information and use it to inform the design of future API.
So these examples that I provided for Swift Markdown were really more to show how to get started writing and referencing them, not to suggest a particular content style. I think we need to use our imagination and broaden our horizon a little bit.
For example, how do you feel about code listings in the TSPL? Or try[language].org? I think they can be worth running for lots of reasons, even if it's just to prove to yourself as a reader that the code does what it says. Code is obvious to different people at different times and we should remember that. Maybe it's not always the case that code needs to run, but it doesn't hurt. If we are talking about efficiency, energy usage, etc., we can address that. But code is meant to be run at the end of the day, and sometimes seeing it working inspires confidence.
Consider another example: how about if we allow readers to start adding code to snippets wherever they see them, and then run them on the spot? Or, perhaps a snippet causes something to be drawn to the screen. In some cases, running the snippet is necessary to understand the final result, sometimes it's not.
Just because a snippet can run, doesn't necessarily mean that it always must be. It's just a capability that can provide some opportunities in the future, not a mandate.
I also want to stress that snippets fill a granularity gap, they aren't meant to displace longer form content, such as articles, tutorials, or larger sample code projects, nor are they meant to stand in when you just need to know one API. It's really more about "what is the shortest path to X", or "here's an interesting way to compose these two views that you might not have considered", or "this is a common compositional task that people often do but forget".
Not sure I follow here, can you elaborate? All code presented to the reader as documentation should be valid and usable, no matter the size.
Are there or should there be any restrictions to what this markdown content can contain? The two cases that I'm most curious about are:
- Can the snippet description link to local images? If so, would that local file be located next to the snippet source, in the DocC catalog, or somewhere else entirely?
- Can the snippet description contain directives (for example to reference other snippets in the same project)?

Can the snippet description link to local images? If so, would that local file be located next to the snippet source, in the DocC catalog, or somewhere else entirely?
Interesting question. Originally I wasn't thinking folks would really want to insert local images from the disk, but is there is a scenario you are thinking of where the convention should be explicit about this?

Can the snippet description contain directives (for example to reference other snippets in the same project)?
So, I don't think it makes sense for a snippet to include another snippet, of course, because they would flatten out in a strange way in the documentation.
As for other directives, I think it would depend. I can't think of any of the current block DocC block directives that would be super useful off the top of my head, but perhaps someone could do something custom with block directives.
Probably we will want to establish content guidelines after the convention is established once we know more about how folks want to use them.
For now, would you say it is enough to say that DocC will ignore block directives and try to resolve images and links as is usually done in Markdown content?

Not sure I follow here, can you elaborate? All code presented to the reader as documentation should be valid and usable, no matter the size.
from what youāve described, a snippet is:
-
a piece of code that is self-contained, reasonably self-explanatory, and should be compile-able as a target
-
code that should be built and tested as part of the projectās CI pipeline
that to me, sounds like an Example, more than a Snippet.