Unexpected behaviour when setting tab stops #197

Open
opened 2026-01-30 21:45:20 +00:00 by claunia · 0 comments
Owner

Originally created by @j4james on GitHub (Mar 21, 2018).

Originally assigned to: @zadjii-msft on GitHub.

  • Your Windows build number:

Microsoft Windows [Version 10.0.16299.309]

  • What you're doing and what's happening:

I've been experimenting with the various escape sequences for setting tab stops and noticed a number of areas in which the behaviour is different from what I would have expected. This seems largely due to the fact that the default tab stops are treated differently from the manually specified tabs.

If you start with just the default tab stops (every 8 columns), and try to use the single-column tab clear command (TBC) to clear one of those tabs, it appears to have no effect in the Windows console. Example test case:

printf "\033[3g\033c|\t|\t|\n\t\033[0g\r\tx\n"

In a Linux console, the TBC clears the tab stop at column 8 (zero-based), so the x appears in column 16 as expected:

|       |       |
                x

In the Windows 10 console, though, the TBC command has no effect, so the x appears in column 8:

|       |       |
        x

The horizontal tab set (HTS) command is similarly out of sync with the default tab stops. So if you start with the defaults, and try to set an addition tab with HTS, that one tab stop appears to wipe out all the defaults. Example test case:

printf "\033[3g\033c|\t|\t|\n    \033H\r\tx\tx\n"

In a Linux console this adds a tab stop in column 4, leaving the default 8 column tabs unchanged, so you get one x in column 4 and one in column 8:

|       |       |
    x   x

In the Windows 10 console, the defaults are erased, so while the first x appears in column 4, the second x appears in the rightmost column of the display:

|       |       |
    x                                                       x

Then there's the all-column TBC, which is supposed to clear all tab stops, but just resets to the defaults in the Windows console. Example test case:

printf "\033[3g\033c|\t|\t|\n\033[3g\tx\n"

On Linux this works as expected - all tabs are cleared, so the x appears in the rightmost column of the display:

|       |       |
                                                            x

On Windows the tabs are reset to the default positions, so the x appears in column 8:

|       |       |
        x

Now if I really did want to reset the tabs to their defaults, I would have expected the ESC c (reset) command to have that effect, but in the Windows console that leaves the most recently set tab stops unchanged. Example test case:

printf "\033[3g\033c    \033H\033c|       |       |\n\tx\n"

This sequence sets a tab in column 4, resets the terminal with ESC c, then attempts to output an x in the first tab position. On Linux the tabs are reset to the 8 column defaults, so the x appears in column 8:

|       |       |
        x

On Windows the tab stop in column 4 is retained, even after the reset, so the x appears in column 4:

|       |       |
    x

Note that in all of these test cases I'm starting with a combination of CSI 3g and ESC c as a cross platform way to reset the tabs before getting to the actual test. If you wanted to be certain of the behaviour, though, it would be preferable to test each sequence from a fresh console.

I should also note there are a few other areas where the tab functionality differs from what I would expect, but I thought it best to check first whether the current behaviour is intentionally the way it is, in which case my other complaints would likely be non-issues too.

  • What's wrong / what should be happening instead:

I'd expect the default tab stops and manual specified tab stops to work in exactly the same way, exhibiting behaviour more in line with what I see in a Linux console. I'd also expect the reset command to reset the tab stops to their default values.

Originally created by @j4james on GitHub (Mar 21, 2018). Originally assigned to: @zadjii-msft on GitHub. * Your Windows build number: Microsoft Windows [Version 10.0.16299.309] * What you're doing and what's happening: I've been experimenting with the various escape sequences for setting tab stops and noticed a number of areas in which the behaviour is different from what I would have expected. This seems largely due to the fact that the default tab stops are treated differently from the manually specified tabs. If you start with just the default tab stops (every 8 columns), and try to use the single-column tab clear command (TBC) to clear one of those tabs, it appears to have no effect in the Windows console. Example test case: printf "\033[3g\033c|\t|\t|\n\t\033[0g\r\tx\n" In a Linux console, the TBC clears the tab stop at column 8 (zero-based), so the _x_ appears in column 16 as expected: | | | x In the Windows 10 console, though, the TBC command has no effect, so the _x_ appears in column 8: | | | x The horizontal tab set (HTS) command is similarly out of sync with the default tab stops. So if you start with the defaults, and try to set an addition tab with HTS, that one tab stop appears to wipe out all the defaults. Example test case: printf "\033[3g\033c|\t|\t|\n \033H\r\tx\tx\n" In a Linux console this adds a tab stop in column 4, leaving the default 8 column tabs unchanged, so you get one _x_ in column 4 and one in column 8: | | | x x In the Windows 10 console, the defaults are erased, so while the first _x_ appears in column 4, the second _x_ appears in the rightmost column of the display: | | | x x Then there's the all-column TBC, which is supposed to clear all tab stops, but just resets to the defaults in the Windows console. Example test case: printf "\033[3g\033c|\t|\t|\n\033[3g\tx\n" On Linux this works as expected - all tabs are cleared, so the _x_ appears in the rightmost column of the display: | | | x On Windows the tabs are reset to the default positions, so the _x_ appears in column 8: | | | x Now if I really did want to reset the tabs to their defaults, I would have expected the `ESC c` (reset) command to have that effect, but in the Windows console that leaves the most recently set tab stops unchanged. Example test case: printf "\033[3g\033c \033H\033c| | |\n\tx\n" This sequence sets a tab in column 4, resets the terminal with `ESC c`, then attempts to output an _x_ in the first tab position. On Linux the tabs are reset to the 8 column defaults, so the _x_ appears in column 8: | | | x On Windows the tab stop in column 4 is retained, even after the reset, so the _x_ appears in column 4: | | | x Note that in all of these test cases I'm starting with a combination of `CSI 3g` and `ESC c` as a cross platform way to reset the tabs before getting to the actual test. If you wanted to be certain of the behaviour, though, it would be preferable to test each sequence from a fresh console. I should also note there are a few other areas where the tab functionality differs from what I would expect, but I thought it best to check first whether the current behaviour is intentionally the way it is, in which case my other complaints would likely be non-issues too. * What's wrong / what should be happening instead: I'd expect the default tab stops and manual specified tab stops to work in exactly the same way, exhibiting behaviour more in line with what I see in a Linux console. I'd also expect the reset command to reset the tab stops to their default values.
claunia added the Product-ConhostWork-Item labels 2026-01-30 21:45:20 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#197