DillanCMills / tab-stop

Utility extension for VS Code to insert spaces on the current line to a user-configured column number
MIT License
1 stars 1 forks source link

Feature request: corresponding dedent command #2

Open jkyeung opened 3 months ago

jkyeung commented 3 months ago

This would be roughly the inverse of tab-stop.indent, but probably trickier. Basically, it should do what Shift+Tab does out of the box, but with the user-defined tab stops instead of stops at every n spaces.

DillanCMills commented 3 months ago

So would you expect it to remove white space (or your own self-defined character) from the beginning of the line and move any non-matching strings left to each tab stop until eventually reaching position 0?

jkyeung commented 3 months ago

Yes.

The positioning of the cursor is slightly intricate:

At least, that is what I observe in my VS Code setup.

DillanCMills commented 3 months ago

Okay, I will play around with it!

DillanCMills commented 3 months ago

When dedenting, would it make more sense to remove characters from the start of the line or from directly left of the cursor?

    foo                  = bar

to

foo                  = bar

or

    foo = bar

assuming properly aligned tab-stops.

The second case seems more useful to me, but it does break from the standard flow for tab/shift-tab. For multi-line selections, the entire line would be selected so it would behave like the first result above regardless.

jkyeung commented 3 months ago

Good question. I agree with you that the second seems more useful. But I'm not sure what the most intuitive way to achieve it would be.

I probably won't know for sure until I have the functionality in my hands and actually try it out for a while. But my guess right now is that if I have manually placed the cursor somewhere between foo and =, then I'll want tab-stop.dedent to leave foo where it is and move = bar left to the nearest tab stop, or immediately adjacent to foo if there are no tab stops in between. And at the end of the operation, the cursor will keep its absolute starting position as long as it's still left of =. Otherwise, the cursor will end up exactly at =. (So, if there are no tab stops between foo and = bar, then I think I would expect the end result to be foo= bar, with the cursor between foo and =.)

If, on the other hand, the cursor starts anywhere to the left of foo, then I'm pretty sure I would expect the usual Shift+Tab behavior (i.e. treat the whole line as a unit).

It's weird for me to consider what should happen if the cursor starts inside foo. Or inside bar. Or, if there are trailing spaces, and the cursor starts to the right of bar. I have some ideas, but I need to end here for now, and give everyone a chance to mull things over for a while.

DillanCMills commented 3 months ago

I think the easiest is to make dedent a reverse of indent. For indent, each cursor will insert character to the left of the cursor up to the next tab stop. For multi-line blocks, it will create a pseudo-cursor at the beginning of each line, as well as the starting point for the first line.

For dedent, the inverse would be to remove character to the left of the cursor up to the previous tab stop or to either the beginning of the line or a non-matching character.

I expect dedent will be a little funky (and just not work) if you have tabs at the beginning of your line and are trying to dedent a space character. But that case should be handled by shift-tab anyway.

For just spaces, this will work well enough. If character is defined as something custom, especially a multi-character string, it will need a little more attention to get right. But it should be straightforward enough to figure out using this approach.

I think it will be a natural companion to the built-in tab and shift-tab and you will need to decide which to use in each scenario. But its strength is really aligning components of the line, not the line itself (tab is fine for that), so I think this strategy will work great.

jkyeung commented 3 months ago

Interesting. I agree that it would be simplest, both conceptually and to implement, for tab-stop.indent to just insert characters left of the cursor, and tab-stop.dedent to delete characters left of the cursor. All that other stuff I mentioned comes from trying to emulate the built-in Tab and Shift+Tab as closely as possible. I actually had to experiment with things I consider weird and awkward, just to try to discover the "full" specification and write it down here.

I'm coming at this from the angle of trying to completely replace the built-in Tab and Shift+Tab, for use with source code that has unconventional tab stops, purely for indentation. Alignment other than indentation is actually not much of a priority for me.

What I think makes the situation tricky, from my perspective, is that my mental model of single-line editing is a completely different thing than multi-line editing. When I'm adjusting the indentation of a single line, I will put the cursor at the leftmost nonblank, then use Tab or Shift+Tab exactly as though they were Space or Backspace, respectively, except that they are aligned to tab stops (which I think is what you're describing). I don't have any active selection in that case.

On the other hand, when I'm adjusting the indentation for multiple lines, I will put the cursor at the left edge and highlight the lines of interest (and I always highlight complete lines, with the cursor never leaving the left edge; so if I've highlighted the selection from top to bottom, the cursor will be on the left edge at the first unselected line). This is where Tab and Shift+Tab are, in my mind, not simply bigger versions of Space and Backspace; rather, they're special selection indentation operators.

DillanCMills commented 3 months ago

okay well let me try implementing the ideas I have in mind and I will create a pre-release version you can play with and see if it matches your expectations. Then we can adjust from there. I like this idea so hopefully we can flesh out something good.