obsidian-execute-code icon indicating copy to clipboard operation
obsidian-execute-code copied to clipboard

Store Output in File

Open chlohal opened this issue 2 years ago • 7 comments

To have a proper equivalence to ipynb notebooks, I thought that it might be nice to persist the output in the file.

In this PR, the Outputter adds a code block with the language output directly below the running block. This code block takes every output from stdout and saves it, so the data can be preserved in a platform-neutral way :)

save-output-in-file *note: my Obsidian has a visual glitch because of another plugin I have-- that's why the running indicator remains while it shouldn't

I don't think that this is ready to merge, but I wanted to ask for feedback/opinions/code review :)

TODO:

  • [ ] should we escape output that contains markdown?
  • [ ] Restore log from the output code block (currently, closing and re-opening simply deletes the old log from the Outputter)
  • [ ] Can we support HTML in this view?
  • [ ] Can we support stderr too? Is there any way to distinguish them?
  • [ ] Somehow update positions when the user edits the file
  • [ ] General: Bug fixes, resilience testing, polish
  • [ ] Give the user a setting to choose whether or not to add output to file
  • [ ] If the file is opened and there is already an output block, start output at the end, not the start

chlohal avatar Oct 16 '22 15:10 chlohal

Thanks for this, I think this is a great feature.

From a quick look at your code and testing it myself, I'm not entirely sure what last5 is in getCodeBlockTextRangeByIndex, but aside from that, I think your approach has an issue (which is what I hit when I was working on reusable code blocks, I'm not 100% sure that this is the same issue, but I could get the expected error when testing your implementation in obsidian). Basically, findContainer is looking for markdown-reading-view to parse this tree of code blocks to find the index of the code block, however what happens is that markdown-reading-view doesn't actually render all the markdown on the page, and instead roughly what's on screen (you can try this yourself by copy-pasting a large number of code blocks down the file and looking at how obsidian continually changes what's rendered). This is an issue then, because now there's the (likely) chance that while your code block might be the 5th rendered in the reading view, it's actually the 15th in the document, and so the actual 5th block in the document will be incorrectly given the output block, rather than the 15th. Again, I'm not 100% sure that this is what's going on with your implementation, but it seems consistent with the output I've managed to get. Let me know what you think.

image

As for solutions to this, I don't have any myself right now. The way I got around this was just comparing the full code block contents against all code blocks in the file while traversing (CodeInjector.parseFile), but this breaks with multiple identical code blocks and is slower.

milan338 avatar Oct 19 '22 12:10 milan338

I'm not entirely sure what last5 is in getCodeBlockTextRangeByIndex

Thanks, I should comment that: last5 is a sliding buffer for the parser. At any time, it only needs to keep track of the last 5 characters (technically, last 4, but 5 makes the code easier to read), so it's a waste to store more.

I think you're right about the bug with markdown-reading-view only rendering the blocks in the viewport. I'm having it on my computer too. What you said is exactly what my implementation does (and also a reminder that I need to write more comments), so I think that is probably the root cause. The viewport-only rendering is something that I didn't consider; thanks for bringing it up :)

chlohal avatar Oct 20 '22 12:10 chlohal

I think I might've found the solution: there's an undocumented renderer API on the previewMode object. This provides access to the sections used in rendering, which lets us find the exact range from the <pre> element.

Although I'm nervous about using an undocumented API, if it works on Mac (iirc you have a mac-- could you test it, please?) and linux as well, then I think it'd be justifiable. What do you think?

chlohal avatar Oct 20 '22 15:10 chlohal

I think this would be a really great feature! Great work!

I am unsure how good it works for code blocks with identical content. Do you use the equality of the content to match the blocks in rendering and markdown?

If the renderer API works we can use it, I think. But in this case, I think it would be better to create this feature as an opt-in and mark it as beta until we are sure the API works as intended.

twibiral avatar Oct 20 '22 15:10 twibiral

Thank you!

Do you use the equality of the content to match the blocks in rendering and markdown?

Nope-- this uses the index of the block in the file. If it is the third <pre> element, it'll be matched with the third code block. It still uses the index with the renderer API, although it's not as important; it is only for the case where there are multiple code blocks in a section.

I definitely agree with you wrt creating this as an opt-in feature.

chlohal avatar Oct 20 '22 16:10 chlohal

While trying your code I found those problems:

  • [ ] Executing code, closing the note, reopening the note, and executing the code again results in duplicate output blocks. (Two output blocks one directly after the other with the same content)
  • [ ] When executing, clearing, executing, clearing to much gets deleted. and two subsequent gets merged somehow.
"""python
print(123)
"""
"""python
a = 42
"""

becomes

"""python
print(123)
""""""python
a = 42
"""

twibiral avatar Nov 05 '22 15:11 twibiral

Is this still being worked on? I think this would be very useful, especially if you work primarily in the editor-with-live-preview and don't want to switch back just to run the code =] Still an amazing plugin either way though! Reminds me of my emacs days...

For managing duplication: I think there are instances where I would want the output of the code and I would want results persisted for each run. For instance, if the code calculates something based on different inputs, it might be nice to have a history of different runs. Or it might be nice if I change the code to see that the results don't change. Perhaps there could be a separate run command for this though, or maybe you can implement this one first and then another PR could be done to add the deduplication logic as an option.

Testare avatar May 05 '23 18:05 Testare