Skip to content
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

Include images in documentation in generated content #928

Open
tymokvo opened this issue Jun 28, 2024 · 17 comments
Open

Include images in documentation in generated content #928

tymokvo opened this issue Jun 28, 2024 · 17 comments

Comments

@tymokvo
Copy link

tymokvo commented Jun 28, 2024

Hello,

I have been trying to include visual content in library documentation. My use case is that I am working on a project that interfaces heavily with CAD workflows. So, it would be very valuable to be able to save snapshots of diagrams/drawings and include them in documentation that is then built into the static content by fsdocs.

I first asked on the fsharp slack for advice on the topic.

For example:

/// Pi is the ratio of circle's circumference to its diameter.
/// ![remote image](https://www.probability.ca/jeff/writing/pi1.jpg)
///
/// ![local image](images/pi.png)
let pi = 3.14159

Would result in an HTML tag like <img src="{root}content/img/pi.png" /> (or some other path in the output directory) in the documentation for that value.

It would be nice to be able to support embedded HTML, especially for resizing images. But I think supporting markdown images would be a good start if it is not already supported.

I have tried running fsdocs build --saveimages=all flag, but it seems to have no effect. This is possibly due to #683.


Looking through the markdown parser, it seems that this syntax should be supported to include an image:

| '!' :: DirectLink(body, link, rest) ->
let (value, ctx) = accLiterals.Value
yield! value
let link, title = getLinkAndTitle (String(Array.ofList link), MarkdownRange.zero)
yield DirectImage(String(Array.ofList body), link, title, ctx.CurrentRange)
yield! parseChars [] rest ctx
| '!' :: IndirectLink(body, link, original, rest) ->
let (value, ctx) = accLiterals.Value
yield! value
let key =
if String.IsNullOrEmpty(link) then
String(body |> Array.ofSeq)
else
link
yield IndirectImage(String(Array.ofList body), original, key, ctx.CurrentRange)
yield! parseChars [] rest ctx

And looking through the BuildCommand, it seems that remote and local images should be supported:

let createImageSaver (rootOutputFolderAsGiven) =
// Download images so that they can be embedded
let wc = new WebClient()
let mutable counter = 0
fun (url: string) ->
if
url.StartsWith("http", StringComparison.Ordinal)
|| url.StartsWith("https", StringComparison.Ordinal)
then
counter <- counter + 1
let ext = Path.GetExtension(url)
let url2 = sprintf "savedimages/saved%d%s" counter ext
let fn = sprintf "%s/%s" rootOutputFolderAsGiven url2
ensureDirectory (sprintf "%s/savedimages" rootOutputFolderAsGiven)
printfn "downloading %s --> %s" url fn
wc.DownloadFile(url, fn)
url2
else
url

Though, the imageSaverOpt value in processFile seems to only ever be passed into the Literate methods, which is what I assume the linked issue is in reference to.

@nhirschey
Copy link
Collaborator

nhirschey commented Jun 29, 2024

If you want text to be interpreted as markdown you need to use (** … *) comments. See the docs about literate scripts for reference. https://fsprojects.github.io/FSharp.Formatting/literate.html

If it’s not inside a (** *) comment it’s not parsed as markdown.

(** Pi is the ratio of circle's circumference to its diameter.
![remote image](https://www.probability.ca/jeff/writing/pi1.jpg)

![local image](images/pi.png) *)
let pi = 3.14159

then just fsdocs build, no special options needed.

@tymokvo
Copy link
Author

tymokvo commented Jun 29, 2024

Does this work for .fs files in a project? The docs seem to indicate that this only works for literate scripts. I tried this in a .fs file and it seems to have no effect.

@nhirschey
Copy link
Collaborator

No, docs files must be .fsx, .md, or .ipynb.

@nojaf
Copy link
Collaborator

nojaf commented Jul 1, 2024

Did you try using relative paths for your Markdown image? That might work in xml doc comment.

@nhirschey
Copy link
Collaborator

nhirschey commented Jul 1, 2024

Did you try using relative paths for your Markdown image? That might work in xml doc comment.

Yeah, good point that may be possible. For this use I think you’d probably need to turn on “use markdown comments”. See https://fsprojects.github.io/FSharp.Formatting/apidocs.html#Markdown-Comments

@tymokvo
Copy link
Author

tymokvo commented Jul 1, 2024

Hm, ok. Thanks for the tips, I will try those.

@tymokvo
Copy link
Author

tymokvo commented Jul 1, 2024

So the <img/> tag is preserved when using an XML comment. But, the image content is not copied into the output directory and the path is not resolved. It is just copied verbatim out of the documentation string.

The image also ends up in the index page for the namespace, which is probably not desirable in most cases.

@nojaf
Copy link
Collaborator

nojaf commented Jul 2, 2024

I'm not sure if those expectations are realistic. I would not expect fsdocs-tool to be that clever and copy things without them being part of the docs (input) folder.

@tymokvo
Copy link
Author

tymokvo commented Jul 2, 2024

Oh, I thought the createImageSaver snippet from above was intended to do that. For literate scripts, at least.

If imageSaverOpt is not none, I would expect that the WebClient download the images from the source and copy them to <root output folder>/savedimages/saved/<counter>/<url path>.

Granted, I think this only works with images for which the path starts with http currently, but I believe the WebClient could point to local images and it would work the same way.

@nojaf
Copy link
Collaborator

nojaf commented Jul 3, 2024

Hmm, I'm not sure that code is hit when documentation is generated.
To be use, you would need to debug it I guess.

@tymokvo
Copy link
Author

tymokvo commented Jul 5, 2024

With hindsight, I think that was @nhirschey 's point; that the indicated path is only followed for literate scripts.

So, I think this amounts to a feature request for supporting images in documentation comments for project source files. Is a PR welcome in that direction or is there an RFC process? I looked through the contributing.md but didn't see anything.

@nojaf
Copy link
Collaborator

nojaf commented Jul 8, 2024

We don't have a formal RFC process, but I need more details about your project before committing.

Could you provide a rough outline of your project?
Is this feature achievable with an F# script outside fsdocs?

@tymokvo
Copy link
Author

tymokvo commented Jul 9, 2024

Could you provide a rough outline of your project?

Sure. I work in R&D for an electrical engineering company. We use F# when we need a .NET language to interface with desktop CAD/BIM applications. Our approach is heavily reliant on DSLs that can be understood by our domain experts to automate the design of complex building systems. There are some simple examples in a demo we gave last year.

However, our domain experts' work is heavily reliant on visual workflows. E.g. printing a PDF from a CAD application, drawing over the building plans, making wiring diagrams, etc. So, the named concepts in the DSLs that we create are typically deeply related to this visual media. This is why it would be very helpful to be able to embed images in the documentation to bridge the communication gap between engineers thinking visually, and programmers building tools textually.

Is this feature achievable with an F# script outside fsdocs?

I suppose it's technically possible but it seems quite difficult. Unless there is some kind of script-based plugin system to FSharp.Formatting of which I'm not aware. So please let me know if that's the case.

In order to achieve what I'm thinking of, I think we would basically need to:

  • Crack the projects
  • Get all of the member definitions from the project
  • Parse the comments as markdown
  • Extract image paths
  • Copy images to the content directory
  • Match the declared member to the generated documentation HTML
  • Insert an <img/> tag into the generated HTML with the path to the copied image

@nhirschey
Copy link
Collaborator

nhirschey commented Jul 9, 2024

One issue with putting it in XML comments: if you put an image in the XML comment, what do you envision appearing in the ionide and visual studio tooltips when they parse it? I ask because I expect an error unless the visual studio and ionide teams also adopt img tags.

The “spec” for fsharp XML comments is in the fsharp repo here. Currently I believe we’re following that spec.

One thought: if you write your literate comments in .fs files using the (** *) syntax then you should be able to make a build script that reads the .fs file, adds #r references, and write the output as a (mostly) duplicated .fsx file. This does not require modifying the spec of fsharp XML comments. You’d have normal literate comments (which is what you actually seem to want, just in the implementation file, not a separate .fsx script). See https://fsprojects.github.io/FSharp.Formatting/literate.html#Processing-literate-files-programatically, which hopefully helps communicate what I’m thinking.

Broadly, I appreciate wanting documentation to reside in the implementation itself. But I’m thinking the above suggested workflow is the most seamless way to do it. It would also give you the full power of literate scripts (hiding certain code, etc) which you don’t get by adding <img> to XML comments.

Idea: maybe we add a way to make fsdocs do this to a .fs file automatically. Perhaps with a comment at the top of the file

@tymokvo
Copy link
Author

tymokvo commented Jul 9, 2024

what do you envision appearing in the ionide and visual studio tooltips when they parse it? I ask because I expect an error unless the visual studio and ionide teams also adopt img tags.

VSCode supports both HTML and Markdown images, but only for publicly accessible URLs:

Markdown demo (credit to @TheAngryByrd)

So, in the case that the image is publicly-hosted, it should "just work". But, for local paths, I would expect a broken image to render right now. There's an open issue in vscode about supporting local paths.

It would be nice for the tooltips and the documentation to work the same, but since this project supports watch mode, it's not so bad to just use the documentation site.

Broadly, I appreciate wanting documentation to reside in the implementation itself. But I’m thinking the above suggested workflow is the most seamless way to do it. It would also give you the full power of literate scripts (hiding certain code, etc) which you don’t get by adding to XML comments.

Ok, I will look into this approach; thank you for the suggestion! Without trying it, my intuition is that this would be quite confusing to have to look in a script file for a visual reference that is related to the API documentation though.

I believe it also has the drawback that the tooltips will not work in the implementation files as the (** *) comment syntax is not recognized by VSCode.

@nojaf
Copy link
Collaborator

nojaf commented Jul 10, 2024

Hi,

We discussed this with the maintainers yesterday, but we're still a bit confused about some aspects.

We understand that you want to use images in XML comments, but we're not clear on why you can't use relative paths and place the images in the content folder. Could you clarify why you need the copy functionality?

Regarding watch mode in API docs, I'm skeptical that changes to the F# source code would automatically reflect in the browser. Does this currently work?

It would be helpful if you could create a sample repository that highlights the issues you're facing.

We appreciate your patience with this conversation.

@tymokvo
Copy link
Author

tymokvo commented Jul 10, 2024

Ok, I will try to find some time next week to set up a demo repository. Thank you again for thinking through it!

One quick note, XML vs. Markdown formatting is not a concern for me. Just the support for getting images into generated documentation pages in a way that is readable and clarifies the documentation.

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

No branches or pull requests

3 participants