asciidoctor-vscode icon indicating copy to clipboard operation
asciidoctor-vscode copied to clipboard

Reveal includes in rendered preview

Open ryandeussing opened this issue 4 years ago • 5 comments

When I'm looking at the rendered preview of an AsciiDoc file, I'd love to be able to see what sections of the file are includes, and the files that are included.

The solution I have in mind would be an instant tool-tip with a link to the include, e.g. include::directory/filename.adoc

Perhaps some subtle affordance in the preview could make it clear where an include exists.

For context, I am often reviewing and editing AsciiDoc files that make use of dozens of includes. I can't read the document from source (as the source shows includes, but not the content of the include), and when I read the document in preview, I spend a lot of time hunting down the source file to which I want to make edits.

Thank you!

ryandeussing avatar Jun 30 '21 14:06 ryandeussing

That's a good idea!

One way to achieve this is to use an include processor extension. For reference, here's an example of an include processor extension: https://docs.asciidoctor.org/asciidoctor/latest/extensions/include-processor/

The idea would be to read the content of the include and prepend it with a link:

link:part-a.adoc[" ",role=include-ref,title="part-a.adoc"]

With a bit of CSS, it could look like this:

include-ref

Not quite sure yet if we should support this feature out-of-the-box. Summoning @ahus1, I would appreciate your opinion on this.

ggrossetie avatar Jul 02 '21 13:07 ggrossetie

This is very cool! My only suggestions, if this becomes standard would be:

  • optionality / ability to disable
  • ability to edit css

ryandeussing avatar Jul 15 '21 15:07 ryandeussing

Hi, the UX you describe sounds useful (sorry for answering so late).

As an alternative approach, the implementation can be achieved without an include processor: If you set the option "sourcemap" to "true", each node will contain the source location. I use it in the AsciiDoc IntelliJ plugin to include the positions in the rendered HTML. When the user clicks on the contents in the preview, the AsciiDoc IntelliJ will open the file you clicked on.

By adding a little JavaScript (or postprocessor), these could be visualized like above.

Some links to the implementation of the AsciiDoc plugin:

  • setting the sourcemap option: https://github.com/asciidoctor/asciidoctor-intellij-plugin/blob/main/src/main/java/org/asciidoc/intellij/AsciiDoc.java#L1112-L1115
  • adding the source information to the role attribute, thereby the HTML backend will add them to HTML: https://github.com/asciidoctor/asciidoctor-intellij-plugin/blob/main/src/main/resources/sourceline-treeprocessor.rb
  • when the user clicks on some text, interpolate the position in the block to jump at an estimated file position: https://github.com/asciidoctor/asciidoctor-intellij-plugin/blob/main/src/main/resources/org/asciidoc/intellij/editor/javafx/pickSourceLine.js via "JavaPanelBridge.scrollEditorToLine"

An HTML output would look like this, where the second paragraph is from an include.

<div id="content">
<div class="paragraph has-source-line data-line-stdin-1">
<p>This is some text.</p>
</div>
<div class="paragraph has-source-line data-line-*****/test2.adoc-1">
<p>More text.</p>
</div>
<div class="paragraph has-source-line data-line-stdin-3">
<p>This is it.</p>
</div>
</div>

UPDATE: I got the idea of a tree processor from the AsciiDocFX editor; see https://github.com/asciidocfx/AsciidocFX/blob/master/conf/public/js/asciidoctor-data-line.js - although that doesn't add the file name.

ahus1 avatar Jul 15 '21 16:07 ahus1

I thought I might have a little think about this because it's a feature I would like as well. In fact, the lack of usability for this case is the limitation preventing me from sectionalising existing documents into more reasonable sized documents and using the include macro.

I like the idea of using a tree processor to add the source file name to the data-line attribute or similar. I would favour adding a separate attribute with the filename, perhaps data-file which seems more semantically correct. However AFAICT this is currently difficult with the Asciidoctor API as it would require a custom converter on all elements (we can't just add a data- attribute to the existing converter if I have this right see https://github.com/asciidoctor/asciidoctor/issues/1305).

We currently use the sourcemap option in the preview to synchronise the preview with the document but we don't include information on which file it came from.

Regarding the UI, while it might be a good idea to add a UI element to show an include (with additional options in settings to disable it), I think we could retain the existing "double click takes you to the source" and improve it to work for the include macro as suggested (see https://github.com/asciidoctor/asciidoctor-vscode/blob/0e1175f2f7f9f9e0ac26e2aa87c15764c4a7d6f5/preview-src/index.ts#L104-L122 and https://github.com/asciidoctor/asciidoctor-vscode/blob/0e1175f2f7f9f9e0ac26e2aa87c15764c4a7d6f5/src/features/preview.ts#L416-L424)

VS Code doesn't currently have a very nice API for identifying visible editors but I think I could probably open a new editor with the include file quite easily for an initial implementation. Perhaps after the next release, I can look into this.

I think AsciidocFX does add the file name in https://github.com/asciidoctor/asciidoctor-intellij-plugin/blob/c13a5b7a6881882a23afbe2ad9c3df6c6dff85cd/src/main/resources/sourceline-treeprocessor.rb#L21

I had a bash at an initial Tree Processor implementation, something like:

function filenameTreeProcessor () {
  const self = this

  self.process(function (document, reader) {
    document.findBy({ traverse_documents: true }).forEach(function (node) {
      if (node && node.getSourceLocation()) {
        if (node.getSourceLocation().getLineNumber) {
          const lineno = node.getSourceLocation().getLineNumber()
          const filename = node.getSourceLocation().getPath() === '<stdin>' ? 'stdin-' : node.getSourceLocation().getPath() + '-'
          node.addRole(`data-line-${filename}${lineno}`)
        }
      }
    })
    return reader
  })
}

module.exports.register = function register (registry) {
  if (typeof registry.register === 'function') {
    registry.register(function () {
      this.treeProcessor(filenameTreeProcessor)
    })
  } else if (typeof registry.block === 'function') {
    registry.treeProcessor(filenameTreeProcessor)
  }
  return registry
}

(although we could also just modify the existing code in https://github.com/asciidoctor/asciidoctor-vscode/blob/0e1175f2f7f9f9e0ac26e2aa87c15764c4a7d6f5/src/asciidocParser.ts#L129-L134)

How should spaces in the filename for the include file be handled (presumably a limitation in the IntelliJ and AsciidocFX implementations?)?. Perhaps it could be URI encoded and decoded or base64 encoded/decoded or perhaps just spaces should be modified in some fashion (attribute definitions in HTML seem to allow very many characters, see here and not many characters are invalid on linux for directories for example).

Interested in any thoughts.

danyill avatar Oct 17 '21 04:10 danyill

The IntelliJ plugin currently picks up a click on the preview and opens he file. There are no additional UI elements that indicate an include. So far this worked ok. If the visual indicator proves difficult, I recommend to skip it for a first release and improve on a later release.

+1 for adding this to the VS Code plugin.

ahus1 avatar Oct 21 '21 21:10 ahus1