Merge branch 'master' into joao/spdlog-0.17.0

This commit is contained in:
Joao Moreno 2018-07-11 20:17:58 +02:00
commit b75e5daa15
117 changed files with 1634 additions and 1126 deletions

View file

@ -3,7 +3,7 @@ phases:
condition: eq(variables['VSCODE_BUILD_WIN32'], 'true')
queue: Hosted VS2017
variables:
VSCODE_ARCH: ia32
VSCODE_ARCH: x64
steps:
- template: win32/product-build-win32.yml

View file

@ -131,9 +131,8 @@ steps:
ESRP: 'ESRP CodeSign'
- powershell: |
. build/tfs/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { .\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) }
.\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY)
displayName: Import ESRP Auth Certificate
- powershell: |

View file

@ -81,8 +81,6 @@ export async function activate(context: ExtensionContext): Promise<API> {
const telemetryReporter = new TelemetryReporter(name, version, aiKey);
deactivateTasks.push(() => telemetryReporter.dispose());
await new Promise(c => setTimeout(c, 10000));
const config = workspace.getConfiguration('git', null);
const enabled = config.get<boolean>('enabled');

View file

@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/88217b1c7d36ed5d35adc3099ba7978aabe2531b",
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/d7df3e324468b6535af67573d2956f9a852aa586",
"name": "JavaScript (with React support)",
"scopeName": "source.js",
"patterns": [
@ -139,6 +139,10 @@
"name": "keyword.control.with.js",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(with)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
},
{
"name": "keyword.control.js",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(package)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
},
{
"name": "keyword.other.debugger.js",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(debugger)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
@ -312,7 +316,7 @@
"name": "punctuation.separator.comma.js"
}
},
"end": "(?<!,)((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$))",
"end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))",
"patterns": [
{
"include": "#comment"

View file

@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/88217b1c7d36ed5d35adc3099ba7978aabe2531b",
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/d7df3e324468b6535af67573d2956f9a852aa586",
"name": "JavaScript (with React support)",
"scopeName": "source.js.jsx",
"patterns": [
@ -139,6 +139,10 @@
"name": "keyword.control.with.js.jsx",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(with)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
},
{
"name": "keyword.control.js.jsx",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(package)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
},
{
"name": "keyword.other.debugger.js.jsx",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(debugger)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
@ -312,7 +316,7 @@
"name": "punctuation.separator.comma.js.jsx"
}
},
"end": "(?<!,)((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$))",
"end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))",
"patterns": [
{
"include": "#comment"

View file

@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/40288b872220e5c0b844b1de507f1749ed14589b",
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/d7df3e324468b6535af67573d2956f9a852aa586",
"name": "TypeScript",
"scopeName": "source.ts",
"patterns": [
@ -139,6 +139,10 @@
"name": "keyword.control.with.ts",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(with)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
},
{
"name": "keyword.control.ts",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(package)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
},
{
"name": "keyword.other.debugger.ts",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(debugger)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
@ -309,7 +313,7 @@
"name": "punctuation.separator.comma.ts"
}
},
"end": "(?<!,)((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$))",
"end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))",
"patterns": [
{
"include": "#comment"

View file

@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/88217b1c7d36ed5d35adc3099ba7978aabe2531b",
"version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/d7df3e324468b6535af67573d2956f9a852aa586",
"name": "TypeScriptReact",
"scopeName": "source.tsx",
"patterns": [
@ -139,6 +139,10 @@
"name": "keyword.control.with.tsx",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(with)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
},
{
"name": "keyword.control.tsx",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(package)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
},
{
"name": "keyword.other.debugger.tsx",
"match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(debugger)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))"
@ -312,7 +316,7 @@
"name": "punctuation.separator.comma.tsx"
}
},
"end": "(?<!,)((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$))",
"end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))",
"patterns": [
{
"include": "#comment"

View file

@ -11,6 +11,7 @@ import API from '../utils/api';
import { Delayer } from '../utils/async';
import { disposeAll } from '../utils/dispose';
import * as languageModeIds from '../utils/languageModeIds';
import * as typeConverters from '../utils/typeConverters';
import { ResourceMap } from './resourceMap';
enum BufferKind {
@ -18,10 +19,6 @@ enum BufferKind {
JavaScript = 2,
}
interface IDiagnosticRequestor {
requestDiagnostic(resource: Uri): void;
}
function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined {
switch (mode) {
case languageModeIds.typescript: return 'TS';
@ -37,7 +34,6 @@ class SyncedBuffer {
constructor(
private readonly document: TextDocument,
public readonly filepath: string,
private readonly diagnosticRequestor: IDiagnosticRequestor,
private readonly client: ITypeScriptServiceClient
) { }
@ -101,16 +97,11 @@ class SyncedBuffer {
public onContentChanged(events: TextDocumentContentChangeEvent[]): void {
for (const { range, text } of events) {
const args: Proto.ChangeRequestArgs = {
file: this.filepath,
line: range.start.line + 1,
offset: range.start.character + 1,
endLine: range.end.line + 1,
endOffset: range.end.character + 1,
insertString: text
insertString: text,
...typeConverters.Range.toFormattingRequestArgs(this.filepath, range)
};
this.client.execute('change', args, false);
}
this.diagnosticRequestor.requestDiagnostic(this.document.uri);
}
}
@ -123,9 +114,13 @@ class SyncedBufferMap extends ResourceMap<SyncedBuffer> {
public get allBuffers(): Iterable<SyncedBuffer> {
return this.values;
}
}
public get allResources(): Iterable<string> {
return this.keys;
class PendingDiagnostics extends ResourceMap<number> {
public getFileList(): Set<string> {
return new Set(Array.from(this.entries)
.sort((a, b) => a[1] - b[1])
.map(entry => entry[0]));
}
}
@ -138,8 +133,7 @@ export default class BufferSyncSupport {
private readonly modeIds: Set<string>;
private readonly disposables: Disposable[] = [];
private readonly syncedBuffers: SyncedBufferMap;
private readonly pendingDiagnostics = new Map<string, number>();
private readonly pendingDiagnostics: PendingDiagnostics;
private readonly diagnosticDelayer: Delayer<any>;
private pendingGetErr: { request: Promise<any>, files: string[], token: CancellationTokenSource } | undefined;
private listening: boolean = false;
@ -153,10 +147,12 @@ export default class BufferSyncSupport {
this.diagnosticDelayer = new Delayer<any>(300);
this.syncedBuffers = new SyncedBufferMap(path => this.normalizePath(path));
const pathNormalizer = (path: Uri) => this.client.normalizedPath(path);
this.syncedBuffers = new SyncedBufferMap(pathNormalizer);
this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer);
this.updateConfiguration();
workspace.onDidChangeConfiguration(() => this.updateConfiguration(), null);
workspace.onDidChangeConfiguration(this.updateConfiguration, this, this.disposables);
}
private readonly _onDelete = new EventEmitter<Uri>();
@ -210,10 +206,10 @@ export default class BufferSyncSupport {
return;
}
const syncedBuffer = new SyncedBuffer(document, filepath, this, this.client);
const syncedBuffer = new SyncedBuffer(document, filepath, this.client);
this.syncedBuffers.set(resource, syncedBuffer);
syncedBuffer.open();
this.requestDiagnostic(resource);
this.requestDiagnostic(syncedBuffer);
}
public closeResource(resource: Uri): void {
@ -240,25 +236,24 @@ export default class BufferSyncSupport {
}
syncedBuffer.onContentChanged(e.contentChanges);
this.requestDiagnostic(syncedBuffer);
if (this.pendingGetErr) {
this.pendingGetErr.token.cancel();
this.pendingGetErr = undefined;
this.diagnosticDelayer.trigger(() => {
this.sendPendingDiagnostics();
}, 200);
// In this case we always want to re-trigger all diagnostics
this.triggerDiagnostics();
}
}
public requestAllDiagnostics() {
for (const buffer of this.syncedBuffers.allBuffers) {
if (this.shouldValidate(buffer)) {
this.pendingDiagnostics.set(buffer.filepath, Date.now());
this.pendingDiagnostics.set(buffer.resource, Date.now());
}
}
this.diagnosticDelayer.trigger(() => {
this.sendPendingDiagnostics();
}, 200);
this.triggerDiagnostics();
}
public getErr(resources: Uri[]): any {
@ -268,65 +263,55 @@ export default class BufferSyncSupport {
}
for (const resource of handledResources) {
const file = this.client.normalizedPath(resource);
if (file) {
this.pendingDiagnostics.set(file, Date.now());
}
this.pendingDiagnostics.set(resource, Date.now());
}
this.diagnosticDelayer.trigger(() => {
this.sendPendingDiagnostics();
}, 200);
this.triggerDiagnostics();
}
public requestDiagnostic(resource: Uri): void {
const file = this.client.normalizedPath(resource);
if (!file) {
return;
}
this.pendingDiagnostics.set(file, Date.now());
const buffer = this.syncedBuffers.get(resource);
if (!buffer || !this.shouldValidate(buffer)) {
return;
}
let delay = 300;
const lineCount = buffer.lineCount;
delay = Math.min(Math.max(Math.ceil(lineCount / 20), 300), 800);
private triggerDiagnostics(delay: number = 200) {
this.diagnosticDelayer.trigger(() => {
this.sendPendingDiagnostics();
}, delay);
}
private requestDiagnostic(buffer: SyncedBuffer): void {
if (!this.shouldValidate(buffer)) {
return;
}
this.pendingDiagnostics.set(buffer.resource, Date.now());
const lineCount = buffer.lineCount;
const delay = Math.min(Math.max(Math.ceil(lineCount / 20), 300), 800);
this.triggerDiagnostics(delay);
}
public hasPendingDiagnostics(resource: Uri): boolean {
const file = this.client.normalizedPath(resource);
return !file || this.pendingDiagnostics.has(file);
return this.pendingDiagnostics.has(resource);
}
private sendPendingDiagnostics(): void {
const files = new Set(Array.from(this.pendingDiagnostics.entries())
.sort((a, b) => a[1] - b[1])
.map(entry => entry[0]));
const fileList = this.pendingDiagnostics.getFileList();
// Add all open TS buffers to the geterr request. They might be visible
for (const file of this.syncedBuffers.allResources) {
if (!this.pendingDiagnostics.get(file)) {
files.add(file);
for (const buffer of this.syncedBuffers.values) {
if (!this.pendingDiagnostics.has(buffer.resource)) {
fileList.add(buffer.filepath);
}
}
if (this.pendingGetErr) {
for (const file of this.pendingGetErr.files) {
files.add(file);
fileList.add(file);
}
}
if (files.size) {
const fileList = Array.from(files);
if (fileList.size) {
const files = Array.from(fileList);
const args: Proto.GeterrRequestArgs = {
delay: 0,
files: fileList
files
};
const token = new CancellationTokenSource();
@ -338,7 +323,7 @@ export default class BufferSyncSupport {
this.pendingGetErr = undefined;
}
}),
files: fileList,
files,
token
};
}
@ -363,8 +348,4 @@ export default class BufferSyncSupport {
return this._validateTypeScript;
}
}
private normalizePath(path: Uri): string | null {
return this.client.normalizedPath(path);
}
}

View file

@ -51,17 +51,11 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit
options: vscode.FormattingOptions,
token: vscode.CancellationToken
): Promise<vscode.TextEdit[]> {
const absPath = this.client.toPath(document.uri);
if (!absPath) {
const file = this.client.toPath(document.uri);
if (!file) {
return [];
}
const args: Proto.FormatRequestArgs = {
file: absPath,
line: range.start.line + 1,
offset: range.start.character + 1,
endLine: range.end.line + 1,
endOffset: range.end.character + 1
};
const args = typeConverters.Range.toFormattingRequestArgs(file, range);
return this.doFormat(document, options, args, token);
}

View file

@ -45,12 +45,16 @@ export class ResourceMap<T> {
}
}
public clear() {
this._map.clear();
}
public get values(): Iterable<T> {
return this._map.values();
}
public get keys(): Iterable<string> {
return this._map.keys();
public get entries() {
return this._map.entries();
}
private toKey(resource: Uri): string | null {

View file

@ -23,13 +23,8 @@ class TagClosing {
) {
vscode.workspace.onDidChangeTextDocument(
event => this.onDidChangeTextDocument(event.document, event.contentChanges),
null, this.disposables);
vscode.window.onDidChangeActiveTextEditor(
() => this.updateEnabledState(),
null, this.disposables);
this.updateEnabledState();
null,
this.disposables);
}
public dispose() {
@ -38,10 +33,6 @@ class TagClosing {
this.timeout = undefined;
}
private updateEnabledState() {
}
private onDidChangeTextDocument(
document: vscode.TextDocument,
changes: vscode.TextDocumentContentChangeEvent[]
@ -66,6 +57,11 @@ class TagClosing {
return;
}
const secondToLastCharacter = lastChange.text[lastChange.text.length - 2];
if (secondToLastCharacter === '>') {
return;
}
const rangeStart = lastChange.range.start;
const version = document.version;
this.timeout = setTimeout(async () => {
@ -124,12 +120,8 @@ export class ActiveDocumentDependentRegistration {
register: () => vscode.Disposable,
) {
this._registration = new ConditionalRegistration(register);
vscode.window.onDidChangeActiveTextEditor(this.update, this, this._disposables);
this.update();
vscode.window.onDidChangeActiveTextEditor(() => {
this.update();
}, null, this._disposables);
}
public dispose() {

View file

@ -10,11 +10,11 @@ import * as nls from 'vscode-nls';
import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import API from '../utils/api';
import * as languageIds from '../utils/languageModeIds';
import * as fileSchemes from '../utils/fileSchemes';
import { isTypeScriptDocument } from '../utils/languageModeIds';
import { escapeRegExp } from '../utils/regexp';
import * as typeConverters from '../utils/typeConverters';
import FileConfigurationManager from './fileConfigurationManager';
import * as fileSchemes from '../utils/fileSchemes';
import { escapeRegExp } from '../utils/regexp';
const localize = nls.loadMessageBundle();
@ -83,14 +83,20 @@ export class UpdateImportsOnFileRenameHandler {
this.client.bufferSyncSupport.closeResource(targetResource);
this.client.bufferSyncSupport.openTextDocument(document);
// Workaround for https://github.com/Microsoft/vscode/issues/52967
// Never attempt to update import paths if the file does not contain something the looks like an export
const tree = await this.client.execute('navtree', { file: newFile });
const hasExport = (node: Proto.NavigationTree): boolean => {
return !!node.kindModifiers.match(/\bexport\b/g) || !!(node.childItems && node.childItems.some(hasExport));
};
if (!tree.body || !tree.body || !hasExport(tree.body)) {
return;
if (!this.client.apiVersion.gte(API.v300) && !fs.lstatSync(newResource.fsPath).isDirectory()) {
// Workaround for https://github.com/Microsoft/vscode/issues/52967
// Never attempt to update import paths if the file does not contain something the looks like an export
try {
const tree = await this.client.execute('navtree', { file: newFile });
const hasExport = (node: Proto.NavigationTree): boolean => {
return !!node.kindModifiers.match(/\bexports?\b/g) || !!(node.childItems && node.childItems.some(hasExport));
};
if (!tree.body || !tree.body || !hasExport(tree.body)) {
return;
}
} catch {
// noop
}
}
const edits = await this.getEditsForFileRename(targetFile, document, oldFile, newFile);
@ -203,7 +209,12 @@ export class UpdateImportsOnFileRenameHandler {
return undefined;
}
if (this.client.apiVersion.gte(API.v292) && fs.lstatSync(resource.fsPath).isDirectory()) {
const isDirectory = fs.lstatSync(resource.fsPath).isDirectory();
if (isDirectory && this.client.apiVersion.gte(API.v300)) {
return resource;
}
if (isDirectory && this.client.apiVersion.gte(API.v292)) {
const files = await vscode.workspace.findFiles({
base: resource.fsPath,
pattern: '**/*.{ts,tsx,js,jsx}',
@ -293,6 +304,3 @@ export class UpdateImportsOnFileRenameHandler {
}
}
function isTypeScriptDocument(document: vscode.TextDocument) {
return document.languageId === languageIds.typescript || document.languageId === languageIds.typescriptreact;
}

View file

@ -75,12 +75,8 @@ export class ConfigurationDependentRegistration {
register: () => vscode.Disposable,
) {
this._registration = new ConditionalRegistration(register);
this.update();
vscode.workspace.onDidChangeConfiguration(() => {
this.update();
}, null, this._disposables);
vscode.workspace.onDidChangeConfiguration(this.update, this, this._disposables);
}
public dispose() {

View file

@ -24,6 +24,14 @@ export namespace Range {
endLine: range.end.line + 1,
endOffset: range.end.character + 1
});
export const toFormattingRequestArgs = (file: string, range: vscode.Range): Proto.FormatRequestArgs => ({
file,
line: range.start.line + 1,
offset: range.start.character + 1,
endLine: range.end.line + 1,
endOffset: range.end.character + 1
});
}
export namespace Position {

View file

@ -499,17 +499,16 @@ suite('workspace-namespace', () => {
});
});
// TODO@Joh this test fails randomly
// test('findFiles, cancellation', () => {
test('findFiles, cancellation', () => {
// const source = new CancellationTokenSource();
// const token = source.token; // just to get an instance first
// source.cancel();
const source = new vscode.CancellationTokenSource();
const token = source.token; // just to get an instance first
source.cancel();
// return vscode.workspace.findFiles('*.js', null, 100, token).then((res) => {
// assert.equal(res, void 0);
// });
// });
return vscode.workspace.findFiles('*.js', null, 100, token).then((res) => {
assert.deepEqual(res, []);
});
});
test('findTextInFiles', async () => {
const results: vscode.TextSearchResult[] = [];

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.26.0",
"distro": "bf3aa85b8880d2d1c80030e21a3a2ddd384fa1e7",
"distro": "655112e16611a8427ba96dbd6de2b05ecf2e3f6b",
"author": {
"name": "Microsoft Corporation"
},

View file

@ -840,6 +840,8 @@ export const EventType = {
SUBMIT: 'submit',
RESET: 'reset',
FOCUS: 'focus',
FOCUS_IN: 'focusin',
FOCUS_OUT: 'focusout',
BLUR: 'blur',
INPUT: 'input',
// Local Storage

View file

@ -9,6 +9,7 @@
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
outline-style: none;
}
.monaco-breadcrumbs .monaco-breadcrumb-item {
@ -16,13 +17,14 @@
align-items: center;
flex: 0 1 auto;
white-space: nowrap;
cursor: default;
cursor: pointer;
align-self: center;
height: 100%;
}
.monaco-breadcrumbs .monaco-breadcrumb-item:not(:last-child)::after {
background-image: url(./collapsed.svg);
opacity: .7;
width: 16px;
height: 16px;
display: inline-block;
@ -35,6 +37,3 @@
background-image: url(./collpased-dark.svg);
}
.monaco-breadcrumbs .monaco-breadcrumb-item.focused {
font-weight: bold;
}

View file

@ -44,8 +44,12 @@ export class SimpleBreadcrumbsItem extends BreadcrumbsItem {
export interface IBreadcrumbsWidgetStyles {
breadcrumbsBackground?: Color;
breadcrumbsActiveForeground?: Color;
breadcrumbsInactiveForeground?: Color;
breadcrumbsForeground?: Color;
breadcrumbsHoverBackground?: Color;
breadcrumbsHoverForeground?: Color;
breadcrumbsFocusForeground?: Color;
breadcrumbsFocusAndSelectionBackground?: Color;
breadcrumbsFocusAndSelectionForeground?: Color;
}
export interface IBreadcrumbsItemEvent {
@ -123,11 +127,23 @@ export class BreadcrumbsWidget {
if (style.breadcrumbsBackground) {
content += `.monaco-breadcrumbs { background-color: ${style.breadcrumbsBackground}}`;
}
if (style.breadcrumbsActiveForeground) {
content += `.monaco-breadcrumbs:focus .monaco-breadcrumb-item { color: ${style.breadcrumbsActiveForeground}}\n`;
if (style.breadcrumbsForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item { color: ${style.breadcrumbsForeground}}\n`;
}
if (style.breadcrumbsInactiveForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item { color: ${style.breadcrumbsInactiveForeground}}\n`;
if (style.breadcrumbsFocusForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused { color: ${style.breadcrumbsFocusForeground}}\n`;
}
if (style.breadcrumbsFocusAndSelectionBackground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused.selected { background-color: ${style.breadcrumbsFocusAndSelectionBackground}}\n`;
}
if (style.breadcrumbsFocusAndSelectionForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused.selected { color: ${style.breadcrumbsFocusAndSelectionForeground}}\n`;
}
if (style.breadcrumbsHoverBackground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover:not(.focused):not(.selected) { background-color: ${style.breadcrumbsHoverBackground}}\n`;
}
if (style.breadcrumbsHoverForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover:not(.focused):not(.selected) { color: ${style.breadcrumbsHoverForeground}}\n`;
}
if (this._styleElement.innerHTML !== content) {
this._styleElement.innerHTML = content;
@ -153,13 +169,15 @@ export class BreadcrumbsWidget {
}
focusPrev(): any {
this._focus((this._focusedItemIdx - 1 + this._nodes.length) % this._nodes.length);
// this._domNode.focus();
if (this._focusedItemIdx > 0) {
this._focus(this._focusedItemIdx - 1);
}
}
focusNext(): any {
this._focus((this._focusedItemIdx + 1) % this._nodes.length);
// this._domNode.focus();
if (this._focusedItemIdx + 1 < this._nodes.length) {
this._focus(this._focusedItemIdx + 1);
}
}
private _focus(nth: number): void {
@ -173,14 +191,29 @@ export class BreadcrumbsWidget {
dom.addClass(node, 'focused');
}
}
this._reveal(this._focusedItemIdx);
this._onDidFocusItem.fire({ type: 'focus', item: this._items[this._focusedItemIdx], node: this._nodes[this._focusedItemIdx] });
}
getSelected(): BreadcrumbsItem {
reveal(item: BreadcrumbsItem): void {
let idx = this._items.indexOf(item);
if (idx >= 0) {
this._reveal(idx);
}
}
private _reveal(nth: number): void {
const node = this._nodes[nth];
if (node) {
this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft });
}
}
getSelection(): BreadcrumbsItem {
return this._items[this._selectedItemIdx];
}
setSelected(item: BreadcrumbsItem): void {
setSelection(item: BreadcrumbsItem): void {
this._select(this._items.indexOf(item));
}
@ -203,9 +236,7 @@ export class BreadcrumbsWidget {
let removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix));
this._render(prefix);
dispose(removed);
if (prefix >= this._focusedItemIdx) {
this._focus(-1);
}
this._focus(-1);
}
private _render(start: number): void {

View file

@ -5,5 +5,5 @@
.context-view {
position: absolute;
z-index: 1000;
z-index: 2000;
}

View file

@ -5,7 +5,7 @@
import { GestureEvent } from 'vs/base/browser/touch';
export interface IDelegate<T> {
export interface IVirtualDelegate<T> {
getHeight(element: T): number;
getTemplateId(element: T): string;
}
@ -14,6 +14,7 @@ export interface IRenderer<TElement, TTemplateData> {
templateId: string;
renderTemplate(container: HTMLElement): TTemplateData;
renderElement(element: TElement, index: number, templateData: TTemplateData): void;
disposeElement(element: TElement, index: number, templateData: TTemplateData): void;
disposeTemplate(templateData: TTemplateData): void;
}

View file

@ -6,7 +6,7 @@
import 'vs/css!./list';
import { IDisposable } from 'vs/base/common/lifecycle';
import { range } from 'vs/base/common/arrays';
import { IDelegate, IRenderer, IListEvent, IListOpenEvent } from './list';
import { IVirtualDelegate, IRenderer, IListEvent, IListOpenEvent } from './list';
import { List, IListStyles, IListOptions } from './listWidget';
import { IPagedModel } from 'vs/base/common/paging';
import { Event, mapEvent } from 'vs/base/common/event';
@ -50,6 +50,10 @@ class PagedRenderer<TElement, TTemplateData> implements IRenderer<number, ITempl
promise.done(entry => this.renderer.renderElement(entry, index, data.data));
}
disposeElement(): void {
// noop
}
disposeTemplate(data: ITemplateData<TTemplateData>): void {
data.disposable.dispose();
data.disposable = null;
@ -65,12 +69,12 @@ export class PagedList<T> implements IDisposable {
constructor(
container: HTMLElement,
delegate: IDelegate<number>,
virtualDelegate: IVirtualDelegate<number>,
renderers: IPagedRenderer<T, any>[],
options: IListOptions<any> = {}
) {
const pagedRenderers = renderers.map(r => new PagedRenderer<T, ITemplateData<T>>(r, () => this.model));
this.list = new List(container, delegate, pagedRenderers, options);
this.list = new List(container, virtualDelegate, pagedRenderers, options);
}
getHTMLElement(): HTMLElement {

View file

@ -12,7 +12,7 @@ import { domEvent } from 'vs/base/browser/event';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollEvent, ScrollbarVisibility } from 'vs/base/common/scrollable';
import { RangeMap, IRange, relativeComplement, intersect, shift } from './rangeMap';
import { IDelegate, IRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list';
import { IVirtualDelegate, IRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list';
import { RowCache, IRow } from './rowCache';
import { isWindows } from 'vs/base/common/platform';
import * as browser from 'vs/base/browser/browser';
@ -72,7 +72,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
constructor(
container: HTMLElement,
private delegate: IDelegate<T>,
private virtualDelegate: IVirtualDelegate<T>,
renderers: IRenderer<T, any>[],
options: IListViewOptions = DefaultOptions
) {
@ -156,8 +156,8 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const inserted = elements.map<IItem<T>>(element => ({
id: String(this.itemId++),
element,
size: this.delegate.getHeight(element),
templateId: this.delegate.getTemplateId(element),
size: this.virtualDelegate.getHeight(element),
templateId: this.virtualDelegate.getTemplateId(element),
row: null
}));
@ -322,6 +322,12 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
throw new Error(`Got index ${index} and there are ${this.items.length} items. File issue to joao!`);
}
const renderer = this.renderers.get(item.templateId);
if (renderer.disposeElement) {
renderer.disposeElement(item.element, index, item.row.templateData);
}
this.cache.release(item.row);
item.row = null;
}

View file

@ -15,7 +15,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Event, Emitter, EventBufferer, chain, mapEvent, anyEvent } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IDelegate, IRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListOpenEvent } from './list';
import { IVirtualDelegate, IRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListOpenEvent } from './list';
import { ListView, IListViewOptions } from './listView';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
@ -68,6 +68,10 @@ class TraitRenderer<T> implements IRenderer<T, ITraitTemplateData>
this.trait.renderIndex(index, templateData);
}
disposeElement(): void {
// noop
}
splice(start: number, deleteCount: number, insertCount: number): void {
const rendered: IRenderedContainer[] = [];
@ -807,6 +811,14 @@ class PipelineRenderer<T> implements IRenderer<T, any> {
}
}
disposeElement(element: T, index: number, templateData: any[]): void {
let i = 0;
for (const renderer of this.renderers) {
renderer.disposeElement(element, index, templateData[i++]);
}
}
disposeTemplate(templateData: any[]): void {
let i = 0;
@ -871,7 +883,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
constructor(
container: HTMLElement,
delegate: IDelegate<T>,
virtualDelegate: IVirtualDelegate<T>,
renderers: IRenderer<T, any>[],
options: IListOptions<T> = DefaultOptions
) {
@ -882,7 +894,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
renderers = renderers.map(r => new PipelineRenderer(r.templateId, [this.focus.renderer, this.selection.renderer, r]));
this.view = new ListView(container, delegate, renderers, options);
this.view = new ListView(container, virtualDelegate, renderers, options);
this.view.domNode.setAttribute('role', 'tree');
DOM.addClass(this.view.domNode, this.idPrefix);
this.view.domNode.tabIndex = 0;

View file

@ -5,9 +5,7 @@
'use strict';
export interface ISpliceable<T> {
splice(start: number, deleteCount: number, elements: T[]): void;
}
import { ISpliceable } from 'vs/base/common/sequence';
export interface ISpreadSpliceable<T> {
splice(start: number, deleteCount: number, ...elements: T[]): void;

View file

@ -15,6 +15,7 @@ import { Event } from 'vs/base/common/event';
import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { $, Builder } from 'vs/base/browser/builder';
import { RunOnceScheduler } from 'vs/base/common/async';
export interface IMenuOptions {
context?: any;
@ -241,6 +242,8 @@ class SubmenuActionItem extends MenuActionItem {
private mysubmenu: Menu;
private submenuContainer: Builder;
private mouseOver: boolean;
private showScheduler: RunOnceScheduler;
private hideScheduler: RunOnceScheduler;
constructor(
action: IAction,
@ -249,6 +252,20 @@ class SubmenuActionItem extends MenuActionItem {
private submenuOptions?: IMenuOptions
) {
super(action, action, { label: true, isMenu: true });
this.showScheduler = new RunOnceScheduler(() => {
if (this.mouseOver) {
this.cleanupExistingSubmenu(false);
this.createSubmenu();
}
}, 250);
this.hideScheduler = new RunOnceScheduler(() => {
if (!this.mouseOver && this.parentData.submenu === this.mysubmenu) {
this.parentData.parent.focus();
this.cleanupExistingSubmenu(true);
}
}, 750);
}
public render(container: HTMLElement): void {
@ -278,26 +295,14 @@ class SubmenuActionItem extends MenuActionItem {
if (!this.mouseOver) {
this.mouseOver = true;
setTimeout(() => {
if (this.mouseOver) {
this.cleanupExistingSubmenu(false);
this.createSubmenu();
}
}, 250);
this.showScheduler.schedule();
}
});
$(this.builder).on(EventType.MOUSE_LEAVE, (e) => {
this.mouseOver = false;
setTimeout(() => {
if (!this.mouseOver && this.parentData.submenu === this.mysubmenu) {
this.parentData.parent.focus();
this.cleanupExistingSubmenu(true);
}
}, 750);
this.hideScheduler.schedule();
});
}
@ -360,6 +365,9 @@ class SubmenuActionItem extends MenuActionItem {
public dispose() {
super.dispose();
this.hideScheduler.dispose();
this.showScheduler.dispose();
if (this.mysubmenu) {
this.mysubmenu.dispose();
this.mysubmenu = null;

View file

@ -14,7 +14,7 @@ import * as dom from 'vs/base/browser/dom';
import * as arrays from 'vs/base/common/arrays';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { domEvent } from 'vs/base/browser/event';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ISelectBoxDelegate, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
@ -67,12 +67,16 @@ class SelectListRenderer implements IRenderer<ISelectOptionItem, ISelectListTemp
}
}
disposeElement(): void {
// noop
}
disposeTemplate(templateData: ISelectListTemplateData): void {
templateData.disposables = dispose(templateData.disposables);
}
}
export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptionItem> {
export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISelectOptionItem> {
private static readonly DEFAULT_DROPDOWN_MINIMUM_BOTTOM_MARGIN = 32;
@ -238,6 +242,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptio
if (selected !== undefined) {
this.select(selected);
// Set current = selected since this is not necessarily a user exit
this._currentSelection = this.selected;
}
}
@ -523,9 +529,11 @@ export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptio
// List methods
// List mouse controller - active exit, select option, fire onDidSelect, return focus to parent select
// List mouse controller - active exit, select option, fire onDidSelect if change, return focus to parent select
private onMouseUp(e: MouseEvent): void {
dom.EventHelper.stop(e);
// Check our mouse event is on an option (not scrollbar)
if (!e.toElement.classList.contains('option-text')) {
return;
@ -542,59 +550,58 @@ export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptio
this.selectList.setFocus([this.selected]);
this.selectList.reveal(this.selectList.getFocus()[0]);
this._onDidSelect.fire({
index: this.selectElement.selectedIndex,
selected: this.selectElement.title
});
// Reset Selection Handler
this._currentSelection = -1;
// Only fire if selection change
if (this.selected !== this._currentSelection) {
// Set current = selected
this._currentSelection = this.selected;
this._onDidSelect.fire({
index: this.selectElement.selectedIndex,
selected: this.selectElement.title
});
}
this.hideSelectDropDown(true);
}
dom.EventHelper.stop(e);
}
// List Exit - passive - hide drop-down, fire onDidSelect
// List Exit - passive - implicit no selection change, hide drop-down
private onListBlur(): void {
if (this._currentSelection >= 0) {
if (this.selected !== this._currentSelection) {
// Reset selected to current if no change
this.select(this._currentSelection);
}
this._onDidSelect.fire({
index: this.selectElement.selectedIndex,
selected: this.selectElement.title
});
this.hideSelectDropDown(false);
}
// List keyboard controller
// List exit - active - hide ContextView dropdown, return focus to parent select, fire onDidSelect
// List exit - active - hide ContextView dropdown, reset selection, return focus to parent select
private onEscape(e: StandardKeyboardEvent): void {
dom.EventHelper.stop(e);
// Reset selection to value when opened
this.select(this._currentSelection);
this.hideSelectDropDown(true);
this._onDidSelect.fire({
index: this.selectElement.selectedIndex,
selected: this.selectElement.title
});
}
// List exit - active - hide ContextView dropdown, return focus to parent select, fire onDidSelect
// List exit - active - hide ContextView dropdown, return focus to parent select, fire onDidSelect if change
private onEnter(e: StandardKeyboardEvent): void {
dom.EventHelper.stop(e);
// Reset current selection
this._currentSelection = -1;
// Only fire if selection change
if (this.selected !== this._currentSelection) {
this._currentSelection = this.selected;
this._onDidSelect.fire({
index: this.selectElement.selectedIndex,
selected: this.selectElement.title
});
}
this.hideSelectDropDown(true);
this._onDidSelect.fire({
index: this.selectElement.selectedIndex,
selected: this.selectElement.title
});
}
// List navigation - have to handle a disabled option (jump over)

View file

@ -4,15 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./tree';
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IListOptions, List, IIdentityProvider, IMultipleSelectionController } from 'vs/base/browser/ui/list/listWidget';
import { TreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/treeModel';
import { IIterator, empty } from 'vs/base/common/iterator';
import { IDelegate, IRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list';
import { TreeModel, ITreeNode, ITreeElement, getNodeLocation } from 'vs/base/browser/ui/tree/treeModel';
import { Iterator, ISequence } from 'vs/base/common/iterator';
import { IVirtualDelegate, IRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list';
import { append, $ } from 'vs/base/browser/dom';
import { Event, Relay, chain } from 'vs/base/common/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { tail2 } from 'vs/base/common/arrays';
function toTreeListOptions<T>(options?: IListOptions<T>): IListOptions<ITreeNode<T>> {
if (!options) {
@ -44,9 +45,9 @@ function toTreeListOptions<T>(options?: IListOptions<T>): IListOptions<ITreeNode
};
}
class TreeDelegate<T> implements IDelegate<ITreeNode<T>> {
class TreeDelegate<T> implements IVirtualDelegate<ITreeNode<T>> {
constructor(private delegate: IDelegate<T>) { }
constructor(private delegate: IVirtualDelegate<T>) { }
getHeight(element: ITreeNode<T>): number {
return this.delegate.getHeight(element.element);
@ -59,10 +60,17 @@ class TreeDelegate<T> implements IDelegate<ITreeNode<T>> {
interface ITreeListTemplateData<T> {
twistie: HTMLElement;
elementDisposable: IDisposable;
templateData: T;
}
function renderTwistie<T>(node: ITreeNode<T>, twistie: HTMLElement): void {
if (node.children.length === 0 && !node.collapsible) {
twistie.innerText = '';
} else {
twistie.innerText = node.collapsed ? '▹' : '◢';
}
}
class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeListTemplateData<TTemplateData>> {
readonly templateId: string;
@ -83,21 +91,22 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis
const contents = append(el, $('.tl-contents'));
const templateData = this.renderer.renderTemplate(contents);
return { twistie, elementDisposable: Disposable.None, templateData };
return { twistie, templateData };
}
renderElement(node: ITreeNode<T>, index: number, templateData: ITreeListTemplateData<TTemplateData>): void {
templateData.elementDisposable.dispose();
this.renderedNodes.set(node, templateData);
templateData.elementDisposable = toDisposable(() => this.renderedNodes.delete(node));
templateData.twistie.innerText = node.children.length === 0 ? '' : (node.collapsed ? '▹' : '◢');
templateData.twistie.style.width = `${10 + node.depth * 10}px`;
renderTwistie(node, templateData.twistie);
this.renderer.renderElement(node.element, index, templateData.templateData);
}
disposeElement(node: ITreeNode<T>): void {
this.renderedNodes.delete(node);
}
disposeTemplate(templateData: ITreeListTemplateData<TTemplateData>): void {
this.renderer.disposeTemplate(templateData.templateData);
}
@ -109,7 +118,7 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis
return;
}
templateData.twistie.innerText = node.children.length === 0 ? '' : (node.collapsed ? '▹' : '◢');
renderTwistie(node, templateData.twistie);
}
dispose(): void {
@ -118,21 +127,12 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis
}
}
function getLocation<T>(node: ITreeNode<T>): number[] {
const location = [];
while (node.parent) {
location.push(node.parent.children.indexOf(node));
node = node.parent;
}
return location.reverse();
}
function isInputElement(e: HTMLElement): boolean {
return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA';
}
export interface ITreeOptions<T> extends IListOptions<T> { }
export class Tree<T> implements IDisposable {
private view: List<ITreeNode<T>>;
@ -141,9 +141,9 @@ export class Tree<T> implements IDisposable {
constructor(
container: HTMLElement,
delegate: IDelegate<T>,
delegate: IVirtualDelegate<T>,
renderers: IRenderer<T, any>[],
options?: IListOptions<T>
options?: ITreeOptions<T>
) {
const treeDelegate = new TreeDelegate(delegate);
@ -168,13 +168,13 @@ export class Tree<T> implements IDisposable {
onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables);
}
splice(location: number[], deleteCount: number, toInsert: IIterator<ITreeElement<T>> = empty()): IIterator<ITreeElement<T>> {
splice(location: number[], deleteCount: number, toInsert: ISequence<ITreeElement<T>> = Iterator.empty()): Iterator<ITreeElement<T>> {
return this.model.splice(location, deleteCount, toInsert);
}
private onMouseClick(e: IListMouseEvent<ITreeNode<T>>): void {
const node = e.element;
const location = getLocation(node);
const location = getNodeLocation(node);
this.model.toggleCollapsed(location);
}
@ -190,12 +190,17 @@ export class Tree<T> implements IDisposable {
}
const node = nodes[0];
const location = getLocation(node);
const didCollapse = this.model.setCollapsed(location, true);
const location = getNodeLocation(node);
const didChange = this.model.setCollapsed(location, true);
if (!didCollapse) {
console.log('should focus parent');
// this.view.setFocus([]);
if (!didChange) {
if (location.length === 1) {
return;
}
const [parentLocation] = tail2(location);
const parentListIndex = this.model.getListIndex(parentLocation);
this.view.setFocus([parentListIndex]);
}
}
@ -210,8 +215,17 @@ export class Tree<T> implements IDisposable {
}
const node = nodes[0];
const location = getLocation(node);
this.model.setCollapsed(location, false);
const location = getNodeLocation(node);
const didChange = this.model.setCollapsed(location, false);
if (!didChange) {
if (node.children.length === 0) {
return;
}
const [focusedIndex] = this.view.getFocus();
this.view.setFocus([focusedIndex + 1]);
}
}
private onSpace(e: StandardKeyboardEvent): void {
@ -225,7 +239,7 @@ export class Tree<T> implements IDisposable {
}
const node = nodes[0];
const location = getLocation(node);
const location = getNodeLocation(node);
this.model.toggleCollapsed(location);
}

View file

@ -6,12 +6,13 @@
'use strict';
import { ISpliceable } from 'vs/base/common/sequence';
import { IIterator, map, collect, iter, empty } from 'vs/base/common/iterator';
import { Iterator, ISequence } from 'vs/base/common/iterator';
import { Emitter, Event } from 'vs/base/common/event';
export interface ITreeElement<T> {
readonly element: T;
readonly children?: IIterator<ITreeElement<T>> | ITreeElement<T>[];
readonly children?: Iterator<ITreeElement<T>> | ITreeElement<T>[];
readonly collapsible?: boolean;
readonly collapsed?: boolean;
}
@ -20,6 +21,7 @@ export interface ITreeNode<T> {
readonly element: T;
readonly children: IMutableTreeNode<T>[];
readonly depth: number;
readonly collapsible: boolean;
readonly collapsed: boolean;
readonly visibleCount: number;
}
@ -49,11 +51,11 @@ function getVisibleNodes<T>(nodes: IMutableTreeNode<T>[], result: ITreeNode<T>[]
return result;
}
function getTreeElementIterator<T>(elements: IIterator<ITreeElement<T>> | ITreeElement<T>[] | undefined): IIterator<ITreeElement<T>> {
function getTreeElementIterator<T>(elements: Iterator<ITreeElement<T>> | ITreeElement<T>[] | undefined): Iterator<ITreeElement<T>> {
if (!elements) {
return empty();
return Iterator.empty();
} else if (Array.isArray(elements)) {
return iter(elements);
return Iterator.iterate(elements);
} else {
return elements;
}
@ -61,15 +63,16 @@ function getTreeElementIterator<T>(elements: IIterator<ITreeElement<T>> | ITreeE
function treeElementToNode<T>(treeElement: ITreeElement<T>, parent: IMutableTreeNode<T>, visible: boolean, treeListElements: ITreeNode<T>[]): IMutableTreeNode<T> {
const depth = parent.depth + 1;
const { element, collapsed } = treeElement;
const node = { parent, element, children: [], depth, collapsed: !!collapsed, visibleCount: 0 };
const { element, collapsible, collapsed } = treeElement;
const node = { parent, element, children: [], depth, collapsible: !!collapsible, collapsed: !!collapsed, visibleCount: 0 };
if (visible) {
treeListElements.push(node);
}
const children = getTreeElementIterator(treeElement.children);
node.children = collect(map(children, el => treeElementToNode(el, node, visible && !treeElement.collapsed, treeListElements)));
node.children = Iterator.collect(Iterator.map(children, el => treeElementToNode(el, node, visible && !treeElement.collapsed, treeListElements)));
node.collapsible = node.collapsible || node.children.length > 0;
node.visibleCount = 1 + getVisibleCount(node.children);
return node;
@ -77,11 +80,22 @@ function treeElementToNode<T>(treeElement: ITreeElement<T>, parent: IMutableTree
function treeNodeToElement<T>(node: IMutableTreeNode<T>): ITreeElement<T> {
const { element, collapsed } = node;
const children = map(iter(node.children), treeNodeToElement);
const children = Iterator.map(Iterator.iterate(node.children), treeNodeToElement);
return { element, children, collapsed };
}
export function getNodeLocation<T>(node: ITreeNode<T>): number[] {
const location = [];
while (node.parent) {
location.push(node.parent.children.indexOf(node));
node = node.parent;
}
return location.reverse();
}
export class TreeModel<T> {
private root: IMutableTreeNode<T> = {
@ -89,6 +103,7 @@ export class TreeModel<T> {
element: undefined,
children: [],
depth: 0,
collapsible: false,
collapsed: false,
visibleCount: 1
};
@ -98,7 +113,7 @@ export class TreeModel<T> {
constructor(private list: ISpliceable<ITreeNode<T>>) { }
splice(location: number[], deleteCount: number, toInsert?: IIterator<ITreeElement<T>> | ITreeElement<T>[]): IIterator<ITreeElement<T>> {
splice(location: number[], deleteCount: number, toInsert?: ISequence<ITreeElement<T>>): Iterator<ITreeElement<T>> {
if (location.length === 0) {
throw new Error('Invalid tree location');
}
@ -106,7 +121,7 @@ export class TreeModel<T> {
const { parentNode, listIndex, visible } = this.findParentNode(location);
const treeListElementsToInsert: ITreeNode<T>[] = [];
const elementsToInsert = getTreeElementIterator(toInsert);
const nodesToInsert = collect(map(elementsToInsert, el => treeElementToNode(el, parentNode, visible, treeListElementsToInsert)));
const nodesToInsert = Iterator.collect(Iterator.map(elementsToInsert, el => treeElementToNode(el, parentNode, visible, treeListElementsToInsert)));
const lastIndex = location[location.length - 1];
const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert);
const visibleDeleteCount = getVisibleCount(deletedNodes);
@ -117,7 +132,11 @@ export class TreeModel<T> {
this.list.splice(listIndex, visibleDeleteCount, treeListElementsToInsert);
}
return map(iter(deletedNodes), treeNodeToElement);
return Iterator.map(Iterator.iterate(deletedNodes), treeNodeToElement);
}
getListIndex(location: number[]): number {
return this.findNode(location).listIndex;
}
setCollapsed(location: number[], collapsed: boolean): boolean {
@ -131,6 +150,10 @@ export class TreeModel<T> {
private _setCollapsed(location: number[], collapsed?: boolean | undefined): boolean {
const { node, listIndex, visible } = this.findNode(location);
if (!node.collapsible) {
return false;
}
if (typeof collapsed === 'undefined') {
collapsed = !node.collapsed;
}
@ -142,6 +165,8 @@ export class TreeModel<T> {
node.collapsed = collapsed;
if (visible) {
this._onDidChangeCollapseState.fire(node);
let visibleCountDiff: number;
if (collapsed) {
@ -162,8 +187,6 @@ export class TreeModel<T> {
mutableNode.visibleCount += visibleCountDiff;
mutableNode = mutableNode.parent;
}
this._onDidChangeCollapseState.fire(node);
}
return true;

View file

@ -5,74 +5,86 @@
'use strict';
export interface IIteratorResult<T> {
export interface IteratorResult<T> {
readonly done: boolean;
readonly value: T | undefined;
}
export interface IIterator<T> {
next(): IIteratorResult<T>;
export interface Iterator<T> {
next(): IteratorResult<T>;
}
const _empty: IIterator<any> = {
next() {
return { done: true, value: undefined };
export module Iterator {
const _empty: Iterator<any> = {
next() {
return { done: true, value: undefined };
}
};
export function empty<T>(): Iterator<T> {
return _empty;
}
};
export function empty<T>(): IIterator<T> {
return _empty;
}
export function iterate<T>(array: T[], index = 0, length = array.length): Iterator<T> {
return {
next(): IteratorResult<T> {
if (index >= length) {
return { done: true, value: undefined };
}
export function iter<T>(array: T[], index = 0, length = array.length): IIterator<T> {
return {
next(): IIteratorResult<T> {
if (index >= length) {
return { done: true, value: undefined };
return { done: false, value: array[index++] };
}
};
}
return { done: false, value: array[index++] };
}
};
}
export function map<T, R>(iterator: IIterator<T>, fn: (t: T) => R): IIterator<R> {
return {
next() {
const { done, value } = iterator.next();
return { done, value: done ? undefined : fn(value) };
}
};
}
export function filter<T>(iterator: IIterator<T>, fn: (t: T) => boolean): IIterator<T> {
return {
next() {
while (true) {
export function map<T, R>(iterator: Iterator<T>, fn: (t: T) => R): Iterator<R> {
return {
next() {
const { done, value } = iterator.next();
return { done, value: done ? undefined : fn(value) };
}
};
}
if (done) {
return { done, value: undefined };
}
export function filter<T>(iterator: Iterator<T>, fn: (t: T) => boolean): Iterator<T> {
return {
next() {
while (true) {
const { done, value } = iterator.next();
if (fn(value)) {
return { done, value };
if (done) {
return { done, value: undefined };
}
if (fn(value)) {
return { done, value };
}
}
}
}
};
}
};
}
export function forEach<T>(iterator: IIterator<T>, fn: (t: T) => void): void {
for (let next = iterator.next(); !next.done; next = iterator.next()) {
fn(next.value);
export function forEach<T>(iterator: Iterator<T>, fn: (t: T) => void): void {
for (let next = iterator.next(); !next.done; next = iterator.next()) {
fn(next.value);
}
}
export function collect<T>(iterator: Iterator<T>): T[] {
const result: T[] = [];
forEach(iterator, value => result.push(value));
return result;
}
}
export function collect<T>(iterator: IIterator<T>): T[] {
const result: T[] = [];
forEach(iterator, value => result.push(value));
return result;
export type ISequence<T> = Iterator<T> | T[];
export function getSequenceIterator<T>(arg: Iterator<T> | T[]): Iterator<T> {
if (Array.isArray(arg)) {
return Iterator.iterate(arg);
} else {
return arg;
}
}
export interface INextIterator<T> {

View file

@ -5,7 +5,7 @@
'use strict';
import { IIterator } from 'vs/base/common/iterator';
import { Iterator } from 'vs/base/common/iterator';
class Node<E> {
element: E;
@ -94,7 +94,7 @@ export class LinkedList<E> {
};
}
iterator(): IIterator<E> {
iterator(): Iterator<E> {
let element = {
done: undefined,
value: undefined,

View file

@ -7,7 +7,7 @@
import URI from 'vs/base/common/uri';
import { CharCode } from 'vs/base/common/charCode';
import { IIterator } from './iterator';
import { Iterator } from './iterator';
export function values<V = any>(set: Set<V>): V[];
export function values<K = any, V = any>(map: Map<K, V>): V[];
@ -306,7 +306,7 @@ export class TernarySearchTree<E> {
return node && node.value || candidate;
}
findSuperstr(key: string): IIterator<E> {
findSuperstr(key: string): Iterator<E> {
let iter = this._iter.reset(key);
let node = this._root;
while (node) {
@ -333,7 +333,7 @@ export class TernarySearchTree<E> {
return undefined;
}
private _nodeIterator(node: TernarySearchTreeNode<E>): IIterator<E> {
private _nodeIterator(node: TernarySearchTreeNode<E>): Iterator<E> {
let res = {
done: false,
value: undefined

View file

@ -8,7 +8,7 @@ import * as path from 'path';
import * as cp from 'child_process';
import { fork } from 'vs/base/node/stdFork';
import * as nls from 'vs/nls';
import { PPromise, TPromise, TValueCallback, TProgressCallback, ErrorCallback } from 'vs/base/common/winjs.base';
import { TPromise, TValueCallback, ErrorCallback } from 'vs/base/common/winjs.base';
import * as Types from 'vs/base/common/types';
import { IStringDictionary } from 'vs/base/common/collections';
import URI from 'vs/base/common/uri';
@ -19,6 +19,8 @@ import { LineDecoder } from 'vs/base/node/decoder';
import { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode, Executable } from 'vs/base/common/processes';
export { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode };
export type TProgressCallback<T> = (progress: T) => void;
export interface LineData {
line: string;
source: Source;
@ -152,18 +154,16 @@ export abstract class AbstractProcess<TProgressData> {
return 'other';
}
public start(): PPromise<SuccessData, TProgressData> {
public start(pp: TProgressCallback<TProgressData>): TPromise<SuccessData> {
if (Platform.isWindows && ((this.options && this.options.cwd && TPath.isUNC(this.options.cwd)) || !this.options && !this.options.cwd && TPath.isUNC(process.cwd()))) {
return TPromise.wrapError(new Error(nls.localize('TaskRunner.UNC', 'Can\'t execute a shell command on a UNC drive.')));
}
return this.useExec().then((useExec) => {
let cc: TValueCallback<SuccessData>;
let ee: ErrorCallback;
let pp: TProgressCallback<TProgressData>;
let result = new PPromise<any, TProgressData>((c, e, p) => {
let result = new TPromise<any>((c, e) => {
cc = c;
ee = e;
pp = p;
});
if (useExec) {

View file

@ -94,8 +94,8 @@ export interface IChannelClient {
* channels (each from a separate client) to pick from.
*/
export interface IClientRouter {
routeCall(command: string, arg: any): string;
routeEvent(event: string, arg: any): string;
routeCall(command: string, arg: any): TPromise<string>;
routeEvent(event: string, arg: any): TPromise<string>;
}
/**
@ -433,24 +433,20 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos
getChannel<T extends IChannel>(channelName: string, router: IClientRouter): T {
const call = (command: string, arg: any) => {
const id = router.routeCall(command, arg);
const channelPromise = router.routeCall(command, arg)
.then(id => this.getClient(id))
.then(client => client.getChannel(channelName));
if (!id) {
return TPromise.wrapError(new Error('Client id should be provided'));
}
return getDelayedChannel(this.getClient(id).then(client => client.getChannel(channelName)))
return getDelayedChannel(channelPromise)
.call(command, arg);
};
const listen = (event: string, arg: any) => {
const id = router.routeEvent(event, arg);
const channelPromise = router.routeEvent(event, arg)
.then(id => this.getClient(id))
.then(client => client.getChannel(channelName));
if (!id) {
return TPromise.wrapError(new Error('Client id should be provided'));
}
return getDelayedChannel(this.getClient(id).then(client => client.getChannel(channelName)))
return getDelayedChannel(channelPromise)
.listen(event, arg);
};
@ -462,6 +458,10 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos
}
private getClient(clientId: string): TPromise<IChannelClient> {
if (!clientId) {
return TPromise.wrapError(new Error('Client id should be provided'));
}
const client = this.channelClients[clientId];
if (client) {

View file

@ -5,7 +5,7 @@
import * as assert from 'assert';
import { ListView } from 'vs/base/browser/ui/list/listView';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { range } from 'vs/base/common/arrays';
suite('ListView', function () {
@ -14,7 +14,7 @@ suite('ListView', function () {
element.style.height = '200px';
element.style.width = '200px';
const delegate: IDelegate<number> = {
const delegate: IVirtualDelegate<number> = {
getHeight() { return 20; },
getTemplateId() { return 'template'; }
};
@ -25,6 +25,7 @@ suite('ListView', function () {
templateId: 'template',
renderTemplate() { templatesCount++; },
renderElement() { },
disposeElement() { },
disposeTemplate() { templatesCount--; }
};

View file

@ -5,8 +5,8 @@
import * as assert from 'assert';
import { TreeModel, ITreeNode } from 'vs/base/browser/ui/tree/treeModel';
import { ISpliceable } from 'vs/base/browser/ui/list/splice';
import { iter } from 'vs/base/common/iterator';
import { ISpliceable } from 'vs/base/common/sequence';
import { Iterator } from 'vs/base/common/iterator';
function toSpliceable<T>(arr: T[]): ISpliceable<T> {
return {
@ -33,7 +33,7 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{ element: 0 },
{ element: 1 },
{ element: 2 }
@ -55,9 +55,9 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{
element: 0, children: iter([
element: 0, children: Iterator.iterate([
{ element: 10 },
{ element: 11 },
{ element: 12 },
@ -92,9 +92,9 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{
element: 0, collapsed: true, children: iter([
element: 0, collapsed: true, children: Iterator.iterate([
{ element: 10 },
{ element: 11 },
{ element: 12 },
@ -120,7 +120,7 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{ element: 0 },
{ element: 1 },
{ element: 2 }
@ -145,9 +145,9 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{
element: 0, children: iter([
element: 0, children: Iterator.iterate([
{ element: 10 },
{ element: 11 },
{ element: 12 },
@ -179,9 +179,9 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{
element: 0, children: iter([
element: 0, children: Iterator.iterate([
{ element: 10 },
{ element: 11 },
{ element: 12 },
@ -207,9 +207,9 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{
element: 0, collapsed: true, children: iter([
element: 0, collapsed: true, children: Iterator.iterate([
{ element: 10 },
{ element: 11 },
{ element: 12 },
@ -232,9 +232,9 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{
element: 0, children: iter([
element: 0, children: Iterator.iterate([
{ element: 10 },
{ element: 11 },
{ element: 12 },
@ -263,9 +263,9 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{
element: 0, collapsed: true, children: iter([
element: 0, collapsed: true, children: Iterator.iterate([
{ element: 10 },
{ element: 11 },
{ element: 12 },
@ -303,7 +303,7 @@ suite('TreeModel2', function () {
const list = [] as ITreeNode<number>[];
const model = new TreeModel<number>(toSpliceable(list));
model.splice([0], 0, iter([
model.splice([0], 0, Iterator.iterate([
{
element: 1, children: [
{

View file

@ -8,7 +8,7 @@
import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache } from 'vs/base/common/map';
import * as assert from 'assert';
import URI from 'vs/base/common/uri';
import { IIteratorResult } from 'vs/base/common/iterator';
import { IteratorResult } from 'vs/base/common/iterator';
suite('Map', () => {
@ -419,7 +419,7 @@ suite('Map', () => {
map.set('/user/foo/flip/flop', 3);
map.set('/usr/foo', 4);
let item: IIteratorResult<number>;
let item: IteratorResult<number>;
let iter = map.findSuperstr('/user');
item = iter.next();

View file

@ -67,7 +67,8 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
process.once('exit', () => dispose(disposables));
const environmentService = new EnvironmentService(initData.args, process.execPath);
const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', { routeCall: () => 'main', routeEvent: () => 'main' }));
const mainRoute = () => TPromise.as('main');
const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', { routeCall: mainRoute, routeEvent: mainRoute }));
const logService = new FollowerLogService(logLevelClient, createSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath));
disposables.push(logService);
@ -78,21 +79,13 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IRequestService, new SyncDescriptor(RequestService));
const windowsChannel = server.getChannel('windows', { routeCall: () => 'main', routeEvent: () => 'main' });
const windowsChannel = server.getChannel('windows', { routeCall: mainRoute, routeEvent: mainRoute });
const windowsService = new WindowsChannelClient(windowsChannel);
services.set(IWindowsService, windowsService);
const activeWindowManager = new ActiveWindowManager(windowsService);
const dialogChannel = server.getChannel('dialog', {
routeCall: () => {
logService.info('Routing dialog call request to the client', activeWindowManager.activeClientId);
return activeWindowManager.activeClientId;
},
routeEvent: () => {
logService.info('Routing dialog listen request to the client', activeWindowManager.activeClientId);
return activeWindowManager.activeClientId;
}
});
const route = () => activeWindowManager.getActiveClientId();
const dialogChannel = server.getChannel('dialog', { routeCall: route, routeEvent: route });
services.set(IDialogService, new DialogChannelClient(dialogChannel));
const instantiationService = new InstantiationService(services);

View file

@ -426,7 +426,7 @@ export class CodeApplication {
// Create a URL handler which forwards to the last active window
const activeWindowManager = new ActiveWindowManager(windowsService);
const route = () => activeWindowManager.activeClientId;
const route = () => activeWindowManager.getActiveClientId();
const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', { routeCall: route, routeEvent: route });
const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);

View file

@ -662,11 +662,66 @@ export class CodeMenu {
const terminal = this.createMenuItem(nls.localize({ key: 'miToggleTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal"), 'workbench.action.terminal.toggleTerminal');
const problems = this.createMenuItem(nls.localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems"), 'workbench.actions.view.problems');
// Appearance
const appearanceMenu = new Menu();
const fullscreen = new MenuItem(this.withKeybinding('workbench.action.toggleFullScreen', { label: this.mnemonicLabel(nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")), click: () => this.windowsMainService.getLastActiveWindow().toggleFullScreen(), enabled: this.windowsMainService.getWindowCount() > 0 }));
const toggleZenMode = this.createMenuItem(nls.localize('miToggleZenMode', "Toggle Zen Mode"), 'workbench.action.toggleZenMode');
const toggleCenteredLayout = this.createMenuItem(nls.localize('miToggleCenteredLayout', "Toggle Centered Layout"), 'workbench.action.toggleCenteredLayout');
const toggleMenuBar = this.createMenuItem(nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar"), 'workbench.action.toggleMenuBar');
const toggleSidebar = this.createMenuItem(nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar"), 'workbench.action.toggleSidebarVisibility');
let moveSideBarLabel: string;
if (this.currentSidebarLocation !== 'right') {
moveSideBarLabel = nls.localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right");
} else {
moveSideBarLabel = nls.localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left");
}
const moveSidebar = this.createMenuItem(moveSideBarLabel, 'workbench.action.toggleSidebarPosition');
const togglePanel = this.createMenuItem(nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel"), 'workbench.action.togglePanel');
let statusBarLabel: string;
if (this.currentStatusbarVisible) {
statusBarLabel = nls.localize({ key: 'miHideStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Hide Status Bar");
} else {
statusBarLabel = nls.localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Show Status Bar");
}
const toggleStatusbar = this.createMenuItem(statusBarLabel, 'workbench.action.toggleStatusbarVisibility');
let activityBarLabel: string;
if (this.currentActivityBarVisible) {
activityBarLabel = nls.localize({ key: 'miHideActivityBar', comment: ['&& denotes a mnemonic'] }, "Hide &&Activity Bar");
} else {
activityBarLabel = nls.localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar");
}
const toggleActivtyBar = this.createMenuItem(activityBarLabel, 'workbench.action.toggleActivityBarVisibility');
const zoomIn = this.createMenuItem(nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In"), 'workbench.action.zoomIn');
const zoomOut = this.createMenuItem(nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "Zoom O&&ut"), 'workbench.action.zoomOut');
const resetZoom = this.createMenuItem(nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom"), 'workbench.action.zoomReset');
arrays.coalesce([
fullscreen,
toggleZenMode,
toggleCenteredLayout,
isWindows || isLinux ? toggleMenuBar : void 0,
__separator__(),
moveSidebar,
toggleSidebar,
togglePanel,
toggleStatusbar,
toggleActivtyBar,
__separator__(),
zoomIn,
zoomOut,
resetZoom
]).forEach(item => appearanceMenu.append(item));
const appearance = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance")), submenu: appearanceMenu });
// Editor Layout
const editorLayoutMenu = new Menu();
@ -707,50 +762,18 @@ export class CodeMenu {
const editorLayout = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout")), submenu: editorLayoutMenu });
// Workbench Layout
const toggleSidebar = this.createMenuItem(nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar"), 'workbench.action.toggleSidebarVisibility');
let moveSideBarLabel: string;
if (this.currentSidebarLocation !== 'right') {
moveSideBarLabel = nls.localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right");
} else {
moveSideBarLabel = nls.localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left");
}
const moveSidebar = this.createMenuItem(moveSideBarLabel, 'workbench.action.toggleSidebarPosition');
const togglePanel = this.createMenuItem(nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel"), 'workbench.action.togglePanel');
let statusBarLabel: string;
if (this.currentStatusbarVisible) {
statusBarLabel = nls.localize({ key: 'miHideStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Hide Status Bar");
} else {
statusBarLabel = nls.localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Show Status Bar");
}
const toggleStatusbar = this.createMenuItem(statusBarLabel, 'workbench.action.toggleStatusbarVisibility');
let activityBarLabel: string;
if (this.currentActivityBarVisible) {
activityBarLabel = nls.localize({ key: 'miHideActivityBar', comment: ['&& denotes a mnemonic'] }, "Hide &&Activity Bar");
} else {
activityBarLabel = nls.localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar");
}
const toggleActivtyBar = this.createMenuItem(activityBarLabel, 'workbench.action.toggleActivityBarVisibility');
// Editor
const toggleWordWrap = this.createMenuItem(nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap"), 'editor.action.toggleWordWrap');
const toggleMinimap = this.createMenuItem(nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap"), 'editor.action.toggleMinimap');
const toggleRenderWhitespace = this.createMenuItem(nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace"), 'editor.action.toggleRenderWhitespace');
const toggleRenderControlCharacters = this.createMenuItem(nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters"), 'editor.action.toggleRenderControlCharacter');
// Zoom
const zoomIn = this.createMenuItem(nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In"), 'workbench.action.zoomIn');
const zoomOut = this.createMenuItem(nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "Zoom O&&ut"), 'workbench.action.zoomOut');
const resetZoom = this.createMenuItem(nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom"), 'workbench.action.zoomReset');
arrays.coalesce([
commands,
openView,
__separator__(),
appearance,
editorLayout,
__separator__(),
explorer,
search,
scm,
@ -762,27 +785,10 @@ export class CodeMenu {
debugConsole,
terminal,
__separator__(),
fullscreen,
toggleZenMode,
toggleCenteredLayout,
isWindows || isLinux ? toggleMenuBar : void 0,
__separator__(),
editorLayout,
__separator__(),
moveSidebar,
toggleSidebar,
togglePanel,
toggleStatusbar,
toggleActivtyBar,
__separator__(),
toggleWordWrap,
toggleMinimap,
toggleRenderWhitespace,
toggleRenderControlCharacters,
__separator__(),
zoomIn,
zoomOut,
resetZoom
toggleRenderControlCharacters
]).forEach(item => viewMenu.append(item));
}

View file

@ -282,19 +282,19 @@ const editorConfiguration: IConfigurationNode = {
'type': 'number',
'default': EDITOR_MODEL_DEFAULTS.tabSize,
'minimum': 1,
'description': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `editor.detectIndentation` is on."),
'description': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when [`editor.detectIndentation`](#editor.detectIndentation) is on."),
'errorMessage': nls.localize('tabSize.errorMessage', "Expected 'number'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.")
},
'editor.insertSpaces': {
'type': 'boolean',
'default': EDITOR_MODEL_DEFAULTS.insertSpaces,
'description': nls.localize('insertSpaces', "Insert spaces when pressing Tab. This setting is overridden based on the file contents when `editor.detectIndentation` is on."),
'description': nls.localize('insertSpaces', "Insert spaces when pressing Tab. This setting is overridden based on the file contents when [`editor.detectIndentation`](#editor.detectIndentation) is on."),
'errorMessage': nls.localize('insertSpaces.errorMessage', "Expected 'boolean'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.")
},
'editor.detectIndentation': {
'type': 'boolean',
'default': EDITOR_MODEL_DEFAULTS.detectIndentation,
'description': nls.localize('detectIndentation', "When opening a file, `editor.tabSize` and `editor.insertSpaces` will be detected based on the file contents.")
'description': nls.localize('detectIndentation', "When opening a file, [`editor.tabSize`](#editor.tabSize) and [`editor.insertSpaces`](#editor.insertSpaces) will be detected based on the file contents.")
},
'editor.roundedSelection': {
'type': 'boolean',
@ -440,7 +440,7 @@ const editorConfiguration: IConfigurationNode = {
'- `ctrlCmd` refers to a value the setting can take and should not be localized.',
'- `Control` and `Command` refer to the modifier keys Ctrl or Cmd on the keyboard and can be localized.'
]
}, "The modifier to be used to add multiple cursors with the mouse. `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier.")
}, "The modifier to be used to add multiple cursors with the mouse. `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier. [Read more](https://code.visualstudio.com/docs/editor/codebasics#_multicursor-modifier)")
},
'editor.multiCursorMergeOverlapping': {
'type': 'boolean',

View file

@ -22,7 +22,7 @@ import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model
import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase';
import { IWordAtPosition, EndOfLineSequence } from 'vs/editor/common/model';
import { globals } from 'vs/base/common/platform';
import { IIterator } from 'vs/base/common/iterator';
import { Iterator } from 'vs/base/common/iterator';
export interface IMirrorModel {
readonly uri: URI;
@ -59,7 +59,7 @@ export interface ICommonModel {
getLinesContent(): string[];
getLineCount(): number;
getLineContent(lineNumber: number): string;
createWordIterator(wordDefinition: RegExp): IIterator<string>;
createWordIterator(wordDefinition: RegExp): Iterator<string>;
getWordUntilPosition(position: IPosition, wordDefinition: RegExp): IWordAtPosition;
getValueInRange(range: IRange): string;
getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range;
@ -147,7 +147,7 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel {
};
}
public createWordIterator(wordDefinition: RegExp): IIterator<string> {
public createWordIterator(wordDefinition: RegExp): Iterator<string> {
let obj = {
done: false,
value: ''

View file

@ -138,7 +138,7 @@ let showReferencesCommand: ICommandHandler = (accessor: ServicesAccessor, resour
return TPromise.as(controller.toggleWidget(
new Range(position.lineNumber, position.column, position.lineNumber, position.column),
createCancelablePromise(_ => Promise.reject(new ReferencesModel(references))),
createCancelablePromise(_ => Promise.resolve(new ReferencesModel(references))),
defaultReferenceSearchOptions)).then(() => true);
});
};

View file

@ -14,7 +14,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass } from 'vs/base/browser/dom';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { IDelegate, IListEvent, IRenderer } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IListEvent, IRenderer } from 'vs/base/browser/ui/list/list';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@ -173,7 +173,10 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
data.readMore.onmousedown = null;
data.readMore.onclick = null;
}
}
disposeElement(): void {
// noop
}
disposeTemplate(templateData: ISuggestionTemplateData): void {
@ -349,7 +352,7 @@ export interface ISelectedSuggestion {
model: CompletionModel;
}
export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>, IDisposable {
export class SuggestWidget implements IContentWidget, IVirtualDelegate<ICompletionItem>, IDisposable {
private static readonly ID: string = 'editor.widget.suggestWidget';

View file

@ -90,6 +90,7 @@ export class MenuId {
static readonly MenubarRecentMenu = new MenuId();
static readonly MenubarSelectionMenu = new MenuId();
static readonly MenubarViewMenu = new MenuId();
static readonly MenubarAppearanceMenu = new MenuId();
static readonly MenubarLayoutMenu = new MenuId();
static readonly MenubarGoMenu = new MenuId();
static readonly MenubarDebugMenu = new MenuId();

View file

@ -27,12 +27,12 @@ class WindowRouter implements IClientRouter {
constructor(private windowId: number) { }
routeCall(): string {
return `window:${this.windowId}`;
routeCall(): TPromise<string> {
return TPromise.as(`window:${this.windowId}`);
}
routeEvent(): string {
return `window:${this.windowId}`;
routeEvent(): TPromise<string> {
return TPromise.as(`window:${this.windowId}`);
}
}

View file

@ -10,7 +10,7 @@ import { createDecorator, IInstantiationService } from 'vs/platform/instantiatio
import { IDisposable, toDisposable, combinedDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { PagedList, IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { IDelegate, IRenderer, IListMouseEvent, IListTouchEvent } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IRenderer, IListMouseEvent, IListTouchEvent } from 'vs/base/browser/ui/list/list';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { attachListStyler, defaultListStyles, computeStyles } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@ -208,7 +208,7 @@ export class WorkbenchList<T> extends List<T> {
constructor(
container: HTMLElement,
delegate: IDelegate<T>,
delegate: IVirtualDelegate<T>,
renderers: IRenderer<T, any>[],
options: IListOptions<T>,
@IContextKeyService contextKeyService: IContextKeyService,
@ -279,7 +279,7 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
constructor(
container: HTMLElement,
delegate: IDelegate<number>,
delegate: IVirtualDelegate<number>,
renderers: IPagedRenderer<T, any>[],
options: IListOptions<T>,
@IContextKeyService contextKeyService: IContextKeyService,

View file

@ -223,6 +223,11 @@ export const scrollbarSliderActiveBackground = registerColor('scrollbarSlider.ac
export const progressBarBackground = registerColor('progressBar.background', { dark: Color.fromHex('#0E70C0'), light: Color.fromHex('#0E70C0'), hc: contrastBorder }, nls.localize('progressBarBackground', "Background color of the progress bar that can show for long running operations."));
export const breadcrumbsForeground = registerColor('breadcrumb.breadcrumbsForeground', { light: Color.fromHex('#6C6C6C').transparent(.7), dark: Color.fromHex('#CCCCCC').transparent(.7), hc: Color.white.transparent(.7) }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items."));
export const breadcrumbsFocusForeground = registerColor('breadcrumb.breadcrumbsFocusForeground', { light: '#6C6C6C', dark: '#CCCCCC', hc: Color.white }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items."));
export const breadcrumbsActiveSelectionForeground = registerColor('breadcrumb.breadcrumbsActiveSelectionForeground', { light: '#6C6C6C', dark: '#CCCCCC', hc: Color.white }, nls.localize('breadcrumbsSelectedForegound', "Color of selected breadcrumb items."));
export const breadcrumbsActiveSelectionBackground = registerColor('breadcrumb.breadcrumbsActiveSelectionBackground', { light: '#F3F3F3', dark: '#252526', hc: Color.black }, nls.localize('breadcrumbsSelectedBackground', "Background color of selected breadcrumb items."));
/**
* Editor background color.
* Because of bug https://monacotools.visualstudio.com/DefaultCollection/Monaco/_workitems/edit/13254

View file

@ -6,7 +6,7 @@
'use strict';
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, lighten, badgeBackground, badgeForeground, progressBarBackground } from 'vs/platform/theme/common/colorRegistry';
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, lighten, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionBackground, breadcrumbsActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
@ -265,28 +265,22 @@ export function attachStylerCallback(themeService: IThemeService, colors: { [nam
export interface IBreadcrumbsWidgetStyleOverrides extends IStyleOverrides {
breadcrumbsBackground?: ColorIdentifier;
breadcrumbsItemHoverBackground?: ColorIdentifier;
breadcrumbsItemHoverForeground?: ColorIdentifier;
breadcrumbsItemFocusBackground?: ColorIdentifier;
breadcrumbsItemFocusForeground?: ColorIdentifier;
breadcrumbsActiveItemSelectionBackground?: ColorIdentifier;
breadcrumbsActiveItemSelectionForeground?: ColorIdentifier;
breadcrumbsInactiveItemSelectionBackground?: ColorIdentifier;
breadcrumbsInactiveItemSelectionForeground?: ColorIdentifier;
breadcrumbsForeground?: ColorIdentifier;
breadcrumbsHoverBackground?: ColorIdentifier;
breadcrumbsHoverForeground?: ColorIdentifier;
breadcrumbsFocusForeground?: ColorIdentifier;
breadcrumbsFocusAndSelectionBackground?: ColorIdentifier;
breadcrumbsFocusAndSelectionForeground?: ColorIdentifier;
}
export const defaultBreadcrumbsStyles = <IBreadcrumbsWidgetStyleOverrides>{
breadcrumbsBackground: editorBackground,
breadcrumbsItemHoverBackground: listHoverBackground,
breadcrumbsItemHoverForeground: listHoverForeground,
breadcrumbsItemFocusBackground: listFocusBackground,
breadcrumbsItemFocusForeground: listFocusForeground,
breadcrumbsItemSelectionBackground: listActiveSelectionBackground,
breadcrumbsItemSelectionForeground: listActiveSelectionForeground,
breadcrumbsActiveItemSelectionBackground: listActiveSelectionBackground,
breadcrumbsActiveItemSelectionForeground: listActiveSelectionForeground,
breadcrumbsInactiveItemSelectionBackground: editorBackground,
breadcrumbsInactiveItemSelectionForeground: listInactiveSelectionForeground,
breadcrumbsForeground: breadcrumbsForeground,
breadcrumbsHoverBackground: editorBackground,
breadcrumbsHoverForeground: breadcrumbsFocusForeground,
breadcrumbsFocusForeground: breadcrumbsFocusForeground,
breadcrumbsFocusAndSelectionBackground: breadcrumbsActiveSelectionBackground,
breadcrumbsFocusAndSelectionForeground: breadcrumbsActiveSelectionForeground,
};
export function attachBreadcrumbsStyler(widget: IThemable, themeService: IThemeService, style?: IBreadcrumbsWidgetStyleOverrides): IDisposable {

View file

@ -162,6 +162,7 @@ export interface IWindowsService {
getWindowCount(): TPromise<number>;
log(severity: string, ...messages: string[]): TPromise<void>;
showItemInFolder(path: string): TPromise<void>;
getActiveWindowId(): TPromise<number | undefined>;
// This needs to be handled from browser process to prevent
// foreground ordering issues on Windows
@ -357,19 +358,31 @@ export interface IRunActionInWindowRequest {
export class ActiveWindowManager implements IDisposable {
private disposables: IDisposable[] = [];
private _activeWindowId: number;
private firstActiveWindowIdPromise: TPromise<any> | null;
private _activeWindowId: number | undefined;
constructor(@IWindowsService windowsService: IWindowsService) {
const onActiveWindowChange = latch(anyEvent(windowsService.onWindowOpen, windowsService.onWindowFocus));
onActiveWindowChange(this.setActiveWindow, this, this.disposables);
this.firstActiveWindowIdPromise = windowsService.getActiveWindowId()
.then(id => (typeof this._activeWindowId === 'undefined') && this.setActiveWindow(id));
}
private setActiveWindow(windowId: number) {
if (this.firstActiveWindowIdPromise) {
this.firstActiveWindowIdPromise = null;
}
this._activeWindowId = windowId;
}
get activeClientId(): string {
return `window:${this._activeWindowId}`;
getActiveClientId(): TPromise<string> {
if (this.firstActiveWindowIdPromise) {
return this.firstActiveWindowIdPromise;
}
return TPromise.as(`window:${this._activeWindowId}`);
}
dispose() {

View file

@ -68,6 +68,7 @@ export interface IWindowsChannel extends IChannel {
call(command: 'toggleSharedProcess'): TPromise<void>;
call(command: 'log', arg: [string, string[]]): TPromise<void>;
call(command: 'showItemInFolder', arg: string): TPromise<void>;
call(command: 'getActiveWindowId'): TPromise<number>;
call(command: 'openExternal', arg: string): TPromise<boolean>;
call(command: 'startCrashReporter', arg: CrashReporterStartOptions): TPromise<void>;
call(command: 'openAccessibilityOptions'): TPromise<void>;
@ -166,6 +167,7 @@ export class WindowsChannel implements IWindowsChannel {
case 'quit': return this.service.quit();
case 'log': return this.service.log(arg[0], arg[1]);
case 'showItemInFolder': return this.service.showItemInFolder(arg);
case 'getActiveWindowId': return this.service.getActiveWindowId();
case 'openExternal': return this.service.openExternal(arg);
case 'startCrashReporter': return this.service.startCrashReporter(arg);
case 'openAccessibilityOptions': return this.service.openAccessibilityOptions();
@ -364,6 +366,10 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('showItemInFolder', path);
}
getActiveWindowId(): TPromise<number | undefined> {
return this.channel.call('getActiveWindowId');
}
openExternal(url: string): TPromise<boolean> {
return this.channel.call('openExternal', url);
}

View file

@ -14,7 +14,7 @@ import product from 'vs/platform/node/product';
import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions } from 'vs/platform/windows/common/windows';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { shell, crashReporter, app, Menu, clipboard, BrowserWindow } from 'electron';
import { Event, fromNodeEventEmitter, mapEvent, filterEvent, anyEvent } from 'vs/base/common/event';
import { Event, fromNodeEventEmitter, mapEvent, filterEvent, anyEvent, latch } from 'vs/base/common/event';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IWindowsMainService, ISharedProcess } from 'vs/platform/windows/electron-main/windows';
@ -32,6 +32,8 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
private disposables: IDisposable[] = [];
private _activeWindowId: number | undefined;
readonly onWindowOpen: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-created', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id));
readonly onWindowFocus: Event<number> = anyEvent(
mapEvent(filterEvent(mapEvent(this.windowsMainService.onWindowsCountChanged, () => this.windowsMainService.getLastActiveWindow()), w => !!w), w => w.id),
@ -54,6 +56,10 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
@ILogService private logService: ILogService
) {
urlService.registerHandler(this);
// remember last active window id
latch(anyEvent(this.onWindowOpen, this.onWindowFocus))
(id => this._activeWindowId = id, null, this.disposables);
}
pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void> {
@ -435,6 +441,10 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
return TPromise.as(null);
}
getActiveWindowId(): TPromise<number | undefined> {
return TPromise.as(this._activeWindowId);
}
openExternal(url: string): TPromise<boolean> {
this.logService.trace('windowsService#openExternal');
return TPromise.as(shell.openExternal(url));

View file

@ -332,7 +332,7 @@ namespace TaskDTO {
let definition = TaskDefinitionDTO.to(task.definition, executeOnly);
let id = `${task.source.extensionId}.${definition._key}`;
let result: ContributedTask = {
_id: id, // uuidMap.getUUID(identifier),
_id: id, // uuidMap.getUUID(identifier)
_source: source,
_label: label,
type: definition.type,

View file

@ -71,11 +71,10 @@ export class ExtHostProgress implements ExtHostProgressShape {
function mergeProgress(result: IProgressStep, currentValue: IProgressStep): IProgressStep {
result.message = currentValue.message;
if (typeof currentValue.increment === 'number' && typeof result.message === 'number') {
result.increment += currentValue.increment;
} else if (typeof currentValue.increment === 'number') {
if (typeof currentValue.increment === 'number') {
result.increment = currentValue.increment;
}
return result;
}

View file

@ -13,7 +13,6 @@ import * as strings from 'vs/base/common/strings';
import URI, { UriComponents } from 'vs/base/common/uri';
import { PPromise, TPromise } from 'vs/base/common/winjs.base';
import * as extfs from 'vs/base/node/extfs';
import * as pfs from 'vs/base/node/pfs';
import { IFileMatch, IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search';
import * as vscode from 'vscode';
import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol';
@ -30,9 +29,9 @@ export class ExtHostSearch implements ExtHostSearchShape {
private _fileSearchManager: FileSearchManager;
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _extfs = extfs, private _pfs = pfs) {
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _extfs = extfs) {
this._proxy = mainContext.getProxy(MainContext.MainThreadSearch);
this._fileSearchManager = new FileSearchManager(this._pfs);
this._fileSearchManager = new FileSearchManager();
}
private _transformScheme(scheme: string): string {
@ -518,7 +517,7 @@ class FileSearchEngine {
private globalExcludePattern: glob.ParsedExpression;
constructor(private config: ISearchQuery, private provider: vscode.SearchProvider, private _pfs: typeof pfs) {
constructor(private config: ISearchQuery, private provider: vscode.SearchProvider) {
this.filePattern = config.filePattern;
this.includePattern = config.includePattern && glob.parse(config.includePattern);
this.maxResults = config.maxResults || null;
@ -633,19 +632,6 @@ class FileSearchEngine {
return null;
}
if (noSiblingsClauses && this.isLimitHit) {
// If the limit was hit, check whether filePattern is an exact relative match because it must be included
return this.checkFilePatternRelativeMatch(fq.folder).then(({ exists, size }) => {
if (exists) {
onResult({
base: fq.folder,
relativePath: this.filePattern,
basename: path.basename(this.filePattern),
});
}
});
}
this.matchDirectoryTree(tree, queryTester, onResult);
return null;
}).then(
@ -743,24 +729,6 @@ class FileSearchEngine {
matchDirectory(rootEntries);
}
private checkFilePatternRelativeMatch(base: URI): TPromise<{ exists: boolean, size?: number }> {
if (!this.filePattern || path.isAbsolute(this.filePattern) || base.scheme !== 'file') {
return TPromise.wrap({ exists: false });
}
const absolutePath = path.join(base.fsPath, this.filePattern);
return this._pfs.stat(absolutePath).then(stat => {
return {
exists: !stat.isDirectory(),
size: stat.size
};
}, err => {
return {
exists: false
};
});
}
private matchFile(onResult: (result: IInternalFileMatch) => void, candidate: IInternalFileMatch): void {
if (this.isFilePatternMatch(candidate.relativePath) && (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename))) {
if (this.exists || (this.maxResults && this.resultCount >= this.maxResults)) {
@ -800,12 +768,10 @@ class FileSearchManager {
private readonly expandedCacheKeys = new Map<string, string[]>();
constructor(private _pfs: typeof pfs) { }
fileSearch(config: ISearchQuery, provider: vscode.SearchProvider): PPromise<ISearchCompleteStats, IFileMatch[]> {
let searchP: PPromise;
return new PPromise<ISearchCompleteStats, IFileMatch[]>((c, e, p) => {
const engine = new FileSearchEngine(config, provider, this._pfs);
const engine = new FileSearchEngine(config, provider);
searchP = this.doSearch(engine, FileSearchManager.BATCH_SIZE).then(
result => {

View file

@ -30,9 +30,12 @@
padding-left: 12px;
}
.monaco-workbench > .part > .title > .title-label span {
.monaco-workbench > .part > .title > .title-label h2 {
font-size: 11px;
cursor: default;
font-weight: normal;
-webkit-margin-before: 0;
-webkit-margin-after: 0;
}
.monaco-workbench > .part > .title > .title-label a {

View file

@ -434,7 +434,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
$(parent).div({
'class': 'title-label'
}, div => {
titleLabel = div.span();
titleLabel = div.element('h2');
});
const $this = this;

View file

@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------------------------
* 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 { BreadcrumbsWidget } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget';
import { IDisposable } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { GroupIdentifier } from 'vs/workbench/common/editor';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export const IBreadcrumbsService = createDecorator<IBreadcrumbsService>('IEditorBreadcrumbsService');
export interface IBreadcrumbsService {
_serviceBrand: any;
register(group: GroupIdentifier, widget: BreadcrumbsWidget): IDisposable;
getWidget(group: GroupIdentifier): BreadcrumbsWidget;
}
export class BreadcrumbsService implements IBreadcrumbsService {
_serviceBrand: any;
private readonly _map = new Map<number, BreadcrumbsWidget>();
register(group: number, widget: BreadcrumbsWidget): IDisposable {
if (this._map.has(group)) {
throw new Error(`group (${group}) has already a widget`);
}
this._map.set(group, widget);
return {
dispose: () => this._map.delete(group)
};
}
getWidget(group: number): BreadcrumbsWidget {
return this._map.get(group);
}
}
registerSingleton(IBreadcrumbsService, BreadcrumbsService);

View file

@ -12,11 +12,11 @@ import { compareFileNames } from 'vs/base/common/comparers';
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { dispose, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { dirname, isEqual } from 'vs/base/common/resources';
import { dirname, isEqual, basenameOrAuthority } from 'vs/base/common/resources';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDataSource, IRenderer, ISelectionEvent, ISorter, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree';
import 'vs/css!./media/editorbreadcrumbs';
import 'vs/css!./media/breadcrumbscontrol';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
@ -35,13 +35,15 @@ import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { FileLabel } from 'vs/workbench/browser/labels';
import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/editorBreadcrumbsModel';
import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
import { EditorInput } from 'vs/workbench/common/editor';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorBreadcrumbs, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { symbolKindToCssClass } from 'vs/editor/common/modes';
class Item extends BreadcrumbsItem {
@ -49,6 +51,7 @@ class Item extends BreadcrumbsItem {
constructor(
readonly element: BreadcrumbElement,
readonly options: IBreadcrumbsControlOptions,
@IInstantiationService private readonly _instantiationService: IInstantiationService
) {
super();
@ -74,12 +77,20 @@ class Item extends BreadcrumbsItem {
render(container: HTMLElement): void {
if (this.element instanceof FileElement) {
// file/folder
let label = this._instantiationService.createInstance(FileLabel, container, {});
label.setFile(this.element.uri, {
hidePath: true,
fileKind: this.element.isFile ? FileKind.FILE : FileKind.FOLDER
});
this._disposables.push(label);
if (this.options.showIcons) {
let label = this._instantiationService.createInstance(FileLabel, container, {});
label.setFile(this.element.uri, {
hidePath: true,
fileKind: this.element.isFile ? FileKind.FILE : FileKind.FOLDER,
fileDecorations: { colors: this.options.showDecorationColors, badges: false }
});
this._disposables.push(label);
} else {
let label = new IconLabel(container);
label.setValue(basenameOrAuthority(this.element.uri));
this._disposables.push(label);
}
} else if (this.element instanceof OutlineGroup) {
// provider
@ -89,14 +100,28 @@ class Item extends BreadcrumbsItem {
} else if (this.element instanceof OutlineElement) {
// symbol
if (this.options.showIcons) {
let icon = document.createElement('div');
icon.className = `symbol-icon ${symbolKindToCssClass(this.element.symbol.kind)}`;
container.appendChild(icon);
}
let label = new IconLabel(container);
label.setValue(this.element.symbol.name);
label.setValue(this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE'));
this._disposables.push(label);
}
}
}
export class EditorBreadcrumbs implements IEditorBreadcrumbs {
export interface IBreadcrumbsControlOptions {
showIcons: boolean;
showDecorationColors: boolean;
}
export class BreadcrumbsControl {
static HEIGHT = 25;
static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false);
static CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false);
@ -104,17 +129,16 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs {
private readonly _ckBreadcrumbsVisible: IContextKey<boolean>;
private readonly _ckBreadcrumbsActive: IContextKey<boolean>;
private readonly _cfEnabled: Config<boolean>;
private readonly _disposables = new Array<IDisposable>();
private readonly _domNode: HTMLDivElement;
readonly domNode: HTMLDivElement;
private readonly _widget: BreadcrumbsWidget;
private _disposables = new Array<IDisposable>();
private _breadcrumbsDisposables = new Array<IDisposable>();
private _breadcrumbsPickerShowing = false;
constructor(
container: HTMLElement,
private readonly _options: IBreadcrumbsControlOptions,
private readonly _editorGroup: EditorGroupView,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IContextViewService private readonly _contextViewService: IContextViewService,
@ -124,127 +148,105 @@ export class EditorBreadcrumbs implements IEditorBreadcrumbs {
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IThemeService private readonly _themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService,
@IBreadcrumbsService breadcrumbsService: IBreadcrumbsService,
) {
this._domNode = document.createElement('div');
dom.addClasses(this._domNode, 'editor-breadcrumbs', 'show-file-icons');
dom.append(container, this._domNode);
this.domNode = document.createElement('div');
dom.addClasses(this.domNode, 'breadcrumbs-control');
dom.append(container, this.domNode);
this._widget = new BreadcrumbsWidget(this._domNode);
this._widget.onDidSelectItem(this._onDidSelectItem, this, this._disposables);
this._widget.onDidFocusItem(this._onDidSelectItem, this, this._disposables);
this._widget = new BreadcrumbsWidget(this.domNode);
this._widget.onDidSelectItem(this._onSelectEvent, this, this._disposables);
this._widget.onDidFocusItem(this._onFocusEvent, this, this._disposables);
this._widget.onDidChangeFocus(this._updateCkBreadcrumbsActive, this, this._disposables);
this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService));
this._cfEnabled = Config.create(configurationService, 'breadcrumbs.enabled');
this._disposables.push(this._cfEnabled.onDidChange(value => {
if (!value) {
this.closeEditor(undefined);
this._editorGroup.relayout();
} else if (this._editorGroup.activeEditor) {
this.openEditor(this._editorGroup.activeEditor);
this._editorGroup.relayout();
}
}));
this._ckBreadcrumbsVisible = BreadcrumbsControl.CK_BreadcrumbsVisible.bindTo(this._contextKeyService);
this._ckBreadcrumbsActive = BreadcrumbsControl.CK_BreadcrumbsActive.bindTo(this._contextKeyService);
this._ckBreadcrumbsVisible = EditorBreadcrumbs.CK_BreadcrumbsVisible.bindTo(this._contextKeyService);
this._ckBreadcrumbsActive = EditorBreadcrumbs.CK_BreadcrumbsActive.bindTo(this._contextKeyService);
this._disposables.push(breadcrumbsService.register(this._editorGroup.id, this._widget));
}
dispose(): void {
dispose(this._disposables);
this._widget.dispose();
this._disposables = dispose(this._disposables);
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
this._ckBreadcrumbsVisible.reset();
this._cfEnabled.dispose();
}
getPreferredHeight(): number {
return this._cfEnabled.value ? 25 : 0;
this._ckBreadcrumbsActive.reset();
this._widget.dispose();
this.domNode.remove();
}
layout(dim: dom.Dimension): void {
this._domNode.style.width = `${dim.width}px`;
this._domNode.style.height = `${dim.height}px`;
this._widget.layout(dim);
}
setActive(value: boolean): void {
dom.toggleClass(this._domNode, 'active', value);
}
update(): void {
const input = this._editorGroup.activeEditor;
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
openEditor(input: EditorInput): void {
if (!this._cfEnabled.value) {
// not enabled -> return early
if (!input || !input.getResource() || !this._fileService.canHandleResource(input.getResource())) {
// cleanup and return when there is no input or when
// we cannot handle this input
this._ckBreadcrumbsVisible.set(false);
dom.toggleClass(this.domNode, 'hidden', true);
return;
}
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
let uri = input.getResource();
if (!uri || !this._fileService.canHandleResource(uri)) {
return this.closeEditor(undefined);
}
dom.toggleClass(this._domNode, 'hidden', false);
dom.toggleClass(this.domNode, 'hidden', false);
this._ckBreadcrumbsVisible.set(true);
let control = this._editorGroup.activeControl.getControl() as ICodeEditor;
let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService);
let listener = model.onDidUpdate(_ => this._widget.setItems(model.getElements().map(element => new Item(element, this._instantiationService))));
this._widget.setItems(model.getElements().map(element => new Item(element, this._instantiationService)));
this._breadcrumbsDisposables.push(model, listener);
let updateBreadcrumbs = () => {
let items = model.getElements().map(element => new Item(element, this._options, this._instantiationService));
this._widget.setItems(items);
this._widget.reveal(items[items.length - 1]);
};
let listener = model.onDidUpdate(updateBreadcrumbs);
updateBreadcrumbs();
this._breadcrumbsDisposables = [model, listener];
}
closeEditor(input: EditorInput): void {
clear(): void {
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
this._ckBreadcrumbsVisible.set(false);
dom.toggleClass(this._domNode, 'hidden', true);
dom.toggleClass(this.domNode, 'hidden', true);
}
focus(): void {
this._widget.domFocus();
}
focusNext(): void {
this._widget.focusNext();
}
focusPrev(): void {
this._widget.focusPrev();
}
select(): void {
const item = this._widget.getFocused();
if (item) {
this._widget.setSelected(item);
private _onFocusEvent(event: IBreadcrumbsItemEvent): void {
if (event.item && this._breadcrumbsPickerShowing) {
return this._widget.setSelection(event.item);
}
}
private _onDidSelectItem(event: IBreadcrumbsItemEvent): void {
private _onSelectEvent(event: IBreadcrumbsItemEvent): void {
if (!event.item) {
return;
}
if (event.type === 'focus' && !this._breadcrumbsPickerShowing) {
// focus change only moves the picker when already active
return;
}
this._editorGroup.focus();
this._contextViewService.showContextView({
getAnchor() {
return event.node;
},
render: (container: HTMLElement) => {
dom.addClasses(container, 'monaco-breadcrumbs-picker', 'monaco-workbench', 'show-file-icons');
let color = this._themeService.getTheme().getColor(SIDE_BAR_BACKGROUND);
container.style.borderColor = color.darken(.2).toString();
container.style.boxShadow = `${color.toString()} 6px 6px 6px -6px;`;
render: (parent: HTMLElement) => {
const container = document.createElement('div');
parent.appendChild(container);
const theme = this._themeService.getTheme();
const color = theme.getColor(editorBackground).darken(theme.type === 'dark' ? .2 : .1);
container.style.borderColor = color.toString();
container.style.boxShadow = `2px 2px 3px ${color.toString()}`;
container.style.position = 'absolute';
container.style.zIndex = '1000';
dom.addClasses(container, 'monaco-breadcrumbs-picker', 'show-file-icons');
let { element } = event.item as Item;
let ctor: IConstructorSignature2<HTMLElement, BreadcrumbElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker;
let res = this._instantiationService.createInstance(ctor, container, element);
res.layout({ width: 250, height: 300 });
res.layout({ width: 330, height: 220 });
let listener = res.onDidPickElement(data => {
this._contextViewService.hideContextView();
this._widget.setSelection(undefined);
if (!data) {
return;
}
@ -313,8 +315,8 @@ export abstract class BreadcrumbsPicker {
this.focus = dom.trackFocus(this._domNode);
this.focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables);
this._tree.setInput(this._getInput(input)).then(_ => {
this._tree.domFocus();
this._tree.focusFirst();
this._tree.domFocus();
}, onUnexpectedError);
}
@ -464,32 +466,41 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
//#region config
abstract class Config<T> {
export abstract class BreadcrumbsConfig<T> {
name: string;
value: T;
onDidChange: Event<T>;
abstract dispose(): void;
static create<T>(service: IConfigurationService, name: string): Config<T> {
private constructor() {
// internal
}
let value: T = service.getValue(name);
let onDidChange = new Emitter<T>();
let listener = service.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(name)) {
value = service.getValue(name);
onDidChange.fire(value);
}
});
static IsEnabled = BreadcrumbsConfig._stub<boolean>('breadcrumbs.enabled');
private static _stub<T>(name: string): { bindTo(service: IConfigurationService): BreadcrumbsConfig<T> } {
return {
name,
get value() { return value; },
onDidChange: onDidChange.event,
dispose(): void {
listener.dispose();
onDidChange.dispose();
bindTo(service) {
let value: T = service.getValue(name);
let onDidChange = new Emitter<T>();
let listener = service.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(name)) {
value = service.getValue(name);
onDidChange.fire(value);
}
});
return {
name,
get value() { return value; },
onDidChange: onDidChange.event,
dispose(): void {
listener.dispose();
onDidChange.dispose();
}
};
}
};
}
@ -517,10 +528,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.focus',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT,
when: EditorBreadcrumbs.CK_BreadcrumbsVisible,
when: BreadcrumbsControl.CK_BreadcrumbsVisible,
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
groups.activeGroup.breadcrumbs.focus();
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
//todo@joh focus last?
breadcrumbs.getWidget(groups.activeGroup.id).domFocus();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@ -528,10 +541,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyCode.RightArrow,
secondary: [KeyMod.Shift | KeyCode.RightArrow],
when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive),
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
groups.activeGroup.breadcrumbs.focusNext();
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).focusNext();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@ -539,21 +553,24 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyCode.LeftArrow,
secondary: [KeyMod.Shift | KeyCode.LeftArrow],
when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive),
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
groups.activeGroup.breadcrumbs.focusPrev();
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).focusPrev();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'breadcrumbs.selectFocused',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyCode.Enter,
secondary: [KeyCode.UpArrow, KeyCode.Space],
when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive),
secondary: [KeyCode.DownArrow, KeyCode.Space],
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
groups.activeGroup.breadcrumbs.select();
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
widget.setSelection(widget.getFocused());
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@ -561,9 +578,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape],
when: ContextKeyExpr.and(EditorBreadcrumbs.CK_BreadcrumbsVisible, EditorBreadcrumbs.CK_BreadcrumbsActive),
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
handler(accessor) {
let groups = accessor.get(IEditorGroupsService);
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).setFocused(undefined);
breadcrumbs.getWidget(groups.activeGroup.id).setSelection(undefined);
groups.activeGroup.activeControl.focus();
}
});

View file

@ -117,6 +117,7 @@ export interface IEditorGroupView extends IDisposable, ISerializableView, IEdito
isEmpty(): boolean;
setActive(isActive: boolean): void;
setLabel(label: string): void;
relayout(): void;
shutdown(): void;
}

View file

@ -19,7 +19,7 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_EMPTY_BACKGROUND, EDITOR_GROUP_FOCUSED_EMPTY_BORDER } from 'vs/workbench/common/theme';
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder, IEditorBreadcrumbs } from 'vs/workbench/services/group/common/editorGroupsService';
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
import { EditorControl } from 'vs/workbench/browser/parts/editor/editorControl';
import { IProgressService } from 'vs/platform/progress/common/progress';
@ -34,7 +34,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { RunOnceWorker } from 'vs/base/common/async';
import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor';
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { join } from 'vs/base/common/paths';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
@ -46,7 +46,6 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { EditorBreadcrumbs } from 'vs/workbench/browser/parts/editor/editorBreadcrumbs';
export class EditorGroupView extends Themable implements IEditorGroupView {
@ -105,9 +104,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private titleContainer: HTMLElement;
private titleAreaControl: TitleControl;
private breadcrumbsContainer: HTMLElement;
private breadcrumbsControl: EditorBreadcrumbs;
private progressBar: ProgressBar;
private editorContainer: HTMLElement;
@ -194,14 +190,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Title control
this.createTitleAreaControl();
// Breadcrumbs container
this.breadcrumbsContainer = document.createElement('div');
addClass(this.breadcrumbsContainer, 'editor-breadcrumbs');
this.element.appendChild(this.breadcrumbsContainer);
// Breadcrumbs control
this.createEditorBreadcrumbs();
// Editor container
this.editorContainer = document.createElement('div');
addClass(this.editorContainer, 'editor-container');
@ -409,16 +397,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
}
private createEditorBreadcrumbs(): void {
if (this.breadcrumbsControl) {
this.breadcrumbsControl.dispose();
clearNode(this.breadcrumbsContainer);
}
this.breadcrumbsControl = this.scopedInstantiationService.createInstance(EditorBreadcrumbs, this.breadcrumbsContainer, this);
}
private restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): TPromise<void> {
if (this._group.count === 0) {
return TPromise.as(void 0); // nothing to show
@ -615,10 +593,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return this._label;
}
get breadcrumbs(): IEditorBreadcrumbs {
return this.breadcrumbsControl;
}
get disposed(): boolean {
return this._disposed;
}
@ -644,8 +618,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Update title control
this.titleAreaControl.setActive(isActive);
this.breadcrumbsControl.setActive(isActive);
// Update styles
this.updateStyles();
@ -817,8 +789,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Show in title control after editor control because some actions depend on it
this.titleAreaControl.openEditor(editor);
this.breadcrumbsControl.openEditor(editor);
return openEditorPromise;
}
@ -988,7 +958,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Forward to title control & breadcrumbs
this.titleAreaControl.closeEditor(editor);
this.breadcrumbsControl.closeEditor(editor);
}
private doCloseActiveEditor(focusNext = this.accessor.activeGroup === this, fromError?: boolean): void {
@ -1375,9 +1344,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this.dimension = new Dimension(width, height);
// Forward to controls
this.titleAreaControl.layout(new Dimension(this.dimension.width, EDITOR_TITLE_HEIGHT));
this.breadcrumbsControl.layout(new Dimension(this.dimension.width, this.breadcrumbsControl.getPreferredHeight()));
this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - (EDITOR_TITLE_HEIGHT + this.breadcrumbsControl.getPreferredHeight())));
this.titleAreaControl.layout(new Dimension(this.dimension.width, this.titleAreaControl.getPreferredHeight()));
this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - this.titleAreaControl.getPreferredHeight()));
}
relayout(): void {
@ -1404,8 +1372,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this.titleAreaControl.dispose();
this.breadcrumbsControl.dispose();
super.dispose();
}
}

View file

@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control.hidden {
display: none;
}
.monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .breadcrumbs-control {
opacity: .8;
}
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item .symbol-icon{
margin-left: 4px;
padding-right: 2px;
}
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) {
/*first-child is the style-element*/
margin-left: 8px;
}
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumbs .monaco-breadcrumb-item:last-child {
/*first-child is the style-element*/
margin-right: 8px;
}
.monaco-breadcrumbs-picker {
border-style: solid;
border-width: 1px;
}

View file

@ -1,18 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .editor-breadcrumbs {
opacity: .8;
}
.monaco-workbench>.part.editor>.content .editor-group-container .editor-breadcrumbs .monaco-breadcrumbs .monaco-breadcrumb-item:nth-child(2) { /*first-child is the style-element*/
padding-left: 8px;
}
.monaco-breadcrumbs-picker {
border: 1px #dddddd solid;
box-shadow: #dddddd 6px 6px 6px -6px;
}

View file

@ -44,12 +44,16 @@
.monaco-workbench > .part.editor > .content .editor-group-container > .title {
position: relative;
height: 35px;
display: flex;
flex-wrap: nowrap;
box-sizing: border-box;
overflow: hidden;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs {
flex-wrap: wrap;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title.title-border-bottom::after {
content: '';
position: absolute;
@ -112,4 +116,4 @@
.monaco-workbench > .part.editor > .content .grid-view-container {
width: 100%;
height: 100%;
}
}

View file

@ -13,10 +13,48 @@
padding-left: 20px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs.title-label {
flex: none;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .monaco-icon-label::before {
height: 35px; /* tweak the icon size of the editor labels when icons are enabled */
}
/* Breadcrumbs */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs {
flex: 1 50%;
overflow: hidden;
line-height: 35px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item {
font-size: 0.9em;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2) {
padding-left: 2px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child {
padding-right: 2px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item {
font-style: italic;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:not(:last-child)::after {
content: '/';
height: inherit;
width: inherit;
background-image: none;
}
.monaco-workbench.windows > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:not(:last-child)::after {
content: '\\';
}
/* Title Actions */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions {
display: flex;
@ -26,4 +64,4 @@
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .title-actions {
opacity: 1;
}
}

View file

@ -252,4 +252,20 @@
cursor: default;
flex: initial;
padding-left: 4px;
}
height: 35px;
}
/* Breadcrumbs */
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control {
flex: 1 100%;
height: 25px;
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before {
height: 18px; /* tweak the icon size of the editor labels when icons are enabled */
}
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child {
padding-right: 8px;
}

View file

@ -21,7 +21,7 @@
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label a,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label span,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label h2,
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label span {
cursor: pointer;
}

View file

@ -11,7 +11,7 @@ import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
import { addDisposableListener, EventType, addClass, EventHelper, removeClass } from 'vs/base/browser/dom';
import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom';
import { IEditorPartOptions } from 'vs/workbench/browser/parts/editor/editor';
import { IAction } from 'vs/base/common/actions';
import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
@ -35,6 +35,12 @@ export class NoTabsTitleControl extends TitleControl {
this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, this.titleContainer, void 0));
this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e)));
// Breadcrumbs
const breadcrumbsContainer = document.createElement('div');
addClass(breadcrumbsContainer, 'no-tabs-breadcrumbs');
this.titleContainer.appendChild(breadcrumbsContainer);
this.createBreadcrumbsControl(breadcrumbsContainer, { showIcons: false, showDecorationColors: false });
// Right Actions Container
const actionsContainer = document.createElement('div');
addClass(actionsContainer, 'title-actions');
@ -156,6 +162,9 @@ export class NoTabsTitleControl extends TitleControl {
const editor = this.group.activeEditor;
this.lastRenderedActiveEditor = editor;
const isEditorPinned = this.group.isPinned(this.group.activeEditor);
const isGroupActive = this.accessor.activeGroup === this.group;
// Clear if there is no editor
if (!editor) {
removeClass(this.titleContainer, 'dirty');
@ -165,9 +174,6 @@ export class NoTabsTitleControl extends TitleControl {
// Otherwise render it
else {
const isEditorPinned = this.group.isPinned(this.group.activeEditor);
const isGroupActive = this.accessor.activeGroup === this.group;
// Dirty state
this.updateEditorDirty(editor);
@ -177,7 +183,9 @@ export class NoTabsTitleControl extends TitleControl {
const { labelFormat } = this.accessor.partOptions;
let description: string;
if (labelFormat === 'default' && !isGroupActive) {
if (this.breadcrumbsControl) {
description = ''; // hide description when showing breadcrumbs
} else if (labelFormat === 'default' && !isGroupActive) {
description = ''; // hide description when group is not active and style is 'default'
} else {
description = editor.getDescription(this.getVerbosity(labelFormat)) || '';
@ -188,7 +196,7 @@ export class NoTabsTitleControl extends TitleControl {
title = ''; // dont repeat what is already shown
}
this.editorLabel.setLabel({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['title-label'] });
this.editorLabel.setLabel({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
if (isGroupActive) {
this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND);
} else {
@ -198,6 +206,16 @@ export class NoTabsTitleControl extends TitleControl {
// Update Editor Actions Toolbar
this.updateEditorActionsToolbar();
}
// Update Breadcrumbs
if (this.breadcrumbsControl) {
if (isGroupActive) {
this.breadcrumbsControl.update();
toggleClass(this.breadcrumbsControl.domNode, 'preview', !isEditorPinned);
} else {
this.breadcrumbsControl.clear();
}
}
}
private getVerbosity(style: string): Verbosity {

View file

@ -39,6 +39,8 @@ import { addClass, addDisposableListener, hasClass, EventType, EventHelper, remo
import { localize } from 'vs/nls';
import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
interface IEditorInputLabel {
name: string;
@ -78,9 +80,10 @@ export class TabsTitleControl extends TitleControl {
@IMenuService menuService: IMenuService,
@IQuickOpenService quickOpenService: IQuickOpenService,
@IThemeService themeService: IThemeService,
@IExtensionService extensionService: IExtensionService
@IExtensionService extensionService: IExtensionService,
@IConfigurationService configurationService: IConfigurationService
) {
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService);
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService);
}
protected create(parent: HTMLElement): void {
@ -108,6 +111,12 @@ export class TabsTitleControl extends TitleControl {
// Close Action
this.closeOneEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL));
// Breadcrumbs
const breadcrumbsContainer = document.createElement('div');
addClass(breadcrumbsContainer, 'tabs-breadcrumbs');
this.titleContainer.appendChild(breadcrumbsContainer);
this.createBreadcrumbsControl(breadcrumbsContainer, { showIcons: true, showDecorationColors: false });
}
private createScrollbar(): void {
@ -240,6 +249,11 @@ export class TabsTitleControl extends TitleControl {
// Redraw all tabs
this.redraw();
// Update Breadcrumbs
if (this.breadcrumbsControl) {
this.breadcrumbsControl.update();
}
}
closeEditor(editor: IEditorInput): void {
@ -287,6 +301,11 @@ export class TabsTitleControl extends TitleControl {
this.clearEditorActionsToolbar();
}
// Update Breadcrumbs
if (this.breadcrumbsControl) {
this.breadcrumbsControl.update();
}
}
moveEditor(editor: IEditorInput, fromIndex: number, targetIndex: number): void {
@ -910,6 +929,11 @@ export class TabsTitleControl extends TitleControl {
return;
}
if (this.breadcrumbsControl) {
this.breadcrumbsControl.layout({ width: dimension.width, height: BreadcrumbsControl.HEIGHT });
this.scrollbar.getDomNode().style.height = `${dimension.height - BreadcrumbsControl.HEIGHT}px`;
}
const visibleContainerWidth = this.tabsContainer.offsetWidth;
const totalContainerWidth = this.tabsContainer.scrollWidth;
@ -1198,4 +1222,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
`);
}
}
});
});

View file

@ -33,10 +33,12 @@ import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
import { listActiveSelectionBackground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry';
import { LocalSelectionTransfer, DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillResourceDataTransfers } from 'vs/workbench/browser/dnd';
import { applyDragImage } from 'vs/base/browser/dnd';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BreadcrumbsConfig, BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
export interface IToolbarActions {
primary: IAction[];
@ -48,6 +50,8 @@ export abstract class TitleControl extends Themable {
protected readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
protected readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
protected breadcrumbsControl: BreadcrumbsControl;
private currentPrimaryEditorActionIds: string[] = [];
private currentSecondaryEditorActionIds: string[] = [];
protected editorActionsToolbar: ToolBar;
@ -72,7 +76,8 @@ export abstract class TitleControl extends Themable {
@IMenuService private menuService: IMenuService,
@IQuickOpenService protected quickOpenService: IQuickOpenService,
@IThemeService themeService: IThemeService,
@IExtensionService private extensionService: IExtensionService
@IExtensionService private extensionService: IExtensionService,
@IConfigurationService protected configurationService: IConfigurationService
) {
super(themeService);
@ -89,6 +94,24 @@ export abstract class TitleControl extends Themable {
protected abstract create(parent: HTMLElement): void;
protected createBreadcrumbsControl(container: HTMLElement, options: IBreadcrumbsControlOptions): void {
const config = this._register(BreadcrumbsConfig.IsEnabled.bindTo(this.configurationService));
config.onDidChange(value => {
if (!value && this.breadcrumbsControl) {
this.breadcrumbsControl.dispose();
this.breadcrumbsControl = undefined;
this.group.relayout();
} else if (value && !this.breadcrumbsControl) {
this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group);
this.breadcrumbsControl.update();
this.group.relayout();
}
});
if (config.value) {
this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group);
}
}
protected createEditorActionsToolBar(container: HTMLElement): void {
const context = { groupId: this.group.id } as IEditorCommandsContext;
@ -327,9 +350,18 @@ export abstract class TitleControl extends Themable {
layout(dimension: Dimension): void {
// Optionally implemented in subclasses
if (this.breadcrumbsControl) {
this.breadcrumbsControl.layout(undefined);
}
}
getPreferredHeight(): number {
return EDITOR_TITLE_HEIGHT + (this.breadcrumbsControl ? BreadcrumbsControl.HEIGHT : 0);
}
dispose(): void {
this.breadcrumbsControl = dispose(this.breadcrumbsControl);
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
super.dispose();

View file

@ -32,12 +32,15 @@
zoom: 1;
}
.monaco-workbench > .part.menubar > .menubar-menu-button.open,
.monaco-workbench > .part.menubar > .menubar-menu-button:focus,
.monaco-workbench > .part.menubar > .menubar-menu-button:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.monaco-workbench > .part.menubar.light > .menubar-menu-button.open,
.monaco-workbench > .part.menubar.light > .menubar-menu-button:focus,
.monaco-workbench > .part.menubar.light > .menubar-menu-button:hover {
background-color: rgba(0, 0, 0, 0.1);
}

View file

@ -12,6 +12,7 @@ fileMenuRegistration();
editMenuRegistration();
selectionMenuRegistration();
viewMenuRegistration();
appearanceMenuRegistration();
layoutMenuRegistration();
goMenuRegistration();
debugMenuRegistration();
@ -494,9 +495,25 @@ function viewMenuRegistration() {
order: 2
});
// TODO: Appearance Submenu
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '2_appearance',
title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"),
submenu: MenuId.MenubarAppearanceMenu,
order: 1
});
// TODO: Editor Layout Submenu
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '2_appearance',
title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"),
submenu: MenuId.MenubarLayoutMenu,
order: 2
});
// Viewlets
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '2_views',
group: '3_views',
command: {
id: 'workbench.view.explorer',
title: nls.localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer")
@ -505,7 +522,7 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '2_views',
group: '3_views',
command: {
id: 'workbench.view.search',
title: nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search")
@ -514,7 +531,7 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '2_views',
group: '3_views',
command: {
id: 'workbench.view.scm',
title: nls.localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM")
@ -523,7 +540,7 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '2_views',
group: '3_views',
command: {
id: 'workbench.view.debug',
title: nls.localize({ key: 'miViewDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug")
@ -532,7 +549,7 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '2_views',
group: '3_views',
command: {
id: 'workbench.view.extensions',
title: nls.localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions")
@ -542,7 +559,7 @@ function viewMenuRegistration() {
// Panels
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '3_panels',
group: '4_panels',
command: {
id: 'workbench.action.output.toggleOutput',
title: nls.localize({ key: 'miToggleOutput', comment: ['&& denotes a mnemonic'] }, "&&Output")
@ -551,7 +568,7 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '3_panels',
group: '4_panels',
command: {
id: 'workbench.debug.action.toggleRepl',
title: nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console")
@ -560,7 +577,7 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '3_panels',
group: '4_panels',
command: {
id: 'workbench.action.terminal.toggleTerminal',
title: nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Integrated Terminal")
@ -569,7 +586,7 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '3_panels',
group: '4_panels',
command: {
id: 'workbench.actions.view.problems',
title: nls.localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems")
@ -577,101 +594,9 @@ function viewMenuRegistration() {
order: 4
});
// Toggle View
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '4_toggle_view',
command: {
id: 'workbench.action.toggleFullScreen',
title: nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '4_toggle_view',
command: {
id: 'workbench.action.toggleZenMode',
title: nls.localize('miToggleZenMode', "Toggle Zen Mode")
},
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '4_toggle_view',
command: {
id: 'workbench.action.toggleCenteredLayout',
title: nls.localize('miToggleCenteredLayout', "Toggle Centered Layout")
},
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '4_toggle_view',
command: {
id: 'workbench.action.toggleMenuBar',
title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar")
},
order: 4
});
// TODO: Editor Layout Submenu
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"),
submenu: MenuId.MenubarLayoutMenu,
group: '5_layout',
order: 1
});
// Workbench Layout
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '6_workbench_layout',
command: {
id: 'workbench.action.toggleSidebarVisibility',
title: nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '6_workbench_layout',
command: {
id: 'workbench.action.toggleSidebarPosition',
title: nls.localize({ key: 'miMoveSidebarLeftRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left/Right")
},
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '6_workbench_layout',
command: {
id: 'workbench.action.toggleStatusbarVisibility',
title: nls.localize({ key: 'miToggleStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Status Bar")
},
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '6_workbench_layout',
command: {
id: 'workbench.action.toggleActivityBarVisibility',
title: nls.localize({ key: 'miToggleActivityBar', comment: ['&& denotes a mnemonic'] }, "Toggle &&Activity Bar")
},
order: 4
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '6_workbench_layout',
command: {
id: 'workbench.action.togglePanel',
title: nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel")
},
order: 5
});
// Toggle Editor Settings
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '8_editor',
group: '5_editor',
command: {
id: 'workbench.action.toggleWordWrap',
title: nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap")
@ -680,7 +605,7 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '8_editor',
group: '5_editor',
command: {
id: 'workbench.action.toggleMinimap',
title: nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap")
@ -689,7 +614,7 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '8_editor',
group: '5_editor',
command: {
id: 'workbench.action.toggleRenderWhitespace',
title: nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace")
@ -698,17 +623,100 @@ function viewMenuRegistration() {
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '8_editor',
group: '5_editor',
command: {
id: 'workbench.action.toggleRenderControlCharacters',
title: nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters")
},
order: 4
});
}
function appearanceMenuRegistration() {
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '1_toggle_view',
command: {
id: 'workbench.action.toggleFullScreen',
title: nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '1_toggle_view',
command: {
id: 'workbench.action.toggleZenMode',
title: nls.localize('miToggleZenMode', "Toggle Zen Mode")
},
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '1_toggle_view',
command: {
id: 'workbench.action.toggleCenteredLayout',
title: nls.localize('miToggleCenteredLayout', "Toggle Centered Layout")
},
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '1_toggle_view',
command: {
id: 'workbench.action.toggleMenuBar',
title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar")
},
order: 4
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',
command: {
id: 'workbench.action.toggleSidebarVisibility',
title: nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',
command: {
id: 'workbench.action.toggleSidebarPosition',
title: nls.localize({ key: 'miMoveSidebarLeftRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left/Right")
},
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',
command: {
id: 'workbench.action.toggleStatusbarVisibility',
title: nls.localize({ key: 'miToggleStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Status Bar")
},
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',
command: {
id: 'workbench.action.toggleActivityBarVisibility',
title: nls.localize({ key: 'miToggleActivityBar', comment: ['&& denotes a mnemonic'] }, "Toggle &&Activity Bar")
},
order: 4
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',
command: {
id: 'workbench.action.togglePanel',
title: nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel")
},
order: 5
});
// Zoom
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '9_zoom',
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '3_zoom',
command: {
id: 'workbench.action.zoomIn',
title: nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In")
@ -716,8 +724,8 @@ function viewMenuRegistration() {
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '9_zoom',
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '3_zoom',
command: {
id: 'workbench.action.zoomOut',
title: nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "&&Zoom Out")
@ -725,8 +733,8 @@ function viewMenuRegistration() {
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '9_zoom',
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '3_zoom',
command: {
id: 'workbench.action.zoomReset',
title: nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom")

View file

@ -43,6 +43,13 @@ interface CustomMenu {
actions?: IAction[];
}
enum MenubarState {
HIDDEN,
VISIBLE,
FOCUSED,
OPEN
}
export class MenubarPart extends Part {
private keys = [
@ -84,19 +91,22 @@ export class MenubarPart extends Part {
private focusedMenu: {
index: number;
holder: Builder;
widget: Menu;
holder?: Builder;
widget?: Menu;
};
private customMenus: CustomMenu[];
private menuUpdater: RunOnceScheduler;
private actionRunner: IActionRunner;
private focusToReturn: Builder;
private container: Builder;
private recentlyOpened: IRecentlyOpened;
private updatePending: boolean;
private focusingWithAlt: boolean;
private _modifierKeyStatus: IModifierKeyStatus;
private _isFocused: boolean;
private _focusState: MenubarState;
private _onVisibilityChange: Emitter<Dimension>;
private initialSizing: {
@ -143,9 +153,7 @@ export class MenubarPart extends Part {
this.actionRunner = this._register(new ActionRunner());
this._register(this.actionRunner.onDidBeforeRun(() => {
if (this.focusedMenu && this.focusedMenu.holder) {
this.focusedMenu.holder.hide();
}
this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE;
}));
this._onVisibilityChange = this._register(new Emitter<Dimension>());
@ -157,7 +165,7 @@ export class MenubarPart extends Part {
this.doSetupMenubar();
}
this.isFocused = false;
this._focusState = MenubarState.HIDDEN;
this.windowService.getRecentlyOpened().then((recentlyOpened) => {
this.recentlyOpened = recentlyOpened;
@ -213,12 +221,12 @@ export class MenubarPart extends Part {
return this.configurationService.getValue<string>('window.titleBarStyle');
}
private get isFocused(): boolean {
return this._isFocused;
private get focusState(): MenubarState {
return this._focusState;
}
private set isFocused(value: boolean) {
if (this._isFocused && !value) {
private set focusState(value: MenubarState) {
if (this._focusState >= MenubarState.FOCUSED && value < MenubarState.FOCUSED) {
// Losing focus, update the menu if needed
if (this.updatePending) {
@ -227,13 +235,91 @@ export class MenubarPart extends Part {
}
}
this._isFocused = value;
if (!this._isFocused && this.currentMenubarVisibility === 'toggle') {
if (this.container) {
this.hideMenubar();
}
if (value === this._focusState) {
return;
}
switch (value) {
case MenubarState.HIDDEN:
if (this.isVisible) {
this.hideMenubar();
}
if (this.isOpen) {
this.cleanupCustomMenu();
}
if (this.isFocused) {
this.focusedMenu = null;
if (this.focusToReturn) {
this.focusToReturn.domFocus();
this.focusToReturn = null;
}
}
break;
case MenubarState.VISIBLE:
if (!this.isVisible) {
this.showMenubar();
}
if (this.isOpen) {
this.cleanupCustomMenu();
}
if (this.isFocused) {
if (this.focusedMenu) {
this.customMenus[this.focusedMenu.index].buttonElement.domBlur();
}
this.focusedMenu = null;
if (this.focusToReturn) {
this.focusToReturn.domFocus();
this.focusToReturn = null;
}
}
break;
case MenubarState.FOCUSED:
if (!this.isVisible) {
this.showMenubar();
}
if (this.isOpen) {
this.cleanupCustomMenu();
}
if (this.focusedMenu) {
this.customMenus[this.focusedMenu.index].buttonElement.domFocus();
}
break;
case MenubarState.OPEN:
if (!this.isVisible) {
this.showMenubar();
}
if (this.focusedMenu) {
this.showCustomMenu(this.focusedMenu.index);
}
break;
}
this._focusState = value;
}
private get isVisible(): boolean {
return this.focusState >= MenubarState.VISIBLE;
}
private get isFocused(): boolean {
return this.focusState >= MenubarState.FOCUSED;
}
private get isOpen(): boolean {
return this.focusState >= MenubarState.OPEN;
}
private onDidChangeFullscreen(): void {
@ -256,26 +342,42 @@ export class MenubarPart extends Part {
this.container.style('visibility', null);
}
private onModifierKeyToggled(modiferKeyStatus: IModifierKeyStatus): void {
private onModifierKeyToggled(modifierKeyStatus: IModifierKeyStatus): void {
const altKeyPressed = (!this._modifierKeyStatus || !this._modifierKeyStatus.altKey) && modifierKeyStatus.altKey;
const altKeyAlone = altKeyPressed && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey;
const allModifiersReleased = !modifierKeyStatus.altKey && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey;
if (this.currentMenubarVisibility === 'toggle') {
const altKeyPressed = (!this._modifierKeyStatus || !this._modifierKeyStatus.altKey) && modiferKeyStatus.altKey;
if (altKeyPressed && !modiferKeyStatus.ctrlKey && !modiferKeyStatus.shiftKey) {
this.showMenubar();
} else if (!this.isFocused) {
this.hideMenubar();
if (altKeyAlone) {
if (!this.isVisible) {
this.focusState = MenubarState.VISIBLE;
}
} else if (!allModifiersReleased && !this.isFocused) {
this.focusState = MenubarState.HIDDEN;
}
}
this._modifierKeyStatus = modiferKeyStatus;
if (allModifiersReleased && this.focusingWithAlt) {
if (!this.isFocused) {
this.focusedMenu = { index: 0 };
this.focusState = MenubarState.FOCUSED;
} else if (!this.isOpen) {
this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE;
}
}
this._modifierKeyStatus = modifierKeyStatus;
if (this.currentEnableMenuBarMnemonics && this.customMenus) {
this.customMenus.forEach(customMenu => {
let child = customMenu.titleElement.child();
if (child) {
child.style('text-decoration', modiferKeyStatus.altKey ? 'underline' : null);
child.style('text-decoration', modifierKeyStatus.altKey ? 'underline' : null);
}
});
}
this.focusingWithAlt = altKeyAlone;
}
private onRecentlyOpenedChange(): void {
@ -286,8 +388,6 @@ export class MenubarPart extends Part {
}
private registerListeners(): void {
this._register(browser.onDidChangeFullscreen(() => this.onDidChangeFullscreen()));
// Update when config changes
this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)));
@ -303,7 +403,14 @@ export class MenubarPart extends Part {
// Listen to keybindings change
this._register(this.keybindingService.onDidUpdateKeybindings(() => this.setupMenubar()));
this._register(ModifierKeyEmitter.getInstance().event(this.onModifierKeyToggled, this));
// These listeners only apply when the custom menubar is being used
if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') {
// Listen to fullscreen changes
this._register(browser.onDidChangeFullscreen(() => this.onDidChangeFullscreen()));
// Listen for alt key presses
this._register(ModifierKeyEmitter.getInstance().event(this.onModifierKeyToggled, this));
}
}
private doSetupMenubar(): void {
@ -473,7 +580,7 @@ export class MenubarPart extends Part {
// Create the top level menu button element
if (firstTimeSetup) {
const buttonElement = $(this.container).div({ class: 'menubar-menu-button' }).attr('role', 'menu');
const buttonElement = $(this.container).div({ class: 'menubar-menu-button' }).attr({ 'role': 'menu', 'tabindex': 0 });
buttonElement.attr('aria-label', this.topLevelTitles[menuTitle].replace(/&&(.)/g, '$1'));
const titleElement = $(buttonElement).div({ class: 'menubar-menu-title', 'aria-hidden': true });
@ -533,30 +640,56 @@ export class MenubarPart extends Part {
updateActions(menu, this.customMenus[menuIndex].actions);
if (firstTimeSetup) {
this.customMenus[menuIndex].buttonElement.on(EventType.KEY_UP, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
let eventHandled = true;
if ((event.equals(KeyCode.DownArrow) || event.equals(KeyCode.Enter)) && !this.isOpen) {
this.focusedMenu = { index: menuIndex };
this.focusState = MenubarState.OPEN;
} else {
eventHandled = false;
}
if (eventHandled) {
event.preventDefault();
event.stopPropagation();
}
});
this.customMenus[menuIndex].buttonElement.on(EventType.CLICK, () => {
if (this._modifierKeyStatus && (this._modifierKeyStatus.shiftKey || this._modifierKeyStatus.ctrlKey)) {
return; // supress keyboard shortcuts that shouldn't conflict
}
this.toggleCustomMenu(menuIndex);
this.isFocused = !this.isFocused;
if (this.isOpen) {
if (this.isCurrentMenu(menuIndex)) {
this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE;
} else {
this.cleanupCustomMenu();
this.showCustomMenu(menuIndex);
}
} else {
this.focusedMenu = { index: menuIndex };
this.focusState = MenubarState.OPEN;
}
});
this.customMenus[menuIndex].buttonElement.on(EventType.MOUSE_ENTER, () => {
if (this.isFocused && !this.isCurrentMenu(menuIndex)) {
this.toggleCustomMenu(menuIndex);
if (this.isOpen && !this.isCurrentMenu(menuIndex)) {
this.customMenus[menuIndex].buttonElement.domFocus();
this.cleanupCustomMenu();
this.showCustomMenu(menuIndex);
} else if (this.isFocused && !this.isOpen) {
this.customMenus[menuIndex].buttonElement.domFocus();
}
});
this.customMenus[menuIndex].buttonElement.on(EventType.MOUSE_LEAVE, () => {
if (!this.isFocused) {
this.cleanupCustomMenu();
if (!this.isOpen && this.isFocused) {
this.customMenus[menuIndex].buttonElement.domBlur();
}
});
this.customMenus[menuIndex].buttonElement.on(EventType.BLUR, () => {
this.cleanupCustomMenu();
});
}
}
@ -569,6 +702,8 @@ export class MenubarPart extends Part {
this.focusPrevious();
} else if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Tab)) {
this.focusNext();
} else if (event.equals(KeyCode.Escape) && this.isFocused && !this.isOpen) {
this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE;
} else {
eventHandled = false;
}
@ -579,9 +714,31 @@ export class MenubarPart extends Part {
}
});
}
this.container.on(EventType.FOCUS_IN, (e) => {
let event = e as FocusEvent;
if (event.relatedTarget) {
if (!this.container.getHTMLElement().contains(event.relatedTarget as HTMLElement)) {
this.focusToReturn = $(event.relatedTarget as HTMLElement);
}
}
});
this.container.on(EventType.FOCUS_OUT, (e) => {
let event = e as FocusEvent;
if (event.relatedTarget) {
if (!this.container.getHTMLElement().contains(event.relatedTarget as HTMLElement)) {
this.focusToReturn = null;
this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE;
}
}
});
}
private focusPrevious(): void {
if (!this.focusedMenu) {
return;
}
@ -592,7 +749,13 @@ export class MenubarPart extends Part {
return;
}
this.toggleCustomMenu(newFocusedIndex);
if (this.isOpen) {
this.cleanupCustomMenu();
this.showCustomMenu(newFocusedIndex);
} else if (this.isFocused) {
this.focusedMenu.index = newFocusedIndex;
this.customMenus[newFocusedIndex].buttonElement.domFocus();
}
}
private focusNext(): void {
@ -606,7 +769,13 @@ export class MenubarPart extends Part {
return;
}
this.toggleCustomMenu(newFocusedIndex);
if (this.isOpen) {
this.cleanupCustomMenu();
this.showCustomMenu(newFocusedIndex);
} else if (this.isFocused) {
this.focusedMenu.index = newFocusedIndex;
this.customMenus[newFocusedIndex].buttonElement.domFocus();
}
}
private getMenubarMenus(): IMenubarData {
@ -665,32 +834,14 @@ export class MenubarPart extends Part {
if (this.focusedMenu.widget) {
this.focusedMenu.widget.dispose();
}
this.focusedMenu = { index: this.focusedMenu.index };
}
this.focusedMenu = null;
}
public focusCustomMenu(menuTitle: string): void {
this.toggleCustomMenu(0);
}
private toggleCustomMenu(menuIndex: number): void {
private showCustomMenu(menuIndex: number): void {
const customMenu = this.customMenus[menuIndex];
if (this.focusedMenu) {
let hiding: boolean = this.isCurrentMenu(menuIndex);
// Need to cleanup currently displayed menu
this.cleanupCustomMenu();
// Hiding this menu
if (hiding) {
return;
}
}
customMenu.buttonElement.domFocus();
let menuHolder = $(customMenu.buttonElement).div({ class: 'menubar-menu-items-holder' });
$(menuHolder.getHTMLElement().parentElement).addClass('open');
@ -711,14 +862,12 @@ export class MenubarPart extends Part {
let menuWidget = this._register(new Menu(menuHolder.getHTMLElement(), customMenu.actions, menuOptions));
this._register(menuWidget.onDidCancel(() => {
this.cleanupCustomMenu();
this.isFocused = false;
this.focusState = MenubarState.FOCUSED;
}));
this._register(menuWidget.onDidBlur(() => {
setTimeout(() => {
this.cleanupCustomMenu();
this.isFocused = false;
}, 100);
}));

View file

@ -5,7 +5,7 @@
'use strict';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { clearNode, addClass, removeClass, toggleClass, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import URI from 'vs/base/common/uri';
@ -26,7 +26,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { Severity } from 'vs/platform/notification/common/notification';
export class NotificationsListDelegate implements IDelegate<INotificationViewItem> {
export class NotificationsListDelegate implements IVirtualDelegate<INotificationViewItem> {
private static readonly ROW_HEIGHT = 42;
private static readonly LINE_HEIGHT = 22;
@ -278,6 +278,10 @@ export class NotificationRenderer implements IRenderer<INotificationViewItem, IN
data.renderer.setInput(notification);
}
disposeElement(): void {
// noop
}
disposeTemplate(templateData: INotificationTemplateData): void {
templateData.toDispose = dispose(templateData.toDispose);
}

View file

@ -6,7 +6,7 @@
'use strict';
import 'vs/css!./quickInput';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import * as dom from 'vs/base/browser/dom';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
@ -127,13 +127,17 @@ class ListElementRenderer implements IRenderer<ListElement, IListElementTemplate
data.detail.set(element.item.detail, detailHighlights);
}
disposeElement(): void {
// noop
}
disposeTemplate(data: IListElementTemplateData): void {
data.toDisposeElement = dispose(data.toDisposeElement);
data.toDisposeTemplate = dispose(data.toDisposeTemplate);
}
}
class ListElementDelegate implements IDelegate<ListElement> {
class ListElementDelegate implements IVirtualDelegate<ListElement> {
getHeight(element: ListElement): number {
return element.item.detail ? 44 : 22;

View file

@ -12,7 +12,7 @@
visibility: hidden !important;
}
.monaco-workbench > .sidebar > .title > .title-label span {
.monaco-workbench > .sidebar > .title > .title-label h2 {
text-transform: uppercase;
}

View file

@ -3,8 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-panel-view .panel > .panel-header > .title {
.monaco-panel-view .panel > .panel-header h3.title {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: 11px;
-webkit-margin-before: 0;
-webkit-margin-after: 0;
display: flex;
}

View file

@ -100,7 +100,7 @@ export abstract class ViewletPanel extends Panel implements IView {
protected renderHeader(container: HTMLElement): void {
this.headerContainer = container;
this.renderHeaderTitle(container);
this.renderHeaderTitle(container, this.title);
const actions = append(container, $('.actions'));
this.toolbar = new ToolBar(actions, this.contextMenuService, {
@ -119,8 +119,8 @@ export abstract class ViewletPanel extends Panel implements IView {
this.updateActionsVisibility();
}
protected renderHeaderTitle(container: HTMLElement): void {
append(container, $('.title', null, this.title));
protected renderHeaderTitle(container: HTMLElement, title: string): void {
append(container, $('h3.title', null, title));
}
focus(): void {

View file

@ -15,11 +15,11 @@
/* Font Families (with CJK support) */
.monaco-shell { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; }
.monaco-shell:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; }
.monaco-shell:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; }
.monaco-shell:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Meiryo", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; }
.monaco-shell:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
.monaco-shell { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; }
.monaco-shell:lang(zh-Hans) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; }
.monaco-shell:lang(zh-Hant) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; }
.monaco-shell:lang(ja) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Meiryo", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; }
.monaco-shell:lang(ko) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

View file

@ -3,10 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench-container {
position: absolute;
}
.monaco-workbench {
font-size: 13px;
line-height: 1.4em;

View file

@ -16,7 +16,6 @@ import { toErrorMessage } from 'vs/base/common/errorMessage';
import product from 'vs/platform/node/product';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import pkg from 'vs/platform/node/package';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
import { Workbench, IWorkbenchStartedInfo } from 'vs/workbench/electron-browser/workbench';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService, configurationTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
@ -46,7 +45,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { ILifecycleService, LifecyclePhase, ShutdownReason, StartupKind } from 'vs/platform/lifecycle/common/lifecycle';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@ -93,7 +91,7 @@ import { NotificationService } from 'vs/workbench/services/notification/common/n
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { DialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService';
import { DialogChannel } from 'vs/platform/dialogs/common/dialogIpc';
import { EventType, addDisposableListener, addClass, getClientArea } from 'vs/base/browser/dom';
import { EventType, addDisposableListener, addClass } from 'vs/base/browser/dom';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { OpenerService } from 'vs/editor/browser/services/openerService';
import { SearchHistoryService } from 'vs/workbench/services/search/node/searchHistoryService';
@ -120,7 +118,6 @@ export class WorkbenchShell extends Disposable {
private storageService: IStorageService;
private environmentService: IEnvironmentService;
private logService: ILogService;
private contextViewService: ContextViewService;
private configurationService: IConfigurationService;
private contextService: IWorkspaceContextService;
private telemetryService: ITelemetryService;
@ -135,8 +132,6 @@ export class WorkbenchShell extends Disposable {
private container: HTMLElement;
private previousErrorValue: string;
private previousErrorTime: number;
private content: HTMLElement;
private contentsContainer: HTMLElement;
private configuration: IWindowConfiguration;
private workbench: Workbench;
@ -160,20 +155,15 @@ export class WorkbenchShell extends Disposable {
this.previousErrorTime = 0;
}
private createContents(parent: HTMLElement): HTMLElement {
private renderContents(): void {
// ARIA
aria.setARIAContainer(document.body);
// Workbench Container
const workbenchContainer = document.createElement('div');
parent.appendChild(workbenchContainer);
// Instantiation service with services
const [instantiationService, serviceCollection] = this.initServiceCollection(parent);
const [instantiationService, serviceCollection] = this.initServiceCollection(this.container);
// Workbench
this.workbench = this.createWorkbench(instantiationService, serviceCollection, parent, workbenchContainer);
this.workbench = this.createWorkbench(instantiationService, serviceCollection, this.container);
// Window
this.workbench.getInstantiationService().createInstance(ElectronWindow);
@ -186,13 +176,11 @@ export class WorkbenchShell extends Disposable {
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
clearTimeout(timeoutHandle);
});
return workbenchContainer;
}
private createWorkbench(instantiationService: IInstantiationService, serviceCollection: ServiceCollection, parent: HTMLElement, workbenchContainer: HTMLElement): Workbench {
private createWorkbench(instantiationService: IInstantiationService, serviceCollection: ServiceCollection, container: HTMLElement): Workbench {
try {
const workbench = instantiationService.createInstance(Workbench, parent, workbenchContainer, this.configuration, serviceCollection, this.lifecycleService, this.mainProcessClient);
const workbench = instantiationService.createInstance(Workbench, container, this.configuration, serviceCollection, this.lifecycleService, this.mainProcessClient);
// Set lifecycle phase to `Restoring`
this.lifecycleService.phase = LifecyclePhase.Restoring;
@ -418,9 +406,6 @@ export class WorkbenchShell extends Disposable {
serviceCollection.set(ICommandService, new SyncDescriptor(CommandService));
this.contextViewService = instantiationService.createInstance(ContextViewService, this.container);
serviceCollection.set(IContextViewService, this.contextViewService);
serviceCollection.set(IMarkerService, new SyncDescriptor(MarkerService));
serviceCollection.set(IModeService, new SyncDescriptor(WorkbenchModeServiceImpl));
@ -472,13 +457,8 @@ export class WorkbenchShell extends Disposable {
// Shell Class for CSS Scoping
addClass(this.container, 'monaco-shell');
// Controls
this.content = document.createElement('div');
addClass(this.content, 'monaco-shell-content');
this.container.appendChild(this.content);
// Create Contents
this.contentsContainer = this.createContents(this.content);
this.renderContents();
// Layout
this.layout();
@ -521,12 +501,6 @@ export class WorkbenchShell extends Disposable {
}
private layout(): void {
const clientArea = getClientArea(this.container);
this.contentsContainer.style.width = `${clientArea.width}px`;
this.contentsContainer.style.height = `${clientArea.height}px`;
this.contextViewService.layout();
this.workbench.layout();
}

View file

@ -191,7 +191,6 @@ export class Workbench extends Disposable implements IPartService {
_serviceBrand: any;
private workbenchParams: WorkbenchParams;
private workbenchContainer: Builder;
private workbench: Builder;
private workbenchStarted: boolean;
private workbenchCreated: boolean;
@ -200,6 +199,7 @@ export class Workbench extends Disposable implements IPartService {
private editorService: EditorService;
private editorGroupService: IEditorGroupsService;
private viewletService: IViewletService;
private contextViewService: ContextViewService;
private contextKeyService: IContextKeyService;
private keybindingService: IKeybindingService;
private backupFileService: IBackupFileService;
@ -236,7 +236,6 @@ export class Workbench extends Disposable implements IPartService {
private closeEmptyWindowScheduler: RunOnceScheduler = this._register(new RunOnceScheduler(() => this.onAllEditorsClosed(), 50));
constructor(
private parent: HTMLElement,
private container: HTMLElement,
private configuration: IWindowConfiguration,
serviceCollection: ServiceCollection,
@ -250,7 +249,6 @@ export class Workbench extends Disposable implements IPartService {
@IEnvironmentService private environmentService: IEnvironmentService,
@IWindowService private windowService: IWindowService,
@INotificationService private notificationService: NotificationService,
@IContextViewService private contextViewService: ContextViewService,
@ITelemetryService private telemetryService: TelemetryService
) {
super();
@ -300,11 +298,10 @@ export class Workbench extends Disposable implements IPartService {
}
private createWorkbench(): void {
this.workbenchContainer = $('.monaco-workbench-container');
this.workbench = $().div({
'class': `monaco-workbench ${isWindows ? 'windows' : isLinux ? 'linux' : 'mac'}`,
id: Identifiers.WORKBENCH_CONTAINER
}).appendTo(this.workbenchContainer);
});
}
private createGlobalActions(): void {
@ -355,6 +352,10 @@ export class Workbench extends Disposable implements IPartService {
// List
serviceCollection.set(IListService, this.instantiationService.createInstance(ListService));
// Context view service
this.contextViewService = this.instantiationService.createInstance(ContextViewService, this.workbench.getHTMLElement());
serviceCollection.set(IContextViewService, this.contextViewService);
// Use themable context menus when custom titlebar is enabled to match custom menubar
if (!isMacintosh && this.getCustomTitleBarStyle() === 'custom') {
serviceCollection.set(IContextMenuService, new SyncDescriptor(HTMLContextMenuService, null, this.telemetryService, this.notificationService, this.contextViewService));
@ -975,12 +976,6 @@ export class Workbench extends Disposable implements IPartService {
// Apply font aliasing
this.setFontAliasing(this.fontAliasing);
// Apply title style if shown
const titleStyle = this.getCustomTitleBarStyle();
if (titleStyle) {
DOM.addClass(this.parent, `titlebar-style-${titleStyle}`);
}
// Apply fullscreen state
if (browser.isFullscreen()) {
this.workbench.addClass('fullscreen');
@ -999,7 +994,7 @@ export class Workbench extends Disposable implements IPartService {
this.createNotificationsHandlers();
// Add Workbench to DOM
this.workbenchContainer.build(this.container);
this.workbench.appendTo(this.container);
}
private createTitlebarPart(): void {
@ -1292,6 +1287,8 @@ export class Workbench extends Disposable implements IPartService {
}
layout(options?: ILayoutOptions): void {
this.contextViewService.layout();
if (this.workbenchStarted && !this.workbenchShutdown) {
this.workbenchLayout.layout(options);
}

View file

@ -55,6 +55,7 @@
.monaco-editor .review-widget .body .review-comment .review-comment-contents {
margin-left: 20px;
user-select: text;
}
.monaco-editor .review-widget .body pre {

View file

@ -23,7 +23,7 @@ import { basename } from 'vs/base/common/paths';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { TPromise } from 'vs/base/common/winjs.base';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IDelegate, IListContextMenuEvent, IRenderer } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IListContextMenuEvent, IRenderer } from 'vs/base/browser/ui/list/list';
import { IEditor } from 'vs/workbench/common/editor';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@ -221,7 +221,7 @@ export class BreakpointsView extends ViewletPanel {
}
}
class BreakpointsDelegate implements IDelegate<IEnablement> {
class BreakpointsDelegate implements IVirtualDelegate<IEnablement> {
constructor(private debugService: IDebugService) {
// noop
@ -338,6 +338,10 @@ class BreakpointsRenderer implements IRenderer<IBreakpoint, IBreakpointTemplateD
}
}
disposeElement(): void {
// noop
}
disposeTemplate(templateData: IBreakpointTemplateData): void {
dispose(templateData.toDispose);
}
@ -383,6 +387,10 @@ class ExceptionBreakpointsRenderer implements IRenderer<IExceptionBreakpoint, IB
data.checkbox.checked = exceptionBreakpoint.enabled;
}
disposeElement(): void {
// noop
}
disposeTemplate(templateData: IBaseBreakpointTemplateData): void {
dispose(templateData.toDispose);
}
@ -440,6 +448,10 @@ class FunctionBreakpointsRenderer implements IRenderer<FunctionBreakpoint, IBase
}
}
disposeElement(): void {
// noop
}
disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void {
dispose(templateData.toDispose);
}
@ -511,6 +523,10 @@ class FunctionBreakpointInputRenderer implements IRenderer<IFunctionBreakpoint,
data.inputBox.select();
}
disposeElement(): void {
// noop
}
disposeTemplate(templateData: IInputTemplateData): void {
dispose(templateData.toDispose);
}

View file

@ -88,10 +88,10 @@ export class CallStackView extends TreeViewsViewletPanel {
}
protected renderHeaderTitle(container: HTMLElement): void {
const title = dom.append(container, $('.title.debug-call-stack-title'));
const name = dom.append(title, $('span'));
name.textContent = this.options.title;
this.pauseMessage = dom.append(title, $('span.pause-message'));
let titleContainer = dom.append(container, $('.debug-call-stack-title'));
super.renderHeaderTitle(titleContainer, this.options.title);
this.pauseMessage = dom.append(titleContainer, $('span.pause-message'));
this.pauseMessage.hidden = true;
this.pauseMessageLabel = dom.append(this.pauseMessage, $('span.label'));
}

View file

@ -2321,7 +2321,12 @@ export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigure
}
return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId, shouldRecommend).then(() => {
this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.success', 'The extension was successfully added to this workspace folder\'s recommendations.'));
this.notificationService.prompt(Severity.Info,
localize('AddToWorkspaceFolderRecommendations.success', 'The extension was successfully added to this workspace folder\'s recommendations.'),
[{
label: localize('viewChanges', "View Changes"),
run: () => this.openExtensionsFile(configurationFile)
}]);
}, err => {
this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err));
});
@ -2333,7 +2338,12 @@ export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigure
}
return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId, shouldRecommend).then(() => {
this.notificationService.info(localize('AddToWorkspaceFolderIgnoredRecommendations.success', 'The extension was successfully added to this workspace folder\'s unwanted recommendations.'));
this.notificationService.prompt(Severity.Info,
localize('AddToWorkspaceFolderIgnoredRecommendations.success', 'The extension was successfully added to this workspace folder\'s unwanted recommendations.'),
[{
label: localize('viewChanges', "View Changes"),
run: () => this.openExtensionsFile(configurationFile)
}]);
}, err => {
this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err));
});
@ -2381,7 +2391,13 @@ export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecomm
}
return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId, shouldRecommend).then(() => {
this.notificationService.info(localize('AddToWorkspaceRecommendations.success', 'The extension was successfully added to this workspace\'s recommendations.'));
this.notificationService.prompt(Severity.Info,
localize('AddToWorkspaceRecommendations.success', 'The extension was successfully added to this workspace\'s recommendations.'),
[{
label: localize('viewChanges', "View Changes"),
run: () => this.openWorkspaceConfigurationFile(workspaceConfig)
}]);
}, err => {
this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err));
});
@ -2392,7 +2408,12 @@ export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecomm
}
return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId, shouldRecommend).then(() => {
this.notificationService.info(localize('AddToWorkspaceUnwantedRecommendations.success', 'The extension was successfully added to this workspace\'s unwanted recommendations.'));
this.notificationService.prompt(Severity.Info,
localize('AddToWorkspaceUnwantedRecommendations.success', 'The extension was successfully added to this workspace\'s unwanted recommendations.'),
[{
label: localize('viewChanges', "View Changes"),
run: () => this.openWorkspaceConfigurationFile(workspaceConfig)
}]);
}, err => {
this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err));
});

View file

@ -11,7 +11,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Action } from 'vs/base/common/actions';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDelegate } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { once } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
@ -38,7 +38,7 @@ export interface ITemplateData {
extensionDisposables: IDisposable[];
}
export class Delegate implements IDelegate<IExtension> {
export class Delegate implements IVirtualDelegate<IExtension> {
getHeight() { return 62; }
getTemplateId() { return 'extension'; }
}
@ -194,6 +194,10 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
});
}
disposeElement(): void {
// noop
}
private updateRecommendationStatus(extension: IExtension, data: ITemplateData) {
const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason();
let ariaLabel = extension.displayName + '. ';

View file

@ -71,9 +71,8 @@ export class ExtensionsListView extends ViewletPanel {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService);
}
renderHeader(container: HTMLElement): void {
const titleDiv = append(container, $('div.title'));
append(titleDiv, $('span')).textContent = this.options.title;
renderHeaderTitle(container: HTMLElement): void {
super.renderHeaderTitle(container, this.options.title);
this.badgeContainer = append(container, $('.count-badge-wrapper'));
this.badge = new CountBadge(this.badgeContainer);

View file

@ -6,4 +6,9 @@
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label.extensions {
-webkit-mask: url('extensions-dark.svg') no-repeat 50% 50%;
-webkit-mask-size: 21px;
}
.extensions .split-view-view .panel-header .count-badge-wrapper {
position: absolute;
right: 12px;
}

View file

@ -21,7 +21,7 @@ import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/parts/exte
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService, IExtensionDescription, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { append, $, addClass, toggleClass, Dimension } from 'vs/base/browser/dom';
import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@ -216,7 +216,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
const TEMPLATE_ID = 'runtimeExtensionElementTemplate';
const delegate = new class implements IDelegate<IRuntimeExtension>{
const delegate = new class implements IVirtualDelegate<IRuntimeExtension>{
getHeight(element: IRuntimeExtension): number {
return 62;
}
@ -369,6 +369,8 @@ export class RuntimeExtensionsEditor extends BaseEditor {
}
},
disposeElement: () => null,
disposeTemplate: (data: IRuntimeExtensionTemplateData): void => {
data.disposables = dispose(data.disposables);
}

View file

@ -436,6 +436,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: EditorContextKeys.focus.toNegated(),
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_C,
win: {
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C)
},
id: COPY_RELATIVE_PATH_COMMAND_ID,
handler: (accessor, resource: URI | object) => {
const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService));

View file

@ -251,12 +251,12 @@ configurationRegistry.registerConfiguration({
nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.onWindowChange' }, "A dirty file is automatically saved when the window loses focus.")
],
'default': AutoSaveConfiguration.OFF,
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Accepted values: '{0}', '{1}', '{2}' (editor loses focus), '{3}' (window loses focus). If set to '{4}', you can configure the delay in 'files.autoSaveDelay'.", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY)
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Accepted values: '{0}', '{1}', '{2}' (editor loses focus), '{3}' (window loses focus). If set to '{4}', you can configure the delay in [`files.autoSaveDelay`](#files.autoSaveDelay). Read more about autosave [here](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY)
},
'files.autoSaveDelay': {
'type': 'number',
'default': 1000,
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveDelay' }, "Controls the delay in ms after which a dirty file is saved automatically. Only applies when 'files.autoSave' is set to '{0}'", AutoSaveConfiguration.AFTER_DELAY)
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveDelay' }, "Controls the delay in ms after which a dirty file is saved automatically. Only applies when [`files.autoSave`](#files.autoSave) is set to '{0}'", AutoSaveConfiguration.AFTER_DELAY)
},
'files.watcherExclude': {
'type': 'object',

View file

@ -26,7 +26,7 @@ import { attachStylerCallback } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { IDelegate, IRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list';
import { EditorLabel } from 'vs/workbench/browser/labels';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { TPromise } from 'vs/base/common/winjs.base';
@ -176,8 +176,7 @@ export class OpenEditorsView extends ViewletPanel {
}
protected renderHeaderTitle(container: HTMLElement): void {
const title = dom.append(container, $('.title'));
dom.append(title, $('span', null, this.title));
super.renderHeaderTitle(container, this.title);
const count = dom.append(container, $('.count'));
this.dirtyCountElement = dom.append(count, $('.monaco-count-badge'));
@ -489,7 +488,7 @@ class OpenEditorActionRunner extends ActionRunner {
}
}
class OpenEditorsDelegate implements IDelegate<OpenEditor | IEditorGroup> {
class OpenEditorsDelegate implements IVirtualDelegate<OpenEditor | IEditorGroup> {
public static readonly ITEM_HEIGHT = 22;
@ -595,6 +594,10 @@ class EditorGroupRenderer implements IRenderer<IEditorGroup, IEditorGroupTemplat
templateData.actionBar.context = { groupId: editorGroup.id };
}
disposeElement(): void {
// noop
}
disposeTemplate(templateData: IEditorGroupTemplateData): void {
templateData.actionBar.dispose();
dispose(templateData.toDispose);
@ -691,6 +694,10 @@ class OpenEditorRenderer implements IRenderer<OpenEditor, IOpenEditorTemplateDat
});
}
disposeElement(): void {
// noop
}
disposeTemplate(templateData: IOpenEditorTemplateData): void {
templateData.actionBar.dispose();
templateData.root.dispose();

View file

@ -32,7 +32,7 @@ import {
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { IDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list';
import { IVirtualDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@ -614,7 +614,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
}
}
class Delegate implements IDelegate<IListEntry> {
class Delegate implements IVirtualDelegate<IListEntry> {
getHeight(element: IListEntry) {
if (element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) {
@ -667,6 +667,9 @@ class KeybindingHeaderRenderer implements IRenderer<IListEntry, any> {
renderElement(entry: IListEntry, index: number, template: any): void {
}
disposeElement(): void {
}
disposeTemplate(template: any): void {
}
}
@ -704,6 +707,8 @@ class KeybindingItemRenderer implements IRenderer<IKeybindingItemEntry, Keybindi
template.when.render(keybindingEntry);
}
disposeElement(): void { }
disposeTemplate(template: KeybindingItemTemplate): void {
template.actions.dispose();
}

View file

@ -9,6 +9,7 @@
.settings-editor {
padding-top: 11px;
padding-left: 2px;
max-width: 1100px;
margin: auto;
}
@ -196,11 +197,18 @@
opacity: 0.7;
margin-top: 3px;
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;
height: 18px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description * {
margin: 0px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description code {
line-height: 15px; /** For some reason, this is needed, otherwise <code> will take up 20px height */
}
.settings-editor > .settings-body > .settings-tree-container .setting-measure-container.monaco-tree-row {
position: absolute;
visibility: hidden;
@ -209,7 +217,6 @@
.settings-editor > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-description,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-measure-helper .setting-item-description {
height: initial;
white-space: pre-wrap;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-value-description {

View file

@ -33,7 +33,7 @@ import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbenc
import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout';
import { ISettingsEditorViewState, NonExpandableTree, resolveExtensionsSettings, resolveSettingsTree, SearchResultIdx, SearchResultModel, SettingsAccessibilityProvider, SettingsDataSource, SettingsRenderer, SettingsTreeController, SettingsTreeElement, SettingsTreeFilter, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTree';
import { TOCDataSource, TOCRenderer, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree';
import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_FIRST_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_TOC_ROW_FOCUS } from 'vs/workbench/parts/preferences/common/preferences';
import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_FIRST_ROW_FOCUS, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences';
import { IPreferencesService, ISearchResult, ISettingsEditorModel } from 'vs/workbench/services/preferences/common/preferences';
import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
import { DefaultSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
@ -234,6 +234,16 @@ export class SettingsEditor2 extends BaseEditor {
this._register(DOM.addDisposableListener(this.showConfiguredSettingsOnlyCheckbox, 'change', e => this.onShowConfiguredOnlyClicked()));
}
private revealSetting(settingName: string): void {
const element = this.settingsTreeModel.getElementByName(settingName);
if (element) {
this.settingsTree.setSelection([element]);
this.settingsTree.setFocus(element);
this.settingsTree.reveal(element, 0);
this.settingsTree.domFocus();
}
}
private openSettingsFile(): TPromise<IEditor> {
const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget;
@ -314,6 +324,7 @@ export class SettingsEditor2 extends BaseEditor {
const renderer = this.instantiationService.createInstance(SettingsRenderer, this.settingsTreeContainer);
this._register(renderer.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value)));
this._register(renderer.onDidOpenSettings(() => this.openSettingsFile()));
this._register(renderer.onDidClickSettingLink(settingName => this.revealSetting(settingName)));
const treeClass = 'settings-editor-tree';
this.settingsTree = this.instantiationService.createInstance(NonExpandableTree, this.settingsTreeContainer,
@ -693,7 +704,10 @@ export class SettingsEditor2 extends BaseEditor {
// Count unique results
const counts = {};
const filterResult = results[SearchResultIdx.Local];
counts['filterResult'] = filterResult.filterMatches.length;
if (filterResult) {
counts['filterResult'] = filterResult.filterMatches.length;
}
if (nlpResult) {
counts['nlpResult'] = nlpResult.filterMatches.length;
}

View file

@ -61,7 +61,7 @@ export const tocData: ITOCEntry = {
{
id: 'editor/suggestions',
label: localize('suggestions', "Suggestions"),
settings: ['editor.*suggestion*']
settings: ['editor.*suggest*']
},
{
id: 'editor/files',

View file

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { Button } from 'vs/base/browser/ui/button/button';
@ -12,11 +13,12 @@ import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
import * as arrays from 'vs/base/common/arrays';
import { Color } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import * as objects from 'vs/base/common/objects';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IAccessibilityProvider, IDataSource, IFilter, IRenderer, ITree } from 'vs/base/parts/tree/browser/tree';
@ -24,6 +26,7 @@ import { localize } from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { registerColor, selectBackground, selectBorder } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
@ -97,6 +100,7 @@ export interface ITOCEntry {
export class SettingsTreeModel {
private _root: SettingsTreeGroupElement;
private _treeElementsById = new Map<string, SettingsTreeElement>();
private _treeElementsBySettingName = new Map<string, SettingsTreeElement>();
constructor(
private _viewState: ISettingsEditorViewState,
@ -125,6 +129,10 @@ export class SettingsTreeModel {
return this._treeElementsById.get(id);
}
getElementByName(name: string): SettingsTreeElement {
return this._treeElementsBySettingName.get(name);
}
private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement {
const element = new SettingsTreeGroupElement();
element.id = tocEntry.id;
@ -153,6 +161,7 @@ export class SettingsTreeModel {
private createSettingsTreeSettingElement(setting: ISetting, parent: SettingsTreeGroupElement): SettingsTreeSettingElement {
const element = createSettingsTreeSettingElement(setting, parent, this._viewState.settingsTarget, this._configurationService);
this._treeElementsById.set(element.id, element);
this._treeElementsBySettingName.set(setting.key, element);
return element;
}
}
@ -444,12 +453,16 @@ export class SettingsRenderer implements IRenderer {
private readonly _onDidOpenSettings: Emitter<void> = new Emitter<void>();
public readonly onDidOpenSettings: Event<void> = this._onDidOpenSettings.event;
private readonly _onDidClickSettingLink: Emitter<string> = new Emitter<string>();
public readonly onDidClickSettingLink: Event<string> = this._onDidClickSettingLink.event;
private measureContainer: HTMLElement;
constructor(
_measureContainer: HTMLElement,
@IThemeService private themeService: IThemeService,
@IContextViewService private contextViewService: IContextViewService
@IContextViewService private contextViewService: IContextViewService,
@IOpenerService private readonly openerService: IOpenerService,
) {
this.measureContainer = DOM.append(_measureContainer, $('.setting-measure-container.monaco-tree-row'));
}
@ -677,7 +690,28 @@ export class SettingsRenderer implements IRenderer {
template.labelElement.textContent = element.displayLabel;
template.labelElement.title = titleTooltip;
template.descriptionElement.textContent = element.description;
const enumDescriptionText = element.setting.enumDescriptions ?
'\n' + element.setting.enumDescriptions
.map((desc, i) => ` - \`${element.setting.enum[i]}\`: ${desc}`)
.join('\n') :
'';
const descriptionText = element.description + enumDescriptionText;
const renderedDescription = renderMarkdown({ value: descriptionText }, {
actionHandler: {
callback: (content: string) => {
if (startsWith(content, '#')) {
this._onDidClickSettingLink.fire(content.substr(1));
} else {
this.openerService.open(URI.parse(content)).then(void 0, onUnexpectedError);
}
},
disposeables: template.toDispose
}
});
renderedDescription.classList.add('setting-item-description-markdown');
template.descriptionElement.innerHTML = '';
template.descriptionElement.appendChild(renderedDescription);
this.renderValue(element, isSelected, <ISettingItemTemplate>template);
@ -842,6 +876,21 @@ export class SettingsTreeController extends WorkbenchTreeController {
) {
super({}, configurationService);
}
protected onLeftClick(tree: ITree, element: any, eventish: IMouseEvent, origin?: string): boolean {
const isLink = eventish.target.tagName.toLowerCase() === 'a' ||
eventish.target.parentElement.tagName.toLowerCase() === 'a'; // <code> inside <a>
if (isLink && DOM.findParentWithClass(eventish.target, 'setting-item-description-markdown', tree.getHTMLElement())) {
return true;
}
// Without this, clicking on the setting description causes the tree to lose focus. I don't know why.
// The superclass does not always call it because of DND which is not used here.
eventish.preventDefault();
return super.onLeftClick(tree, element, eventish, origin);
}
}
export class SettingsAccessibilityProvider implements IAccessibilityProvider {

View file

@ -51,7 +51,7 @@
display: none;
}
.scm-viewlet .scm-provider > .name > .type {
.scm-viewlet .scm-provider > .type {
opacity: 0.7;
margin-left: 0.5em;
font-size: 0.9em;

Some files were not shown because too many files have changed in this diff Show more