-
Notifications
You must be signed in to change notification settings - Fork 102
Contiguous tabstops not working as expected (2nd selects part of 1st) #236
Comments
Yeah, I can reproduce this. It’ll be awfully tricky to fix. Each tab stop is represented by a “marker.” Markers keep track of arbitrary ranges of the document and are quite good at adapting to additions, deletions, and movement, but in this case it’s doing the opposite of what we want in an ambiguous situation. I’ll use parentheses to illustrate the invisible markers. After tab expansion, you’ve got:
You type
The editor knows to put
…but we’re getting this instead:
Markers are something that Atom core handles for us. I’ll see if there’s any way for us to tell it what ought to happen in this situation. |
For reference: using |
This discussion adds some context. Pinging @nathansobo because I’m curious if this ever came up again. I don’t see any open issues in the |
Thinking about this some more. When the cursor is right at the border of two tab stops (the end of one and the beginning of another) and a character is typed, which marker should “claim” the new text input? I think the answer is “whichever tab stop is active,” as in whichever one we’ve cycled into with the Tab key. I had thought that the key to solving this was the ability to tell a marker to make its head exclusive but its tail inclusive, or vice-versa, but I don’t think that’ll solve this in the general case. Right now, when a snippet is expanded, we place its tab stop markers once, and rely on the built-in functionality of markers to expand/move as necessary and keep track of the evolving boundaries of each tab stop. I think we need to go further than this:
In other words, if two markers have equal claim to a newly-typed character, the marker that corresponds to the active tab stop should always win out. This seems like it would solve all the cases that I can think of off the top of my head, but the next step here would be to write some new specs and make sure. |
@savetheclocktower thank you so much for diagnosing the problem! I'm surprised I haven't seen more people who have run into this. |
@kswedberg, based on the example in this ticket I made a spec: describe "when the snippet has two adjacent tab stops", ->
it "ensures insertions are treated as part of the active tab stop", ->
editor.setText('t19')
editor.setCursorScreenPosition([0, 3])
simulateTabKeyEvent()
expect(editor.getText()).toBe("barbaz")
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 3]]
editor.insertText('w')
expect(editor.getText()).toBe('wbaz')
editor.insertText('at')
expect(editor.getText()).toBe('watbaz')
simulateTabKeyEvent()
expect(editor.getSelectedBufferRange()).toEqual [[0, 3], [0, 6]]
editor.insertText('foo')
expect(editor.getText()).toBe('watfoo') The good news: I was able to make the changes I described above and get this test to pass. The bad news: I broke eight other specs. Here’s an example of one that my strategy won’t solve: "has a placeholder that mirrors another tab stop's content":
prefix: 't17'
body: "$4console.${3:log}('${2:uh $1}', $1);$0" When it expands, I think the next step is to cheat and see how other IDEs handle this. I’ll also look at some of the similar issues and assemble some more examples of problematic snippets. This is only a problem when two tab stops are directly adjacent to one another, so maybe we can cheat and find a way to put implied space between them that isn't actually represented in the editor. |
I wonder, would being able to make individual endpoints of the marker exclusive be of help? That would take some effort to implement but I wanted to put the idea out there. |
@nathansobo, that was originally what I was planning to ask for before I went on yesterday’s windmill tilt. I think it would probably help, yeah. I’ll try to create an issue on atom/text-buffer today. Where I’m stuck — and will probably remain stuck until I get some inspiration or steal answers off of someone else’s test — is the step before that. Given a snippet like When I figure that part out, my guess is that it will reveal some scenarios where we want a marker to be inclusive on one end and exclusive on the other. My other concern is how dynamic we need the markers to be. When I draw the markers for This doesn’t feel like it should be this much of a brain-teaser. |
@savetheclocktower if you do want to look at how other IDEs handle this, Visual Studio Code seems to have figured it out—though I don't know how or where. Others, like TextMate, have, too, but VS Code's code base is probably closer to Atom's in that they're both JS derivatives, as far as I can tell: TypeScript/CoffeeScript. |
@kswedberg Way ahead of you! 😜️ Turns out VScode does things extremely similarly to what I’ve got in mind. They use “decoration” instead of “marker” and “stickiness” instead of “exclusive,” but they make it so that only the active tab stop’s markers grow when the user types at their edges. BUT they also have a special case for when an active placeholder is within an inactive placeholder. If I can figure this out, it‘ll solve the failure I described above — which, BTW, is the only remaining test failure to solve. (The others were regressions with transformed tab stops and I got them fixed.) |
@nathansobo I’ve read the docs and the source code and I’m still not sure of the answer to this question: is it possible (and safe) to change a |
@savetheclocktower That's the only public way to change exclusivity right now. There's a private method called |
I think I got it working! In theory! It's a data-structure nightmare right now because of all the cross-referencing and the need to replace |
👏👏 👏 |
Prerequisites
Description
Backspacing on second tabstop deletes part of text in first tabstop when tabstops are contiguous.
Steps to Reproduce
Expected behavior: [What you expect to happen]
Only "baz" should be selected for replacement
Actual behavior: [What actually happens]
"yzzbaz" is selected for replacement. Only the first character of the first tabstop replacement ("x") is left unselected.
Reproduces how often: [What percentage of the time does it reproduce?]
100%
Versions
Atom 1.17.0
MacOs Sierra 10.12.5
The text was updated successfully, but these errors were encountered: