Vscode: Allow custom setting/control of workbench tab titles

Created on 16 Feb 2018  ·  32Comments  ·  Source: microsoft/vscode

Hi there,

In my project, I have files with quite long file names (beyond my control). This limits me to having 3-4 tabs show in my workbench at once. I would like to be able to rename these tabs, or control their label format myself, in order to have more condense tab titles and thus more easily navigate my tabs. This was roughly suggested by #21662 but was not really addressed by #12965 -- any of the options for workbench.editor.labelFormat still give me tab titles that are too long. Can workbench.editor.labelFormat be modified, for example, to optionally take a formatter lambda function we can provide? As just one example of a reasonable formatter one might write, it could be that you fix tab titles to 20 chars, and for long filenames you truncate and prepend with "...". Additionally, one might want to assign nickname/shorthand tab titles for commonly used files. Please pardon me if there is already a way to do this (would love to know how!), but if not, I'd sincerely appreciate considering this as a feature request.

Thanks!

feature-request workbench-tabs

Most helpful comment

Okay, I implemented this so far in #102725, following the example of window.title:

With this, you can use variables to customize tab names. Please note that i've let tab _labels_ unchanged so far.

I have the following questions:

  • I believe there should be a bit of a discussion as many of the possible variables of window.title don't make much sense (active editor, ssh, etc.). What options should we offer users when customizing their tab names?
  • Should this feature replace workbench.editor.labelFormat and allow you to customize both the tab name and the tab label?
  • Should we also include some sort of syntax for truncating or is workbench.editor.tabSizing sufficient?

Thank you!

All 32 comments

It is a shame that is not possible to configure tab titles.
It is also a shame that the configuration for tab titles are almost imposible to find if you don't know that they are called labels, incredibly hard to deduce.

Custom editor tab title formatting could be useful to solve this problem:

image

In a repo where the adopted pattern is the directory is the most meaningful name, and every directory contains an index.js and styles.css file, you end-up with this, regardless of any tab title-related settings I know of so far (including workbench.editor.labelFormat.

In this case, if (I say "if" since this ask only applies, for me, to this particular scenario, which is quite common out there) the file is some generic name like these, I'd want to replace the file name with the directory name, like {directoryName} so I'd get MyComponent with a JS-related icon instead of index.js, or similar instead of styles.css.

@stefcameron that's exactly it. Take a few patterns to structure bigger react applications for example
https://hackernoon.com/fractal-a-react-app-structure-for-infinite-scale-4dab943092af
https://medium.freecodecamp.org/scaling-your-redux-app-with-ducks-6115955638be

The directory is the meaningful name, not the file, as index.ts is the same in any directory. Also, I have a big 49 inch screen so I divide my vscode in 7 vertical regions next to each other (best thing I've done in years), each has one file, index.ts, but then of course a different one as they're in different directories.

At the moment you can set workbench.editor.labelFormat to e.g. medium so it shows you a portion of the path in the tab when you pull a second or third index.ts in the same vscode region. However, I want to see the path inside the tab even when I only have one index.ts per region in order to keep them apart i.e. which index.ts am I looking at... The only way I know how to do that atm is with "breadcrumbs.enabled": true but then I'd love to have it inside my tab even if I only have one index.ts per editor region.

As a matter of fact, after spending some time reading current and past issues, https://github.com/Microsoft/vscode/issues/47142 is exactly what I want.

...show differences on inactive tabs...

even if there's only one index.ts per editor region and thus only one tab in any of my seven opened regions next to each other. It would allow me to have a bunch of index.ts next to each other, and have eyes and mind move fast through my application and work effectively and joyfully.

@evdama 49in screen!? Impressive. 😄 Yes, I could see #47142 working for my issue as well. I'd still have index.js and styles.css everywhere, but I'd also see the folder name _underneath_ -- perhaps that's the important detail, since with current settings, we can display the folder name _after_, but in my case, that information is quickly and completely lost, hidden behind other tabs (or I can't put more than like 3 or 4 tabs in a single editor, which isn't useful to me).

@pixmaster
@stefcameron yeah, one week old 49WL95C-W connected to my macbook via USB-C :)
Agreed, foldername underneath filename is what I think is visually very appealing and makes for a great workflow.

I also have a 49-inch ultra-widescreen... best thing I did too

There is precedent for this (well, for more granular control) in the window.title option. It would be great to get an equivalent for tabs!

  // Controls the window title based on the active editor. Variables are substituted based on the context:`${activeEditorShort}`: the file name (e.g. myFile.txt).
  // - `${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt).
  // - `${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt).
  // - `${activeFolderShort}`: the name of the folder the file is contained in (e.g. myFileFolder).
  // - `${activeFolderMedium}`: the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder).
  // - `${activeFolderLong}`: the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder).
  // - `${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder).
  // - `${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder).
  // - `${rootName}`: name of the workspace (e.g. myFolder or myWorkspace).
  // - `${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace).
  // - `${appName}`: e.g. VS Code.
  // - `${dirty}`: a dirty indicator if the active editor is dirty.
  // - `${separator}`: a conditional separator (" - ") that only shows when surrounded by variables with values or static text.
  "window.title": "${activeEditorShort}${separator}${rootName}",

workbench.editor.labelFormat does seem good enough for the vast majority of use cases though. I've got it set to "short" so I always see the current folder name.

In the hopes that it helps someone else that finds themselves here, there is a setting for allowing tab to auto-shrink if there isn't enough room:

"workbench.editor.tabSizing": "shrink"

That was good enough for me.

Thanks @dansteren , that's exactly what I was looking for.

+1 to @danielo515's note - the setting does not show up if you search for anything with tabs, you have to search for labels.

Also with regards to issue at hand - I'd love to see something like @Nick-Lucas' suggestion but allowing newlines and somehow also supporting tab subtitles (the lower contrast part).

I got here via #40386, and I would like to reiterate the request to hide file extensions, since the nice file type icons give the same information.

If possible, it would be nice to only hide the extension if a type-specific icon is already on the tab. If there's an extension that doesn't have its own specific icon, the extension would appear in that tab label, and the icon would disappear instead.

But that may be overkill, and merely the option to hide all extensions would be great.

it would be nice to only hide the extension if a type-specific icon is already on the tab

Any progress on this?

it would be nice to only hide the extension if a type-specific icon is already on the tab

Any progress on this?

I'm trying to address this issue, will be putting up a PR which solves extension cropping in a few days. Then I'll look into porting the titlebar variable feature to tab labels.

Okay, I implemented this so far in #102725, following the example of window.title:

With this, you can use variables to customize tab names. Please note that i've let tab _labels_ unchanged so far.

I have the following questions:

  • I believe there should be a bit of a discussion as many of the possible variables of window.title don't make much sense (active editor, ssh, etc.). What options should we offer users when customizing their tab names?
  • Should this feature replace workbench.editor.labelFormat and allow you to customize both the tab name and the tab label?
  • Should we also include some sort of syntax for truncating or is workbench.editor.tabSizing sufficient?

Thank you!

@mehanix

Okay, I implemented this so far in #102725, following the example of window.title:

🎉 Thanks for taking this on!

I have the following questions:

  • I believe there should be a bit of a discussion as many of the possible variables of window.title don't make much sense (active editor, ssh, etc.). What options should we offer users when customizing their tab names?

The main reason why I was originally looking for tab name customization is to solve this problem where a project uses index.js files for every single component. So you end-up with dozens of useless "index.js" tabs open and can't tell them apart. What I was hoping for is a way to say that the tab name should be the directory name IIF the file name is "index.js".

Also note that tab labels don't help with this problem, as I explained here.

I'm not sure how this would work with the token syntax because of the conditional, and because it probably shouldn't hard-code "index.js" as the common name to check for (even though that's a pretty common pattern in the community).

Maybe it could be similar to how ${separator} works, since the value of the token is also configurable in the "Tab Title Separator" option you also captured in your screenshot.

We could have an ${alternateIf} token, which points to a "Tab Title Alternate" option, which gives you a way to set the conditional file name to match, and then a list of options which we could start as only "Directory name" for now, unless other folks can think of additional alternatives to name.

Another way to solve this issue would be to do something similar to what #47142 was proposing (and closed in favor of this issue). What if this feature worked with the tab labels feature and provided tokens like ${labelPlacementTop}, ${labelPlacementBottom}, ${labelPlacementOpposite}.

By default, if you set a label option (e.g. "short"), the label is displayed on the right (in a left-to-right language, I presume, and on the left in a r-to-l language). If you set ${labelPlacementOpposite} in the tab title format, then it goes on the opposite side (left for l-to-r, and right for r-to-l). And then you have ${labelPlacementTop} which puts it on top, and ${labelPlacementBottom} which puts it on the bottom.

Top and bottom options would probably solve my use case because I could set the label to "short" to show the directory name, and the placement to bottom, so that the dir name isn't cut off when I have many same-named tabs open like the screenshot I posted in my earliest comment.

But it would alway be displayed, which is different from my ${alternateIf} suggestion. At least it would be configurable in the workspace for that project alone where I know the index.js and styles.css patterns are being used.

  • Should this feature replace workbench.editor.labelFormat and allow you to customize both the tab name and the tab label?

Maybe that's where I'm going with my suggestions? I still think there's value in the Tab Label option we have today. I just don't like that it makes the tab title even longer because of where it gets displayed.

  • Should we also include some sort of syntax for truncating or is workbench.editor.tabSizing sufficient?

I think the existing option is sufficient.

Thank you for the detailed response, @stefcameron!

Following your suggestions and what I think would make most sense, I propose the tab name system works in the following way:

  • By default, tab names appear the same as they currently do, and the tab label option is not removed.
  • Add a workbench.editor.tabNameBlacklist option, which would work on the same principle as search.exclude: define glob patterns or specific filenames for which the tab title name would be displayed differently.
  • Add a workbench.editor.tabTitleAlternate option (or use the one I already implemented) where the user can define their preferred naming system for the blacklisted filenames.

I'm not sure whether it should be called a blacklist or something else, i'm open to suggestions. Maybe tabAlternateFilenames ?

In your use case, I would be able to do the following:

  • Add **/index.js as well as any other filename to tabNameBlacklist.
  • Set tabTitleAlternate as ${folderShort}

And get the desired results.

I believe this system would best accomodate as many use cases and programming languages/frameworks as possible which use the "folder is the most meaningful descriptor" naming pattern.

As for the tab label, i also believe it is best if we kept it and added the possiblity of changing where it is displayed on the tab. However, i will leave this to be treated in another PR and will not be dealing with it now.

Please let me know what you think about this possible solution to this issue.

Wouldn't it be more explicit, efficient, and provide more fine grained control to add just 1 option that specifies renaming map?

"workbench.editor.tabTitleOverrides": {
  "**/index.js": "${folderShort}"
}

vscode already suffers from a huge option fatigue. tabNameBlacklist and tabTitleAlternate would add 2 options to solve 1 problem, and in a pretty un-intuitive way since it's not obvious without further documentation that these two are intended to be used and work together. It'd be yet another case where people have to look up some stack overflow thread to figure out how to do, while something like tabTitleOverrides seems pretty straight forward.

Also, for more advanced cases where tab titles might depend on context, such as what other tabs are open at the moment, an API like this would be awesome:

function customTabTitle(currentTab: TabFilePath, openTabs: TabFilePath[]): TabTitle {}

so that userland has an option to customize this to their liking.

Wouldn't it be more explicit, efficient, and provide more fine grained control to add just 1 option that specifies renaming map?

"workbench.editor.tabTitleOverrides": {
  "**/index.js": "${folderShort}"
}

@darsain

Yes, that's a great idea! I tried to solve this in a way that would take advantage of the UI elements already in place, however I agree this would be a much simpler fix and also allow for fine grained control. I imagine this would be configured by directly writing into settings.json, and to simply provide an edit in settings.json link for configuration.

I'll look into updating the PR. Thanks!

@mehanix @darsain I like where this is going! I think the glob mapping option would provide some nice flexibility for various scenarios, and it would also solve my use case.

@stefcameron @darsain Okay, i implemented the following: (more details on the PR page #102725)

ezgif com-optimize

done

I believe that with this you can cover a lot of use cases (since the glob syntax is so powerful compared to a simple name match)

I would love to know what you think!

@mehanix What you've implemented as a solution looks great to me! I agree, it looks it would be quite a flexible way to solve a lot of related use cases. Love the fact you can also use constant strings in the names, not just the variables. Nice work, thank you! 😀

This looks awesome! Can't wait to use for React:

{
  "**/components/*/*": "${folderShort} (${fileExtension})"
}

Which would yield "ComponentName (html)", "ComponentName (tsx)", etc.

Thank you so much @mehanix for taking this on so long after this issue was opened :)

I think that your proposed solution will solve many classes of problems; however, the specific type of issue I was having at the time (and still to some extent) is that I had a codebase where filenames were like

CompanyNameAVeryLongNameForMySourceFileGeneratorFunctorLambda.cpp

So even if you used some tokens like ${folderShort} or ${fileExtension}, you would still be stuck with this big unwieldy file name if you wanted to uniquely identify the tab.

So an example of what I wanted to do would be more along the lines of what I think (?) @darsain was getting at, e.g. give a lambda like

workbench.tab.tabTitleFormatter = (path) => { return path.filename.remove('CompanyName').substr(0, 10); };

to strip the "constants" in the filename and keep all tab titles no more than 10 characters (ok, this lambda is probably not the exact thing I'd want, but something like this).

Is it possible to have the option/configuration for this functionality take a lambda that returns a string or format string, rather than just having it take a format string directly? That would provide the most flexibility and would hopefully solve all problem types. Let me know your thoughts. Thank you again for taking this on!

Perhaps that use case could be solved by supporting RegEx and capture groups alongside/instead of globbing?

So something like this

{
  ".*/components/.*/CompanyName(.*)": "\1"
}

I think it’s still easy to imagine transformations that are difficult to express as regex. Regex does probably capture a lot of the common transformations people want to do but a lambda may be simpler to write/read and will give full flexibility. You could have a combination of regex and lambda where the lambda is passed the file path plus whatever tokens are derived from the regex.

Globbing definitely seems useful; I guess for something to apply to all files you could just do “*” ? And what if a file matches multiple glob patterns, will multiple rules apply or just one? E.g. trim CompanyName, then if it’s a tsx file format as “TrimmedName (tsx)”? This is again behavior that could be done with lambdas that may be able to be done with other solutions too but just want to make sure this kind of thing is possible since it’s the most general solution.

While i can see how regex could treat more cases, it is also a lot more complex and difficult to set up for users.

Globbing is already used in VS Code in a number of places, however, as far as i know, regex isn't used in any settings thus far. I think it would be a bit more consistent to keep this as-is for now, until we also get feedback on my current PR.

As for lambda functions, is anyone aware of a safe method to implement this? some sort of sanitizing function? To me, lambdas sound dangerously close to code injection, but then again - I'm not incredibly experienced and will be researching this in the next few days.

mehanix: Globbing is already used in VS Code in a number of places

Yep, I agree this is an important precedent, I just don't see another way without introducing a lambda of some sort like @DABH is proposing

DABH: but a lambda may be simpler to write/read and will give full flexibility

Is this even possible or wise to do? Settings are stored in JSON after all, so you at least would have to provide the path to a JS file somewhere, but that might raise future security and performance concerns if arbitrary code can be executed from within a setting?

Edit: @mehanix your edit showed up just as I was hitting post on this. Yes my suspicion is that any arbitrary code execution would be rejected by anyone reviewing, but I have nothing but my own developer instincts to base that on.

@mehanix @Nick-Lucas Thanks for thinking this through. I agree that in the settings file one might be constrained to static expressions rather than specifying a (path to a) script.

So I wonder if there is any other way to have a lambda/script for this somewhere? I have not dug into the VS Code internals too much, but presumably there's some code that sets an icon for a tab based on filetype -- that sounds quite similar to some code that would set the tab title based on the filename. Maybe rather than an entry in settings.json, the lambda/script could live as a customizable VS Code plugin, since plugins run code? So for example, change VS Code so that tab titles are set via the "identity lambda" i.e.

workbench.tabs.tabTitleFormatter = (filename) => filename;

but then the plugin would be able to overwrite that property with a lambda you specify? Do you think something like that is possible, or have you seen any examples of any similarly-behaving plugins?

I guess there could be two complementary solutions, one just based on regexes/globs in the settings.json, and the other being some plugin with lambdas. Unless someone has a cleverer solution :)

I feel like a plugin is starting to get pretty heavy-handed to address this. Functions in JSON won't work, though, obviously. I don't have any clever ideas; I don't know VSCode internals enough.

I have to say, I like the idea of a RegEx map with matchers, though globs would still be way more useful than not having this feature at all. RegEx feels like a possible middle ground between globs with no matchers, and a lambda that requires module loading and potentially-unsafe/expensive code execution, or an entire plugin.

Perhaps the best solution really is to provide the Glob implementation in VS Code because it's simple and easy to configure for perhaps 80% of uses, and then to expose this to VS Code Extensions, so that extensions from the community can handle the corner cases?

Given we're able to extend everything from icons to intellisense in VS Code, it seems plausible that language extensions might want to handle displayed filenames too? Additionally then someone could publish a real power-user extension with regex and lots of complex options, without having to weigh down the default settings with complexity.

Right that's what I'm thinking, if we can just make tab titles _extensible_ then someone sometime can make an extension that allows arbitrary customization. I imagine doing that (making titles extensible) would only be a small change to VS Code? My question is whether it's possible to do that, to make it extensible so that someone could make an extension later. Idk the internals well enough sadly.

But yeah in the meantime the simpler settings/configuration options exposed by the proposed solution will still help a lot of folks I think.

We would love this feature in our company. @bpasero would you mind giving @mehanix some feedback so that the PR would be merged? I'm sure you have a lot on your plate but we'd really appreciate if you'd have a look.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mrkiley picture mrkiley  ·  3Comments

vsccarl picture vsccarl  ·  3Comments

sirius1024 picture sirius1024  ·  3Comments

curtw picture curtw  ·  3Comments

lukehoban picture lukehoban  ·  3Comments