microsoft / terminal

The new Windows Terminal and the original Windows console host, all in the same place!
MIT License
95k stars 8.22k forks source link

Request: "Tab title" text resolves environment variables #10144

Open rkhoury opened 3 years ago

rkhoury commented 3 years ago

Description of the new feature/enhancement

When entering the "Tab title" text in the settings, it would be great if environment variables could be resolved. When I've tried this, for example with standard Command Prompt, all I get are strings of "%SOMEENVIRONMENTVAR%" in my tab title.

Proposed technical implementation details (optional)

For all "Tab title" strings, just run an expand pass that attempts to substitute any "%..%" sub-strings with any available environment variable.

zadjii-msft commented 3 years ago

This isn't a terrible idea. It's a lot like #1320, but kind of it's own specific request. I'll leave this open as a task as a child of that one. Thanks!

vertigo220 commented 2 years ago

This would be useful for setting the tab title to %cd% for example, so it's easy to see what each tab is, instead of all just saying "Command Prompt" or "Windows PowerShell." That's the feature I was looking for, and I tried %cd% to see if it was a hidden ability. Ideally, there should be different options, such as just the current directory or the whole path, and whether to show e.g. "C:\Program Files\WindowsAp..." vs "...d8bbwe\WindowsTerminal.exe" vs "C:...\WindowsTerminal.exe" or some other variation, and it should show the available variables and formatting that can be used in the settings.

zadjii-msft commented 2 years ago

@vertigo220 FWIW that's probably better served by configuring your shell to change the Terminal's title. bash already does something like this with OSC 0 (wikipedia, xterm docs)

vertigo220 commented 2 years ago

Thanks. Figured out how to do it for PS, though haven't tested yet (and I hate this obsession with putting appdata-type crap in "Documents," which I guess just doesn't mean documents anymore...), but I can't find any way to do it with cmd. I've found how to change the title (no thanks to MS's help page, which shows the command usage incorrectly) and a workaround to create a makeshift profile/bashrc-type file, but I don't see a way to get it to change the title every time the directory is changed. I assume this isn't possible?

zadjii-msft commented 2 years ago

For CMD you can put escape sequences in the prompt with the prompt command. For example:

prompt $e]0;$p$e\test$g

image

vertigo220 commented 2 years ago

Very cool, thanks :)

For some reason, I had to put $g twice on the end, otherwise I didn't get the ">":

prompt $e]0;$p$e\$p$e$g$g

vertigo220 commented 2 years ago

So I figured out the last escape ($e) isn't necessary and is why I needed two $g's, so I just removed them and am left with prompt $e]0;$p$e\$p$g which does exactly what I want. However, I'd really like to do this only in Terminal, due to the tabs, and not in the standard command terminal, since it's not needed there and I need to see from the title if it's an admin window or not. Of course, I'll hopefully use the standard one less and start using Terminal instead, but for when I do use it, it would be preferable to not have the title changed this way.

I saw that some things, like VisualStudio IIRC, can have their own profiles for PowerShell, and was wondering if the same applies to cmd. This has all made me think of another potentially really good feature, which would be an box in profile settings where code can be placed to run when a tab of that profile is created, so each profile can have its own "bashrc."

vertigo220 commented 2 years ago

TUTORIAL/GUIDE

For anyone who comes across this later looking to do the same, I just spent a bit of time on this trying to get both the title to be show the CWD (current working directory) and the CWD to be used when duplicating or splitting a tab. Both involve the use of the prompt command (cmd) or variable (PS). Getting it working in PS was relatively easy, but cmd was trickier because each thing requires its own slight modification, and they seemed to conflict, so it took a good bit of trial and error.

This post is meant to try and put everything together about how to do this relatively concisely while also actually explaining things. Much of it is covered here, but it doesn't fully explain everything, requires going to yet another page for more instructions, and even then still doesn't cover everything. Hopefully these instructions will be quick and easy to understand so others won't have to hunt down the various bits and piece them together and figure out the nuances like I did.

CMD:

I couldn't find any reference on the square bracket ("]"), semicolon, backslash, or the numbers. All I knew was 0 to show the CWD in the title, thanks to the above post, and double-9's to make the CWD available to the terminal for use when duplicating/splitting a tab. I tried to use them in various combinations together, but that didn't work. I may be wrong here, but my understanding is that they're used in this format: $e]#;... escapes the "]" and the # tells what the character strings following the ";" are used for, e.g. 0 for window title and 9;9 for setting the CWD, and the $e\ is just and escape of the backslash which separates each section of the prompt. So one section to set the title bar, one to set the CWD, and one to set the actual prompt. This led to the following:

prompt $e]0;$p$e\$e]9;9;$p$e\$p$g

The first chunk ($e]0;$p) sets the title bar, then a separator ($e), then the second chunk ($e]9;9;$p) sets the CWD, then another separator, and finally the prompt ($p$g). The list of what the various characters (e.g. p, g, etc) do is here. With an understanding of how it actually works, hopefully it will be easier for others to customize to their liking.

The next step is to make the cmd shell run this every time a terminal is opened, so it can do its thing. There are different ways of doing this (**skip to the end of this section for how to do it on a profile basis), but I personally prefer doing it via the registry (if you don't like messing with that, skip to after the next line of code). Just go to "HKLM\SOFTWARE\Microsoft\Command Processor" and create a new key of type "String Value" (REG_SZ), name it "AutoRun" (without the quotes), and either put the prompt command in there or put the path to a .cmd file that contains the command, e.g. bashrc.cmd. You can also just run the following command from an elevated terminal (replace the actual prompt config with your own):

reg add "HKLM\SOFTWARE\Microsoft\Command Processor" /v "AutoRun" /t REG_SZ /d "prompt $e]0;$p$e\$e]9;9;$p$e\$p$g" /f

For a non-registry method, use the following command (replace the actual prompt config with your own):

setx PROMPT "$e]0;$p$e\$e]9;9;$p$e\%PROMPT%"

That's all there is to it.

_To do this per profile_**, simply go to the profile in settings and add the prompt line to the 'Command line' setting, e.g. %SystemRoot%\System32\cmd.exe /k prompt $e]0;$p$e\$e]9;9;$p$e\$p$g

PowerShell:

***Skip to the end for setting per profile

Open PowerShell and type notepad $PROFILE which opens (or asks you to create) the user PS profile, which should be located in "$env:userprofile (%userprofile% in cmd/explorer)\Documents\WindowsPowerShell." Then just add/modify the "Prompt" function depending on what you want:

To only set the title to the CWD:

function Prompt
{
  $host.ui.RawUI.WindowTitle = $(get-location)
  "PS $(get-location)> "
}

To only duplicate/split tab using the CWD:

function Prompt
{
  $loc = $($executionContext.SessionState.Path.CurrentLocation);
  $out = "PS $loc$('>' * ($nestedPromptLevel + 1)) ";
  $out += "$([char]27)]9;9;`"$loc`"$([char]27)\"
  return $out
}

To do both:

function Prompt
{
  $host.ui.RawUI.WindowTitle = $(get-location)
  $loc = $($executionContext.SessionState.Path.CurrentLocation);
  $out = "PS $loc$('>' * ($nestedPromptLevel + 1)) ";
  $out += "$([char]27)]9;9;`"$loc`"$([char]27)\"
  return $out
}

I still don't fully understand this, since it seems the output of the function is the same regardless of which one is used, yet they only work as described here. Also, I feel the need to mention that I wasted yet another several minutes due to still another MS failure (I hate using MS as a reference, they can't seem to get anything right), as I copy-pasted the first function from a MS DevBlog that oh-so-helpfully used MS-Word-style slanted quotes that caused it to not work and led to me having to troubleshoot the issue. /smh

***PowerShell can also be configured on a per-profile basis in a similar way to cmd:

Modify the profile 'Command line' setting, e.g. %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoExit -File "%userprofile%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile_Terminal_1.ps1". Note the use of %userprofile% instead of $env:userprofile despite this being PS, because, at this time, it's Windows/cmd processing the command. This threw me for a bit. Obviously, you will need to have such a file with its own custom config (see above) and if you want it to be different than what the standard PS window will use, it must NOT be "%userprofile%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1" since that's the file that uses.

(This is a broken method, only read if you're interested in trying to get it working) You can also add aliases for each profile to your standard profile file ("%userprofile%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"), e.g. Set-Alias -Name Terminal1 -Value "$env:userprofile\Documents\WindowsPowerShell\Microsoft.PowerShell_profile_Terminal_1.ps1" Note $env:userprofile is used here, since this will be run by PowerShell once it's loaded. Then, in theory, you would modify the 'Command line' setting to %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoExit Terminal1 and it would work the same. This could be very beneficial as it would allow switching the behavior mid-session. Unfortunately, while it may work for one-time changes, such as fonts/colors/etc, it does not work for prompt, since it only runs when running the alias (and even then, only if the function is explicitly called, which is not necessary in a profile file. It seems when run as an argument to powershell.exe, the function is added to the PowerShell instance and is called whenever "prompt" is run, which is every time a command is run, whereas calling the script once PS is running simply runs it once and doesn't integrate the function, so it's only called that one time, not every command. If someone knows how to get it to work properly, please let me know.

Hopefully someone finds this useful.

vertigo220 commented 2 years ago

@zadjii-msft In addition to my question above regarding the possibility of a separate profile for cmd with Terminal to allow the use of a different prompt variable with Terminal vs the standard command window, since it is possible to get the CWD to Terminal for displaying in the tab, would it be possible to have an option to split it and make the tabs double-height, so it could have two lines, with the top being e.g. "C:\Windo..." and the bottom being "...ystem32" to allow for seeing either end of the CWD in the title to be able to better tell what tab is what? Or, at least, just display the current dir only instead of the full path, perhaps with the drive letter included, e.g. "C:...\system32?"

zadjii-msft commented 2 years ago

the possibility of a separate profile for cmd with Terminal to allow the use of a different prompt variable with Terminal vs the standard command window

Maybe - that might be covered by #2785. I don't know if PROMPT would work with that, since cmd is a special type of evil, but it probably should.

would it be possible to have an option to split it and make the tabs double-height, so it could have two lines, with the top being e.g. "C:\Windo..." and the bottom being "...ystem32" to allow for seeing either end of the CWD in the title to be able to better tell what tab is what?

I doubt we'd enable the tabs to be two lines in height, where the first row is just the first bit of the tab title, and the second is just the end. There is tabWidthMode:titleLength which displays the whole title.

Ultimately, Command Prompt is not a modern shell and not one we can make changes to. So, if you want something like c:\...\System32 as the title, you're gonna need to rely on PowerShell.

zadjii-msft commented 2 years ago

This has all made me think of another potentially really good feature, which would be an box in profile settings where code can be placed to run when a tab of that profile is created, so each profile can have its own "bashrc."

I mean, you can totally do that today - doing something like commandline: cmd /k cmdrc.bat, and putting your "bashrc" content into the bat file cmdrc.bat should accomplish the same thing (note: this is an untested off the cuff suggestion)

vertigo220 commented 2 years ago

Beautiful. I just put %SystemRoot%\System32\cmd.exe /k prompt $e]0;$p$e\$e]9;9;$p$e\$p$g in the command prompt 'Command Line' setting and removed the AutoRun registry setting, and it works the way I want in Terminal and in the standard command prompt. Perfect, and thanks again!

As for splitting the title, it seems once it's in the hands of Terminal, it's no longer up to the shell, ancient (cmd) or not (PS) and should be doable, though I obviously could be wrong or misunderstanding some part of this. I am slowly switching to PowerShell at least, but I still do/have a lot of batch scripts so cmd will be sticking around for a while for me, unfortunately.