Subject: Re: bug#60655: 30.0.50; tree-sitter: `treesit-transpose-sexps' is broken.

From: Theodor Thornhill <[email-masked]>
Date: Mon, 09 Jan 2023 14:37:11 +0100

Mickey Petersen <[email-masked]> writes:

> Theodor Thornhill <[email-masked]> writes:
>
>> Mickey Petersen <[email-masked]> writes:
>>
>>> Theodor Thornhill <[email-masked]> writes:
>>>
>>>> Mickey Petersen <[email-masked]> > The tree-sitter-enabled
>>>> function, `treesit-transpose-sexps', that is called by
>>>> transpose-sexps, is broken.
>>>>>
>>>>> It uses a naive method of sibling adjacency to determine
>>>>> transpositions. But it is unfortunately not correct.
>>>>>
>>>>> Python:
>>>>>
>>>>>
>>>>>   def -!-foo():
>>>>>       pass
>>>>>

[...]

>
> That, in my opinion, is the wrong way to look at it.
>
> `C-M-t' already works well: it transposes stuff around point. Nothing
> more, nothing less.
>
> If I write rubbish code as a human, no amount of machine intelligence
> will (yet) undo that. Nor should a 'clever' mechanism that is only
> clever by half. Trying to transpose things on, near or around
> point is a useful addition if, and only if, it can do so in a manner
> that is sensible, and predictable, to its operator.
>
> You will very quickly run into umpteen problems generalizing this.
> That's why I have shown restraint and limited Combobulate to things
> that I feel are simple (but made it quite easy for someone to
> customize, if they disagree!)

Yes, I agree. But the commands M-t and C-M-t are different for a reason,
so they should be used in different circumstances, right?

>
> As a user, I may well want to put my code into an erroneous state,
> temporarily, because I am doing something that cannot be represented
> atomically as a single command. Therefore, `self-insert-command' (for
> example) does not predict what I am about to type and intercedes when
> it disagrees with me: it merely abides.
>
> When I do this with `C-M-t' it is because it is an intentional act on
> my behalf. The example I gave above is illustrative; it's designed to
> highlight the problem.
>

Sure, I believe I understand what you mean.

>>>
>>>
>>>
>>>>> You could make a cogent argument that both approaches are wrong from a
>>>>> syntactic perspective, but I think that misses the broader point that
>>>>> `C-M-t' now does something errant and unexpected.
>>>>
>>>> I don't really see how "foo def():" is any better at all.  We gain some
>>>> great improvements with this "naive" method - namely:
>>>>
>>>> if 5 + 5 == 10 then 10 else 100 + 100.  If point is on the else the 100
>>>> + 100 wil be swapped by 10, but the old behavior will be broken.
>>>>
>>>
>>> The old behaviour was consistent. It had a simple *modus operandi*:
>>> swap two things around point. As someone who has used `C-M-t' for
>>> decades, I know what it'll do in pretty much all situations, because I
>>> know what `C-M-k` and `C-M-f/b` do at all times.

Yeah, but "thing" is very vague, so the consistency is an accident more
than anything else.

>>
>> It may be consistent, but imo it is too close to transpose-words, and
>> too likely to create useless code in non-lisp languages.
>>
>
> No, transpose word and transpose sexp are very different; do different
> things; and apply in vastly different circumstances:
>
> Let -!- be point:
>
>     d = {'Hello, World!': -!- 1}
>
>     # C-M-t
>     d = {1: 'Hello, World!'}
>
>     # M-t
>     d = {'Hello, 1!': World}
>
> `transpose-sexps' works just fine the way it is: enriching it with a
> greater understanding of certain contexts is a fruitful endeavour, if
> it is done sympathetically.
>

You say they are used in vastly different circumstances, and I argued
that the "def foo" case is a case where it should't be used.  I still
believe there are oceans of improvements to be made, but I don't
understand the desire not to improve what is there, whatever an
improvement is considered to be.

>>>
>>> Neither approach is great if you holistically approach this task as
>>> "making it correct at all times", and it is easy to confect scenarios
>>> that result in something that is semantically wrong, but syntactically
>>> correct; something that is plain wrong, both semantically and
>>> syntactically; and something that is occasionally correct.
>>>
>>
>> I see what you mean, but to me semantically _and_ syntactically correct
>> is the benefit we should pursue when we actually have the parse tree.
>> The current implementation will semantically correct in many interesting
>> cases, such as the one I outlined, and is a huge improvement to the
>> current "transpose-words"-like behavior.
>>
>

I see two directions myself.  Either we try to make a point that
transpose-words and transpose-sexps operate on different contexts, or we
try our best to blend the two, to avoid backwards incompatible changes.
I'm ok with both, myself.  We could even try to make a transpose-dwim
command that will guess the surroundings and choose modus-operandi
accordingly?

>
> Sure. But please be careful when you make changes to `transpose-subr'
> (or `transpose-subr-1') so that they don't break its existing contract
> with its current users. It is a *very* powerful set of commands that
> can swap arbitrary tracts of text.
>

Of course - I'm trying hard to be conservative :)

Theo