Skip to content

Conversation

@Veykril
Copy link
Member

@Veykril Veykril commented Oct 19, 2020

This change minifies the resulting diff of import insertions by inserting or replacing the produced use tree directly through an action return value instead replacing the entire container node. This action has to be applied by the caller now. This unfortunately pulls the AssistBuilder into scope of insert_use back again but I tried to at least keep it away from the insert_use fn itself.

I'm open to more/better ideas regarding this :)

Fixes #6196

@matklad
Copy link
Contributor

matklad commented Oct 19, 2020

Would returning SyntaxRewriter work here? See AssistBuilder::rewrite. As usual, this is "how to compose tree edits?" open probelm.

@Veykril
Copy link
Member Author

Veykril commented Oct 19, 2020

SyntaxRewriter doesn't have any insertion logic yet from what I've seen which was why I didn't consider it I think. On a second thought it would probably make a lot more sense to have this use a SyntaxRewriter instead though.

@Veykril
Copy link
Member Author

Veykril commented Oct 19, 2020

I don't think a SyntaxRewriter can be used here as it can't model insertions(at least I don't see how), as it is meant for rewriting existing nodes only, so just replacements and deletions.

@matklad
Copy link
Contributor

matklad commented Oct 20, 2020

I don't think a SyntaxRewriter can be used here as it can't model insertions(at least I don't see how)

It can't model them now, but it makes sense to extend it to allow this. Implementation wise, rewrite_children function can look into some auxilary map to decide if it needs to appaend element to new_children

That is, we can add

struct SyntaxRewriter {
  ...
  insertions: FxHashMap<InsetPost, Vec<SyntaxElement>>,
}

enum InsertPos { FirstChildOf(SyntaxNode), After(SyntaxElement) }

and that might do the trick?

@Veykril
Copy link
Member Author

Veykril commented Oct 20, 2020

Oh alright, I wasn't sure whether I should touch that part or not, but in that case I'll take another look at SyntaxRewriter

@matklad
Copy link
Contributor

matklad commented Oct 20, 2020 via email

@Veykril
Copy link
Member Author

Veykril commented Oct 21, 2020

I just realized the problem with using SyntaxRewriter for this after all, as the AssistBuilder::rewrite function makes use of algo::diff this will again introduce the same problem with the diff becoming the entire input node on insertion.

Unless I also do changes to TreeDiff to track insertions as well, I guess I can look into that.

Edit: Blocked on #6310/#6365

bors bot added a commit that referenced this pull request Oct 23, 2020
6251: Semantic Highlight: Add Callable modifier for variables r=matklad a=GrayJack

This PR added the `HighlightModifier::Callable` variant and assigned it to variables and parameters that are fn pointers, closures and implements FnOnce trait.

This allows to colorize these variables/parameters when used in call expression.



6310: Rewrite algo::diff to support insertion and deletion r=matklad a=Veykril

This in turn also makes `algo::diff` generate finer diffs(maybe even minimal diffs?) as insertions and deletions aren't always represented as as replacements of parent nodes now.

Required for #6287 to go on.

Co-authored-by: GrayJack <gr41.j4ck@gmail.com>
Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
bors bot added a commit that referenced this pull request Nov 2, 2020
6365: Do insertion lookahead in algo::diff r=matklad a=Veykril

This is the last blocker for #6287 after this I can update that PR to properly fix things through using `SyntaxRewriter`.

This PR also shuffles tests around a bit and adds some more.

Ideally this is just a hack until we implement a "proper" diff algorithm that approximates a minimal diff. Maybe something like [gumtree](https://github.com/GumTreeDiff/gumtree)?

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
@Veykril Veykril force-pushed the fix-auto-import-cursor branch from bfc35c9 to cd349db Compare November 2, 2020 20:41
@Veykril
Copy link
Member Author

Veykril commented Nov 2, 2020

Alright, updated this PR to my recent changes and tested the server locally. It now does not move the cursor in imports anymore 🎉

I had to rewrite extract_struct_from_enum_variant.rs quite a bit to basically use SyntaxRewriters everywhere to prevent overlapping edits to happen.

@matklad
Copy link
Contributor

matklad commented Nov 3, 2020

Perfect, thanks! Rewrite of extract_struct_from_enum_variant is very welcome! (btw, since the code is in your ICache, note that extending it to work with record variants would be nice: #4468 (comment) ;) )

bors r+

@bors
Copy link
Contributor

bors bot commented Nov 3, 2020

@SomeoneToIgnore
Copy link
Contributor

SomeoneToIgnore commented Nov 9, 2020

Do you think it's possible to somehow insert the first import without rewriting the whole file?
In terms of the unit test of the algo module, I want to have:

#[test]
fn first_use_added() {
    check_diff(
        r#"fn main() {
    stdi<|>
}"#,
        r#"use foo::bar;

fn main() {
    stdi<|>
}"#,
        expect![[r#"
            insertions:

            Line 0: Node(SOURCE_FILE@0..25)
            -> use foo::bar;
            -> "\n\n"

            replacements:



            deletions:


        "#]],
    );
}

and instead I get the

insertions:

Line 0: Node(FN@0..25)
-> "\n\n"
-> fn main() {
    stdi<|>
}

replacements:

Line 0: Token(FN_KW@0..2 "fn") -> use
Line 1: Token(IDENT@3..7 "main") -> foo::bar
Line 1: Node(PARAM_LIST@7..9) -> ;

deletions:

Line 1: " "
Line 1: {
    stdi<|>
}

diff.

@Veykril
Copy link
Member Author

Veykril commented Nov 9, 2020

I'm not sure, maybe with some special handling. The problem is what an insertion really means for diffing here, currently it just checks if the node in the original tree still exists in the new tree, then it marks everything between as insertions, otherwise it marks stuff as replacements. Prior to my changes this just replaced the whole parent node, so when fiddling with imports you'd just get a SOURCE_FILE replacement everytime.

Edit: Hm actually this should already mark that as in insertion since the fn main node is still the same isnt it? thats weird

@SomeoneToIgnore
Copy link
Contributor

SomeoneToIgnore commented Nov 9, 2020

It does mark it so, but the condition last_lhs is None for the first comparison (old fn block vs new use block) and that's why no actual insertion happens.
I've tried to brute force things by getting a lhs.parent() (which is a source file in this case) instead of last_lhs, but it did not work, the insertion places the new use statement afterwards the old fn node despite that.

I'm not well familiar with syntax trees and the proper ways to make things work here, but interested to find some solution to this so appreciate any pointers or fixes 🙂

@Veykril
Copy link
Member Author

Veykril commented Nov 9, 2020

Yes I just noticed that again as well, insertions are anchored after elements, so if the first element is whats being inserted we have a problem here. Maybe we just gotta keep track of elements that are to be inserted directly into their parent as the first child in a separate Field in TreeDiff, as in an extra map similar to the insertions one and then insert into the TextBuilder accordingly. Doesn't sound like the best solution but diffing is quite hard as I noticed 😅 And we need to try real hard to not throw away the cursor positions too much.

I could give it a try sometime later today unless you wanna experiment.

@SomeoneToIgnore
Copy link
Contributor

You're definitely better than me in this, so go ahead if you want to.
Thanks for looking into this.

bors bot added a commit that referenced this pull request Nov 9, 2020
6512: Don't replace parent node when inserting as first child in algo::diff r=SomeoneToIgnore a=Veykril

This makes the diff a bit more detailed.

See #6287 (comment) for context
cc @SomeoneToIgnore 

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
@Veykril Veykril deleted the fix-auto-import-cursor branch November 27, 2020 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-import causes cursor position to jump

4 participants