obsidian-execute-code
obsidian-execute-code copied to clipboard
Store Output in File
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 :)
*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
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.
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.
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 :)
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?
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.
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.
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
"""
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.