-
Notifications
You must be signed in to change notification settings - Fork 13
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
nodes mapping #658
Comments
@atulbhatt-system32 how do we resolve it? |
@edenvidal |
@atulbhatt-system32 can you explain it further please? 🙏 what is it, and why is it a burden? to what? |
@edenvidal, @atulbhatt-system32 |
so, the issue is - inconsistent element ids during html parsing and rendering, right? @atulbhatt-system32 @VictoriaShyika lmk if i get it - when html is parsed, each element gets an id. these ids can change with each new render when changes happen, leading to issues like unexpected collapsing or expanding of elements. if an id changes after several re-renders, it might refer to a different element. we need to look into:
last question - is this current behavior already enforces us to compromise in the past? or to write redundant code that covers it? i'd be happy to know even more about it. |
@maxdnc, check this out. |
@atulbhatt-system32 is this relevant? |
Yes @edenvidal. As there can be expanded nodes which are not selected so we need to update those too after uids changes. |
@vicki29 I think it will tough to solve this. Why not for now we make sure at least the nodes in which move operation has happened after drag and drop those remain expanded. |
@atulbhatt-system32, not exactly. the hovered container expands while dragging. check figma for reference. other than that, i don't see a case where a node should expand or collapse. (maybe im wrong) |
@edenvidal |
@atulbhatt-system32 i think yes, this is how it happens |
@atulbhatt-system32 how far did @vicki29 took us with this? thanks |
There wasn't anything that she was able to do. |
Nudge to note I have submitted an upwork proposal but not heard back. Update: the situation with undo is firmer than described in the proposal as I have a better understanding of how that's handled, and have a solution for it. |
from @Mrdigs
from @atulbhatt-system32 Overall about how relying on decorations can help us with our problem - A video explaining it will be much better. and here we are... (let's continue here) |
Before I can continue with this ticket, I need to confirm that the stakeholders understand the impact of persisting uids across edits has on code reliant on sorting the uids in numerical order:- Imagine we have a document which contains the following node. I am using "id" attributes here to show the uid of each node. My change also associates the uid with each node within the source code, except they are invisible. The attribute is for demonstration purposes only.
Now, the user inserts another
At this stage, the new As it has to be unique, it cannot be 1 or 2. Let's assume it becomes 3:*
Now, if the nodes were to be sorted by uid, and these sorted uids were used to generate the updated html as Atul states, then the two This is the potential problem I am worried about, because before my change "Para Y" would have acquired uid 2 and "Para X" would have acquired uid 3 - and so sorting the uids would retain the insertion order. _* When uids are generated, the code is traversing the DOM in depth-first order, so there is no way of knowing which uids are already used elsewhere in the document and this is why I wanted to use the "uuid" library.
|
@Mrdigs But still I'm not sure how the decorations will help with keeping track of which uids should be expanded. |
Hi, I'm afraid my screen recorder isn't recording audio for some reason, so I will have to transcript the video.
Hopefully this explains it for you? Screencast.2024-07-15.08.33.18.mp4 |
@Mrdigs For some reason the video doesn't play for me. @edenvidal is it the same for you? |
@Mrdigs can you share it from a google drive link. I am unable to play it. |
@atulbhatt-system32 Sure, here it is (weird, I can hear myself typing in it now - so the microphone was working) |
In summary, |
@atulbhatt-system32 Yes, correct. The uid of a node never changes, therefore it's expanded state is never lost. Only new nodes are given new uids. That means the sequence of the uids is in insertion order, not source code order. I need you to tell me whether this is a problem for the other parts of the code that sort the uids, as I cannot see what is the purpose sorting them. |
@atulbhatt-system32 I think I have resolved the video issue: Screencast.Converted.mp4 |
We can decide if sorting will actually be needed if we use this mechanism based on how it will handle the below use case. Ex: Cut and Paste Let's say we have multiple nodes selected. Now I Cut them one by one, that is remove the source code sequentially for all nodes and copy them to clipboard. But the thing is I can't parse it again and again for every time I perform the cut for each node. I need the final updated code and then I parse the html again. So we sort the uids for the purpose of doing cut operation in such a way that the source code location of the nodes are not affected. I hope it makes sense. Now, if the editor can give you the updated source code locations based on the uids selected to cut then I think we don't need to sort at all. It will simplify the process entirely. |
@atulbhatt-system32 Ahhhh, now it makes sense! Thank you 🙏 Well I suppose if all else fails then it could sort on the source code location instead of the uids. But yes, you can take a uid and find it's source code location before parsing by querying the uid decorations - and, the If we can say that one or the other of the above solutions can replace all cases of sorting by uid, then I think we're good to go. Do you agree? |
@Mrdigs Yes I agree. |
Hi, I am close to creating a pull request for this now, so I thought I'd document the changes here beforehand:- In useElementHelper.tsx::parseHtml(), the editor is queried to check if there is a decoration providing the uid of the node at the source code location of the current node. If there is then that uid is re-used for the tree node. If no decoration is found then a new uid is generated by incrementing the maxNodeUidRef current value. Whether an existing decoration is found or a new uid is created, the function adds the uid and source code location to a The purpose of this Map is to record what decorations should exist at any point in the undo stack. In the This is because calling Most of the remaining changes are to deal with the last point above. There is now a companion function to function getEditorModelWithCurrentCode() {
/*The role of helperModel is to perform all the edit operations in it
without affecting the actual codeViewInstanceModel and then apply the changes
to the codeViewInstanceModel all at once.*/
const codeViewInstanceModel = monacoEditorRef.current?.getModel();
const helperModel = editor.createModel("", "html");
codeViewInstanceModel &&
setEditorModelValue(codeViewInstanceModel, helperModel);
return helperModel;
}
function setEditorModelValue(
sourceModel: editor.ITextModel,
targetModel: editor.ITextModel,
) {
if (sourceModel && targetModel) {
const uidDecorations = getUidDecorations(sourceModel);
targetModel.setValue(sourceModel.getValue());
targetModel.deltaDecorations([], uidDecorations);
}
} Here's an example of how it is intended to be used. Note, it never calls const someEdit = () => {
const codeViewInstanceModel = monacoEditorRef.current?.getModel();
const helperModel = getEditorModelWithCurrentCode();
helperModel.applyEdits([editsToApply...]);
setEditorModelValue(helperModel, codeViewInstanceModel);
} I have changed all of the edit operations in useElements.tsx to use this approach. They all appear to still work as before but somebody more familiar with the application needs to test them. The I have rewritten useFormatCode fairly extensively as it was overwriting whole node subtree's and that will remove decorations, losing the uids. This action now uses the "diff-match-patch" library to make formatting edits on the model using a diff between the original code and the formatted code, thereby preserving decorations - and uids. The uid sorting question from earlier in the thread, I have resolved by changing the I think that's probably about it. |
One more thing, I am going to commit with the decorations showing to aid with testing. Once testing is complete, they can be hidden by commenting or removing two lines in the
|
thanks a lot @Mrdigs! before i am testing it, i'd be happy to know. @atulbhatt-system32 can you do the testings? also (@Mrdigs , @atulbhatt-system32 ) is there anything to test from a user experience standpoint? is there anything missing to fully resolve this issue to the core? |
I think it is fully resolved, although I missed testing it on a project with multiple files. Basically, any operation on the tree that modifies the source. I think I have caught and tested all of them: duplicate, copy/cut, paste, move, group, ungroup, and format. I should note that many of these operations create new nodes, including move (drag and drop), which is a cut and paste behind the scenes. In these cases, the new nodes get new uids, and therefore their expanded state is not preserved, so: If an expanded node is moved to a new position, I wouldn't expect the expanded state of that node to be preserved, but all other node's state to be preserved. However the solution does provide a mechanism to handle this in many cases: it would involve the cut operation storing the uid of the cut node in the clipboard (but not the copy operation), and the paste operation creating a decoration containing that uid at the paste location. There's also a good amount of scope for simplifying the code for many of these operations via the decorations, but I have restricted myself to implementing the uid tracking and preventing any regression, due to time constraints and minimising changes. |
from @Mrdigs Let's take a concrete example of two different ways a user might make an edit to a document in the code editor, to demonstrate.
We start with a body element containing two `<p>` elements:
```html
<body>
<p>A paragraph</p>
<p>Another paragraph</p>
</body> Now, imagine the user deletes the two <body>
</body> At this point, the document contains no <body>
<div>
<p>A paragraph</p>
<p>Another paragraph</p>
</div>
</body> Then, the overall effect of the two edits appears to be that the Let's start over with the same original document: <body>
<p>A paragraph</p>
<p>Another paragraph</p>
</body> This time, if the user moves the cursor to after the opening body tag and inserts a line break, a tab, and " <body>
<div>
<p>A paragraph</p>
<p>Another paragraph</p>
</body> Then moves the cursor down and inserts another tab on the 2 following lines: <body>
<div>
<p>A paragraph</p>
<p>Another paragraph</p>
</body> Then moves to the end of the line containing the second <body>
<div>
<p>A paragraph</p>
<p>Another paragraph</p>
</div>
</body> Now, the two The first example describes how the group operation currently works (except it is performed with code instead of the user making direct edits), which is why it does not preserve the correct uids. The second example is how it needs to work. The diff technique I referred to above emulates the second example by determining the minimum number of edits to apply to the document to reach the end state from the beginning state (that is, two inserted lines plus two inserted tabs), leaving the code in between unchanged. |
I see the issue with how currently grouping is directly happening by replacing the existing with wrapped . This is causing the inconsistency. For the context I haven't tested the app at this point for what all might not be working as expected in @Mrdigs implementation. But this gives us an idea on what we need to look on. Also How about background sync. What happens when the changes are done in the file when the rnbw is not focused and then the changes syncs back. |
@atulbhatt-system32 , we're handling the issue internally with @stanislavasal - we'll keep using this thread for your consultant 🙏 |
@atulbhatt-system32 , @stanislavasal - I have resolved the issue related to selected nodes not being re-selected when undoing a group operation. The issue was that parseHtml() was being called before the CodeView had a chance to update its content (and therefore it's decorations), leading to a temporary loss of the correct uids in the node tree. I didn't want to change this order because it would likely have major unintended side effects, and so I have changed parseHtml() to allow the value of my new history state My description above of the "div technique" for grouping and ungrouping operations isn't really related or needed, unless a requirement to retain expanded states during those operations is identified. Also, the technique won't work well for cases where the nodes selected for grouping are not next to each other, because in that case, there is no choice but to "cut" the nodes out of the document, and then re-insert them as it currently does. But there is scope for potentially updating Cheers |
thanks @Mrdigs for the detailed answer and fix. i think it works and feels great (with the caveats you've mentioned). i think it need some ux and heavy testing in order to understand how it should go from here. for now, we'll only need a few things to be able to accept this pr:
|
@edenvidal I think the changes work great I did some basic testing I believe you might have done some proper real user testing by yourself. As for code that also looks good. |
@edenvidal That's great news, I'll review the code and get back to you |
@edenvidal I have removed the green markers - just commented the code that adds them in case they needed in future (and left the CSS in rnbw.css). I did some experimentation with disabling some code that I think may now be redundant (such as needToSelectNodePaths) but found there were the flow of the application demanded them in certain operations. My recommendation is to let this PR settle and allow the code to evolve incrementally with the new approach rather than attempting to make too many changes at once. |
thanks @Mrdigs , this is great! |
No description provided.
The text was updated successfully, but these errors were encountered: