-
Notifications
You must be signed in to change notification settings - Fork 29.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add settings to control the tab subtitle style (#12965) #33815
Add settings to control the tab subtitle style (#12965) #33815
Conversation
@tvald, |
To anticipate a couple of design questions:
I would prefer to see a minimal feature accepted and merged. It can then be expanded with further PRs as users figure out what they more want in the tab description. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good start, some feedback provided.
@@ -104,6 +104,16 @@ let workbenchProperties: { [path: string]: IJSONSchema; } = { | |||
'description': nls.localize('showEditorTabs', "Controls if opened editors should show in tabs or not."), | |||
'default': true | |||
}, | |||
'workbench.editor.tabDescription': { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder how clear this setting for users is. I know we call this "description" internally, but unsure if people will relate this to the small text next to the main label of a tab.
We already have window.title
, maybe this should be workbench.editor.tabSubtitle
?
@@ -211,6 +214,10 @@ export class TabsTitleControl extends TitleControl { | |||
} | |||
})); | |||
|
|||
// Configuration updates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we try to put this into the ITabOptions
bag and use the related IEditorGroupsService.onTabOptionsChanged
event?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into this briefly, and I don't know how to wire it all up. Sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok leave it as is and I will have a look after you are done 👍
@@ -211,6 +214,10 @@ export class TabsTitleControl extends TitleControl { | |||
} | |||
})); | |||
|
|||
// Configuration updates | |||
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I typically prefer to setup listeners from the constructor via registerListeners
(see in parent class: https://github.com/Microsoft/vscode/blob/master/src/vs/workbench/browser/parts/editor/titleControl.ts#L146). Can we do the same in this class and call the super one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I attempted this like so:
protected registerListeners(): void {
super.registerListeners();
// Configuration updates
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged()));
this.onConfigurationChanged(false);
}
Which results in the following error on startup:
Cannot read property 'onDidUpdateConfiguration' of undefined
at TabsTitleControl.registerListeners (file:///Users/tvald/Documents/Tuple/git/vscode/out/vs/workbench/browser/parts/editor/tabsTitleControl.js:47:57)
It appears that configurationService
isn't yet initialized when registerListeners()
is called from super()
. I haven't looked into the injection framework, so I don't know why configurationService
is different from everything else being injected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok leave it as is and I will have a look after you are done 👍
@@ -211,6 +214,10 @@ export class TabsTitleControl extends TitleControl { | |||
} | |||
})); | |||
|
|||
// Configuration updates | |||
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged())); | |||
this.onConfigurationChanged(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This call seems to cause an update even though we are in the middle of creating the control. I would add an extra flag to not cause an update if this method is being called from construction phase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in d925602
}); | ||
} | ||
} | ||
}); | ||
|
||
for (const label of labels) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be a very specific way of treating description labels by using the native path separator. You have to keep in mind though that there are editors that are not about files (e.g. extension editor).
I think a better approach is to push the logic of a short, medium or long description to each EditorInput
. If you see how it is done for the window title (FileEditorInput.getTitle()
) we should do something similar for the description imho. We can just change the getDescription
signature to accept short, medium or long version (we already have a verbose flag which is going into that direction).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That does sound better, but it's more involved than I can commit to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned before, just move the logic into FileEditorInput
, no need to implement it for all other editors.
'default': '${pathDifferential}', | ||
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'tabDescription' }, | ||
`Controls tab descriptions. Variables are substituted based on the context: | ||
\${pathShort}: e.g. parent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be a very specific way of treating description labels by using the native path separator. You have to keep in mind though that there are editors that are not about files (e.g. extension editor). I would rather call this:
descriptionShort
descriptionMedium
descriptionLong
descriptionDifferential
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do note that descriptions are already assumed to be paths when they're trimmed via shorten
. That said, the architecture you mention below sounds sufficiently flexible and could be augmented to provide an editor-type-specific shorten
method as well. Unfortunately, that's more involved than I can commit to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree that the shorten logic is assuming paths but that is only because this algorithm can only work with paths and nothing else, so I think that is fair. But other editors might have the desire to provide short, medium and long descriptions.
I think the change is straightforward: I am not asking to implement this for each and every editor, it is fine to just move your logic into FileEditorInput
.
…g initialization.
- Change configuration type to the enum ['default', 'short', 'medium', 'long'] - Move path logic into FileEditorInput. - Move configuration handling into GroupService/ITabOptions. - Refactor duplicate path shortening logic.
The requested changes are done in d8b5863. This required a couple of significant changes:
|
@tvald thanks, I will have a look tomorrow. |
… show subtitle when all descriptions are identical.
Added a couple of small fixes to handle corner cases in the de-duplication logic. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tvald this took me quite some time to review, I left some comments.
Can you elaborate why you decided to rewrite the code within the if (shortenDuplicates) {
branch from the version I had before? It seems to me that you could have left that code intact and just use getDescription(LONG)
instead of getDescription(true)
for picking the full description when there are duplicates.
'enum': ['default', 'short', 'medium', 'long'], | ||
'default': 'default', | ||
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'tabDescription' }, | ||
`Controls tab subtitle format: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a bit more verbose to understand the setting: Controls the format of the subtitle for an editor tab. Subtitles show up next to the file name depending on this setting and make it easier to understand the location of a file:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 26db623
short: 'parent' | ||
medium: 'workspace/src/parent' | ||
long: '/home/user/workspace/src/parent' | ||
default: '.../parent', when another tab shares the same title`), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefix the enumeration values with "-" to be consistent with other settings where we do that, e.g. workbench.fontAliasing
setting below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 26db623
@@ -104,6 +104,17 @@ let workbenchProperties: { [path: string]: IJSONSchema; } = { | |||
'description': nls.localize('showEditorTabs', "Controls if opened editors should show in tabs or not."), | |||
'default': true | |||
}, | |||
'workbench.editor.tabSubtitle': { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also define the enumDescriptions
property to get more help when typing. See https://github.com/Microsoft/vscode/blob/master/src/vs/workbench/electron-browser/main.contribution.ts#L229 for example how we use it for the workbench.fontAliasing
setting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 26db623
@@ -76,7 +75,7 @@ export class TabsTitleControl extends TitleControl { | |||
@IWindowsService private windowsService: IWindowsService, | |||
@IThemeService themeService: IThemeService, | |||
@IFileService private fileService: IFileService, | |||
@IWorkspacesService private workspacesService: IWorkspacesService | |||
@IWorkspacesService private workspacesService: IWorkspacesService, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like an extra trailing comma.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 26db623
// identify duplicate titles | ||
const mapTitleToDuplicates = new Map<string, AugmentedLabel[]>(); | ||
for (const label of labels) { | ||
getOrSet(mapTitleToDuplicates, label.name, []).push(label); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can already here ignore any IEditorLabel
that does not have a description. Because further below there is a check:
if (typeof label.description !== 'string' || !label.description) {
label.description = '';
}
and then later:
duplicateTitles = duplicateTitles.filter(label => label.description);
If we would just ignore labels without description in the code path of handling duplicates I think we can simplify this a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 26db623
const shortenedDescriptions = shorten(descriptions); | ||
descriptions.forEach((description, i) => { | ||
const duplicateDescriptions = mapDescriptionToDuplicates.get(description); | ||
duplicateDescriptions.map(label => label.description = shortenedDescriptions[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use forEach()
here and not map()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 26db623
There's probably a clean way to retain the old code, if that's desired. Some small rearrangement was necessary to switch from the template system to the enum configuration, and at the time it seemed cleaner to rewrite the whole section. I'm not attached to the new code. |
@tvald thanks, I will keep your changes in around handling duplicates. |
Fixes #1469
Adds a template system for tab descriptions:
${pathShort}
: e.g.parent
${pathMedium}
: e.g.more/parent
${pathLong}
: e.g.workspace/even/more/parent
${pathDifferential}
: e.g..../parent
, when another tab shares the same title (current behavior)