asciidoctor / asciidoctor-vscode

AsciiDoc support for Visual Studio Code using Asciidoctor
Other
334 stars 97 forks source link

Support includes in preview and preview synchronisation #59

Open danyill opened 6 years ago

danyill commented 6 years ago

When a document has includes a bunch of files, changing location in the source document does not align in the preview. This is a pretty cool feature of Asciidoc and supporting it in this extension is a worthy (but non-trivial goal).

What should be the correct behaviour?

One option:

  1. With a preview of the "main" document, the line numbers should align correctly when the user scrolls in the source.
  2. If while the preview of the "main" document is open and an "included" file is opened, then the preview should scroll to the appropriate location in the included file.

I'm looking into doing this by writing a "data-line" and "data-file" attribute to the output. Information for both of these are available when sourcemap is set on asciidoctor. This is semantically better than the current solution of putting a role with the line number in (which is hard to do with the file name without doing weird stuff) but requires the use of templates and converters. I'm not sure whether a better approach is possible.

joaompinto commented 6 years ago

@danyill I agree that addRole is not the proper way to do it, the current use for data-line-number is ugly. Unfortunately I couldn't find an easy way to set attributes, like you mention, we probably need to change the html5 backend to handle those.

danyill commented 6 years ago

hi @joaompinto

Yes, I hope it's not an impossible goal. I have done a little preliminary research. At least for Ruby this can be done using Tilt templates and a Treeprocessor to expose the information based on adding an attribute to a block.

I have a prototype I'll hope to share in some time. It works as follows:

  1. Use a TreeProcessor extension in Ruby. This extension adds to each block an attribute for filename and an attribute for line number:

    
    class LineNumberTreeprocessor < Extensions::Treeprocessor
    
    def process document
        return unless document.blocks?
        if (document.attr? 'apply-data-line') 
    
            process_blocks document
        end
    end
    
    def process_blocks document
        (document.find_by).each do |block|
            if !block.lineno.nil?
                block.set_attr 'data-file', block.file
                block.set_attr 'data-line', block.lineno.to_s
                # this is not semantically righteous
                block.add_role 'data-line-' + block.lineno.to_s
            end
        end
    end

end

2. Create a template which overrides the html output and includes this information. For instance in haml the file `block_paragraph.html.haml` (Asciidoctor uses the filenames to determine what to overwrite):
```haml
- if attr? :'data-file'
  %div{:id=>@id, :class=>['paragraph', role], :'data-file'=>(attr :'data-file'), :'data-line'=>(attr :'data-line')}
- else
  %div{:id=>@id, :class=>['paragraph', role], :'data-line'=>(attr :'data-line')}
- if title?
  .title=title
%p<=content
  1. Call this extension using either the sourcemap attribute or another extension from https://github.com/asciidoctor/asciidoctor-extensions-lab (the docs for it is missing it but there is enable-sourcemap-preprocessor.rb). I use: asciidoctor --trace --verbose -E haml -b html5 -T ./lib/line-number-treeprocessor/templates -r ./lib/enable-sourcemap-preprocessor.rb -r ./lib/line-number-treeprocessor.rb lib/line-number-treeprocessor/sample.adoc

Which produces an output with:

div class="paragraph data-line-139" data-file="/home/mulhollandd/Documents/asciidoctor-extensions-lab/lib/line-number-treeprocessor/sample.adoc" data-line="139"></div>
<p>Integer sollicitudin magna blandit leo condimentum fermentum.
Aliquam sit amet consectetur purus.
Aliquam sed dui blandit ligula fermentum tincidunt.
Donec vitae augue non urna lobortis dignissim non dictum velit.
Maecenas ac metus volutpat, vehicula mauris at, dignissim odio.
Suspendisse at enim facilisis, aliquam ligula vel, imperdiet lectus.
Proin augue leo, finibus ac leo in, volutpat vulputate enim.
Aliquam rutrum est vitae ultricies semper.
Nullam a lacinia urna.</p>
<div class="admonitionblock warning data-line-150">
<table>
<tr>
<td class="icon">
<div class="title">Warning</div>
</td>
<td class="content">
Suspendisse at enim facilisis, aliquam ligula vel, imperdiet lectus.
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1 data-line-1">
<h2 id="_cheshire_cats">Cheshire Cats</h2>
<div class="sectionbody">
<div class="sect2 data-line-3">
<h3 id="_chapter_6_an_excerpt">Chapter 6 (an excerpt)</h3>
<div class="paragraph data-line-5" data-file="/home/mulhollandd/Documents/asciidoctor-extensions-lab/lib/line-number-treeprocessor/sample2.adoc" data-line="5"></div>
<p>There was certainly too much of it in the air. Even the Duchess sneezed occasionally; and as for the baby, it was sneezing and howling alternately without a moment’s pause. The only things in the kitchen that did not sneeze, were the cook, and a large cat which was sitting on the hearth and grinning from ear to ear.</p>
<div class="paragraph data-line-7" data-file="/home/mulhollandd/Documents/asciidoctor-extensions-lab/lib/line-number-treeprocessor/sample2.adoc" data-line="7"></div>
<p>‘Please would you tell me,’ said Alice, a little timidly, for she was not quite sure whether it was good manners for her to speak first, ‘why your cat grins like that?’</p>
<div class="paragraph data-line-9" data-file="/home/mulhollandd/Documents/asciidoctor-extensions-lab/lib/line-number-treeprocessor/sample2.adoc" data-line="9"></div>
<p>‘It’s a Cheshire cat,’ said the Duchess, ‘and that’s why. Pig!’</p>

At the moment I've only overwritten the paragraph but you can see the data-line and data-file attributes here. The source file has an include so you can see this changing.

What do you think? Could we do something like this? It is very verbose to add the file to each paragraph but I don't see a better option. It is also quite verbose to have to overwrite all the templates to get similar performance to adding role but I don't think the other extension hooks allow anything else (as far as I can tell).

joaompinto commented 6 years ago

@danyill I am still thinking on the functional implications of this, right now the preview window is totally refreshed when you switch to a different source file. As it is on your point 2, besides switching/scrolling to the include file would also replace the preview content to the included file only, would that be ok ?

danyill commented 6 years ago

@joaompinto yes, I'm not sure what the best way is to do this smoothly.

Either:

  1. Only when the include file is open change focus and scroll to it.
  2. Ignore include files when scrolling across the "main document" (at least renumber this correctly so that it represents the file which is open)

or

  1. Open include files as the user scrolls (but probably not close them afterwards)

There's probably other options. Both seem slightly tricky UI issues and I'm not sure what is possible within VS.

joaompinto commented 6 years ago

Maybe we need 2 view modes, preview mode, which will render the includes, and edit mode which will just place a button signalling there is an include.

I am still researching on how to trigger VS editor actions from within the preview actions, (this is required (preview -> editor), the preview window runs in a sandbox process, so it's not trivial to implement bidirectional behaviour.

danyill commented 6 years ago

@joaompinto see my PR above for this code in a better form.

From the point of view of minimising preview -> editor interaction: