提交 693de57e 编写于 作者: B Benjamin Pasero 提交者: GitHub

Allow to configure title bar information (fixes #1723) (#19932)

上级 c1c0b65e
......@@ -19,6 +19,7 @@ import Strings = require('./utils/strings');
import { JSONDocument, JSONSchema, LanguageSettings, getLanguageService } from 'vscode-json-languageservice';
import { ProjectJSONContribution } from './jsoncontributions/projectJSONContribution';
import { GlobPatternContribution } from './jsoncontributions/globPatternContribution';
import { WindowTitleContribution } from './jsoncontributions/windowTitleContribution';
import { FileAssociationContribution } from './jsoncontributions/fileAssociationContribution';
import { getLanguageModelCache } from './languageModelCache';
......@@ -128,6 +129,7 @@ let languageService = getLanguageService({
contributions: [
new ProjectJSONContribution(),
new GlobPatternContribution(),
new WindowTitleContribution(),
filesAssociationContribution
]
});
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { MarkedString } from 'vscode-languageserver';
import Strings = require('../utils/strings');
import { JSONWorkerContribution, JSONPath, CompletionsCollector } from 'vscode-json-languageservice';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
export class WindowTitleContribution implements JSONWorkerContribution {
constructor() {
}
private isSettingsFile(resource: string): boolean {
return Strings.endsWith(resource, '/settings.json');
}
public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable<any> {
return null;
}
public collectPropertyCompletions(resource: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable<any> {
return null;
}
public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable<any> {
return null;
}
public getInfoContribution(resource: string, location: JSONPath): Thenable<MarkedString[]> {
if (this.isSettingsFile(resource) && location.length === 1 && location[0] === 'window.title') {
return Promise.resolve([
MarkedString.fromPlainText(localize('windowTitle.description', "Controls the window title based on the active editor. Variables are substituted based on the context:")),
MarkedString.fromPlainText(localize('windowTitle.activeEditorName', "$(activeEditorName): e.g. myFile.txt")),
MarkedString.fromPlainText(localize('windowTitle.activeFilePath', "$(activeFilePath): e.g. /Users/Development/myProject/myFile.txt")),
MarkedString.fromPlainText(localize('windowTitle.rootName', "$(rootName): e.g. myProject")),
MarkedString.fromPlainText(localize('windowTitle.rootPath', "$(rootPath): e.g. /Users/Development/myProject")),
MarkedString.fromPlainText(localize('windowTitle.appName', "$(appName): e.g. VS Code")),
MarkedString.fromPlainText(localize('windowTitle.dirty', "$(dirty): a dirty indicator if the active editor is dirty")),
MarkedString.fromPlainText(localize('windowTitle.separator', "$(separator): a conditional separator (\" - \") that only shows when surrounded by variables with values"))
]);
}
return null;
}
}
\ No newline at end of file
......@@ -134,4 +134,90 @@ export function shorten(paths: string[]): string[] {
}
return shortenedPaths;
}
export interface ISeparator {
label: string;
}
enum Type {
TEXT,
VARIABLE,
SEPARATOR
}
interface ISegment {
value: string;
type: Type;
}
/**
* Helper to insert values for specific template variables into the string. E.g. "this $(is) a $(template)" can be
* passed to this function together with an object that maps "is" and "template" to strings to have them replaced.
* @param value string to which templating is applied
* @param values the values of the templates to use
*/
export function template(template: string, values: { [key: string]: string | ISeparator } = Object.create(null)): string {
const segments: ISegment[] = [];
let inVariable = false;
let char: string;
let curVal = '';
for (let i = 0; i < template.length; i++) {
char = template[i];
// Beginning of variable
if (char === '$' || (inVariable && char === '(')) {
if (curVal) {
segments.push({ value: curVal, type: Type.TEXT });
}
curVal = '';
inVariable = true;
}
// End of variable
else if (char === ')' && inVariable) {
const resolved = values[curVal];
// Variable
if (typeof resolved === 'string') {
if (resolved.length) {
segments.push({ value: resolved, type: Type.VARIABLE });
}
}
// Separator
else if (resolved) {
segments.push({ value: resolved.label, type: Type.SEPARATOR });
}
curVal = '';
inVariable = false;
}
// Text or Variable Name
else {
curVal += char;
}
}
// Tail
if (curVal && !inVariable) {
segments.push({ value: curVal, type: Type.TEXT });
}
return segments.filter((segment, index) => {
// Only keep separator if we have values to the left and right
if (segment.type === Type.SEPARATOR) {
const left = segments[index - 1];
const right = segments[index + 1];
return [left, right].every(segment => segment && segment.type === Type.VARIABLE && segment.value.length > 0);
}
// accept any TEXT and VARIABLE
return true;
}).map(segment => segment.value).join('');
}
\ No newline at end of file
......@@ -98,4 +98,35 @@ suite('Labels', () => {
assert.deepEqual(labels.shorten(['a', 'a/b', 'b']), ['a', 'a/b', 'b']);
assert.deepEqual(labels.shorten(['', 'a', 'b', 'b/c', 'a/c']), ['', 'a', 'b', 'b/c', 'a/c']);
});
test('template', function () {
// simple
assert.strictEqual(labels.template('Foo Bar'), 'Foo Bar');
assert.strictEqual(labels.template('Foo$()Bar'), 'FooBar');
assert.strictEqual(labels.template('$FooBar'), '');
assert.strictEqual(labels.template(')FooBar'), ')FooBar');
assert.strictEqual(labels.template('Foo $(one) Bar', { one: 'value' }), 'Foo value Bar');
assert.strictEqual(labels.template('Foo $(one) Bar $(two)', { one: 'value', two: 'other value' }), 'Foo value Bar other value');
// conditional separator
assert.strictEqual(labels.template('Foo$(separator)Bar'), 'FooBar');
assert.strictEqual(labels.template('Foo$(separator)Bar', { separator: { label: ' - ' } }), 'FooBar');
assert.strictEqual(labels.template('$(separator)Foo$(separator)Bar', { value: 'something', separator: { label: ' - ' } }), 'FooBar');
assert.strictEqual(labels.template('$(value) Foo$(separator)Bar', { value: 'something', separator: { label: ' - ' } }), 'something FooBar');
// // real world example (macOS)
let t = '$(activeEditorName)$(separator)$(rootName)';
assert.strictEqual(labels.template(t, { activeEditorName: '', rootName: '', separator: { label: ' - ' } }), '');
assert.strictEqual(labels.template(t, { activeEditorName: '', rootName: 'root', separator: { label: ' - ' } }), 'root');
assert.strictEqual(labels.template(t, { activeEditorName: 'markdown.txt', rootName: 'root', separator: { label: ' - ' } }), 'markdown.txt - root');
// // real world example (other)
t = '$(dirty)$(activeEditorName)$(separator)$(rootName)$(separator)$(appName)';
assert.strictEqual(labels.template(t, { dirty: '', activeEditorName: '', rootName: '', appName: '', separator: { label: ' - ' } }), '');
assert.strictEqual(labels.template(t, { dirty: '', activeEditorName: '', rootName: '', appName: 'Visual Studio Code', separator: { label: ' - ' } }), 'Visual Studio Code');
assert.strictEqual(labels.template(t, { dirty: '', activeEditorName: '', rootName: 'monaco', appName: 'Visual Studio Code', separator: { label: ' - ' } }), 'monaco - Visual Studio Code');
assert.strictEqual(labels.template(t, { dirty: '', activeEditorName: 'somefile.txt', rootName: 'monaco', appName: 'Visual Studio Code', separator: { label: ' - ' } }), 'somefile.txt - monaco - Visual Studio Code');
assert.strictEqual(labels.template(t, { dirty: '* ', activeEditorName: 'somefile.txt', rootName: 'monaco', appName: 'Visual Studio Code', separator: { label: ' - ' } }), '* somefile.txt - monaco - Visual Studio Code');
});
});
\ No newline at end of file
......@@ -18,31 +18,178 @@ import * as errors from 'vs/base/common/errors';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IAction, Action } from 'vs/base/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IIntegrityService } from 'vs/platform/integrity/common/integrity';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { isMacintosh, isLinux } from 'vs/base/common/platform';
import nls = require('vs/nls');
import * as labels from 'vs/base/common/labels';
import { EditorInput, toResource } from 'vs/workbench/common/editor';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
export class TitlebarPart extends Part implements ITitleService {
public _serviceBrand: any;
private static NLS_UNSUPPORTED = nls.localize('patchedWindowTitle', "[Unsupported]");
private static NLS_EXTENSION_HOST = nls.localize('devExtensionWindowTitlePrefix', "[Extension Development Host]");
private static TITLE_DIRTY = '\u25cf ';
private static TITLE_SEPARATOR = ' - ';
private titleContainer: Builder;
private title: Builder;
private pendingTitle: string;
private initialTitleFontSize: number;
private representedFileName: string;
private titleTemplate: string;
private isPure: boolean;
private activeEditorListeners: IDisposable[];
private workspacePath: string;
constructor(
id: string,
@IContextMenuService private contextMenuService: IContextMenuService,
@IWindowService private windowService: IWindowService,
@IWindowsService private windowsService: IWindowsService
@IConfigurationService private configurationService: IConfigurationService,
@IWindowsService private windowsService: IWindowsService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@IIntegrityService private integrityService: IIntegrityService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IWorkspaceContextService private contextService: IWorkspaceContextService
) {
super(id, { hasTitle: false });
this.isPure = true;
this.activeEditorListeners = [];
this.workspacePath = contextService.hasWorkspace() ? this.tildify(labels.getPathLabel(contextService.getWorkspace().resource)) : '';
this.init();
this.registerListeners();
}
private init(): void {
// Read initial config
this.onConfigurationChanged();
// Initial window title
this.setTitle(this.getWindowTitle());
// Integrity for window title
this.integrityService.isPure().then(r => {
if (!r.isPure) {
this.isPure = false;
this.setTitle(this.getWindowTitle());
}
});
}
private registerListeners(): void {
this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.BLUR, () => { if (this.titleContainer) { this.titleContainer.addClass('blurred'); } }));
this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.FOCUS, () => { if (this.titleContainer) { this.titleContainer.removeClass('blurred'); } }));
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged(true)));
this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
}
private onConfigurationChanged(update?: boolean): void {
const currentTitleTemplate = this.titleTemplate;
this.titleTemplate = this.configurationService.lookup<string>('window.title').value;
if (update && currentTitleTemplate !== this.titleTemplate) {
this.setTitle(this.getWindowTitle());
}
}
private onEditorsChanged(): void {
// Dispose old listeners
dispose(this.activeEditorListeners);
this.activeEditorListeners = [];
const activeEditor = this.editorService.getActiveEditor();
const activeInput = activeEditor ? activeEditor.input : void 0;
// Calculate New Window Title
this.setTitle(this.getWindowTitle());
// Apply listener for dirty and label changes
if (activeInput instanceof EditorInput) {
this.activeEditorListeners.push(activeInput.onDidChangeDirty(() => {
this.setTitle(this.getWindowTitle());
}));
this.activeEditorListeners.push(activeInput.onDidChangeLabel(() => {
this.setTitle(this.getWindowTitle());
}));
}
}
private getWindowTitle(): string {
let title = this.doGetWindowTitle();
if (!title) {
title = this.environmentService.appNameLong;
}
if (!this.isPure) {
title = `${title} ${TitlebarPart.NLS_UNSUPPORTED}`;
}
// Extension Development Host gets a special title to identify itself
if (this.environmentService.isExtensionDevelopment) {
title = `${TitlebarPart.NLS_EXTENSION_HOST} - ${title}`;
}
return title;
}
/**
* Possible template values:
*
* {activeEditorName}: e.g. myFile.txt
* {activeFilePath}: e.g. /Users/Development/myProject/myFile.txt
* {rootName}: e.g. myProject
* {rootPath}: e.g. /Users/Development/myProject
* {appName}: e.g. VS Code
* {dirty}: indiactor
* {separator}: conditional separator
*/
private doGetWindowTitle(): string {
const input = this.editorService.getActiveEditorInput();
const workspace = this.contextService.getWorkspace();
const file = toResource(input, { filter: 'file' });
// Variables
const activeEditorName = input ? input.getName() : '';
const activeFilePath = file ? this.tildify(labels.getPathLabel(file)) : '';
const rootName = workspace ? workspace.name : '';
const rootPath = workspace ? this.workspacePath : '';
const dirty = input && input.isDirty() ? TitlebarPart.TITLE_DIRTY : '';
const appName = this.environmentService.appNameLong;
const separator = TitlebarPart.TITLE_SEPARATOR;
return labels.template(this.titleTemplate, {
activeEditorName,
activeFilePath,
rootName,
rootPath,
dirty,
appName,
separator: { label: separator }
});
}
private tildify(path: string): string {
if (path && (isMacintosh || isLinux) && path.indexOf(this.environmentService.userHome) === 0) {
path = `~${path.substr(this.environmentService.userHome.length)}`;
}
return path;
}
public createContentArea(parent: Builder): Builder {
......@@ -127,7 +274,7 @@ export class TitlebarPart extends Part implements ITitleService {
return actions;
}
public updateTitle(title: string): void {
public setTitle(title: string): void {
// Always set the native window title to identify us properly to the OS
window.document.title = title;
......
......@@ -12,7 +12,7 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import platform = require('vs/base/common/platform');
import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindings } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
......@@ -68,7 +68,7 @@ workbenchActionsRegistry.registerWorkbenchAction(
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseMessagesAction, CloseMessagesAction.ID, CloseMessagesAction.LABEL, { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] }, MessagesVisibleContext), 'Close Notification Messages');
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorAction, CloseEditorAction.ID, CloseEditorAction.LABEL, closeEditorOrWindowKeybindings), 'View: Close Editor', viewCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory);
if (platform.isWindows || platform.isLinux) {
if (isWindows || isLinux) {
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMenuBarAction, ToggleMenuBarAction.ID, ToggleMenuBarAction.LABEL), 'View: Toggle Menu Bar', viewCategory);
}
......@@ -234,10 +234,18 @@ Note that there can still be cases where this setting is ignored (e.g. when usin
'default': 0,
'description': nls.localize('zoomLevel', "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.")
},
'window.showFullPath': {
'type': 'boolean',
'default': false,
'description': nls.localize('showFullPath', "If enabled, will show the full path of opened files in the window title.")
'window.title': {
'type': 'string',
'default': isMacintosh ? '$(activeEditorName)$(separator)$(rootName)' : '$(dirty)$(activeEditorName)$(separator)$(rootName)$(separator)$(appName)',
'description': nls.localize('title',
`Controls the window title based on the active editor. Variables are substituted based on the context:
$(activeEditorName): e.g. myFile.txt
$(activeFilePath): e.g. /Users/Development/myProject/myFile.txt
$(rootName): e.g. myProject
$(rootPath): e.g. /Users/Development/myProject
$(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`)
},
'window.newWindowDimensions': {
'type': 'string',
......@@ -253,7 +261,7 @@ Note that there can still be cases where this setting is ignored (e.g. when usin
},
};
if (platform.isWindows || platform.isLinux) {
if (isWindows || isLinux) {
properties['window.menuBarVisibility'] = {
'type': 'string',
'enum': ['default', 'visible', 'toggle', 'hidden'],
......@@ -268,7 +276,7 @@ if (platform.isWindows || platform.isLinux) {
};
}
if (platform.isWindows) {
if (isWindows) {
properties['window.autoDetectHighContrast'] = {
'type': 'boolean',
'default': true,
......@@ -276,7 +284,7 @@ if (platform.isWindows) {
};
}
if (platform.isMacintosh) {
if (isMacintosh) {
properties['window.titleBarStyle'] = {
'type': 'string',
'enum': ['native', 'custom'],
......
......@@ -449,12 +449,6 @@ export class Workbench implements IPartService {
// Menus/Actions
serviceCollection.set(IMenuService, new SyncDescriptor(MenuService));
// Title bar
this.titlebarPart = this.instantiationService.createInstance(TitlebarPart, Identifiers.TITLEBAR_PART);
this.toDispose.push(this.titlebarPart);
this.toShutdown.push(this.titlebarPart);
serviceCollection.set(ITitleService, this.titlebarPart);
// Sidebar part
this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART);
this.toDispose.push(this.sidebarPart);
......@@ -484,6 +478,12 @@ export class Workbench implements IPartService {
serviceCollection.set(IWorkbenchEditorService, this.editorService);
serviceCollection.set(IEditorGroupService, this.editorPart);
// Title bar
this.titlebarPart = this.instantiationService.createInstance(TitlebarPart, Identifiers.TITLEBAR_PART);
this.toDispose.push(this.titlebarPart);
this.toShutdown.push(this.titlebarPart);
serviceCollection.set(ITitleService, this.titlebarPart);
// File Service
const fileService = this.instantiationService.createInstance(FileService);
serviceCollection.set(IFileService, fileService);
......
......@@ -7,9 +7,6 @@
import { TPromise } from 'vs/base/common/winjs.base';
import errors = require('vs/base/common/errors');
import platform = require('vs/base/common/platform');
import nls = require('vs/nls');
import labels = require('vs/base/common/labels');
import objects = require('vs/base/common/objects');
import URI from 'vs/base/common/uri';
import * as editorCommon from 'vs/editor/common/editorCommon';
......@@ -27,9 +24,6 @@ import { Registry } from 'vs/platform/platform';
import { once } from 'vs/base/common/event';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IIntegrityService } from 'vs/platform/integrity/common/integrity';
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
import { getExcludes, ISearchConfiguration } from 'vs/platform/search/common/search';
......@@ -82,37 +76,18 @@ export abstract class BaseHistoryService {
protected toUnbind: IDisposable[];
private activeEditorListeners: IDisposable[];
private isPure: boolean;
private showFullPath: boolean;
protected excludes: ParsedExpression;
private lastKnownExcludesConfig: IExpression;
private static NLS_UNSUPPORTED = nls.localize('patchedWindowTitle', "[Unsupported]");
constructor(
protected editorGroupService: IEditorGroupService,
protected editorService: IWorkbenchEditorService,
protected contextService: IWorkspaceContextService,
private configurationService: IConfigurationService,
private environmentService: IEnvironmentService,
integrityService: IIntegrityService,
private titleService: ITitleService
private configurationService: IConfigurationService
) {
this.toUnbind = [];
this.activeEditorListeners = [];
this.isPure = true;
// Window Title
this.titleService.updateTitle(this.getWindowTitle(null));
// Integrity
integrityService.isPure().then(r => {
if (!r.isPure) {
this.isPure = false;
this.titleService.updateTitle(this.getWindowTitle(this.editorService.getActiveEditorInput()));
}
});
// Editor Input Changes
this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
......@@ -123,13 +98,6 @@ export abstract class BaseHistoryService {
}
private onConfigurationChanged(update?: boolean): void {
const currentShowPath = this.showFullPath;
this.showFullPath = this.configurationService.lookup<boolean>('window.showFullPath').value;
if (update && currentShowPath !== this.showFullPath) {
this.updateWindowTitle(this.editorService.getActiveEditorInput());
}
const excludesConfig = getExcludes(this.configurationService.getConfiguration<ISearchConfiguration>());
if (!objects.equals(excludesConfig, this.lastKnownExcludesConfig)) {
const configChanged = !!this.lastKnownExcludesConfig;
......@@ -150,21 +118,9 @@ export abstract class BaseHistoryService {
this.activeEditorListeners = [];
const activeEditor = this.editorService.getActiveEditor();
const activeInput = activeEditor ? activeEditor.input : void 0;
// Propagate to history
this.onEditorEvent(activeEditor);
// Apply listener for dirty and label changes
if (activeInput instanceof EditorInput) {
this.activeEditorListeners.push(activeInput.onDidChangeDirty(() => {
this.updateWindowTitle(activeInput); // Calculate New Window Title when dirty state changes
}));
this.activeEditorListeners.push(activeInput.onDidChangeLabel(() => {
this.updateWindowTitle(activeInput); // Calculate New Window Title when label changes
}));
}
this.handleActiveEditorChange(activeEditor);
// Apply listener for selection changes if this is a text editor
const control = getCodeEditor(activeEditor);
......@@ -175,97 +131,12 @@ export abstract class BaseHistoryService {
}
}
private onEditorEvent(editor: IBaseEditor): void {
const input = editor ? editor.input : null;
// Calculate New Window Title
this.updateWindowTitle(input);
// Delegate to implementors
this.handleActiveEditorChange(editor);
}
private updateWindowTitle(input?: IEditorInput): void {
let windowTitle: string = null;
if (input && input.getName()) {
windowTitle = this.getWindowTitle(input);
} else {
windowTitle = this.getWindowTitle(null);
}
this.titleService.updateTitle(windowTitle);
}
protected abstract handleExcludesChange(): void;
protected abstract handleEditorSelectionChangeEvent(editor?: IBaseEditor): void;
protected abstract handleActiveEditorChange(editor?: IBaseEditor): void;
protected getWindowTitle(input?: IEditorInput): string {
let title = this.doGetWindowTitle(input);
if (!this.isPure) {
title = `${title} ${BaseHistoryService.NLS_UNSUPPORTED}`;
}
// Extension Development Host gets a special title to identify itself
if (this.environmentService.isExtensionDevelopment) {
return nls.localize('devExtensionWindowTitle', "[Extension Development Host] - {0}", title);
}
return title;
}
private doGetWindowTitle(input?: IEditorInput): string {
const appName = this.environmentService.appNameLong;
let prefix: string;
const file = toResource(input, { filter: 'file' });
if (file && this.showFullPath) {
prefix = labels.getPathLabel(file);
if ((platform.isMacintosh || platform.isLinux) && prefix.indexOf(this.environmentService.userHome) === 0) {
prefix = `~${prefix.substr(this.environmentService.userHome.length)}`;
}
} else {
prefix = input && input.getName();
}
if (prefix && input) {
if (input.isDirty() && !platform.isMacintosh /* Mac has its own decoration in window */) {
prefix = nls.localize('prefixDecoration', "\u25cf {0}", prefix);
}
}
const workspace = this.contextService.getWorkspace();
if (workspace) {
const wsName = workspace.name;
if (prefix) {
if (platform.isMacintosh) {
return nls.localize('prefixWorkspaceTitleMac', "{0} - {1}", prefix, wsName); // Mac: do not append base title
}
return nls.localize('prefixWorkspaceTitle', "{0} - {1} - {2}", prefix, wsName, appName);
}
if (platform.isMacintosh) {
return wsName; // Mac: do not append base title
}
return nls.localize('workspaceTitle', "{0} - {1}", wsName, appName);
}
if (prefix) {
if (platform.isMacintosh) {
return prefix; // Mac: do not append base title
}
return nls.localize('prefixTitle', "{0} - {1}", prefix, appName);
}
return appName;
}
public dispose(): void {
this.toUnbind = dispose(this.toUnbind);
}
......@@ -305,17 +176,14 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
constructor(
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@IEnvironmentService environmentService: IEnvironmentService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IStorageService private storageService: IStorageService,
@IConfigurationService configurationService: IConfigurationService,
@ILifecycleService private lifecycleService: ILifecycleService,
@IIntegrityService integrityService: IIntegrityService,
@ITitleService titleService: ITitleService,
@IFileService private fileService: IFileService,
@IWindowService private windowService: IWindowService
) {
super(editorGroupService, editorService, contextService, configurationService, environmentService, integrityService, titleService);
super(editorGroupService, editorService, contextService, configurationService);
this.index = -1;
this.stack = [];
......
......@@ -12,9 +12,9 @@ export interface ITitleService {
_serviceBrand: any;
/**
* Update the window title with the given value.
* Set the window title with the given value.
*/
updateTitle(title: string): void;
setTitle(title: string): void;
/**
* Set the represented file name to the title if any.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册