diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index e3f3434460e..540e1b92c43 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -20,7 +20,7 @@ var remote = require('gulp-remote-src'); var File = require('vinyl'); var rimraf = require('rimraf'); var _ = require('underscore'); -var packagejson = require('../package.json'); +var packageJson = require('../package.json'); var util = require('./lib/util'); var buildfile = require('../src/buildfile'); var common = require('./gulpfile.common'); @@ -98,16 +98,22 @@ var product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8' var darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8')); var config = { - version: packagejson.electronVersion, + version: packageJson.electronVersion, productAppName: product.nameLong, - companyName: product.companyName, - copyright: product.copyright, - darwinIcon: product.icons.application.icns, + companyName: 'Microsoft Corporation', + copyright: 'Copyright (C) 2015 Microsoft. All rights reserved', + darwinIcon: 'resources/darwin/code.icns', darwinBundleIdentifier: product.darwinBundleIdentifier, - darwinApplicationCategoryType: product.darwinApplicationCategoryType, // Finder: View-->Arrange by Application Category - darwinBundleDocumentTypes: product.darwinBundleDocumentTypes, + darwinApplicationCategoryType: 'public.app-category.developer-tools', + darwinBundleDocumentTypes: [{ + name: product.nameLong + ' document', + role: 'Editor', + ostypes: ["TEXT", "utxt", "TUTX", "****"], + extensions: ["ascx", "asp", "aspx", "bash", "bash_login", "bash_logout", "bash_profile", "bashrc", "bat", "bowerrc", "c", "cc", "clj", "cljs", "cljx", "clojure", "cmd", "coffee", "config", "cpp", "cs", "cshtml", "csproj", "css", "csx", "ctp", "cxx", "dockerfile", "dot", "dtd", "editorconfig", "edn", "eyaml", "eyml", "fs", "fsi", "fsscript", "fsx", "gemspec", "gitattributes", "gitconfig", "gitignore", "go", "h", "handlebars", "hbs", "hh", "hpp", "htm", "html", "hxx", "ini", "jade", "jav", "java", "js", "jscsrc", "jshintrc", "jshtm", "json", "jsp", "less", "lua", "m", "makefile", "markdown", "md", "mdoc", "mdown", "mdtext", "mdtxt", "mdwn", "mkd", "mkdn", "ml", "mli", "nqp", "p6", "php", "phtml", "pl", "pl6", "pm", "pm6", "pod", "pp", "profile", "properties", "ps1", "psd1", "psgi", "psm1", "py", "r", "rb", "rhistory", "rprofile", "rs", "rt", "scss", "sh", "shtml", "sql", "svg", "svgz", "t", "ts", "txt", "vb", "wxi", "wxl", "wxs", "xaml", "xml", "yaml", "yml", "zsh"], + iconFile: 'resources/darwin/code_file.icns' + }], darwinCredits: darwinCreditsTemplate ? new Buffer(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : void 0, - winIcon: product.icons.application.ico, + winIcon: 'resources/win32/code.ico', token: process.env['GITHUB_TOKEN'] || void 0 }; @@ -175,12 +181,22 @@ function packageTask(platform, arch, opts) { pluginHostSourceMap ).pipe(util.handleAzureJson({ platform: platform })); - var packageJson = gulp.src(['package.json'], { base: '.' }).pipe(json({ name: product.nameShort })); + var version = packageJson.version; + var quality = product.quality; + + if (quality && quality !== 'stable') { + version += '-' + quality; + } + + var packageJsonStream = gulp.src(['package.json'], { base: '.' }).pipe(json({ + name: product.nameShort, + version: version + })); var license = gulp.src(['Credits_*', 'LICENSE.txt', 'ThirdPartyNotices.txt'], { base: '.' }); var api = gulp.src('src/vs/vscode.d.ts').pipe(rename('out/vs/vscode.d.ts')); - var depsSrc = _.flatten(Object.keys(packagejson.dependencies).concat(Object.keys(packagejson.optionalDependencies)) + var depsSrc = _.flatten(Object.keys(packageJson.dependencies).concat(Object.keys(packageJson.optionalDependencies)) .map(function (d) { return ['node_modules/' + d + '/**', '!node_modules/' + d + '/**/{test,tests}/**']; }) ); @@ -193,9 +209,9 @@ function packageTask(platform, arch, opts) { var resources = gulp.src('resources/*', { base: '.' }); if (platform === 'win32') { - resources = es.merge(resources, gulp.src(product.icons.file.ico, { base: '.' })); + resources = es.merge(resources, gulp.src('resources/win32/code_file.ico', { base: '.' })); } else if (platform === 'linux') { - resources = es.merge(resources, gulp.src(product.icons.application.png, { base: '.' })); + resources = es.merge(resources, gulp.src('resources/linux/code.png', { base: '.' })); } var extraExtensions = util.downloadExtensions(builtInExtensions) @@ -205,7 +221,7 @@ function packageTask(platform, arch, opts) { var all = es.merge( api, - packageJson, + packageJsonStream, mixinProduct(), license, sources, diff --git a/extensions/theme-colorful-defaults/package.json b/extensions/theme-colorful-defaults/package.json index a1263407bc7..60811c0ad69 100644 --- a/extensions/theme-colorful-defaults/package.json +++ b/extensions/theme-colorful-defaults/package.json @@ -3,7 +3,7 @@ "displayName": "Colorful Default Themes - Please provide feedback in issue 1849", "description": "The default VS Code Light and Dark themes with a touch of color. We are considering adding these to the default themes in the January release. Please provide feedback in issue 1849.", "categories": [ "Themes" ], - "version": "0.1.8", + "version": "0.1.10", "publisher": "aeschli", "engines": { "vscode": "*" }, "contributes": { diff --git a/extensions/theme-colorful-defaults/themes/dark_plus.tmTheme b/extensions/theme-colorful-defaults/themes/dark_plus.tmTheme index 6b5ad67f8d1..714103ac5dc 100644 --- a/extensions/theme-colorful-defaults/themes/dark_plus.tmTheme +++ b/extensions/theme-colorful-defaults/themes/dark_plus.tmTheme @@ -21,7 +21,12 @@ name Types declaration and references scope - meta.parameter.type, name.class, storage.type, meta.return.type, meta.object.type, return-type, meta.cast, new.storage.type.ts, cast.storage.type.ts, heritage.storage.type.ts, annotation.storage.type.ts, var.annotation.type.ts, field.storage.type.ts + meta.parameter.type, name.class, storage.type, meta.return.type, meta.object.type, return-type, meta.cast, + new.storage.type.ts, cast.storage.type.ts, heritage.storage.type.ts, annotation.storage.type.ts, var.annotation.type.ts, field.storage.type.ts, + new.storage.type.js, cast.storage.type.js, heritage.storage.type.js, annotation.storage.type.js, var.annotation.type.js, field.storage.type.js, + new.storage.type.tsx, cast.storage.type.tsx, heritage.storage.type.tsx, annotation.storage.type.tsx, var.annotation.type.tsx, field.storage.type.tsx, + new.storage.type.jsx, cast.storage.type.jsx, heritage.storage.type.jsx, annotation.storage.type.jsx, var.annotation.type.jsx, field.storage.type.jsx + settings foreground @@ -32,7 +37,7 @@ name TS storage type workaround scope - storage.type.ts + storage.type.ts, storage.type.js, storage.type.tsx, storage.type.jsx settings foreground diff --git a/extensions/theme-colorful-defaults/themes/light_plus.tmTheme b/extensions/theme-colorful-defaults/themes/light_plus.tmTheme index ce0d213665b..ada11dc4264 100644 --- a/extensions/theme-colorful-defaults/themes/light_plus.tmTheme +++ b/extensions/theme-colorful-defaults/themes/light_plus.tmTheme @@ -21,7 +21,12 @@ name Types declaration and references scope - meta.parameter.type, name.class, storage.type, meta.return.type, meta.object.type, return-type, meta.cast, new.storage.type.ts, cast.storage.type.ts, heritage.storage.type.ts, annotation.storage.type.ts, var.annotation.type.ts, field.storage.type.ts + meta.parameter.type, name.class, storage.type, meta.return.type, meta.object.type, return-type, meta.cast, + new.storage.type.ts, cast.storage.type.ts, heritage.storage.type.ts, annotation.storage.type.ts, var.annotation.type.ts, field.storage.type.ts, + new.storage.type.js, cast.storage.type.js, heritage.storage.type.js, annotation.storage.type.js, var.annotation.type.js, field.storage.type.js, + new.storage.type.tsx, cast.storage.type.tsx, heritage.storage.type.tsx, annotation.storage.type.tsx, var.annotation.type.tsx, field.storage.type.tsx, + new.storage.type.jsx, cast.storage.type.jsx, heritage.storage.type.jsx, annotation.storage.type.jsx, var.annotation.type.jsx, field.storage.type.jsx + settings foreground @@ -32,7 +37,7 @@ name TS storage type workaround scope - storage.type.ts + storage.type.ts, storage.type.js, storage.type.tsx, storage.type.jsx settings foreground diff --git a/extensions/typescript/src/features/completionItemProvider.ts b/extensions/typescript/src/features/completionItemProvider.ts index ad18a7d8ce8..10529e46db8 100644 --- a/extensions/typescript/src/features/completionItemProvider.ts +++ b/extensions/typescript/src/features/completionItemProvider.ts @@ -21,7 +21,7 @@ class MyCompletionItem extends CompletionItem { constructor(entry: CompletionEntry) { super(entry.name); - this.sortText = `${entry.name}-${entry.sortText}`; // tsserver sortText is "0" and "1" which is not good when comparing to snippets for instance + this.sortText = entry.sortText; this.kind = MyCompletionItem.convertKind(entry.kind); } diff --git a/extensions/typescript/src/typescriptServiceClient.ts b/extensions/typescript/src/typescriptServiceClient.ts index c6dec079cb7..4ea6128c4cd 100644 --- a/extensions/typescript/src/typescriptServiceClient.ts +++ b/extensions/typescript/src/typescriptServiceClient.ts @@ -300,7 +300,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient let p = this.callbacks[response.request_seq]; if (p) { if (TypeScriptServiceClient.Trace) { - console.log('TypeScript Service: request ' + response.command + '(' + response.request_seq + ') took ' + (Date.now() - p.start) + 'ms. Success: ' + response.success); + console.log('TypeScript Service: request ' + response.command + '(' + response.request_seq + ') took ' + (Date.now() - p.start) + 'ms. Success: ' + response.success + ((!response.success) ? ('. Message: ' + response.message) : '')); } delete this.callbacks[response.request_seq]; this.pendingResponses--; diff --git a/extensions/vscode-api-tests/src/commands.test.ts b/extensions/vscode-api-tests/src/commands.test.ts index b495ad9ab9d..52adb458da8 100644 --- a/extensions/vscode-api-tests/src/commands.test.ts +++ b/extensions/vscode-api-tests/src/commands.test.ts @@ -49,7 +49,7 @@ suite("commands namespace tests", () => { let virtualDocumentUri = Uri.parse('speciale://authority/path') - return commands.executeCommand('workbench.html.preview', virtualDocumentUri).then(success => { + return commands.executeCommand('vscode.previewHtml', virtualDocumentUri).then(success => { assert.ok(success); registration.dispose(); }); diff --git a/extensions/vscode-api-tests/src/window.test.ts b/extensions/vscode-api-tests/src/window.test.ts index faff4a89cfa..c9e8df260ab 100644 --- a/extensions/vscode-api-tests/src/window.test.ts +++ b/extensions/vscode-api-tests/src/window.test.ts @@ -6,7 +6,7 @@ 'use strict'; import * as assert from 'assert'; -import {workspace, window} from 'vscode'; +import {workspace, window, ViewColumn, TextEditor} from 'vscode'; import {join} from 'path'; import {cleanUp, pathEquals} from './utils'; @@ -14,7 +14,7 @@ suite("window namespace tests", () => { teardown(cleanUp); - test('active text editor', () => { + test('editor, active text editor', () => { return workspace.openTextDocument(join(workspace.rootPath, './far.js')).then(doc => { return window.showTextDocument(doc).then((editor) => { const active = window.activeTextEditor; @@ -23,4 +23,39 @@ suite("window namespace tests", () => { }); }); }); + + test('editor, assign and check view columns', () => { + + return workspace.openTextDocument(join(workspace.rootPath, './far.js')).then(doc => { + let p1 = window.showTextDocument(doc, ViewColumn.One).then(editor => { + assert.equal(editor.viewColumn, ViewColumn.One); + }); + let p2 = window.showTextDocument(doc, ViewColumn.Two).then(editor => { + assert.equal(editor.viewColumn, ViewColumn.Two); + }); + let p3 = window.showTextDocument(doc, ViewColumn.Three).then(editor => { + assert.equal(editor.viewColumn, ViewColumn.Three); + }); + return Promise.all([p1, p2, p3]); + }); + }); + + test('editor, onDidChangeTextEditorViewColumn', () => { + + let actualTextEditor: TextEditor; + let actualViewColumn: ViewColumn; + + let registration = window.onDidChangeTextEditorViewColumn(event => { + actualTextEditor = event.textEditor; + actualViewColumn = event.viewColumn; + }); + + return workspace.openTextDocument(join(workspace.rootPath, './far.js')).then(doc => { + return window.showTextDocument(doc, ViewColumn.One).then(editor => { + assert.ok(actualTextEditor === editor); + assert.ok(actualViewColumn === editor.viewColumn); + registration.dispose(); + }); + }); + }); }); \ No newline at end of file diff --git a/package.json b/package.json index 98320242a46..b44c8eb3041 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "ghooks": "^1.0.1", "glob": "^5.0.13", "gulp": "^3.8.9", - "gulp-atom-electron": "^1.1.0", + "gulp-atom-electron": "^1.2.0", "gulp-azure-storage": "^0.3.0", "gulp-bom": "^1.0.0", "gulp-buffer": "0.0.2", @@ -80,9 +80,9 @@ "rimraf": "^2.2.8", "sinon": "^1.17.2", "source-map": "^0.4.4", + "tslint": "^3.2.2", "tslint-microsoft-contrib": "^2.0.0", - "tslint": "^3.2.2", - "typescript": "^1.7.3", + "typescript": "^1.7.3", "uglify-js": "2.4.8", "underscore": "^1.8.2", "vinyl": "^0.4.5" diff --git a/product.json b/product.json index a5df0a91801..c963f157258 100644 --- a/product.json +++ b/product.json @@ -2,27 +2,6 @@ "nameShort": "Code [OSS Build]", "nameLong": "Code [OSS Build]", "win32MutexName": "vscodeoss", - "companyName": "Microsoft Corporation", - "copyright": "Copyright (C) 2015 Microsoft. All rights reserved", "licenseUrl": "https://github.com/Microsoft/vscode/blob/master/LICENSE.txt", - "darwinBundleIdentifier": "com.visualstudio.code.oss", - "darwinApplicationCategoryType": "public.app-category.developer-tools", - "darwinBundleDocumentTypes": [{ - "name": "Code OSS Build document", - "role": "Editor", - "ostypes": ["TEXT", "utxt", "TUTX", "****"], - "extensions": ["ascx", "asp", "aspx", "bash", "bash_login", "bash_logout", "bash_profile", "bashrc", "bat", "bowerrc", "c", "cc", "clj", "cljs", "cljx", "clojure", "cmd", "coffee", "config", "cpp", "cs", "cshtml", "csproj", "css", "csx", "ctp", "cxx", "dockerfile", "dot", "dtd", "editorconfig", "edn", "eyaml", "eyml", "fs", "fsi", "fsscript", "fsx", "gemspec", "gitattributes", "gitconfig", "gitignore", "go", "h", "handlebars", "hbs", "hh", "hpp", "htm", "html", "hxx", "ini", "jade", "jav", "java", "js", "jscsrc", "jshintrc", "jshtm", "json", "jsp", "less", "lua", "m", "makefile", "markdown", "md", "mdoc", "mdown", "mdtext", "mdtxt", "mdwn", "mkd", "mkdn", "ml", "mli", "nqp", "p6", "php", "phtml", "pl", "pl6", "pm", "pm6", "pod", "pp", "profile", "properties", "ps1", "psd1", "psgi", "psm1", "py", "r", "rb", "rhistory", "rprofile", "rs", "rt", "scss", "sh", "shtml", "sql", "svg", "svgz", "t", "ts", "txt", "vb", "wxi", "wxl", "wxs", "xaml", "xml", "yaml", "yml", "zsh"], - "iconFile": "resources/darwin/code_file.icns" - }], - "icons": { - "application": { - "png": "resources/linux/code.png", - "icns": "resources/darwin/code.icns", - "ico": "resources/win32/code.ico" - }, - "file": { - "icns": "resources/darwin/code_file.icns", - "ico": "resources/win32/code_file.ico" - } - } + "darwinBundleIdentifier": "com.visualstudio.code.oss" } \ No newline at end of file diff --git a/src/vs/base/browser/ui/actionbar/actionbar.css b/src/vs/base/browser/ui/actionbar/actionbar.css index 632a5bc8083..d70fbcc27e4 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.css +++ b/src/vs/base/browser/ui/actionbar/actionbar.css @@ -48,14 +48,6 @@ margin-right: 0.3em; } -.monaco-action-bar:focus { - outline: 0; -} - -.monaco-action-bar .action-label:focus { - outline: 0; -} - .monaco-action-bar .action-item.disabled .action-label, .monaco-action-bar .action-item.disabled .action-label:hover { opacity: 0.4; @@ -183,7 +175,6 @@ .monaco-workbench .action-bar-select { height: 20px; margin: 5px 4px; - outline: none; } .vs-dark.monaco-workbench .action-bar-select { diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index e423b844539..b7b4120c57d 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - 'use strict'; import 'vs/css!./actionbar'; @@ -11,7 +10,7 @@ import nls = require('vs/nls'); import lifecycle = require('vs/base/common/lifecycle'); import {Promise} from 'vs/base/common/winjs.base'; import {Builder, $} from 'vs/base/browser/builder'; -import actions = require('vs/base/common/actions'); +import {IAction, IActionRunner, Action, ActionRunner} from 'vs/base/common/actions'; import DOM = require('vs/base/browser/dom'); import {EventType as CommonEventType} from 'vs/base/common/events'; import types = require('vs/base/common/types'); @@ -21,7 +20,7 @@ import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent'; import {CommonKeybindings} from 'vs/base/common/keyCodes'; export interface IActionItem extends IEventEmitter { - actionRunner: actions.IActionRunner; + actionRunner: IActionRunner; setActionContext(context: any): void; render(element: HTMLElement): void; isEnabled(): boolean; @@ -33,21 +32,22 @@ export interface IActionItem extends IEventEmitter { export class BaseActionItem extends EventEmitter implements IActionItem { public builder: Builder; - private gesture: Gesture; - private _actionRunner: actions.IActionRunner; public _callOnDispose: Function[]; public _context: any; - public _action: actions.IAction; + public _action: IAction; - constructor(context: any, action: actions.IAction) { + private gesture: Gesture; + private _actionRunner: IActionRunner; + + constructor(context: any, action: IAction) { super(); this._callOnDispose = []; this._context = context || this; this._action = action; - if (action instanceof actions.Action) { - var l = (action).addBulkListener((events: IEmitterEvent[]) => { + if (action instanceof Action) { + let l = (action).addBulkListener((events: IEmitterEvent[]) => { if (!this.builder) { // we have not been rendered yet, so there @@ -58,20 +58,20 @@ export class BaseActionItem extends EventEmitter implements IActionItem { events.forEach((event: IEmitterEvent) => { switch (event.getType()) { - case actions.Action.ENABLED: + case Action.ENABLED: this._updateEnabled(); break; - case actions.Action.LABEL: + case Action.LABEL: this._updateLabel(); this._updateTooltip(); break; - case actions.Action.TOOLTIP: + case Action.TOOLTIP: this._updateTooltip(); break; - case actions.Action.CLASS: + case Action.CLASS: this._updateClass(); break; - case actions.Action.CHECKED: + case Action.CHECKED: this._updateChecked(); break; default: @@ -88,15 +88,15 @@ export class BaseActionItem extends EventEmitter implements IActionItem { return this._callOnDispose; } - public set actionRunner(actionRunner: actions.IActionRunner) { + public set actionRunner(actionRunner: IActionRunner) { this._actionRunner = actionRunner; } - public get actionRunner(): actions.IActionRunner { + public get actionRunner(): IActionRunner { return this._actionRunner; } - public getAction(): actions.IAction { + public getAction(): IAction { return this._action; } @@ -136,13 +136,12 @@ export class BaseActionItem extends EventEmitter implements IActionItem { public focus(): void { if (this.builder) { this.builder.domFocus(); - this.builder.addClass('focused'); } } public blur(): void { if (this.builder) { - this.builder.removeClass('focused'); + this.builder.domBlur(); } } @@ -187,8 +186,7 @@ export class BaseActionItem extends EventEmitter implements IActionItem { } } -export class Separator extends actions.Action { - +export class Separator extends Action { public static ID = 'actions.monaco.separator'; @@ -212,7 +210,7 @@ export class ActionItem extends BaseActionItem { private cssClass: string; private options: IActionItemOptions; - constructor(context: any, action: actions.IAction, options: IActionItemOptions = {}) { + constructor(context: any, action: IAction, options: IActionItemOptions = {}) { super(context, action); this.options = options; @@ -224,7 +222,7 @@ export class ActionItem extends BaseActionItem { public render(container: HTMLElement): void { super.render(container); - this.$e = $('a.action-label').attr('tabIndex', '-1').appendTo(this.builder); + this.$e = $('a.action-label').appendTo(this.builder); this.$e.attr({ role: 'menuitem' }); if (this.options.label && this.options.keybinding) { @@ -250,7 +248,7 @@ export class ActionItem extends BaseActionItem { } public _updateTooltip(): void { - var title: string = null; + let title: string = null; if (this.getAction().tooltip) { title = this.getAction().tooltip; @@ -288,9 +286,11 @@ export class ActionItem extends BaseActionItem { if (this.getAction().enabled) { this.builder.removeClass('disabled'); this.$e.removeClass('disabled'); + this.$e.attr({ tabindex: 0 }); } else { this.builder.addClass('disabled'); this.$e.addClass('disabled'); + this.$e.removeAttribute('tabindex'); } } @@ -307,24 +307,24 @@ export class ProgressItem extends BaseActionItem { public render(parent: HTMLElement): void { - var container = document.createElement('div'); + let container = document.createElement('div'); $(container).addClass('progress-item'); - var label = document.createElement('div'); + let label = document.createElement('div'); $(label).addClass('label'); label.textContent = this.getAction().label; label.title = this.getAction().label; super.render(label); - var progress = document.createElement('div'); + let progress = document.createElement('div'); progress.textContent = '\u2026'; $(progress).addClass('tag', 'progress'); - var done = document.createElement('div'); + let done = document.createElement('div'); done.textContent = '\u2713'; $(done).addClass('tag', 'done'); - var error = document.createElement('div'); + let error = document.createElement('div'); error.textContent = '!'; $(error).addClass('tag', 'error'); @@ -364,18 +364,17 @@ export enum ActionsOrientation { } export interface IActionItemProvider { - (action: actions.IAction): IActionItem; + (action: IAction): IActionItem; } export interface IActionBarOptions { orientation?: ActionsOrientation; context?: any; - disableTabIndex?: boolean; actionItemProvider?: IActionItemProvider; - actionRunner?: actions.IActionRunner; + actionRunner?: IActionRunner; } -var defaultOptions: IActionBarOptions = { +let defaultOptions: IActionBarOptions = { orientation: ActionsOrientation.HORIZONTAL, context: null }; @@ -384,7 +383,7 @@ export interface IActionOptions extends IActionItemOptions { index?: number; } -export class ActionBar extends EventEmitter implements actions.IActionRunner { +export class ActionBar extends EventEmitter implements IActionRunner { private static nlsActionBarAccessibleLabel = nls.localize('actionBarAccessibleLabel', "Action Bar"); @@ -393,12 +392,14 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { }; public options: IActionBarOptions; - private _actionRunner: actions.IActionRunner; + private _actionRunner: IActionRunner; private _context: any; // Items public items: IActionItem[]; + private focusedItem: number; + private focusTracker: DOM.IFocusTracker; // Elements public domNode: HTMLElement; @@ -416,7 +417,7 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { this._actionRunner = this.options.actionRunner; if (!this._actionRunner) { - this._actionRunner = new actions.ActionRunner(); + this._actionRunner = new ActionRunner(); this.toDispose.push(this._actionRunner); } @@ -428,15 +429,14 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { this.domNode = document.createElement('div'); this.domNode.className = 'monaco-action-bar'; - var isVertical = this.options.orientation === ActionsOrientation.VERTICAL; - + let isVertical = this.options.orientation === ActionsOrientation.VERTICAL; if (isVertical) { this.domNode.className += ' vertical'; } $(this.domNode).on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { - var event = new StandardKeyboardEvent(e); - var eventHandled = true; + let event = new StandardKeyboardEvent(e); + let eventHandled = true; if (event.equals(isVertical ? CommonKeybindings.UP_ARROW : CommonKeybindings.LEFT_ARROW)) { this.focusPrevious(); @@ -444,7 +444,7 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { this.focusNext(); } else if (event.equals(CommonKeybindings.ESCAPE)) { this.cancel(); - } else if (event.equals(CommonKeybindings.ENTER)) { + } else if (event.equals(CommonKeybindings.ENTER) || event.equals(CommonKeybindings.SPACE)) { // Nothing, just staying out of the else branch } else { eventHandled = false; @@ -463,22 +463,31 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { }); $(this.domNode).on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => { - var event = new StandardKeyboardEvent(e); + let event = new StandardKeyboardEvent(e); - if (event.equals(CommonKeybindings.ENTER)) { + // Run action on Enter/Space + if (event.equals(CommonKeybindings.ENTER) || event.equals(CommonKeybindings.SPACE)) { this.doTrigger(event); event.preventDefault(); event.stopPropagation(); } - }); - var focusTracker = DOM.trackFocus(this.domNode); - focusTracker.addBlurListener((e: Event) => { - if (document.activeElement === this.domNode || !DOM.isAncestor(document.activeElement, this.domNode)) { - this.emit('blur', e); + // Recompute focused item + else if (event.equals(CommonKeybindings.TAB) || event.equals(CommonKeybindings.SHIFT_TAB)) { + this.updateFocusedItem(); } }); + this.focusTracker = DOM.trackFocus(this.domNode); + this.focusTracker.addBlurListener((e: Event) => { + if (document.activeElement === this.domNode || !DOM.isAncestor(document.activeElement, this.domNode)) { + this.emit('blur', e); + this.focusedItem = undefined; + } + }); + + this.focusTracker.addFocusListener(() => this.updateFocusedItem()); + this.actionsList = document.createElement('ul'); this.actionsList.className = 'actions-container'; this.actionsList.setAttribute('role', 'menu'); @@ -489,6 +498,16 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { container.appendChild(this.domNode); } + private updateFocusedItem(): void { + for (let i = 0; i < this.actionsList.children.length; i++) { + let elem = this.actionsList.children[i]; + if (DOM.isAncestor(document.activeElement, elem)) { + this.focusedItem = i; + break; + } + } + } + public get context(): any { return this._context; } @@ -498,11 +517,11 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { this.items.forEach(i => i.setActionContext(context)); } - public get actionRunner(): actions.IActionRunner { + public get actionRunner(): IActionRunner { return this._actionRunner; } - public set actionRunner(actionRunner: actions.IActionRunner) { + public set actionRunner(actionRunner: IActionRunner) { if (actionRunner) { this._actionRunner = actionRunner; this.items.forEach(item => item.actionRunner = actionRunner); @@ -513,21 +532,21 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { return $(this.domNode); } - public push(actions: actions.IAction, options?: IActionOptions): void; - public push(actions: actions.IAction[], options?: IActionOptions): void; + public push(actions: IAction, options?: IActionOptions): void; + public push(actions: IAction[], options?: IActionOptions): void; public push(actions: any, options: IActionOptions = {}): void { if (!Array.isArray(actions)) { actions = [actions]; } - var index = types.isNumber(options.index) ? options.index : null; + let index = types.isNumber(options.index) ? options.index : null; - actions.forEach((action: actions.IAction) => { - var actionItemElement = document.createElement('li'); + actions.forEach((action: IAction) => { + let actionItemElement = document.createElement('li'); actionItemElement.className = 'action-item'; actionItemElement.setAttribute('role', 'presentation'); - var item: IActionItem = null; + let item: IActionItem = null; if (this.options.actionItemProvider) { item = this.options.actionItemProvider(action); @@ -548,24 +567,16 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { this.actionsList.insertBefore(actionItemElement, this.actionsList.children[index++]); } - if (!this.options.disableTabIndex && !this.domNode.hasAttribute('tabIndex')) { - this.domNode.tabIndex = 0; // make sure an action bar with actions participates in tab navigation - } - this.items.push(item); }); } public clear(): void { - var item: IActionItem; + let item: IActionItem; while (item = this.items.pop()) { item.dispose(); } $(this.actionsList).empty(); - - if (!this.options.disableTabIndex) { - this.domNode.removeAttribute('tabIndex'); // empty action bar does not participate in tab navigation - } } public length(): number { @@ -593,8 +604,8 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { this.focusedItem = this.items.length - 1; } - var startIndex = this.focusedItem; - var item: IActionItem; + let startIndex = this.focusedItem; + let item: IActionItem; do { this.focusedItem = (this.focusedItem + 1) % this.items.length; @@ -613,8 +624,8 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { this.focusedItem = 0; } - var startIndex = this.focusedItem; - var item: IActionItem; + let startIndex = this.focusedItem; + let item: IActionItem; do { this.focusedItem = this.focusedItem - 1; @@ -639,10 +650,10 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { return; } - for (var i = 0; i < this.items.length; i++) { - var item = this.items[i]; + for (let i = 0; i < this.items.length; i++) { + let item = this.items[i]; - var actionItem = item; + let actionItem = item; if (i === this.focusedItem) { if (types.isFunction(actionItem.focus)) { @@ -657,21 +668,24 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { } private doTrigger(event): void { - //nothing to focus if (typeof this.focusedItem === 'undefined') { - return; + return; //nothing to focus } // trigger action - var actionItem = (this.items[this.focusedItem]); + let actionItem = (this.items[this.focusedItem]); this.run(actionItem._action, actionItem._context || event).done(); } private cancel(): void { + if (document.activeElement instanceof HTMLElement) { + (document.activeElement).blur(); // remove focus from focussed action + } + this.emit(CommonEventType.CANCEL); } - public run(action: actions.IAction, context?: any): Promise { + public run(action: IAction, context?: any): Promise { return this._actionRunner.run(action, context); } @@ -681,6 +695,11 @@ export class ActionBar extends EventEmitter implements actions.IActionRunner { } this.items = null; + if (this.focusTracker) { + this.focusTracker.dispose(); + this.focusTracker = null; + } + this.toDispose = lifecycle.disposeAll(this.toDispose); this.getContainer().destroy(); @@ -695,7 +714,7 @@ export class SelectActionItem extends BaseActionItem { private selected: number; private toDispose: lifecycle.IDisposable[]; - constructor(ctx: any, action: actions.IAction, options: string[], selected: number) { + constructor(ctx: any, action: IAction, options: string[], selected: number) { super(ctx, action); this.select = document.createElement('select'); @@ -722,6 +741,18 @@ export class SelectActionItem extends BaseActionItem { })); } + public focus(): void { + if (this.select) { + this.select.focus(); + } + } + + public blur(): void { + if (this.select) { + this.select.blur(); + } + } + public render(container: HTMLElement): void { DOM.addClass(container, 'select-container'); container.appendChild(this.select); @@ -741,7 +772,7 @@ export class SelectActionItem extends BaseActionItem { } private createOption(value: string): HTMLOptionElement { - var option = document.createElement('option'); + let option = document.createElement('option'); option.value = value; option.text = value; diff --git a/src/vs/base/browser/ui/button/button.css b/src/vs/base/browser/ui/button/button.css index 7ace3b45985..46ede1182be 100644 --- a/src/vs/base/browser/ui/button/button.css +++ b/src/vs/base/browser/ui/button/button.css @@ -12,6 +12,7 @@ text-align: center; color: white; background: #007ACC; + cursor: pointer; } .monaco-button.disabled { diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index beda9a2be45..942479b7497 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -9,6 +9,8 @@ import 'vs/css!./button'; import {EventEmitter} from 'vs/base/common/eventEmitter'; import DOM = require('vs/base/browser/dom'); import {Builder, $} from 'vs/base/browser/builder'; +import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent'; +import {CommonKeybindings} from 'vs/base/common/keyCodes'; export class Button extends EventEmitter { @@ -19,15 +21,31 @@ export class Button extends EventEmitter { constructor(container: any) { super(); - this.$el = $('a.monaco-button').href('#').appendTo(container); + this.$el = $('a.monaco-button').attr('tabindex', '0').appendTo(container); - this.$el.on('click', (e) => { + this.$el.on(DOM.EventType.CLICK, (e) => { if (!this.enabled) { DOM.EventHelper.stop(e); return; } - this.emit('click', e); + this.emit(DOM.EventType.CLICK, e); + }); + + this.$el.on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e); + let eventHandled = false; + if (this.enabled && event.equals(CommonKeybindings.ENTER) || event.equals(CommonKeybindings.SPACE)) { + this.emit(DOM.EventType.CLICK, e); + eventHandled = true; + } else if (event.equals(CommonKeybindings.ESCAPE)) { + this.$el.domBlur(); + eventHandled = true; + } + + if (eventHandled) { + DOM.EventHelper.stop(event, true); + } }); } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 2cca06942b6..9500eff32c2 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -90,7 +90,7 @@ export class Checkbox extends Widget { } public disable(): void { - this.domNode.tabIndex = -1; + this.domNode.removeAttribute('tabIndex'); this.domNode.setAttribute('aria-disabled', String(true)); } } diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 08d8b599fc2..19d005aa692 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -7,12 +7,10 @@ 'use strict'; import 'vs/css!./contextview'; -import Builder = require('vs/base/browser/builder'); +import {Builder, $} from 'vs/base/browser/builder'; import DOM = require('vs/base/browser/dom'); -import Lifecycle = require('vs/base/common/lifecycle'); -import EventEmitter = require('vs/base/common/eventEmitter'); - -var $ = Builder.$; +import {IDisposable, disposeAll} from 'vs/base/common/lifecycle'; +import {EventEmitter} from 'vs/base/common/eventEmitter'; export interface IAnchor { x: number; @@ -30,20 +28,20 @@ export enum AnchorPosition { } export interface IDelegate { - getAnchor(): HTMLElement|IAnchor; - render(container: HTMLElement): Lifecycle.IDisposable; + getAnchor(): HTMLElement | IAnchor; + render(container: HTMLElement): IDisposable; layout?(): void; - anchorAlignment?:AnchorAlignment; // default: left - anchorPosition?:AnchorPosition; // default: below - canRelayout?:boolean; // default: true - onDOMEvent?(e:Event, activeElement: HTMLElement):void; - onHide?(data?: any):void; + anchorAlignment?: AnchorAlignment; // default: left + anchorPosition?: AnchorPosition; // default: below + canRelayout?: boolean; // default: true + onDOMEvent?(e: Event, activeElement: HTMLElement): void; + onHide?(data?: any): void; } export interface IContextViewProvider { showContextView(delegate: IDelegate): void; hideContextView(): void; - layout():void; + layout(): void; } export interface IPosition { @@ -58,8 +56,8 @@ export interface ISize { export interface IView extends IPosition, ISize { } -export function layout(view: ISize, around: IView, inside: IView, anchorPosition: AnchorPosition, anchorAlignment: AnchorAlignment) : IPosition { - var top: number, left: number; +export function layout(view: ISize, around: IView, inside: IView, anchorPosition: AnchorPosition, anchorAlignment: AnchorAlignment): IPosition { + let top: number, left: number; if (anchorPosition === AnchorPosition.BELOW) { top = around.top + around.height - inside.top; @@ -88,16 +86,16 @@ export function layout(view: ISize, around: IView, inside: IView, anchorPosition return { top: top, left: left }; } -export class ContextView extends EventEmitter.EventEmitter { +export class ContextView extends EventEmitter { private static BUBBLE_UP_EVENTS = ['click', 'keydown', 'focus', 'blur']; private static BUBBLE_DOWN_EVENTS = ['click']; - private $container: Builder.Builder; - private $view: Builder.Builder; + private $container: Builder; + private $view: Builder; private delegate: IDelegate; - private toDispose: Lifecycle.IDisposable[]; - private toDisposeOnClean: Lifecycle.IDisposable; + private toDispose: IDisposable[]; + private toDisposeOnClean: IDisposable; constructor(container: HTMLElement) { super(); @@ -122,11 +120,11 @@ export class ContextView extends EventEmitter.EventEmitter { if (container) { this.$container = $(container); this.$view.appendTo(this.$container); - this.$container.on(ContextView.BUBBLE_UP_EVENTS, (e:Event) => { - this.onDOMEvent(e, document.activeElement, false); + this.$container.on(ContextView.BUBBLE_UP_EVENTS, (e: Event) => { + this.onDOMEvent(e, document.activeElement, false); }); - this.$container.on(ContextView.BUBBLE_DOWN_EVENTS, (e:Event) => { - this.onDOMEvent(e, document.activeElement, true); + this.$container.on(ContextView.BUBBLE_DOWN_EVENTS, (e: Event) => { + this.onDOMEvent(e, document.activeElement, true); }, null, true); } } @@ -168,16 +166,16 @@ export class ContextView extends EventEmitter.EventEmitter { private doLayout(): void { // Get anchor - var anchor = this.delegate.getAnchor(); + let anchor = this.delegate.getAnchor(); // Compute around - var around: IView; + let around: IView; // Get the element's position and size (to anchor the view) if (DOM.isHTMLElement(anchor)) { - var $anchor = $( anchor); - var elementPosition = $anchor.getPosition(); - var elementSize = $anchor.getTotalSize(); + let $anchor = $(anchor); + let elementPosition = $anchor.getPosition(); + let elementSize = $anchor.getTotalSize(); around = { top: elementPosition.top, @@ -186,7 +184,7 @@ export class ContextView extends EventEmitter.EventEmitter { height: elementSize.height }; } else { - var realAnchor = anchor; + let realAnchor = anchor; around = { top: realAnchor.y, @@ -197,8 +195,8 @@ export class ContextView extends EventEmitter.EventEmitter { } // Get the container's position - var insidePosition = this.$container.getPosition(); - var inside = { + let insidePosition = this.$container.getPosition(); + let inside = { top: insidePosition.top, left: insidePosition.left, height: window.innerHeight, @@ -206,13 +204,13 @@ export class ContextView extends EventEmitter.EventEmitter { }; // Get the view's size - var viewSize = this.$view.getTotalSize(); - var view = { width: viewSize.width, height: viewSize.height }; + let viewSize = this.$view.getTotalSize(); + let view = { width: viewSize.width, height: viewSize.height }; - var anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW; - var anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT; + let anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW; + let anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT; - var result = layout(view, around, inside, anchorPosition, anchorAlignment); + let result = layout(view, around, inside, anchorPosition, anchorAlignment); this.$view.removeClass('top', 'bottom', 'left', 'right'); this.$view.addClass(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); @@ -242,8 +240,8 @@ export class ContextView extends EventEmitter.EventEmitter { private onDOMEvent(e: Event, element: HTMLElement, onCapture: boolean): void { if (this.delegate) { if (this.delegate.onDOMEvent) { - this.delegate.onDOMEvent(e, document.activeElement); - } else if (onCapture && !DOM.isAncestor( e.target, this.$container.getHTMLElement())) { + this.delegate.onDOMEvent(e, document.activeElement); + } else if (onCapture && !DOM.isAncestor(e.target, this.$container.getHTMLElement())) { this.hide(); } } @@ -253,6 +251,6 @@ export class ContextView extends EventEmitter.EventEmitter { super.dispose(); this.hide(); - this.toDispose = Lifecycle.disposeAll(this.toDispose); + this.toDispose = disposeAll(this.toDispose); } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/dropdown/dropdown.css b/src/vs/base/browser/ui/dropdown/dropdown.css index f1b58940d3e..dde80747c97 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.css +++ b/src/vs/base/browser/ui/dropdown/dropdown.css @@ -22,10 +22,6 @@ vertical-align: top; } -.dropdown > .dropdown-action > .action-label { - outline: none; -} - .dropdown > .dropdown-action > .action-label:hover { color: inherit; text-decoration: none; diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index 248dea13089..936c4d57ae2 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -6,40 +6,38 @@ 'use strict'; import 'vs/css!./dropdown'; -import Builder = require('vs/base/browser/builder'); -import WinJS = require('vs/base/common/winjs.base'); -import Touch = require('vs/base/browser/touch'); -import Actions = require('vs/base/common/actions'); -import ActionBar = require('vs/base/browser/ui/actionbar/actionbar'); -import EventEmitter = require('vs/base/common/eventEmitter'); -import Lifecycle = require('vs/base/common/lifecycle'); -import ContextView = require('vs/base/browser/ui/contextview/contextview'); -import Menu = require('vs/base/browser/ui/menu/menu'); - -var $ = Builder.$; +import {Builder, $} from 'vs/base/browser/builder'; +import {Promise} from 'vs/base/common/winjs.base'; +import {Gesture, EventType} from 'vs/base/browser/touch'; +import {ActionRunner, IAction} from 'vs/base/common/actions'; +import {ActionBar, ActionItem, IActionItem} from 'vs/base/browser/ui/actionbar/actionbar'; +import {EventEmitter} from 'vs/base/common/eventEmitter'; +import {IDisposable, disposeAll} from 'vs/base/common/lifecycle'; +import {ContextView, IContextViewProvider} from 'vs/base/browser/ui/contextview/contextview'; +import {Menu, IMenuOptions} from 'vs/base/browser/ui/menu/menu'; export interface ILabelRenderer { - (container: HTMLElement): Lifecycle.IDisposable; + (container: HTMLElement): IDisposable; } export interface IBaseDropdownOptions { tick?: boolean; label?: string; labelRenderer?: ILabelRenderer; - action?: Actions.IAction; + action?: IAction; } -export class BaseDropdown extends Actions.ActionRunner { +export class BaseDropdown extends ActionRunner { - /*protected*/ toDispose: Lifecycle.IDisposable[]; + /*protected*/ toDispose: IDisposable[]; - /*protected*/ $el: Builder.Builder; - private $boxContainer: Builder.Builder; - private $action: Builder.Builder; - private $label: Builder.Builder; - private $contents: Builder.Builder; + /*protected*/ $el: Builder; + private $boxContainer: Builder; + private $action: Builder; + private $label: Builder; + private $contents: Builder; - constructor (container: HTMLElement, options: IBaseDropdownOptions) { + constructor(container: HTMLElement, options: IBaseDropdownOptions) { super(); this.toDispose = []; @@ -52,12 +50,12 @@ export class BaseDropdown extends Actions.ActionRunner { this.$label.addClass('tick'); } - var labelRenderer = options.labelRenderer; + let labelRenderer = options.labelRenderer; if (!labelRenderer && options.action) { this.$action = $('.dropdown-action').appendTo(this.$el); - var item = new ActionBar.ActionItem(null, options.action, { + let item = new ActionItem(null, options.action, { icon: true, label: true }); @@ -65,33 +63,33 @@ export class BaseDropdown extends Actions.ActionRunner { item.actionRunner = this; item.render(this.$action.getHTMLElement()); - labelRenderer = (container: HTMLElement): Lifecycle.IDisposable => { + labelRenderer = (container: HTMLElement): IDisposable => { container.innerText = ''; return item; }; } if (!labelRenderer) { - labelRenderer = (container: HTMLElement): Lifecycle.IDisposable => { + labelRenderer = (container: HTMLElement): IDisposable => { $(container).text(options.label || ''); return null; }; } - this.$label.on(['click', Touch.EventType.Tap], (e:Event) => { + this.$label.on(['click', EventType.Tap], (e: Event) => { e.preventDefault(); e.stopPropagation(); this.toggleDropdown(); }).appendTo(this.$el); - var cleanupFn = labelRenderer(this.$label.getHTMLElement()); + let cleanupFn = labelRenderer(this.$label.getHTMLElement()); if (cleanupFn) { this.toDispose.push(cleanupFn); } - this.toDispose.push(new Touch.Gesture(this.$label.getHTMLElement())); + this.toDispose.push(new Gesture(this.$label.getHTMLElement())); } public set tooltip(tooltip: string) { @@ -114,7 +112,7 @@ export class BaseDropdown extends Actions.ActionRunner { // noop } - /*protected*/ public onEvent(e:Event, activeElement: HTMLElement): void { + /*protected*/ public onEvent(e: Event, activeElement: HTMLElement): void { this.hide(); } @@ -122,7 +120,7 @@ export class BaseDropdown extends Actions.ActionRunner { super.dispose(); this.hide(); - this.toDispose = Lifecycle.disposeAll(this.toDispose); + this.toDispose = disposeAll(this.toDispose); if (this.$boxContainer) { this.$boxContainer.destroy(); @@ -142,23 +140,23 @@ export class BaseDropdown extends Actions.ActionRunner { } export interface IDropdownOptions extends IBaseDropdownOptions { - contextViewProvider: ContextView.IContextViewProvider; + contextViewProvider: IContextViewProvider; } export class Dropdown extends BaseDropdown { - /*protected*/ _contextViewProvider: ContextView.IContextViewProvider; + /*protected*/ _contextViewProvider: IContextViewProvider; - constructor (container: HTMLElement, options: IDropdownOptions) { + constructor(container: HTMLElement, options: IDropdownOptions) { super(container, options); this.contextViewProvider = options.contextViewProvider; } - /*protected*/ public set contextViewProvider(contextViewProvider: ContextView.IContextViewProvider) { + /*protected*/ public set contextViewProvider(contextViewProvider: IContextViewProvider) { this._contextViewProvider = contextViewProvider; } - /*protected*/ public get contextViewProvider(): ContextView.IContextViewProvider { + /*protected*/ public get contextViewProvider(): IContextViewProvider { return this._contextViewProvider; } @@ -188,17 +186,17 @@ export class Dropdown extends BaseDropdown { } } - /*protected*/ public renderContents(container: HTMLElement): Lifecycle.IDisposable { + /*protected*/ public renderContents(container: HTMLElement): IDisposable { return null; } } export interface IContextMenuDelegate { getAnchor(): any; - getActions(): WinJS.Promise; - getActionItem?(action: Actions.IAction): ActionBar.IActionItem; - getActionsContext?():any; - getMenuClassName?():string; + getActions(): Promise; + getActionItem?(action: IAction): IActionItem; + getActionsContext?(): any; + getMenuClassName?(): string; onHide?(didCancel: boolean): void; } @@ -207,12 +205,12 @@ export interface IContextMenuProvider { } export interface IActionProvider { - getActions(): Actions.IAction[]; + getActions(): IAction[]; } export interface IDropdownMenuOptions extends IBaseDropdownOptions { contextMenuProvider: IContextMenuProvider; - actions?: Actions.IAction[]; + actions?: IAction[]; actionProvider?: IActionProvider; menuClassName?: string; } @@ -220,13 +218,13 @@ export interface IDropdownMenuOptions extends IBaseDropdownOptions { export class DropdownMenu extends BaseDropdown { /*protected*/ _contextMenuProvider: IContextMenuProvider; - private _menuOptions: Menu.IMenuOptions; + private _menuOptions: IMenuOptions; /*protected*/ currentContainer: HTMLElement; - /*protected*/ _actions: Actions.IAction[]; + /*protected*/ _actions: IAction[]; /*protected*/ actionProvider: IActionProvider; private menuClassName: string; - constructor (container:HTMLElement, options: IDropdownMenuOptions) { + constructor(container: HTMLElement, options: IDropdownMenuOptions) { super(container, options); this._contextMenuProvider = options.contextMenuProvider; @@ -244,15 +242,15 @@ export class DropdownMenu extends BaseDropdown { return this._contextMenuProvider; } - public set menuOptions(options: Menu.IMenuOptions) { + public set menuOptions(options: IMenuOptions) { this._menuOptions = options; } - public get menuOptions(): Menu.IMenuOptions { + public get menuOptions(): IMenuOptions { return this._menuOptions; } - /*protected*/ public get actions(): Actions.IAction[] { + /*protected*/ public get actions(): IAction[] { if (this.actionProvider) { return this.actionProvider.getActions(); } @@ -260,7 +258,7 @@ export class DropdownMenu extends BaseDropdown { return this._actions; } - /*protected*/ public set actions(actions:Actions.IAction[]) { + /*protected*/ public set actions(actions: IAction[]) { this._actions = actions; } @@ -269,7 +267,7 @@ export class DropdownMenu extends BaseDropdown { this._contextMenuProvider.showContextMenu({ getAnchor: () => this.$el.getHTMLElement(), - getActions: () => WinJS.Promise.as(this.actions), + getActions: () => Promise.as(this.actions), getActionsContext: () => this.menuOptions ? this.menuOptions.context : null, getActionItem: (action) => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : null, getMenuClassName: () => this.menuClassName, @@ -285,7 +283,7 @@ export class DropdownMenu extends BaseDropdown { } } -export class DropdownGroup extends EventEmitter.EventEmitter { +export class DropdownGroup extends EventEmitter { private el: HTMLElement; diff --git a/src/vs/base/browser/ui/dropdown/linksDropdown.ts b/src/vs/base/browser/ui/dropdown/linksDropdown.ts index 669d81a34f1..a969339a681 100644 --- a/src/vs/base/browser/ui/dropdown/linksDropdown.ts +++ b/src/vs/base/browser/ui/dropdown/linksDropdown.ts @@ -4,26 +4,26 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import WinJS = require('vs/base/common/winjs.base'); -import Platform = require('vs/base/common/platform'); -import Types = require('vs/base/common/types'); -import Actions = require('vs/base/common/actions'); -import Dropdown = require('vs/base/browser/ui/dropdown/dropdown'); +import {Promise} from 'vs/base/common/winjs.base'; +import {isMacintosh} from 'vs/base/common/platform'; +import {isFunction} from 'vs/base/common/types'; +import {Action} from 'vs/base/common/actions'; +import {DropdownMenu, IDropdownMenuOptions} from 'vs/base/browser/ui/dropdown/dropdown'; -export interface ILinksDropdownMenuOptions extends Dropdown.IDropdownMenuOptions { +export interface ILinksDropdownMenuOptions extends IDropdownMenuOptions { tooltip: string; } -export class LinksDropdownMenu extends Dropdown.DropdownMenu { +export class LinksDropdownMenu extends DropdownMenu { - constructor (container:HTMLElement, options: ILinksDropdownMenuOptions) { + constructor(container: HTMLElement, options: ILinksDropdownMenuOptions) { super(container, options); this.tooltip = options.tooltip; } - /*protected*/ public onEvent(e:Event, activeElement: HTMLElement):void { - if (e instanceof KeyboardEvent && ((e).ctrlKey || (Platform.isMacintosh && (e).metaKey))) { + /*protected*/ public onEvent(e: Event, activeElement: HTMLElement): void { + if (e instanceof KeyboardEvent && ((e).ctrlKey || (isMacintosh && (e).metaKey))) { return; // allow to use Ctrl/Meta in workspace dropdown menu } @@ -31,25 +31,25 @@ export class LinksDropdownMenu extends Dropdown.DropdownMenu { } } -export class LinkDropdownAction extends Actions.Action { +export class LinkDropdownAction extends Action { - constructor(id:string, name:string, clazz:string, url:()=>string, forceOpenInNewTab?:boolean); - constructor(id:string, name:string, clazz:string, url:string, forceOpenInNewTab?:boolean); - constructor(id:string, name:string, clazz:string, url:any, forceOpenInNewTab?:boolean) { - super(id, name, clazz, true, (e:Event)=>{ - var urlString = url; + constructor(id: string, name: string, clazz: string, url: () => string, forceOpenInNewTab?: boolean); + constructor(id: string, name: string, clazz: string, url: string, forceOpenInNewTab?: boolean); + constructor(id: string, name: string, clazz: string, url: any, forceOpenInNewTab?: boolean) { + super(id, name, clazz, true, (e: Event) => { + let urlString = url; - if (Types.isFunction(url)) { + if (isFunction(url)) { urlString = url(); } - if (forceOpenInNewTab || (e instanceof MouseEvent && ((e).ctrlKey || (Platform.isMacintosh && (e).metaKey)))) { + if (forceOpenInNewTab || (e instanceof MouseEvent && ((e).ctrlKey || (isMacintosh && (e).metaKey)))) { window.open(urlString, '_blank'); } else { window.location.href = urlString; } - return WinJS.Promise.as(true); + return Promise.as(true); }); } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/findinput/findInput.css b/src/vs/base/browser/ui/findinput/findInput.css index 0e8214898f8..2736256115c 100644 --- a/src/vs/base/browser/ui/findinput/findInput.css +++ b/src/vs/base/browser/ui/findinput/findInput.css @@ -15,10 +15,6 @@ height: 25px; } -.monaco-findInput .monaco-inputbox > .wrapper > .input { - outline: none; -} - .fl:after { clear: both; content: ''; diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 1ea38c09171..1b149b76490 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -15,6 +15,7 @@ import {Widget} from 'vs/base/browser/ui/widget'; import Event, {Emitter} from 'vs/base/common/event'; import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent'; import {StandardMouseEvent} from 'vs/base/browser/mouseEvent'; +import {CommonKeybindings} from 'vs/base/common/keyCodes'; export interface IFindInputOptions { placeholder?:string; @@ -48,6 +49,8 @@ export class FindInput extends Widget { private validation:IInputValidator; private label:string; + private optionsKeyListener: () => void; + private regex:Checkbox; private wholeWords:Checkbox; private caseSensitive:Checkbox; @@ -248,6 +251,35 @@ export class FindInput extends Widget { } })); + // Arrow-Key support to navigate between options + let indexes = [this.caseSensitive.domNode, this.wholeWords.domNode, this.regex.domNode]; + this.optionsKeyListener = dom.addListener(this.domNode, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e); + if (event.equals(CommonKeybindings.LEFT_ARROW) || event.equals(CommonKeybindings.RIGHT_ARROW) || event.equals(CommonKeybindings.ESCAPE)) { + let index = indexes.indexOf(document.activeElement); + if (index >= 0) { + let newIndex: number; + if (event.equals(CommonKeybindings.RIGHT_ARROW)) { + newIndex = (index + 1) % indexes.length; + } else if (event.equals(CommonKeybindings.LEFT_ARROW)) { + if (index === 0) { + newIndex = indexes.length - 1; + } else { + newIndex = index - 1; + } + } + + if (event.equals(CommonKeybindings.ESCAPE)) { + indexes[index].blur(); + } else if (newIndex >= 0) { + indexes[newIndex].focus(); + } + + dom.EventHelper.stop(event, true); + } + } + }); + this.setInputWidth(); let controls = document.createElement('div'); @@ -275,6 +307,15 @@ export class FindInput extends Widget { private clearValidation(): void { this.inputBox.hideMessage(); } + + public dispose(): void { + if (this.optionsKeyListener) { + this.optionsKeyListener(); + this.optionsKeyListener = null; + } + + super.dispose(); + } } interface IMatchCountOpts { diff --git a/src/vs/base/browser/ui/inputbox/inputBox.css b/src/vs/base/browser/ui/inputbox/inputBox.css index e18c56d3b8d..a03a5e8f509 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.css +++ b/src/vs/base/browser/ui/inputbox/inputBox.css @@ -58,10 +58,6 @@ overflow: hidden; } -.monaco-inputbox > .wrapper > .input:focus { - outline: none; -} - .monaco-inputbox > .wrapper > .mirror { position: absolute; display: inline-block; diff --git a/src/vs/base/browser/ui/leftRightWidget/leftRightWidget.ts b/src/vs/base/browser/ui/leftRightWidget/leftRightWidget.ts index d46f28e17d3..e35a7c76fce 100644 --- a/src/vs/base/browser/ui/leftRightWidget/leftRightWidget.ts +++ b/src/vs/base/browser/ui/leftRightWidget/leftRightWidget.ts @@ -6,23 +6,21 @@ 'use strict'; import 'vs/css!./leftRightWidget'; -import Builder = require('vs/base/browser/builder'); -import Lifecycle = require('vs/base/common/lifecycle'); - -var $ = Builder.$; +import {Builder, $} from 'vs/base/browser/builder'; +import {IDisposable} from 'vs/base/common/lifecycle'; export interface IRenderer { - (container:HTMLElement): Lifecycle.IDisposable; + (container: HTMLElement): IDisposable; } export class LeftRightWidget { - private $el: Builder.Builder; - private toDispose: Lifecycle.IDisposable[]; + private $el: Builder; + private toDispose: IDisposable[]; - constructor (container:Builder.Builder, renderLeftFn:IRenderer, renderRightFn:IRenderer); - constructor (container:HTMLElement, renderLeftFn:IRenderer, renderRightFn:IRenderer); - constructor (container:any, renderLeftFn:IRenderer, renderRightFn:IRenderer) { + constructor(container: Builder, renderLeftFn: IRenderer, renderRightFn: IRenderer); + constructor(container: HTMLElement, renderLeftFn: IRenderer, renderRightFn: IRenderer); + constructor(container: any, renderLeftFn: IRenderer, renderRightFn: IRenderer) { this.$el = $('.monaco-left-right-widget').appendTo(container); this.toDispose = [ diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 3504df52ed8..75fb3fd239d 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -6,34 +6,32 @@ 'use strict'; import 'vs/css!./menu'; -import Lifecycle = require('vs/base/common/lifecycle'); -import Builder = require('vs/base/browser/builder'); -import Actions = require('vs/base/common/actions'); -import ActionBar = require('vs/base/browser/ui/actionbar/actionbar'); -import EventEmitter = require('vs/base/common/eventEmitter'); - -var $ = Builder.$; +import {IDisposable} from 'vs/base/common/lifecycle'; +import {Builder, $} from 'vs/base/browser/builder'; +import {IActionRunner, IAction} from 'vs/base/common/actions'; +import {ActionBar, IActionItemProvider, ActionsOrientation} from 'vs/base/browser/ui/actionbar/actionbar'; +import {EventEmitter} from 'vs/base/common/eventEmitter'; export interface IMenuOptions { - context?:any; - actionItemProvider?:ActionBar.IActionItemProvider; - actionRunner?:Actions.IActionRunner; + context?: any; + actionItemProvider?: IActionItemProvider; + actionRunner?: IActionRunner; } -export class Menu extends EventEmitter.EventEmitter { +export class Menu extends EventEmitter { - private actionBar: ActionBar.ActionBar; - private listener: Lifecycle.IDisposable; + private actionBar: ActionBar; + private listener: IDisposable; - constructor (container:HTMLElement, actions:Actions.IAction[], options:IMenuOptions = {}) { + constructor(container: HTMLElement, actions: IAction[], options: IMenuOptions = {}) { super(); $(container).addClass('monaco-menu-container'); - var $menu = $('.monaco-menu').appendTo(container); + let $menu = $('.monaco-menu').appendTo(container); - this.actionBar = new ActionBar.ActionBar($menu, { - orientation: ActionBar.ActionsOrientation.VERTICAL, + this.actionBar = new ActionBar($menu, { + orientation: ActionsOrientation.VERTICAL, actionItemProvider: options.actionItemProvider, context: options.context, actionRunner: options.actionRunner diff --git a/src/vs/base/browser/ui/progressbar/progressbar.ts b/src/vs/base/browser/ui/progressbar/progressbar.ts index 0263a2c3303..64f13bda8a7 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.ts +++ b/src/vs/base/browser/ui/progressbar/progressbar.ts @@ -6,12 +6,12 @@ 'use strict'; import 'vs/css!./progressbar'; -import WinJS = require('vs/base/common/winjs.base'); -import Assert = require('vs/base/common/assert'); -import Browser = require('vs/base/browser/browser'); -import Builder = require('vs/base/browser/builder'); +import {Promise, ValueCallback} from 'vs/base/common/winjs.base'; +import assert = require('vs/base/common/assert'); +import browser = require('vs/base/browser/browser'); +import {Builder, $} from 'vs/base/browser/builder'; import DOM = require('vs/base/browser/dom'); -import Uuid = require('vs/base/common/uuid'); +import uuid = require('vs/base/common/uuid'); const css_done = 'done'; const css_active = 'active'; @@ -20,8 +20,6 @@ const css_discrete = 'discrete'; const css_progress_container = 'progress-container'; const css_progress_bit = 'progress-bit'; -const $ = Builder.$; - /** * A progress bar with support for infinite or discrete progress. */ @@ -29,21 +27,21 @@ export class ProgressBar { private toUnbind: { (): void; }[]; private workedVal: number; - private element: Builder.Builder; + private element: Builder; private animationRunning: boolean; private bit: HTMLElement; private totalWork: number; - private animationStopToken: WinJS.ValueCallback; + private animationStopToken: ValueCallback; private currentProgressToken: string; - constructor(builder: Builder.Builder) { + constructor(builder: Builder) { this.toUnbind = []; this.workedVal = 0; this.create(builder); } - private create(parent: Builder.Builder): void { + private create(parent: Builder): void { parent.div({ 'class': css_progress_container }, (builder) => { this.element = builder.clone(); @@ -100,7 +98,7 @@ export class ProgressBar { this.bit.style.width = 'inherit'; if (delayed) { - WinJS.Promise.timeout(200).then(() => this.off()); + Promise.timeout(200).then(() => this.off()); } else { this.off(); } @@ -110,7 +108,7 @@ export class ProgressBar { else { this.bit.style.opacity = '0'; if (delayed) { - WinJS.Promise.timeout(200).then(() => this.off()); + Promise.timeout(200).then(() => this.off()); } else { this.off(); } @@ -131,10 +129,10 @@ export class ProgressBar { this.element.addClass(css_active); this.element.addClass(css_infinite); - if (!Browser.hasCSSAnimationSupport()) { + if (!browser.hasCSSAnimationSupport()) { // Use a generated token to avoid race conditions from reentrant calls to this function - let currentProgressToken = Uuid.v4().asHex(); + let currentProgressToken = uuid.v4().asHex(); this.currentProgressToken = currentProgressToken; this.manualInfinite(currentProgressToken); @@ -144,13 +142,12 @@ export class ProgressBar { } private manualInfinite(currentProgressToken: string): void { - this.bit.style.width = '5%'; this.bit.style.display = 'inherit'; let counter = 0; let animationFn: () => void = () => { - WinJS.Promise.timeout(50).then(() => { + Promise.timeout(50).then(() => { // Return if another manualInfinite() call was made if (currentProgressToken !== this.currentProgressToken) { @@ -203,10 +200,10 @@ export class ProgressBar { * Tells the progress bar that an amount of work has been completed. */ public worked(value: number): ProgressBar { - Assert.ok(!isNaN(this.totalWork), 'Total work not set'); + assert.ok(!isNaN(this.totalWork), 'Total work not set'); value = Number(value); - Assert.ok(!isNaN(value), 'Value is not a number'); + assert.ok(!isNaN(value), 'Value is not a number'); value = Math.max(1, value); this.workedVal += value; @@ -236,7 +233,7 @@ export class ProgressBar { /** * Returns the builder this progress bar is building in. */ - public getContainer(): Builder.Builder { + public getContainer(): Builder { return $(this.element); } diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 0074a3767b4..e0973503501 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -6,16 +6,14 @@ 'use strict'; import 'vs/css!./sash'; -import Lifecycle = require('vs/base/common/lifecycle'); -import Builder = require('vs/base/browser/builder'); -import Browser = require('vs/base/browser/browser'); -import Types = require('vs/base/common/types'); +import {IDisposable, disposeAll} from 'vs/base/common/lifecycle'; +import {Builder, $} from 'vs/base/browser/builder'; +import {isIPad} from 'vs/base/browser/browser'; +import types = require('vs/base/common/types'); import DOM = require('vs/base/browser/dom'); -import Touch = require('vs/base/browser/touch'); -import Events = require('vs/base/common/eventEmitter'); -import Mouse = require('vs/base/browser/mouseEvent'); - -var $ = Builder.$; +import {Gesture, EventType, GestureEvent} from 'vs/base/browser/touch'; +import {EventEmitter} from 'vs/base/common/eventEmitter'; +import {StandardMouseEvent} from 'vs/base/browser/mouseEvent'; export interface ISashLayoutProvider { } @@ -50,10 +48,10 @@ export enum Orientation { HORIZONTAL } -export class Sash extends Events.EventEmitter { +export class Sash extends EventEmitter { - private $e: Builder.Builder; - private gesture: Touch.Gesture; + private $e: Builder; + private gesture: Gesture; private layoutProvider: ISashLayoutProvider; private isDisabled: boolean; private hidden: boolean; @@ -65,17 +63,17 @@ export class Sash extends Events.EventEmitter { this.$e = $('.monaco-sash').appendTo(container); - this.gesture = new Touch.Gesture(this.$e.getHTMLElement()); + this.gesture = new Gesture(this.$e.getHTMLElement()); this.$e.on('mousedown', (e: MouseEvent) => { this.onMouseDown(e); }); - this.$e.on(Touch.EventType.Start, (e: Touch.GestureEvent) => { this.onTouchStart(e); }); + this.$e.on(EventType.Start, (e: GestureEvent) => { this.onTouchStart(e); }); this.orientation = options.orientation || Orientation.VERTICAL; this.$e.addClass(this.orientation === Orientation.HORIZONTAL ? 'horizontal' : 'vertical'); this.size = options.baseSize || 5; - if (Browser.isIPad) { + if (isIPad) { this.size *= 4; // see also http://ux.stackexchange.com/questions/39023/what-is-the-optimum-button-size-of-touch-screen-applications this.$e.addClass('touch'); } @@ -102,11 +100,11 @@ export class Sash extends Events.EventEmitter { return; } - var mouseDownEvent = new Mouse.StandardMouseEvent(e); - var startX = mouseDownEvent.posx; - var startY = mouseDownEvent.posy; + let mouseDownEvent = new StandardMouseEvent(e); + let startX = mouseDownEvent.posx; + let startY = mouseDownEvent.posy; - var startEvent: ISashEvent = { + let startEvent: ISashEvent = { startX: startX, currentX: startX, instantDiffX: 0, @@ -118,7 +116,7 @@ export class Sash extends Events.EventEmitter { this.$e.addClass('active'); this.emit('start', startEvent); - var overlayDiv = $('div').style({ + let overlayDiv = $('div').style({ position: 'absolute', top: 0, left: 0, @@ -128,16 +126,16 @@ export class Sash extends Events.EventEmitter { cursor: this.orientation === Orientation.VERTICAL ? 'ew-resize' : 'ns-resize' }); - var $window = $(window); + let $window = $(window); - var lastCurrentX = startX; - var lastCurrentY = startY; + let lastCurrentX = startX; + let lastCurrentY = startY; $window.on('mousemove', (e: MouseEvent) => { DOM.EventHelper.stop(e, false); - var mouseMoveEvent = new Mouse.StandardMouseEvent(e); + let mouseMoveEvent = new StandardMouseEvent(e); - var event: ISashEvent = { + let event: ISashEvent = { startX: startX, currentX: mouseMoveEvent.posx, instantDiffX: mouseMoveEvent.posx - lastCurrentX, @@ -162,13 +160,13 @@ export class Sash extends Events.EventEmitter { overlayDiv.appendTo(document.body); } - private onTouchStart(event: Touch.GestureEvent): void { + private onTouchStart(event: GestureEvent): void { DOM.EventHelper.stop(event); - var listeners: Lifecycle.IDisposable[] = []; + let listeners: IDisposable[] = []; - var startX = event.pageX; - var startY = event.pageY; + let startX = event.pageX; + let startY = event.pageY; this.emit('start', { startX: startX, @@ -179,11 +177,11 @@ export class Sash extends Events.EventEmitter { instantDiffY: 0 }); - var lastCurrentX = startX; - var lastCurrentY = startY; + let lastCurrentX = startX; + let lastCurrentY = startY; - listeners.push(DOM.addDisposableListener(this.$e.getHTMLElement(), Touch.EventType.Change, (event: Touch.GestureEvent) => { - if (Types.isNumber(event.pageX) && Types.isNumber(event.pageY)) { + listeners.push(DOM.addDisposableListener(this.$e.getHTMLElement(), EventType.Change, (event: GestureEvent) => { + if (types.isNumber(event.pageX) && types.isNumber(event.pageY)) { this.emit('change', { startX: startX, currentX: event.pageX, @@ -198,17 +196,17 @@ export class Sash extends Events.EventEmitter { } })); - listeners.push(DOM.addDisposableListener(this.$e.getHTMLElement(), Touch.EventType.End, (event: Touch.GestureEvent) => { + listeners.push(DOM.addDisposableListener(this.$e.getHTMLElement(), EventType.End, (event: GestureEvent) => { this.emit('end'); - Lifecycle.disposeAll(listeners); + disposeAll(listeners); })); } public layout(): void { - var style: { top?: string; left?: string; height?: string; width?: string; }; + let style: { top?: string; left?: string; height?: string; width?: string; }; if (this.orientation === Orientation.VERTICAL) { - var verticalProvider = (this.layoutProvider); + let verticalProvider = (this.layoutProvider); style = { left: verticalProvider.getVerticalSashLeft(this) - (this.size / 2) + 'px' }; if (verticalProvider.getVerticalSashTop) { @@ -219,7 +217,7 @@ export class Sash extends Events.EventEmitter { style.height = verticalProvider.getVerticalSashHeight(this) + 'px'; } } else { - var horizontalProvider = (this.layoutProvider); + let horizontalProvider = (this.layoutProvider); style = { top: horizontalProvider.getHorizontalSashTop(this) - (this.size / 2) + 'px' }; if (horizontalProvider.getHorizontalSashLeft) { diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index bd962175f9c..0882b4af4fd 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -14,6 +14,8 @@ import objects = require('vs/base/common/objects'); import dom = require('vs/base/browser/dom'); import numbers = require('vs/base/common/numbers'); import sash = require('vs/base/browser/ui/sash/sash'); +import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent'; +import {CommonKeybindings} from 'vs/base/common/keyCodes'; export enum Orientation { VERTICAL, @@ -115,7 +117,7 @@ export class HeaderView extends View { this.header = document.createElement('div'); this.header.className = 'header'; - var headerSize = this.headerSize + 'px'; + let headerSize = this.headerSize + 'px'; if (orientation === Orientation.HORIZONTAL) { this.header.style.width = headerSize; @@ -148,7 +150,7 @@ export class HeaderView extends View { } private layoutBodyContainer(orientation: Orientation): void { - var size = `calc(100% - ${this.headerSize}px)`; + let size = `calc(100% - ${this.headerSize}px)`; if (orientation === Orientation.HORIZONTAL) { this.body.style.width = size; @@ -184,7 +186,10 @@ export enum CollapsibleState { export class AbstractCollapsibleView extends HeaderView { protected state: CollapsibleState; + private headerClickListener: () => void; + private headerKeyListener: () => void; + private focusTracker: dom.IFocusTracker; constructor(opts: ICollapsibleViewOptions) { super(opts); @@ -198,7 +203,37 @@ export class AbstractCollapsibleView extends HeaderView { dom.addClass(this.header, 'collapsible'); dom.addClass(this.body, 'collapsible'); - this.headerClickListener = dom.addListener(this.header, 'click', () => this.toggleExpansion()); + // Keyboard access + this.header.setAttribute('tabindex', '0'); + this.headerKeyListener = dom.addListener(this.header, dom.EventType.KEY_DOWN, (e) => { + let event = new StandardKeyboardEvent(e); + let eventHandled = false; + if (event.equals(CommonKeybindings.ENTER) || event.equals(CommonKeybindings.SPACE) || event.equals(CommonKeybindings.LEFT_ARROW) || event.equals(CommonKeybindings.RIGHT_ARROW)) { + this.toggleExpansion(); + eventHandled = true; + } else if (event.equals(CommonKeybindings.ESCAPE)) { + this.header.blur(); + eventHandled = true; + } + + if (eventHandled) { + dom.EventHelper.stop(event, true); + } + }); + + // Mouse access + this.headerClickListener = dom.addListener(this.header, dom.EventType.CLICK, () => this.toggleExpansion()); + + // Track state of focus in header so that other components can adjust styles based on that + // (for example show or hide actions based on the state of being focused or not) + this.focusTracker = dom.trackFocus(this.header); + this.focusTracker.addFocusListener(() => { + dom.addClass(this.header, 'focused'); + }); + + this.focusTracker.addBlurListener(() => { + setTimeout(() => dom.removeClass(this.header, 'focused')); // delay to give other components a chance to react + }); } public layout(size: number, orientation: Orientation): void { @@ -257,6 +292,16 @@ export class AbstractCollapsibleView extends HeaderView { this.headerClickListener = null; } + if (this.headerKeyListener) { + this.headerKeyListener(); + this.headerKeyListener = null; + } + + if (this.focusTracker) { + this.focusTracker.dispose(); + this.focusTracker = null; + } + super.dispose(); } } @@ -321,8 +366,7 @@ function sum(a: number, b: number): number { return a + b; } export class SplitView implements sash.IHorizontalSashLayoutProvider, - sash.IVerticalSashLayoutProvider -{ + sash.IVerticalSashLayoutProvider { private orientation: Orientation; private el: HTMLElement; private size: number; @@ -333,9 +377,9 @@ export class SplitView implements private sashOrientation: sash.Orientation; private sashes: sash.Sash[]; private sashesListeners: lifecycle.IDisposable[]; - private measureContainerSize: ()=>number; - private layoutViewElement: (viewElement: HTMLElement, size: number)=>void; - private eventWrapper: (event: sash.ISashEvent)=>ISashEvent; + private measureContainerSize: () => number; + private layoutViewElement: (viewElement: HTMLElement, size: number) => void; + private eventWrapper: (event: sash.ISashEvent) => ISashEvent; private animationTimeout: number; private state: IState; @@ -386,10 +430,10 @@ export class SplitView implements throw new Error('Initial weight must be a positive number.'); } - var viewCount = this.views.length; + let viewCount = this.views.length; // Create view container - var viewElement = document.createElement('div'); + let viewElement = document.createElement('div'); dom.addClass(viewElement, 'split-view-view'); this.viewElements.splice(index, 0, viewElement); @@ -409,7 +453,7 @@ export class SplitView implements // Add sash if (this.views.length > 2) { - var s = new sash.Sash(this.el, this, { orientation: this.sashOrientation }); + let s = new sash.Sash(this.el, this, { orientation: this.sashOrientation }); this.sashes.splice(index - 1, 0, s); this.sashesListeners.push(s.addListener2('start', e => this.onSashStart(s, this.eventWrapper(e)))); this.sashesListeners.push(s.addListener2('change', e => this.onSashChange(s, this.eventWrapper(e)))); @@ -420,17 +464,17 @@ export class SplitView implements } public removeView(view: View): void { - var index = this.views.indexOf(view); + let index = this.views.indexOf(view); if (index < 0) { return; } - var deadView = new DeadView(view); + let deadView = new DeadView(view); this.views[index] = deadView; this.onViewChange(deadView, 0); - var sashIndex = Math.max(index - 1, 0); + let sashIndex = Math.max(index - 1, 0); this.sashes[sashIndex].dispose(); this.sashes.splice(sashIndex, 1); @@ -456,11 +500,11 @@ export class SplitView implements size = Math.max(size, this.views.reduce((t, v) => t + v.minimumSize, 0)); - var diff = Math.abs(this.size - size); - var up = numbers.countToArray(this.views.length - 1, -1); + let diff = Math.abs(this.size - size); + let up = numbers.countToArray(this.views.length - 1, -1); - var collapses = this.views.map(v => v.size - v.minimumSize); - var expands = this.views.map(v => v.maximumSize - v.size); + let collapses = this.views.map(v => v.size - v.minimumSize); + let expands = this.views.map(v => v.maximumSize - v.size); if (size < this.size) { this.expandCollapse(Math.min(diff, collapses.reduce(sum, 0)), collapses, expands, up, []); @@ -473,17 +517,17 @@ export class SplitView implements } private onSashStart(sash: sash.Sash, event: ISashEvent): void { - var i = this.sashes.indexOf(sash); - var collapses = this.views.map(v => v.size - v.minimumSize); - var expands = this.views.map(v => v.maximumSize - v.size); + let i = this.sashes.indexOf(sash); + let collapses = this.views.map(v => v.size - v.minimumSize); + let expands = this.views.map(v => v.maximumSize - v.size); - var up = numbers.countToArray(i, -1); - var down = numbers.countToArray(i + 1, this.views.length); + let up = numbers.countToArray(i, -1); + let down = numbers.countToArray(i + 1, this.views.length); - var collapsesUp = up.map(i => collapses[i]); - var collapsesDown = down.map(i => collapses[i]); - var expandsUp = up.map(i => expands[i]); - var expandsDown = down.map(i => expands[i]); + let collapsesUp = up.map(i => collapses[i]); + let collapsesDown = down.map(i => collapses[i]); + let expandsUp = up.map(i => expands[i]); + let expandsDown = down.map(i => expands[i]); this.state = { start: event.start, @@ -498,10 +542,10 @@ export class SplitView implements } private onSashChange(sash: sash.Sash, event: ISashEvent): void { - var i = this.sashes.indexOf(sash); - var diff = event.current - this.state.start; + let i = this.sashes.indexOf(sash); + let diff = event.current - this.state.start; - for (var i = 0; i < this.views.length; i++) { + for (let i = 0; i < this.views.length; i++) { this.views[i].size = this.state.sizes[i]; } @@ -516,25 +560,25 @@ export class SplitView implements // Main algorithm private expandCollapse(collapse: number, collapses: number[], expands: number[], collapseIndexes: number[], expandIndexes: number[]): void { - var totalCollapse = collapse; - var totalExpand = totalCollapse; + let totalCollapse = collapse; + let totalExpand = totalCollapse; collapseIndexes.forEach(i => { - var collapse = Math.min(collapses[i], totalCollapse); + let collapse = Math.min(collapses[i], totalCollapse); totalCollapse -= collapse; this.views[i].size -= collapse; }); expandIndexes.forEach(i => { - var expand = Math.min(expands[i], totalExpand); + let expand = Math.min(expands[i], totalExpand); totalExpand -= expand; this.views[i].size += expand; }); } private initialLayout(): void { - var totalWeight = 0; - var fixedSize = 0; + let totalWeight = 0; + let fixedSize = 0; this.views.forEach((v, i) => { if (v.sizing === ViewSizing.Flexible) { @@ -544,7 +588,7 @@ export class SplitView implements } }); - var flexibleSize = this.size - fixedSize; + let flexibleSize = this.size - fixedSize; this.views.forEach((v, i) => { if (v.sizing === ViewSizing.Flexible) { @@ -555,7 +599,7 @@ export class SplitView implements }); // Leftover - var index = this.getLastFlexibleViewIndex(); + let index = this.getLastFlexibleViewIndex(); if (index >= 0) { this.views[index].size += this.size - this.views.reduce((t, v) => t + v.size, 0); } @@ -565,7 +609,7 @@ export class SplitView implements } private getLastFlexibleViewIndex(exceptIndex: number = null): number { - for (var i = this.views.length - 1; i >= 0; i--) { + for (let i = this.views.length - 1; i >= 0; i--) { if (exceptIndex === i) { continue; } @@ -578,7 +622,7 @@ export class SplitView implements } private layoutViews(): void { - for (var i = 0; i < this.views.length; i++) { + for (let i = 0; i < this.views.length; i++) { // Layout the view elements this.layoutViewElement(this.viewElements[i], this.views[i].size); @@ -590,18 +634,18 @@ export class SplitView implements this.sashes.forEach(s => s.layout()); // Update sashes enablement - var previous = false; - var collapsesDown = this.views.map(v => previous = (v.size - v.minimumSize > 0) || previous); + let previous = false; + let collapsesDown = this.views.map(v => previous = (v.size - v.minimumSize > 0) || previous); previous = false; - var expandsDown = this.views.map(v => previous = (v.maximumSize - v.size > 0) || previous); + let expandsDown = this.views.map(v => previous = (v.maximumSize - v.size > 0) || previous); - var reverseViews = this.views.slice().reverse(); + let reverseViews = this.views.slice().reverse(); previous = false; - var collapsesUp = reverseViews.map(v => previous = (v.size - v.minimumSize > 0) || previous).reverse(); + let collapsesUp = reverseViews.map(v => previous = (v.size - v.minimumSize > 0) || previous).reverse(); previous = false; - var expandsUp = reverseViews.map(v => previous = (v.maximumSize - v.size > 0) || previous).reverse(); + let expandsUp = reverseViews.map(v => previous = (v.maximumSize - v.size > 0) || previous).reverse(); this.sashes.forEach((s, i) => { if ((collapsesDown[i] && expandsUp[i + 1]) || (expandsDown[i] && collapsesUp[i + 1])) { @@ -631,16 +675,16 @@ export class SplitView implements this.setupAnimation(); - var index = this.views.indexOf(view); - var diff = Math.abs(size - view.size); - var up = numbers.countToArray(index - 1, -1); - var down = numbers.countToArray(index + 1, this.views.length); - var downUp = down.concat(up); + let index = this.views.indexOf(view); + let diff = Math.abs(size - view.size); + let up = numbers.countToArray(index - 1, -1); + let down = numbers.countToArray(index + 1, this.views.length); + let downUp = down.concat(up); - var collapses = this.views.map(v => Math.max(v.size - v.minimumSize, 0)); - var expands = this.views.map(v => Math.max(v.maximumSize - v.size, 0)); + let collapses = this.views.map(v => Math.max(v.size - v.minimumSize, 0)); + let expands = this.views.map(v => Math.max(v.maximumSize - v.size, 0)); - var collapse: number, collapseIndexes: number[], expandIndexes: number[]; + let collapse: number, collapseIndexes: number[], expandIndexes: number[]; if (size < view.size) { collapse = Math.min(downUp.reduce((t, i) => t + expands[i], 0), diff); @@ -688,10 +732,10 @@ export class SplitView implements } private getSashPosition(sash: sash.Sash): number { - var index = this.sashes.indexOf(sash); - var position = 0; + let index = this.sashes.indexOf(sash); + let position = 0; - for (var i = 0; i <= index; i++) { + for (let i = 0; i <= index; i++) { position += this.views[i].size; } diff --git a/src/vs/base/browser/ui/timer/timer.ts b/src/vs/base/browser/ui/timer/timer.ts index 21359157e7d..dbe89450682 100644 --- a/src/vs/base/browser/ui/timer/timer.ts +++ b/src/vs/base/browser/ui/timer/timer.ts @@ -6,38 +6,38 @@ 'use strict'; import 'vs/css!./timer'; -import Timer = require('vs/base/common/timer'); -import eventEmitter = require('vs/base/common/eventEmitter'); +import {TimeKeeper, ITimerEvent, getTimeKeeper} from 'vs/base/common/timer'; +import {ListenerUnbind} from 'vs/base/common/eventEmitter'; import DomUtils = require('vs/base/browser/dom'); interface IUnmatchedStartTimerEvent { - event: Timer.ITimerEvent; + event: ITimerEvent; domNode: HTMLElement; } export class TimeKeeperRenderer { - private listenersToRemove:eventEmitter.ListenerUnbind[]; - private timeKeeper:Timer.TimeKeeper; - private outerDomNode:HTMLElement; - private domNode:HTMLElement; - private renderCnt:number; - private lastEventIndex:number; + private listenersToRemove: ListenerUnbind[]; + private timeKeeper: TimeKeeper; + private outerDomNode: HTMLElement; + private domNode: HTMLElement; + private renderCnt: number; + private lastEventIndex: number; - private textFilter:string; - private textFilterDomNode:HTMLInputElement; - private timeFilter:number; - private timeFilterDomNode:HTMLInputElement; - private intervalTokenId:number; + private textFilter: string; + private textFilterDomNode: HTMLInputElement; + private timeFilter: number; + private timeFilterDomNode: HTMLInputElement; + private intervalTokenId: number; - private renderedEvents:{ - [key:string]:Timer.ITimerEvent; + private renderedEvents: { + [key: string]: ITimerEvent; }; - private onHide:()=>void; + private onHide: () => void; - constructor (onHide:()=>void) { - this.timeKeeper = Timer.getTimeKeeper(); + constructor(onHide: () => void) { + this.timeKeeper = getTimeKeeper(); this.onHide = onHide; this.lastEventIndex = 0; this.renderedEvents = {}; @@ -50,7 +50,7 @@ export class TimeKeeperRenderer { public destroy(): void { document.body.removeChild(this.outerDomNode); window.clearInterval(this.intervalTokenId); - this.listenersToRemove.forEach(function (element) { + this.listenersToRemove.forEach(function(element) { element(); }); this.listenersToRemove = []; @@ -61,7 +61,7 @@ export class TimeKeeperRenderer { this.outerDomNode.className = 'benchmarktimerbox'; // Clear - var cancel:HTMLInputElement = document.createElement('input'); + let cancel: HTMLInputElement = document.createElement('input'); cancel.type = 'button'; cancel.value = 'Clear'; this.listenersToRemove.push(DomUtils.addListener(cancel, 'click', () => this._onClear())); @@ -86,7 +86,7 @@ export class TimeKeeperRenderer { this.outerDomNode.appendChild(document.createTextNode('Hide time under')); this.outerDomNode.appendChild(this.timeFilterDomNode); - var hide:HTMLInputElement = document.createElement('input'); + let hide: HTMLInputElement = document.createElement('input'); hide.type = 'button'; hide.value = 'Close'; this.listenersToRemove.push(DomUtils.addListener(hide, 'click', () => { @@ -94,12 +94,12 @@ export class TimeKeeperRenderer { })); this.outerDomNode.appendChild(hide); - var heading = document.createElement('pre'); + let heading = document.createElement('pre'); heading.appendChild(document.createTextNode(this.renderRow('TOPIC', 'NAME', 'TOOK', 'START', 'END'))); this.outerDomNode.appendChild(heading); this.outerDomNode.appendChild(document.createElement('hr')); - var domNode = document.createElement('div'); + let domNode = document.createElement('div'); domNode.className = 'inner'; this.outerDomNode.appendChild(domNode); @@ -120,7 +120,7 @@ export class TimeKeeperRenderer { }); } - private matchesTextFilter(event:Timer.ITimerEvent): boolean { + private matchesTextFilter(event: ITimerEvent): boolean { if (!this.textFilter) { return true; } @@ -133,7 +133,7 @@ export class TimeKeeperRenderer { return false; } - private matchesTimeFilter(event:Timer.ITimerEvent): boolean { + private matchesTimeFilter(event: ITimerEvent): boolean { if (!this.timeFilter) { return true; } @@ -143,7 +143,7 @@ export class TimeKeeperRenderer { return false; } - private shouldShow(event:Timer.ITimerEvent): boolean { + private shouldShow(event: ITimerEvent): boolean { return this.matchesTextFilter(event) && this.matchesTimeFilter(event); } @@ -151,17 +151,17 @@ export class TimeKeeperRenderer { this.textFilter = this.textFilterDomNode.value; this.timeFilter = parseInt(this.timeFilterDomNode.value, 10); - var domNodes = Array.prototype.slice.call(this.domNode.children, 0); - for (var i = 0; i < domNodes.length; i++) { - var eventId = domNodes[i].getAttribute('data-event-id'); - var event = this.renderedEvents[eventId]; + let domNodes = Array.prototype.slice.call(this.domNode.children, 0); + for (let i = 0; i < domNodes.length; i++) { + let eventId = domNodes[i].getAttribute('data-event-id'); + let event = this.renderedEvents[eventId]; - if (this.shouldShow(event)) { - domNodes[i].style.display = 'inherit'; - } else { - domNodes[i].style.display = 'none'; - } + if (this.shouldShow(event)) { + domNodes[i].style.display = 'inherit'; + } else { + domNodes[i].style.display = 'none'; } + } } private _onClear(): void { @@ -171,18 +171,18 @@ export class TimeKeeperRenderer { DomUtils.clearNode(this.domNode); } - private leftPaddedString(size:number, padChar:string, str:string): string { - var spaces = this._repeatStr(padChar, Math.max(0, size - str.length)); + private leftPaddedString(size: number, padChar: string, str: string): string { + let spaces = this._repeatStr(padChar, Math.max(0, size - str.length)); return spaces + str; } - private rightPaddedString(size:number, padChar:string, str:string): string { - var spaces = this._repeatStr(padChar, Math.max(0, size - str.length)); + private rightPaddedString(size: number, padChar: string, str: string): string { + let spaces = this._repeatStr(padChar, Math.max(0, size - str.length)); return str + spaces; } - private renderRow(topic:string, name:string, timeTook:string, timeStart:string, timerEnd:string): string { - var result = ' '; + private renderRow(topic: string, name: string, timeTook: string, timeStart: string, timerEnd: string): string { + let result = ' '; result += this.rightPaddedString(10, ' ', topic); result += this.rightPaddedString(30, ' ', name); result += ' ' + this.leftPaddedString(15, ' ', timeTook); @@ -220,10 +220,10 @@ export class TimeKeeperRenderer { return this._twoPrecision(t) + ' h'; } - private _renderEvent(domNode:HTMLElement, event:Timer.ITimerEvent): void { - var start = event.startTime.getTime() - Timer.TimeKeeper.PARSE_TIME.getTime(); + private _renderEvent(domNode: HTMLElement, event: ITimerEvent): void { + let start = event.startTime.getTime() - TimeKeeper.PARSE_TIME.getTime(); - var result = this.renderRow( + let result = this.renderRow( event.topic, event.name, this._twoPrecision(event.timeTaken()), @@ -234,11 +234,11 @@ export class TimeKeeperRenderer { domNode.appendChild(document.createTextNode(result)); } - private _renderStartTimerEvent(event:Timer.ITimerEvent): void { - var domNode = document.createElement('pre'); + private _renderStartTimerEvent(event: ITimerEvent): void { + let domNode = document.createElement('pre'); this._renderEvent(domNode, event); this.domNode.appendChild(domNode); - var idString = event.id.toString(); + let idString = event.id.toString(); domNode.setAttribute('data-event-id', idString); domNode.className = 'timer-event-' + (event.id % 2); @@ -254,10 +254,10 @@ export class TimeKeeperRenderer { } private _render(): void { - var allEvents = this.timeKeeper.getCollectedEvents(), didSomething = false;; + let allEvents = this.timeKeeper.getCollectedEvents(), didSomething = false;; - for (var i = this.lastEventIndex; i < allEvents.length; i++) { - var ev = allEvents[i]; + for (let i = this.lastEventIndex; i < allEvents.length; i++) { + let ev = allEvents[i]; if (!ev.stopTime) { // This event is not yet finished => block @@ -278,9 +278,9 @@ export class TimeKeeperRenderer { this.lastEventIndex = allEvents.length; } - private _repeatStr(str:string, cnt:number): string { - var r = ''; - for (var i = 0; i < cnt; i++) { + private _repeatStr(str: string, cnt: number): string { + let r = ''; + for (let i = 0; i < cnt; i++) { r += str; } return r; diff --git a/src/vs/base/browser/ui/toolbar/toolbar.css b/src/vs/base/browser/ui/toolbar/toolbar.css index ccad5186e58..425eb455375 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.css +++ b/src/vs/base/browser/ui/toolbar/toolbar.css @@ -33,24 +33,6 @@ background-image: url('ellipsis-inverse.svg'); } -/*.monaco-toolbar .monaco-action-bar:focus:after { - content: ""; - position: relative; - top: -5px; - height: 0; - width: 100%; - border-top: 2px solid #39F; - display: block; -} - -.vs .monaco-toolbar .monaco-action-bar .action-label:focus { - background-color: #DCEBFC; -} - -.vs-dark .monaco-toolbar .monaco-action-bar .action-label:focus { - background-color: #073655; -}*/ - /* High Contrast Theming */ .hc-black .monaco-toolbar .action-label.toolbar-toggle-more { background: none; @@ -61,14 +43,6 @@ position: absolute; top: 12px; left: 8px; - height: 16px; + height: 16px; width: 16px; -} - -/*.hc-black .monaco-toolbar .monaco-action-bar:focus:after { - border-color: #DF740C; -} - -.hc-black .monaco-toolbar .monaco-action-bar .action-label:focus { - border: 1px solid #DF740C; -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 088b49d017a..d82c520f8c2 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -7,6 +7,7 @@ import 'vs/css!./toolbar'; import nls = require('vs/nls'); +import {Promise} from 'vs/base/common/winjs.base'; import {IDisposable} from 'vs/base/common/lifecycle'; import {Builder, $} from 'vs/base/browser/builder'; import types = require('vs/base/common/types'); @@ -15,7 +16,7 @@ import {ActionBar, ActionsOrientation, IActionItemProvider, BaseActionItem} from import {IContextMenuProvider, DropdownMenu, IActionProvider, ILabelRenderer, IDropdownMenuOptions} from 'vs/base/browser/ui/dropdown/dropdown'; import {ListenerUnbind} from 'vs/base/common/eventEmitter'; -export var CONTEXT = 'context.toolbar'; +export const CONTEXT = 'context.toolbar'; export interface IToolBarOptions { orientation?: ActionsOrientation; @@ -34,9 +35,9 @@ export class ToolBar { constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) { this.options = options; - this.toggleMenuAction = new ToggleMenuAction(); + this.toggleMenuAction = new ToggleMenuAction(() => this.toggleMenuActionItem && this.toggleMenuActionItem.show()); - var element = document.createElement('div'); + let element = document.createElement('div'); element.className = 'monaco-toolbar'; container.appendChild(element); @@ -58,7 +59,6 @@ export class ToolBar { (action).menuActions, contextMenuProvider, this.options.actionItemProvider, - this.options.orientation === ActionsOrientation.HORIZONTAL, this.actionRunner, 'toolbar-toggle-more' ); @@ -85,7 +85,7 @@ export class ToolBar { public setActions(primaryActions: IAction[], secondaryActions?: IAction[]): () => void { return () => { - var primaryActionsToSet = primaryActions ? primaryActions.slice(0) : []; + let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : []; // Inject additional action to open secondary actions if present this.hasSecondaryActions = secondaryActions && secondaryActions.length > 0; @@ -104,7 +104,7 @@ export class ToolBar { // Add after the "..." action if we have secondary actions if (this.hasSecondaryActions) { - var itemCount = this.actionBar.length(); + let itemCount = this.actionBar.length(); this.actionBar.push(primaryActions, { icon: true, label: false, index: itemCount }); } @@ -130,9 +130,18 @@ class ToggleMenuAction extends Action { public static ID = 'toolbar.toggle.more'; private _menuActions: IAction[]; + private toggleDropdownMenu: () => void; - constructor() { + constructor(toggleDropdownMenu: () => void) { super(ToggleMenuAction.ID, nls.localize('more', "More"), null, true); + + this.toggleDropdownMenu = toggleDropdownMenu; + } + + public run(): Promise { + this.toggleDropdownMenu(); + + return Promise.as(true); } public get menuActions() { @@ -145,59 +154,40 @@ class ToggleMenuAction extends Action { } export class DropdownMenuActionItem extends BaseActionItem { - private menuActionsOrProvider: any; - private animateClick: boolean; private dropdownMenu: DropdownMenu; private toUnbind: ListenerUnbind; private contextMenuProvider: IContextMenuProvider; private actionItemProvider: IActionItemProvider; private clazz: string; - constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, animateClick: boolean, actionRunner: IActionRunner, clazz: string); - constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, animateClick: boolean, actionRunner: IActionRunner, clazz: string); - constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, animateClick: boolean, actionRunner: IActionRunner, clazz: string) { + constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, clazz: string); + constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, clazz: string); + constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, clazz: string) { super(null, action); this.menuActionsOrProvider = menuActionsOrProvider; this.contextMenuProvider = contextMenuProvider; this.actionItemProvider = actionItemProvider; - this.animateClick = animateClick; this.actionRunner = actionRunner; this.clazz = clazz; } public render(container: HTMLElement): void { - super.render(container); - - var labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable => { - var e = $('a.action-label').attr({ - tabIndex: '-1', + let labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable => { + this.builder = $('a.action-label').attr({ + tabIndex: '0', role: 'menuitem', title: this._action.label || '', class: this.clazz - }).appendTo(el); + }); - $('span.label').text(this.getAction().label).appendTo(e); - - if (this.animateClick) { - $(el).on('mousedown', (e: MouseEvent) => { - if (e.button === 0) { - $(el).addClass('active'); - } - }); - - $(el).on(['mouseup', 'mouseout'], (e: MouseEvent) => { - if (e.button === 0) { - $(el).removeClass('active'); - } - }); - } + this.builder.appendTo(el); return null; }; - var options: IDropdownMenuOptions = { + let options: IDropdownMenuOptions = { contextMenuProvider: this.contextMenuProvider, labelRenderer: labelRenderer }; @@ -220,6 +210,12 @@ export class DropdownMenuActionItem extends BaseActionItem { this.toUnbind = this.addEmitter(this.dropdownMenu); } + public show(): void { + if (this.dropdownMenu) { + this.dropdownMenu.show(); + } + } + public dispose(): void { this.toUnbind(); this.dropdownMenu.dispose(); diff --git a/src/vs/base/common/diagnostics.ts b/src/vs/base/common/diagnostics.ts index c68524b1af5..1cdf9a2fba3 100644 --- a/src/vs/base/common/diagnostics.ts +++ b/src/vs/base/common/diagnostics.ts @@ -27,10 +27,13 @@ function fifo(array:any[], size:number) { } } -export function register(what:string, fn:Function):(...args:any[])=>void { - return () => { - // Intentional empty, disable for now because it is leaking memory - }; +export function register(what: string, fn: Function): (...args: any[]) => void { + + if (true) { + return () => { + // Intentional empty, disable for now because it is leaking memory + }; + } // register switch var flag = switches[what] || false; diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index eeedd889375..9f5499a76f7 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -233,7 +233,7 @@ export class ConnectionError implements Error { // Bug: Can not subclass a JS Type. Do it manually (as done in WinJS.Class.derive) objects.derive(Error, ConnectionError); -function _xhrToErrorMessage(xhr: IConnectionErrorData, verbose: boolean): string { +function xhrToErrorMessage(xhr: IConnectionErrorData, verbose: boolean): string { let ce = new ConnectionError(xhr); if (verbose) { return ce.verboseMessage; @@ -242,18 +242,28 @@ function _xhrToErrorMessage(xhr: IConnectionErrorData, verbose: boolean): string } } -function _exceptionToErrorMessage(exception: any, verbose: boolean): string { - if (verbose && exception.message && (exception.stack || exception.stacktrace)) { - return nls.localize('stackTrace.format', "{0}: {1}", exception.message, exception.stack || exception.stacktrace); - } - +function exceptionToErrorMessage(exception: any, verbose: boolean): string { if (exception.message) { - return exception.message; + if (verbose && (exception.stack || exception.stacktrace)) { + return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), exception.stack || exception.stacktrace); + } + + return detectSystemErrorMessage(exception); } return nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details."); } +function detectSystemErrorMessage(exception: any): string { + + // See https://nodejs.org/api/errors.html#errors_class_system_error + if (typeof exception.code === 'string' && typeof exception.errno === 'number' && typeof exception.syscall === 'string') { + return nls.localize('nodeExceptionMessage', "A system error occured ({0})", exception.message); + } + + return exception.message; +} + /** * Tries to generate a human readable error message out of the error. If the verbose parameter * is set to true, the error message will include stacktrace details if provided. @@ -280,7 +290,7 @@ export function toErrorMessage(error: any = null, verbose: boolean = false): str } if (!types.isUndefinedOrNull(error.status)) { - return _xhrToErrorMessage(error, verbose); + return xhrToErrorMessage(error, verbose); } if (error.detail) { @@ -288,33 +298,33 @@ export function toErrorMessage(error: any = null, verbose: boolean = false): str if (detail.error) { if (detail.error && !types.isUndefinedOrNull(detail.error.status)) { - return _xhrToErrorMessage(detail.error, verbose); + return xhrToErrorMessage(detail.error, verbose); } if (types.isArray(detail.error)) { for (let i = 0; i < detail.error.length; i++) { if (detail.error[i] && !types.isUndefinedOrNull(detail.error[i].status)) { - return _xhrToErrorMessage(detail.error[i], verbose); + return xhrToErrorMessage(detail.error[i], verbose); } } } else { - return _exceptionToErrorMessage(detail.error, verbose); + return exceptionToErrorMessage(detail.error, verbose); } } if (detail.exception) { if (!types.isUndefinedOrNull(detail.exception.status)) { - return _xhrToErrorMessage(detail.exception, verbose); + return xhrToErrorMessage(detail.exception, verbose); } - return _exceptionToErrorMessage(detail.exception, verbose); + return exceptionToErrorMessage(detail.exception, verbose); } } if (error.stack) { - return _exceptionToErrorMessage(error, verbose); + return exceptionToErrorMessage(error, verbose); } if (error.message) { @@ -324,7 +334,7 @@ export function toErrorMessage(error: any = null, verbose: boolean = false): str return nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details."); } -let canceledName = 'Canceled'; +const canceledName = 'Canceled'; /** * Checks if the given error is a promise in canceled state diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index 25ff8df49cc..91789e1010b 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -198,7 +198,7 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca function scanNext():SyntaxKind { value = ''; - scanError = ScanError.None, + scanError = ScanError.None; tokenOffset = pos; diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index b7f96010d96..95555ff50e1 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -7,6 +7,7 @@ import paths = require('vs/base/common/paths'); import types = require('vs/base/common/types'); import strings = require('vs/base/common/strings'); +import {match} from 'vs/base/common/glob'; export let MIME_TEXT = 'text/plain'; export let MIME_BINARY = 'application/octet-stream'; @@ -71,12 +72,12 @@ export function generateKnownFilenames(onlyExtensions: boolean = true): any { /** * Allow to register extra text mimes dynamically based on filename */ -export function registerTextMimeByFilename(nameOrExtensionOrPrefix: string, mime: string): void { - if (nameOrExtensionOrPrefix && mime) { - if (registeredTextMimesByFilename[nameOrExtensionOrPrefix] && registeredTextMimesByFilename[nameOrExtensionOrPrefix] !== mime) { - console.warn('Overwriting filename <<' + nameOrExtensionOrPrefix + '>> to now point to mime <<' + mime + '>>'); +export function registerTextMimeByFilename(nameOrPatternOrPrefix: string, mime: string): void { + if (nameOrPatternOrPrefix && mime) { + if (registeredTextMimesByFilename[nameOrPatternOrPrefix] && registeredTextMimesByFilename[nameOrPatternOrPrefix] !== mime) { + console.warn('Overwriting filename <<' + nameOrPatternOrPrefix + '>> to now point to mime <<' + mime + '>>'); } - registeredTextMimesByFilename[nameOrExtensionOrPrefix] = mime; + registeredTextMimesByFilename[nameOrPatternOrPrefix] = mime; } } @@ -142,29 +143,29 @@ export function guessMimeTypes(path: string, firstLine?: string): string[] { let exactNameMatch: string; let extensionMatch: string; - let prefixMatch: string; + let patternNameMatch: string; // Check for dynamically registered match based on filename and extension - for (let nameOrExtensionOrPrefix in registeredTextMimesByFilename) { - let nameOrExtensionOrPrefixLower: string = nameOrExtensionOrPrefix.toLowerCase(); + for (let nameOrPatternOrPrefix in registeredTextMimesByFilename) { + let nameOrPatternOrExtensionLower: string = nameOrPatternOrPrefix.toLowerCase(); // First exact name match - if (!exactNameMatch && filename === nameOrExtensionOrPrefixLower) { - exactNameMatch = nameOrExtensionOrPrefix; + if (!exactNameMatch && filename === nameOrPatternOrExtensionLower) { + exactNameMatch = nameOrPatternOrPrefix; break; // take it! } - // Longest extension match - if (nameOrExtensionOrPrefix[0] === '.' && strings.endsWith(filename, nameOrExtensionOrPrefixLower)) { - if (!extensionMatch || nameOrExtensionOrPrefixLower.length > extensionMatch.length) { - extensionMatch = nameOrExtensionOrPrefix; + // Longest pattern match + if (match(nameOrPatternOrExtensionLower, filename)) { + if (!patternNameMatch || nameOrPatternOrExtensionLower.length > patternNameMatch.length) { + patternNameMatch = nameOrPatternOrPrefix; } } - // Longest prefix match - if (nameOrExtensionOrPrefixLower.slice(-1) === '*' && strings.startsWith(filename, nameOrExtensionOrPrefixLower.slice(0, -1))) { - if (!prefixMatch || nameOrExtensionOrPrefixLower.length > prefixMatch.length) { - prefixMatch = nameOrExtensionOrPrefix; + // Longest extension match + if (nameOrPatternOrPrefix[0] === '.' && strings.endsWith(filename, nameOrPatternOrExtensionLower)) { + if (!extensionMatch || nameOrPatternOrExtensionLower.length > extensionMatch.length) { + extensionMatch = nameOrPatternOrPrefix; } } } @@ -174,14 +175,14 @@ export function guessMimeTypes(path: string, firstLine?: string): string[] { return [registeredTextMimesByFilename[exactNameMatch], MIME_TEXT]; } - // 3.) Match on extension comes next - if (extensionMatch) { - return [registeredTextMimesByFilename[extensionMatch], MIME_TEXT]; + // 3.) Match on pattern + if (patternNameMatch) { + return [registeredTextMimesByFilename[patternNameMatch], MIME_TEXT]; } - // 4.) Match on prefix - if (prefixMatch) { - return [registeredTextMimesByFilename[prefixMatch], MIME_TEXT]; + // 4.) Match on extension comes next + if (extensionMatch) { + return [registeredTextMimesByFilename[extensionMatch], MIME_TEXT]; } return [MIME_UNKNOWN]; diff --git a/src/vs/base/node/service.cp.ts b/src/vs/base/node/service.cp.ts index 536acf9c86f..46a6e95e01b 100644 --- a/src/vs/base/node/service.cp.ts +++ b/src/vs/base/node/service.cp.ts @@ -133,7 +133,7 @@ export class Client implements IDisposable { this.child = cp.fork(this.modulePath, args, forkOpts); this._client = new IPCClient({ - send: r => this.child.connected && this.child.send(r), + send: r => this.child && this.child.connected && this.child.send(r), onMessage: cb => { this.child.on('message', (msg) => { diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index ad9671d715b..88a53b855fb 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -182,7 +182,7 @@ export class QuickOpenEntry { return compareAnything(nameA, nameB, lookFor); } - public static compareByScore(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string, scorerCache?: { [key: string]: number }): number { + public static compareByScore(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string, lookForNormalizedLower: string, scorerCache?: { [key: string]: number }): number { const labelA = elementA.getLabel(); const labelB = elementB.getLabel(); @@ -229,7 +229,12 @@ export class QuickOpenEntry { return resourceA.fsPath.length < resourceB.fsPath.length ? -1 : 1; } - return QuickOpenEntry.compare(elementA, elementB, lookFor); + // Finally compare by label or resource path + if (labelA === labelB && resourceA && resourceB) { + return compareAnything(resourceA.fsPath, resourceB.fsPath, lookForNormalizedLower); + } + + return compareAnything(labelA, labelB, lookForNormalizedLower); } /** diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index d8aa438ae66..ed7b7d1a477 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -130,8 +130,14 @@ export class QuickOpenWidget implements IModelProvider { DOM.addDisposableListener(this.inputBox.inputElement, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { let keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); + + // Do not handle Tab: It is used to navigate between elements without mouse + if (keyboardEvent.keyCode === KeyCode.Tab) { + return; + } + // Pass tree navigation keys to the tree but leave focus in input field - if (keyboardEvent.keyCode === KeyCode.Tab || keyboardEvent.keyCode === KeyCode.DownArrow || keyboardEvent.keyCode === KeyCode.UpArrow || keyboardEvent.keyCode === KeyCode.PageDown || keyboardEvent.keyCode === KeyCode.PageUp) { + else if (keyboardEvent.keyCode === KeyCode.Tab || keyboardEvent.keyCode === KeyCode.DownArrow || keyboardEvent.keyCode === KeyCode.UpArrow || keyboardEvent.keyCode === KeyCode.PageDown || keyboardEvent.keyCode === KeyCode.PageUp) { DOM.EventHelper.stop(e, true); this.navigateInTree(keyboardEvent.keyCode, keyboardEvent.shiftKey); @@ -825,7 +831,6 @@ export class QuickOpenWidget implements IModelProvider { const relatedTarget = (e).relatedTarget; if (!this.quickNavigateConfiguration && DOM.isAncestor(relatedTarget, this.builder.getHTMLElement())) { - this.inputBox.focus(); // user clicked somewhere into quick open, so we restore focus to input return; } diff --git a/src/vs/base/parts/quickopen/browser/quickopen.css b/src/vs/base/parts/quickopen/browser/quickopen.css index 2e43cc9c9f8..6b5911286d4 100644 --- a/src/vs/base/parts/quickopen/browser/quickopen.css +++ b/src/vs/base/parts/quickopen/browser/quickopen.css @@ -28,7 +28,6 @@ width: 588px; border: none; margin: 6px; - outline: 0; } .quick-open-widget .quick-open-input .monaco-inputbox { diff --git a/src/vs/base/parts/tree/browser/actionsRenderer.css b/src/vs/base/parts/tree/browser/actionsRenderer.css index 938cc294240..4e3648df340 100644 --- a/src/vs/base/parts/tree/browser/actionsRenderer.css +++ b/src/vs/base/parts/tree/browser/actionsRenderer.css @@ -24,6 +24,7 @@ } .monaco-tree .monaco-tree-row:hover:not(.highlighted) > .content.actions > .primary-action-bar, +.monaco-tree.focused .monaco-tree-row.focused > .content.actions > .primary-action-bar, .monaco-tree .monaco-tree-row > .content.actions.more > .primary-action-bar { display: inherit; } @@ -46,7 +47,6 @@ .monaco-tree.focused .monaco-tree-row.focused:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #DCEBFC; } .monaco-tree.focused .monaco-tree-row.selected:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #4FA7FF; color: white; } .monaco-tree.focused .monaco-tree-row.focused.selected:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #3399FF; color: white; } -.monaco-tree .monaco-tree-row.focused:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #eaeaea; } .monaco-tree .monaco-tree-row.selected:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #CCCEDB; } .monaco-tree .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) > .content.actions > .primary-action-bar { background-color: #F0F0F0; } .monaco-tree .monaco-tree-row.drop-target > .content.actions > .primary-action-bar { background-color: #DDECFF; } @@ -56,7 +56,6 @@ .vs-dark .monaco-tree.focused .monaco-tree-row.focused:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #073655; } .vs-dark .monaco-tree.focused .monaco-tree-row.selected:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #0E639C; color: white; } .vs-dark .monaco-tree.focused .monaco-tree-row.focused.selected:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #094771; color: white; } -.vs-dark .monaco-tree .monaco-tree-row.focused:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #2f3334; } .vs-dark .monaco-tree .monaco-tree-row.selected:not(.highlighted) > .content.actions > .primary-action-bar { background-color: #3F3F46; } .vs-dark .monaco-tree .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) > .content.actions > .primary-action-bar { background-color: #2A2D2E; } .vs-dark .monaco-tree .monaco-tree-row.drop-target > .content.actions > .primary-action-bar { background-color: #383B3D; } @@ -65,7 +64,6 @@ /* High Contrast Theming */ .hc-black .monaco-tree.focused .monaco-tree-row.focused:not(.highlighted) > .content.actions > .primary-action-bar, .hc-black .monaco-tree.focused .monaco-tree-row.selected:not(.highlighted) > .content.actions > .primary-action-bar, -.hc-black .monaco-tree .monaco-tree-row.focused:not(.highlighted) > .content.actions > .primary-action-bar, .hc-black .monaco-tree .monaco-tree-row.selected:not(.highlighted) > .content.actions > .primary-action-bar, .hc-black .monaco-tree .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) > .content.actions > .primary-action-bar, .hc-black .monaco-tree .monaco-tree-row.drop-target > .content.actions > .primary-action-bar, diff --git a/src/vs/base/parts/tree/browser/tree.css b/src/vs/base/parts/tree/browser/tree.css index 968e77f2989..628d9f34143 100644 --- a/src/vs/base/parts/tree/browser/tree.css +++ b/src/vs/base/parts/tree/browser/tree.css @@ -15,10 +15,6 @@ position: relative; } -.monaco-tree:focus { - outline: 0; -} - .monaco-tree > .monaco-scrollable-element { height: 100%; } @@ -32,6 +28,7 @@ .monaco-tree .monaco-tree-rows { position: absolute; width: 100%; + height: 100%; } .monaco-tree .monaco-tree-rows > .monaco-tree-row { @@ -151,14 +148,6 @@ opacity: 0.3; } -/* Fake row */ -.monaco-tree .monaco-tree-rows > .monaco-tree-row.fake { - position: absolute; - visibility: hidden; - top: -1000px; - left: -1000px; - z-index: -100; -} /* Bare row */ .monaco-tree.bare .monaco-tree-wrapper.drop-target, @@ -171,7 +160,6 @@ .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { background-color: #DCEBFC; } .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: #4FA7FF; color: white; } .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { background-color: #3399FF; color: white; } -.monaco-tree .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { background-color: #eaeaea; } .monaco-tree .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: #CCCEDB; } .monaco-tree .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { background-color: #F0F0F0; } .monaco-tree .monaco-tree-wrapper.drop-target, @@ -194,7 +182,6 @@ .vs-dark .monaco-tree.focused .monaco-tree-row.focused:not(.highlighted) { background-color: #073655; } .vs-dark .monaco-tree.focused .monaco-tree-row.selected:not(.highlighted) { background-color: #0E639C; color: white; } .vs-dark .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { background-color: #094771; color: white; } -.vs-dark .monaco-tree .monaco-tree-row.focused:not(.highlighted) { background-color: #2f3334; } .vs-dark .monaco-tree .monaco-tree-row.selected:not(.highlighted) { background-color: #3F3F46; } .vs-dark .monaco-tree .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { background-color: #2A2D2E; } .vs-dark .monaco-tree-wrapper.drop-target, @@ -214,7 +201,6 @@ .hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row { background: none !important; } .hc-black .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { border: 1px dotted #DF740C; } .hc-black .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { border: 1px solid #DF740C; } -.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { border: 1px dotted #DF740C; } .hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { border: 1px solid #DF740C; } .hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { border: 1px dashed #DF740C; } .hc-black .monaco-tree .monaco-tree-wrapper.drop-target, @@ -235,4 +221,4 @@ border: none; content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTExIDEwLjA3aC01LjY1Nmw1LjY1Ni01LjY1NnY1LjY1NnoiLz48L3N2Zz4="); top: 3px; -} +} \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index 03169b39817..4f23421e0cc 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -309,12 +309,6 @@ export interface ITree extends Events.IEventEmitter { */ hasTrait(trait: string, element: any): boolean; - /** - * Allows to render an invisible fake row, for measurement purposes. - * Returns whatever fn(...) returns. - */ - withFakeRow(fn:(container:HTMLElement)=>any):any; - /** * Returns a navigator which allows to discover the visible and * expanded elements in the tree. diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index 4336578cc29..59f601945c6 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -307,10 +307,6 @@ export class Tree extends Events.EventEmitter implements _.ITree { return this.model.hasTrait(trait, element); } - public withFakeRow(fn:(container:HTMLElement)=>any):any { - return this.view.withFakeRow(fn); - } - getNavigator(): INavigator { return new MappedNavigator(this.model.getNavigator(), i => i && i.getElement()); } diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 86d0012bb9e..0b2a86d25a9 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -390,9 +390,6 @@ export class TreeView extends HeightMap implements IScrollable { private lastPointerType:string; private lastClickTimeStamp: number = 0; - private fakeRow: HTMLElement; - private fakeContent: HTMLElement; - private _viewHeight: number; private renderTop: number; private renderHeight: number; @@ -480,15 +477,6 @@ export class TreeView extends HeightMap implements IScrollable { this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-tree-rows'; - this.fakeRow = document.createElement('div'); - this.fakeRow.className = 'monaco-tree-row fake'; - - this.fakeContent = document.createElement('div'); - this.fakeContent.className = 'content'; - - this.fakeRow.appendChild(this.fakeContent); - this.rowsContainer.appendChild(this.fakeRow); - var focusTracker = DOM.trackFocus(this.domNode); focusTracker.addFocusListener((e: FocusEvent) => this.onFocus(e)); focusTracker.addBlurListener((e: FocusEvent) => this.onBlur(e)); @@ -705,6 +693,9 @@ export class TreeView extends HeightMap implements IScrollable { case 'item:removeTrait': this.onItemRemoveTrait(data); break; + case 'focus': + this.onModelFocusChange(); + break; } } @@ -731,10 +722,6 @@ export class TreeView extends HeightMap implements IScrollable { this.scrollableElement.onElementInternalDimensions(); } - public withFakeRow(fn:(container:HTMLElement)=>any):any { - return fn(this.fakeContent); - } - public focusNextPage(eventPayload?:any): void { var lastPageIndex = this.indexAt(this.scrollTop + this.viewHeight); lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; @@ -1065,6 +1052,10 @@ export class TreeView extends HeightMap implements IScrollable { } } + private onModelFocusChange(): void { + DOM.toggleClass(this.domNode, 'no-item-focus', !this.model || !this.model.getFocus()); + } + // HeightMap "events" public onInsertItem(item: ViewItem): void { diff --git a/src/vs/editor/browser/controller/keyboardHandler.ts b/src/vs/editor/browser/controller/keyboardHandler.ts index 55990580065..5a36eafeb06 100644 --- a/src/vs/editor/browser/controller/keyboardHandler.ts +++ b/src/vs/editor/browser/controller/keyboardHandler.ts @@ -240,23 +240,23 @@ export class KeyboardHandler extends ViewEventHandler implements Lifecycle.IDisp let visibleRange = this.viewHelper.visibleRangeForPositionRelativeToEditor(lineNumber, column); if (visibleRange) { - this.textArea.actual.style.top = visibleRange.top + 'px'; - this.textArea.actual.style.left = this.contentLeft + visibleRange.left - this.scrollLeft + 'px'; + DomUtils.StyleMutator.setTop(this.textArea.actual, visibleRange.top); + DomUtils.StyleMutator.setLeft(this.textArea.actual, this.contentLeft + visibleRange.left - this.scrollLeft); } if (Browser.isIE11orEarlier) { - this.textArea.actual.style.width = this.contentWidth + 'px'; + DomUtils.StyleMutator.setWidth(this.textArea.actual, this.contentWidth); } // Show the textarea - this.textArea.actual.style.height = this.context.configuration.editor.lineHeight + 'px'; + DomUtils.StyleMutator.setHeight(this.textArea.actual, this.context.configuration.editor.lineHeight); DomUtils.addClass(this.viewHelper.viewDomNode, 'ime-input'); })); this._toDispose.push(this.textAreaHandler.onCompositionEnd((e) => { this.textArea.actual.style.height = ''; this.textArea.actual.style.width = ''; - this.textArea.actual.style.left = '0px'; - this.textArea.actual.style.top = '0px'; + DomUtils.StyleMutator.setLeft(this.textArea.actual, 0); + DomUtils.StyleMutator.setTop(this.textArea.actual, 0); DomUtils.removeClass(this.viewHelper.viewDomNode, 'ime-input'); })); @@ -271,6 +271,17 @@ export class KeyboardHandler extends ViewEventHandler implements Lifecycle.IDisp this._toDispose = Lifecycle.disposeAll(this._toDispose); } + public focusTextArea(): void { + this.textAreaHandler.writePlaceholderAndSelectTextAreaSync(); + } + + public onConfigurationChanged(e: EditorCommon.IConfigurationChangedEvent): boolean { + // Give textarea same font size & line height as editor, for the IME case (when the textarea is visible) + DomUtils.StyleMutator.setFontSize(this.textArea.actual, this.context.configuration.editor.fontSize); + DomUtils.StyleMutator.setLineHeight(this.textArea.actual, this.context.configuration.editor.lineHeight); + return false; + } + public onScrollChanged(e:EditorCommon.IScrollEvent): boolean { this.scrollLeft = e.scrollLeft; return false; diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 6f4b462e9f8..ef3607e7931 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -431,9 +431,6 @@ export class View extends ViewEventHandler implements EditorBrowser.IView, Lifec if (e.stylingInfo) { Configuration.applyEditorStyling(this.domNode, this.context.configuration.editor.stylingInfo); } - // Give textarea same font size & line height as editor, for the IME case (when the textarea is visible) - DomUtils.StyleMutator.setFontSize(this.textArea, this.context.configuration.editor.fontSize); - DomUtils.StyleMutator.setLineHeight(this.textArea, this.context.configuration.editor.lineHeight); return false; } public onScrollChanged(e:EditorCommon.IScrollEvent): boolean { @@ -619,12 +616,7 @@ export class View extends ViewEventHandler implements EditorBrowser.IView, Lifec if (this._isDisposed) { throw new Error('ViewImpl.focus: View is disposed'); } - // Chrome does not trigger the focus event at all if focus is in url bar and clicking into the editor - // Calling .focus() and then .select() seems to be a good workaround for Chrome in this case - var state = DomUtils.saveParentsScrollTop(this.textArea); - this.textArea.focus(); - DomUtils.selectTextInInputElement(this.textArea); - DomUtils.restoreParentsScrollTop(this.textArea, state); + this.keyboardHandler.focusTextArea(); // IE does not trigger the focus event immediately, so we must help it a little bit this._setHasFocus(true); diff --git a/src/vs/editor/common/controller/textAreaHandler.ts b/src/vs/editor/common/controller/textAreaHandler.ts index b3a41e89edb..3d2a0e6c8b3 100644 --- a/src/vs/editor/common/controller/textAreaHandler.ts +++ b/src/vs/editor/common/controller/textAreaHandler.ts @@ -10,7 +10,7 @@ import {Disposable} from 'vs/base/common/lifecycle'; import {Range} from 'vs/editor/common/core/range'; import {Position} from 'vs/editor/common/core/position'; import {CommonKeybindings} from 'vs/base/common/keyCodes'; -import {IKeyboardEventWrapper, ITextAreaWrapper, IClipboardEvent, ISimpleModel, TextAreaState} from 'vs/editor/common/controller/textAreaState'; +import {IKeyboardEventWrapper, ITextAreaWrapper, IClipboardEvent, ISimpleModel, TextAreaState, IENarratorTextAreaState, NVDATextAreaState} from 'vs/editor/common/controller/textAreaState'; import Event, {Emitter} from 'vs/base/common/event'; enum ReadFromTextArea { @@ -79,7 +79,6 @@ export class TextAreaHandler extends Disposable { private hasFocus:boolean; private asyncReadFromTextArea: RunOnceScheduler; - private asyncSetSelectionToTextArea: RunOnceScheduler; private asyncTriggerCut: RunOnceScheduler; private lastKeyPressTime:number; @@ -104,12 +103,12 @@ export class TextAreaHandler extends Disposable { this.cursorPosition = new Position(1, 1); this.asyncReadFromTextArea = new RunOnceScheduler(null, 0); - this.asyncSetSelectionToTextArea = new RunOnceScheduler(() => this._writePlaceholderAndSelectTextArea(), 0); this.asyncTriggerCut = new RunOnceScheduler(() => this._onCut.fire(), 0); this.lastCopiedValue = null; this.lastCopiedValueIsFromEmptySelection = false; - this.textAreaState = TextAreaState.EMPTY; + this.textAreaState = IENarratorTextAreaState.EMPTY; + // this.textAreaState = NVDATextAreaState.EMPTY; this.hasFocus = false; @@ -136,7 +135,7 @@ export class TextAreaHandler extends Disposable { let shouldEmptyTextArea = (timeSinceLastCompositionEnd >= 100); if (shouldEmptyTextArea) { if (!this.Browser.isIE11orEarlier) { - this.setTextAreaState(TextAreaState.EMPTY); + this.setTextAreaState('compositionstart', this.textAreaState.toEmpty()); } } @@ -254,8 +253,6 @@ export class TextAreaHandler extends Disposable { text: replacedChar, replacePreviousCharacter: true }); - - this.asyncSetSelectionToTextArea.schedule(); })); } @@ -276,18 +273,17 @@ export class TextAreaHandler extends Disposable { } else { if (this.textArea.selectionStart !== this.textArea.selectionEnd) { // Clean up the textarea, to get a clean paste - this.setTextAreaState(TextAreaState.EMPTY); + this.setTextAreaState('paste', this.textAreaState.toEmpty()); } this._scheduleReadFromTextArea(ReadFromTextArea.Paste); } })); - this._writePlaceholderAndSelectTextArea(); + this._writePlaceholderAndSelectTextArea('ctor'); } public dispose(): void { this.asyncReadFromTextArea.dispose(); - this.asyncSetSelectionToTextArea.dispose(); this.asyncTriggerCut.dispose(); super.dispose(); } @@ -295,16 +291,20 @@ export class TextAreaHandler extends Disposable { // --- begin event handlers public setHasFocus(isFocused:boolean): void { + if (this.hasFocus === isFocused) { + // no change + return; + } this.hasFocus = isFocused; if (this.hasFocus) { - this.asyncSetSelectionToTextArea.schedule(); + this._writePlaceholderAndSelectTextArea('focusgain'); } } public setCursorSelections(primary: IEditorRange, secondary: IEditorRange[]): void { this.selection = primary; this.selections = [primary].concat(secondary); - this.asyncSetSelectionToTextArea.schedule(); + this._writePlaceholderAndSelectTextArea('selection changed'); } public setCursorPosition(primary: IEditorPosition): void { @@ -313,13 +313,13 @@ export class TextAreaHandler extends Disposable { // --- end event handlers - private setTextAreaState(textAreaState:TextAreaState): void { + private setTextAreaState(reason:string, textAreaState:TextAreaState): void { if (!this.hasFocus) { textAreaState = textAreaState.resetSelection(); } this.lastValueWrittenToTheTextArea = textAreaState.getValue(); - textAreaState.applyToTextArea(this.textArea, this.hasFocus); + textAreaState.applyToTextArea(reason, this.textArea, this.hasFocus); this.textAreaState = textAreaState; } @@ -346,9 +346,7 @@ export class TextAreaHandler extends Disposable { setTimeout(() => { // cancel reading if previous keydown was canceled, but a keypress/input were still generated if (e.isDefaultPrevented()) { - // this._scheduleReadFromTextArea this.asyncReadFromTextArea.cancel(); - this.asyncSetSelectionToTextArea.schedule(); } }, 0); } @@ -371,7 +369,6 @@ export class TextAreaHandler extends Disposable { // ------------- Operations that are always executed asynchronously private _scheduleReadFromTextArea(command:ReadFromTextArea): void { - this.asyncSetSelectionToTextArea.cancel(); this.asyncReadFromTextArea.setRunner(() => this._readFromTextArea(command)); this.asyncReadFromTextArea.schedule(); } @@ -394,8 +391,6 @@ export class TextAreaHandler extends Disposable { } else { this.executePaste(txt); } - - this.asyncSetSelectionToTextArea.schedule(); } private executePaste(txt:string): void { @@ -413,14 +408,18 @@ export class TextAreaHandler extends Disposable { }); } - private _writePlaceholderAndSelectTextArea(): void { + public writePlaceholderAndSelectTextAreaSync(): void { + this._writePlaceholderAndSelectTextArea('focusTextArea'); + } + + private _writePlaceholderAndSelectTextArea(reason:string): void { if (!this.textareaIsShownAtCursor) { // Do not write to the textarea if it is visible. if (this.Browser.isIPad) { // Do not place anything in the textarea for the iPad - this.setTextAreaState(TextAreaState.EMPTY); + this.setTextAreaState(reason, this.textAreaState.toEmpty()); } else { - this.setTextAreaState(this.textAreaState.fromEditorSelection(this.model, this.selection)); + this.setTextAreaState(reason, this.textAreaState.fromEditorSelection(this.model, this.selection)); } } } @@ -432,7 +431,7 @@ export class TextAreaHandler extends Disposable { if (e.canUseTextData()) { e.setTextData(whatToCopy); } else { - this.setTextAreaState(this.textAreaState.fromText(whatToCopy)); + this.setTextAreaState('copy or cut', this.textAreaState.fromText(whatToCopy)); } if (this.Browser.enableEmptySelectionClipboard) { diff --git a/src/vs/editor/common/controller/textAreaState.ts b/src/vs/editor/common/controller/textAreaState.ts index b7648251c8a..767747f71b8 100644 --- a/src/vs/editor/common/controller/textAreaState.ts +++ b/src/vs/editor/common/controller/textAreaState.ts @@ -49,104 +49,37 @@ export interface ISimpleModel { convertViewPositionToModelPosition(viewLineNumber:number, viewColumn:number): EditorCommon.IEditorPosition; } -export class TextAreaState { +export abstract class TextAreaState { - public static EMPTY = new TextAreaState(null, '', 0, 0, false, 0); + protected previousState:TextAreaState; + protected value:string; + protected selectionStart:number; + protected selectionEnd:number; + protected isInOverwriteMode:boolean; - private previousState:TextAreaState; - private value:string; - private selectionStart:number; - private selectionEnd:number; - private isInOverwriteMode:boolean; - private selectionToken:number; - - constructor(previousState:TextAreaState, value:string, selectionStart:number, selectionEnd:number, isInOverwriteMode:boolean, selectionToken:number) { + constructor(previousState:TextAreaState, value:string, selectionStart:number, selectionEnd:number, isInOverwriteMode:boolean) { this.previousState = previousState ? previousState.shallowClone() : null; this.value = value; this.selectionStart = selectionStart; this.selectionEnd = selectionEnd; this.isInOverwriteMode = isInOverwriteMode; - this.selectionToken = selectionToken; } - private shallowClone(): TextAreaState { - return new TextAreaState(null, this.value, this.selectionStart, this.selectionEnd, this.isInOverwriteMode, this.selectionToken); - } + protected abstract shallowClone(): TextAreaState; - public toString(): string { - return '[ <' + this.value + '>, selectionStart: ' + this.selectionStart + ', selectionEnd: ' + this.selectionEnd + ', isInOverwriteMode: ' + this.isInOverwriteMode + ', selectionToken: ' + this.selectionToken + ']'; - } + public abstract toEmpty(): TextAreaState; - public equals(other:TextAreaState): boolean { - return ( - this.value === other.value - && this.selectionStart === other.selectionStart - && this.selectionEnd === other.selectionEnd - && this.isInOverwriteMode === other.isInOverwriteMode - && this.selectionToken === other.selectionToken - ); - } + public abstract toString(): string; - public fromTextArea(textArea:ITextAreaWrapper): TextAreaState { - return new TextAreaState(this, textArea.value, textArea.selectionStart, textArea.selectionEnd, textArea.isInOverwriteMode(), this.selectionToken); - } + public abstract equals(other:TextAreaState): boolean; - public fromEditorSelection(model:ISimpleModel, selection:EditorCommon.IEditorRange): TextAreaState { - let LIMIT_CHARS = 100; - let PADDING_LINES_COUNT = 0; + public abstract fromTextArea(textArea:ITextAreaWrapper): TextAreaState; - let selectionStartLineNumber = selection.startLineNumber, - selectionStartColumn = selection.startColumn, - selectionEndLineNumber = selection.endLineNumber, - selectionEndColumn = selection.endColumn, - selectionEndLineNumberMaxColumn = model.getLineMaxColumn(selectionEndLineNumber); + public abstract fromEditorSelection(model:ISimpleModel, selection:EditorCommon.IEditorRange); - // If the selection is empty and we have switched line numbers, expand selection to full line (helps Narrator trigger a full line read) - if (selection.isEmpty() && this.selectionToken !== selectionStartLineNumber) { - selectionStartColumn = 1; - selectionEndColumn = selectionEndLineNumberMaxColumn; - } + public abstract fromText(text:string): TextAreaState; - // `pretext` contains the text before the selection - let pretext = ''; - let startLineNumber = Math.max(1, selectionStartLineNumber - PADDING_LINES_COUNT); - if (startLineNumber < selectionStartLineNumber) { - pretext = model.getValueInRange(new Range(startLineNumber, 1, selectionStartLineNumber, 1), EditorCommon.EndOfLinePreference.LF); - } - pretext += model.getValueInRange(new Range(selectionStartLineNumber, 1, selectionStartLineNumber, selectionStartColumn), EditorCommon.EndOfLinePreference.LF); - if (pretext.length > LIMIT_CHARS) { - pretext = pretext.substring(pretext.length - LIMIT_CHARS, pretext.length); - } - - - // `posttext` contains the text after the selection - let posttext = ''; - let endLineNumber = Math.min(selectionEndLineNumber + PADDING_LINES_COUNT, model.getLineCount()); - posttext += model.getValueInRange(new Range(selectionEndLineNumber, selectionEndColumn, selectionEndLineNumber, selectionEndLineNumberMaxColumn), EditorCommon.EndOfLinePreference.LF); - if (endLineNumber > selectionEndLineNumber) { - posttext = '\n' + model.getValueInRange(new Range(selectionEndLineNumber + 1, 1, endLineNumber, model.getLineMaxColumn(endLineNumber)), EditorCommon.EndOfLinePreference.LF); - } - if (posttext.length > LIMIT_CHARS) { - posttext = posttext.substring(0, LIMIT_CHARS); - } - - - // `text` contains the text of the selection - let text = model.getValueInRange(new Range(selectionStartLineNumber, selectionStartColumn, selectionEndLineNumber, selectionEndColumn), EditorCommon.EndOfLinePreference.LF); - if (text.length > 2 * LIMIT_CHARS) { - text = text.substring(0, LIMIT_CHARS) + String.fromCharCode(8230) + text.substring(text.length - LIMIT_CHARS, text.length); - } - - return new TextAreaState(this, pretext + text + posttext, pretext.length, pretext.length + text.length, false, selectionStartLineNumber); - } - - public fromText(text:string): TextAreaState { - return new TextAreaState(this, text, 0, text.length, false, 0) - } - - public resetSelection(): TextAreaState { - return new TextAreaState(this.previousState, this.value, this.value.length, this.value.length, this.isInOverwriteMode, this.selectionToken); - } + public abstract resetSelection(): TextAreaState; public getSelectionStart(): number { return this.selectionStart; @@ -156,8 +89,8 @@ export class TextAreaState { return this.value; } - public applyToTextArea(textArea:ITextAreaWrapper, select:boolean): void { - // console.log('applyToTextArea: ' + this.toString()); + public applyToTextArea(reason:string, textArea:ITextAreaWrapper, select:boolean): void { + // console.log(Date.now() + ': applyToTextArea ' + reason + ': ' + this.toString()); if (textArea.value !== this.value) { textArea.value = this.value; } @@ -216,3 +149,154 @@ export class TextAreaState { return this.value.charAt(prefixLength); } } + +export class IENarratorTextAreaState extends TextAreaState { + public static EMPTY = new IENarratorTextAreaState(null, '', 0, 0, false, 0); + + private selectionToken:number; + + constructor(previousState:TextAreaState, value:string, selectionStart:number, selectionEnd:number, isInOverwriteMode:boolean, selectionToken:number) { + super(previousState, value, selectionStart, selectionEnd, isInOverwriteMode); + this.selectionToken = selectionToken; + } + + protected shallowClone(): TextAreaState { + return new IENarratorTextAreaState(null, this.value, this.selectionStart, this.selectionEnd, this.isInOverwriteMode, this.selectionToken); + } + + public toEmpty(): TextAreaState { + return IENarratorTextAreaState.EMPTY; + } + + public toString(): string { + return '[ <' + this.value + '>, selectionStart: ' + this.selectionStart + ', selectionEnd: ' + this.selectionEnd + ', isInOverwriteMode: ' + this.isInOverwriteMode + ', selectionToken: ' + this.selectionToken + ']'; + } + + public equals(other:TextAreaState): boolean { + if (other instanceof IENarratorTextAreaState) { + return ( + this.value === other.value + && this.selectionStart === other.selectionStart + && this.selectionEnd === other.selectionEnd + && this.isInOverwriteMode === other.isInOverwriteMode + && this.selectionToken === other.selectionToken + ); + } + return false; + } + + public fromTextArea(textArea:ITextAreaWrapper): TextAreaState { + return new IENarratorTextAreaState(this, textArea.value, textArea.selectionStart, textArea.selectionEnd, textArea.isInOverwriteMode(), this.selectionToken); + } + + public fromEditorSelection(model:ISimpleModel, selection:EditorCommon.IEditorRange): TextAreaState { + let LIMIT_CHARS = 100; + let PADDING_LINES_COUNT = 0; + + let selectionStartLineNumber = selection.startLineNumber, + selectionStartColumn = selection.startColumn, + selectionEndLineNumber = selection.endLineNumber, + selectionEndColumn = selection.endColumn, + selectionEndLineNumberMaxColumn = model.getLineMaxColumn(selectionEndLineNumber); + + // If the selection is empty and we have switched line numbers, expand selection to full line (helps Narrator trigger a full line read) + if (selection.isEmpty() && this.selectionToken !== selectionStartLineNumber) { + selectionStartColumn = 1; + selectionEndColumn = selectionEndLineNumberMaxColumn; + } + + // `pretext` contains the text before the selection + let pretext = ''; + let startLineNumber = Math.max(1, selectionStartLineNumber - PADDING_LINES_COUNT); + if (startLineNumber < selectionStartLineNumber) { + pretext = model.getValueInRange(new Range(startLineNumber, 1, selectionStartLineNumber, 1), EditorCommon.EndOfLinePreference.LF); + } + pretext += model.getValueInRange(new Range(selectionStartLineNumber, 1, selectionStartLineNumber, selectionStartColumn), EditorCommon.EndOfLinePreference.LF); + if (pretext.length > LIMIT_CHARS) { + pretext = pretext.substring(pretext.length - LIMIT_CHARS, pretext.length); + } + + + // `posttext` contains the text after the selection + let posttext = ''; + let endLineNumber = Math.min(selectionEndLineNumber + PADDING_LINES_COUNT, model.getLineCount()); + posttext += model.getValueInRange(new Range(selectionEndLineNumber, selectionEndColumn, selectionEndLineNumber, selectionEndLineNumberMaxColumn), EditorCommon.EndOfLinePreference.LF); + if (endLineNumber > selectionEndLineNumber) { + posttext = '\n' + model.getValueInRange(new Range(selectionEndLineNumber + 1, 1, endLineNumber, model.getLineMaxColumn(endLineNumber)), EditorCommon.EndOfLinePreference.LF); + } + if (posttext.length > LIMIT_CHARS) { + posttext = posttext.substring(0, LIMIT_CHARS); + } + + + // `text` contains the text of the selection + let text = model.getValueInRange(new Range(selectionStartLineNumber, selectionStartColumn, selectionEndLineNumber, selectionEndColumn), EditorCommon.EndOfLinePreference.LF); + if (text.length > 2 * LIMIT_CHARS) { + text = text.substring(0, LIMIT_CHARS) + String.fromCharCode(8230) + text.substring(text.length - LIMIT_CHARS, text.length); + } + + return new IENarratorTextAreaState(this, pretext + text + posttext, pretext.length, pretext.length + text.length, false, selectionStartLineNumber); + } + + public fromText(text:string): TextAreaState { + return new IENarratorTextAreaState(this, text, 0, text.length, false, 0) + } + + public resetSelection(): TextAreaState { + return new IENarratorTextAreaState(this.previousState, this.value, this.value.length, this.value.length, this.isInOverwriteMode, this.selectionToken); + } +} + +export class NVDATextAreaState extends TextAreaState { + public static EMPTY = new NVDATextAreaState(null, '', 0, 0, false); + + constructor(previousState:TextAreaState, value:string, selectionStart:number, selectionEnd:number, isInOverwriteMode:boolean) { + super(previousState, value, selectionStart, selectionEnd, isInOverwriteMode); + } + + protected shallowClone(): TextAreaState { + return new NVDATextAreaState(null, this.value, this.selectionStart, this.selectionEnd, this.isInOverwriteMode); + } + + public toEmpty(): TextAreaState { + return NVDATextAreaState.EMPTY; + } + + public toString(): string { + return '[ , selectionStart: ' + this.selectionStart + ', selectionEnd: ' + this.selectionEnd + ', isInOverwriteMode: ' + this.isInOverwriteMode + ']'; + } + + public equals(other:TextAreaState): boolean { + if (other instanceof NVDATextAreaState) { + return ( + this.value === other.value + && this.selectionStart === other.selectionStart + && this.selectionEnd === other.selectionEnd + && this.isInOverwriteMode === other.isInOverwriteMode + ); + } + return false; + } + + public fromTextArea(textArea:ITextAreaWrapper): TextAreaState { + return new NVDATextAreaState(this, textArea.value, textArea.selectionStart, textArea.selectionEnd, textArea.isInOverwriteMode()); + } + + public fromEditorSelection(model:ISimpleModel, selection:EditorCommon.IEditorRange): TextAreaState { + let pretext = model.getValueInRange(new Range(1, 1, selection.startLineNumber, selection.startColumn), EditorCommon.EndOfLinePreference.LF); + let text = model.getValueInRange(selection, EditorCommon.EndOfLinePreference.LF); + let lastLine = model.getLineCount(); + let lastLineMaxColumn = model.getLineMaxColumn(lastLine); + let posttext = model.getValueInRange(new Range(selection.endLineNumber, selection.endColumn, lastLine, lastLineMaxColumn), EditorCommon.EndOfLinePreference.LF); + + return new NVDATextAreaState(this, pretext + text + posttext, pretext.length, pretext.length + text.length, false); + } + + public fromText(text:string): TextAreaState { + return new NVDATextAreaState(this, text, 0, text.length, false) + } + + public resetSelection(): TextAreaState { + return new NVDATextAreaState(this.previousState, this.value, this.value.length, this.value.length, this.isInOverwriteMode); + } +} diff --git a/src/vs/editor/common/modes/languageExtensionPoint.ts b/src/vs/editor/common/modes/languageExtensionPoint.ts index eb1ba6fd10d..6f75a14f90a 100644 --- a/src/vs/editor/common/modes/languageExtensionPoint.ts +++ b/src/vs/editor/common/modes/languageExtensionPoint.ts @@ -62,9 +62,9 @@ let languagesExtPoint = PluginsRegistry.registerExtensionPoint .monaco-tree-row.selected:not(.highlighted) { background-color: rgba(51, 153, 255, .2); color: #6C6C6C !important; diff --git a/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.ts b/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.ts index 544a40dc055..c6b9d5ede5f 100644 --- a/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.ts @@ -330,10 +330,9 @@ class Renderer extends treeDefaults.LegacyRenderer { var fileReferences = element, fileReferencesContainer = builder.$('.reference-file'); - new leftRightWidget.LeftRightWidget(fileReferencesContainer, (left: HTMLElement) => { + let lrwidget = new leftRightWidget.LeftRightWidget(fileReferencesContainer, (left: HTMLElement) => { var resource = fileReferences.resource; - - new fileLabel.FileLabel(left, resource, this._contextService); + let label = new fileLabel.FileLabel(left, resource, this._contextService); return null; diff --git a/src/vs/editor/contrib/rename/browser/rename.css b/src/vs/editor/contrib/rename/browser/rename.css index 441fff1414f..eff77813b26 100644 --- a/src/vs/editor/contrib/rename/browser/rename.css +++ b/src/vs/editor/contrib/rename/browser/rename.css @@ -24,9 +24,4 @@ .monaco-editor.vs-dark .rename-box .rename-input { border: 1px solid #0E639C; -} - -.monaco-editor .rename-box .rename-input, -.monaco-editor .rename-box .rename-input:focus { - outline: none; } \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 50675589ffd..8331079c7eb 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -247,7 +247,15 @@ class Sorter implements Tree.ISorter { } private static suggestionCompare(a: ISuggestion, b: ISuggestion): number { - return (a.sortText || a.label).localeCompare((b.sortText || b.label)); + let cmp = 0; + if (typeof a.sortText === 'string' && typeof b.sortText === 'string') { + cmp = a.sortText.localeCompare(b.sortText); + } + if (cmp === 0) { + cmp = a.label.localeCompare(b.label); + + } + return cmp; } } diff --git a/src/vs/editor/standalone-languages/all.ts b/src/vs/editor/standalone-languages/all.ts index 72b6afad312..611a35378b3 100644 --- a/src/vs/editor/standalone-languages/all.ts +++ b/src/vs/editor/standalone-languages/all.ts @@ -11,137 +11,137 @@ this.MonacoEditorLanguages = this.MonacoEditorLanguages || []; let MonacoEditorLanguages: ILanguageDef[] = this.MonacoEditorLanguages; MonacoEditorLanguages.push({ - id: "bat", - extensions: [ ".bat", ".cmd"], - aliases: [ "Batch", "bat" ], - defModule: "vs/editor/standalone-languages/bat" + id: 'bat', + extensions: [ '.bat', '.cmd'], + aliases: [ 'Batch', 'bat' ], + defModule: 'vs/editor/standalone-languages/bat' }); MonacoEditorLanguages.push({ - id: "coffeescript", - extensions: [ ".coffee" ], - aliases: [ "CoffeeScript", "coffeescript", "coffee" ], - mimetypes: ["text/x-coffeescript", "text/coffeescript"], - defModule: "vs/editor/standalone-languages/coffee" + id: 'coffeescript', + extensions: [ '.coffee' ], + aliases: [ 'CoffeeScript', 'coffeescript', 'coffee' ], + mimetypes: ['text/x-coffeescript', 'text/coffeescript'], + defModule: 'vs/editor/standalone-languages/coffee' }); MonacoEditorLanguages.push({ - id: "c", - extensions: [ ".c", ".h" ], - aliases: [ "C", "c" ], - defModule: "vs/editor/standalone-languages/cpp" + id: 'c', + extensions: [ '.c', '.h' ], + aliases: [ 'C', 'c' ], + defModule: 'vs/editor/standalone-languages/cpp' }); MonacoEditorLanguages.push({ - id: "cpp", - extensions: [ ".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx" ], - aliases: [ "C++", "Cpp", "cpp"], - defModule: "vs/editor/standalone-languages/cpp" + id: 'cpp', + extensions: [ '.cpp', '.cc', '.cxx', '.hpp', '.hh', '.hxx' ], + aliases: [ 'C++', 'Cpp', 'cpp'], + defModule: 'vs/editor/standalone-languages/cpp' }); MonacoEditorLanguages.push({ - id: "csharp", - extensions: [ ".cs", ".csx" ], - aliases: [ "C#", "csharp" ], - defModule: "vs/editor/standalone-languages/csharp" + id: 'csharp', + extensions: [ '.cs', '.csx' ], + aliases: [ 'C#', 'csharp' ], + defModule: 'vs/editor/standalone-languages/csharp' }); MonacoEditorLanguages.push({ - id: "dockerfile", - extensions: [ ".dockerfile" ], - filenames: [ "Dockerfile" ], - aliases: [ "Dockerfile" ], - defModule: "vs/editor/standalone-languages/dockerfile" + id: 'dockerfile', + extensions: [ '.dockerfile' ], + filenames: [ 'Dockerfile' ], + aliases: [ 'Dockerfile' ], + defModule: 'vs/editor/standalone-languages/dockerfile' }); MonacoEditorLanguages.push({ - id: "fsharp", - extensions: [ ".fs", ".fsi", ".ml", ".mli", ".fsx", ".fsscript" ], - aliases: [ "F#", "FSharp", "fsharp" ], - defModule: "vs/editor/standalone-languages/fsharp" + id: 'fsharp', + extensions: [ '.fs', '.fsi', '.ml', '.mli', '.fsx', '.fsscript' ], + aliases: [ 'F#', 'FSharp', 'fsharp' ], + defModule: 'vs/editor/standalone-languages/fsharp' }); MonacoEditorLanguages.push({ - id: "go", - extensions: [ ".go" ], - aliases: [ "Go" ], - defModule: "vs/editor/standalone-languages/go" + id: 'go', + extensions: [ '.go' ], + aliases: [ 'Go' ], + defModule: 'vs/editor/standalone-languages/go' }); MonacoEditorLanguages.push({ - id: "ini", - extensions: [ ".ini", ".properties", ".gitconfig" ], - filenames: ["config", ".gitattributes", ".gitconfig", ".editorconfig"], - aliases: [ "Ini", "ini" ], - defModule: "vs/editor/standalone-languages/ini" + id: 'ini', + extensions: [ '.ini', '.properties', '.gitconfig' ], + filenames: ['config', '.gitattributes', '.gitconfig', '.editorconfig'], + aliases: [ 'Ini', 'ini' ], + defModule: 'vs/editor/standalone-languages/ini' }); MonacoEditorLanguages.push({ - id: "jade", - extensions: [ ".jade" ], - aliases: [ "Jade", "jade" ], - defModule: "vs/editor/standalone-languages/jade" + id: 'jade', + extensions: [ '.jade' ], + aliases: [ 'Jade', 'jade' ], + defModule: 'vs/editor/standalone-languages/jade' }); MonacoEditorLanguages.push({ - id: "java", - extensions: [ ".java", ".jav" ], - aliases: [ "Java", "java" ], - mimetypes: ["text/x-java-source", "text/x-java"], - defModule: "vs/editor/standalone-languages/java" + id: 'java', + extensions: [ '.java', '.jav' ], + aliases: [ 'Java', 'java' ], + mimetypes: ['text/x-java-source', 'text/x-java'], + defModule: 'vs/editor/standalone-languages/java' }); MonacoEditorLanguages.push({ - id: "lua", - extensions: [ ".lua" ], - aliases: [ "Lua", "lua" ], - defModule: "vs/editor/standalone-languages/lua" + id: 'lua', + extensions: [ '.lua' ], + aliases: [ 'Lua', 'lua' ], + defModule: 'vs/editor/standalone-languages/lua' }); MonacoEditorLanguages.push({ - id: "objective-c", - extensions: [ ".m" ], - aliases: [ "Objective-C"], - defModule: "vs/editor/standalone-languages/objective-c" + id: 'objective-c', + extensions: [ '.m' ], + aliases: [ 'Objective-C'], + defModule: 'vs/editor/standalone-languages/objective-c' }); MonacoEditorLanguages.push({ - id: "powershell", - extensions: [ ".ps1", ".psm1", ".psd1" ], - aliases: [ "PowerShell", "powershell", "ps", "ps1" ], - defModule: "vs/editor/standalone-languages/powershell" + id: 'powershell', + extensions: [ '.ps1', '.psm1', '.psd1' ], + aliases: [ 'PowerShell', 'powershell', 'ps', 'ps1' ], + defModule: 'vs/editor/standalone-languages/powershell' }); MonacoEditorLanguages.push({ - id: "python", - extensions: [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi" ], - aliases: [ "Python", "py" ], - firstLine: "^#!/.*\\bpython[0-9.-]*\\b", - defModule: "vs/editor/standalone-languages/python" + id: 'python', + extensions: [ '.py', '.rpy', '.pyw', '.cpy', '.gyp', '.gypi' ], + aliases: [ 'Python', 'py' ], + firstLine: '^#!/.*\\bpython[0-9.-]*\\b', + defModule: 'vs/editor/standalone-languages/python' }); MonacoEditorLanguages.push({ - id: "r", - extensions: [ ".r", ".rhistory", ".rprofile", ".rt" ], - aliases: [ "R", "r" ], - defModule: "vs/editor/standalone-languages/r" + id: 'r', + extensions: [ '.r', '.rhistory', '.rprofile', '.rt' ], + aliases: [ 'R', 'r' ], + defModule: 'vs/editor/standalone-languages/r' }); MonacoEditorLanguages.push({ - id: "ruby", - extensions: [ ".rb", ".rbx", ".rjs", ".gemspec", ".pp" ], - filenames: [ "rakefile" ], - aliases: [ "Ruby", "rb" ], - defModule: "vs/editor/standalone-languages/ruby" + id: 'ruby', + extensions: [ '.rb', '.rbx', '.rjs', '.gemspec', '.pp' ], + filenames: [ 'rakefile' ], + aliases: [ 'Ruby', 'rb' ], + defModule: 'vs/editor/standalone-languages/ruby' }); MonacoEditorLanguages.push({ - id: "swift", - aliases: ["Swift","swift"], - extensions: [".swift"], - mimetypes: ["text/swift"], - defModule: "vs/editor/standalone-languages/swift" + id: 'swift', + aliases: ['Swift','swift'], + extensions: ['.swift'], + mimetypes: ['text/swift'], + defModule: 'vs/editor/standalone-languages/swift' }); MonacoEditorLanguages.push({ - id: "sql", - extensions: [ ".sql" ], - aliases: [ "SQL" ], - defModule: "vs/editor/standalone-languages/sql" + id: 'sql', + extensions: [ '.sql' ], + aliases: [ 'SQL' ], + defModule: 'vs/editor/standalone-languages/sql' }); MonacoEditorLanguages.push({ - id: "vb", - extensions: [ ".vb" ], - aliases: [ "Visual Basic", "vb" ], - defModule: "vs/editor/standalone-languages/vb" + id: 'vb', + extensions: [ '.vb' ], + aliases: [ 'Visual Basic', 'vb' ], + defModule: 'vs/editor/standalone-languages/vb' }); MonacoEditorLanguages.push({ - id: "xml", - extensions: [ ".xml", ".dtd", ".ascx", ".csproj", ".config", ".wxi", ".wxl", ".wxs", ".xaml", ".svg", ".svgz" ], - firstLine : "(\\<\\?xml.*)|(\\ { function assertTextAreaState(actual:TextAreaState, value:string, selectionStart:number, selectionEnd:number, isInOverwriteMode:boolean, selectionToken:number): void { - let desired = new TextAreaState(null, value, selectionStart, selectionEnd, isInOverwriteMode, selectionToken); + let desired = new IENarratorTextAreaState(null, value, selectionStart, selectionEnd, isInOverwriteMode, selectionToken); assert.ok(desired.equals(actual), desired.toString() + ' == ' + actual.toString()); } @@ -24,7 +24,7 @@ suite('TextAreaState', () => { textArea._selectionStart = 1; textArea._selectionEnd = 12; textArea._isInOverwriteMode = false; - let actual = TextAreaState.EMPTY.fromTextArea(textArea); + let actual = IENarratorTextAreaState.EMPTY.fromTextArea(textArea); assertTextAreaState(actual, 'Hello world!', 1, 12, false, 0); assert.equal(actual.getValue(), 'Hello world!'); @@ -43,22 +43,22 @@ suite('TextAreaState', () => { textArea._selectionEnd = 12; textArea._isInOverwriteMode = false; - let state = new TextAreaState(null, 'Hi world!', 2, 2, false, 0); - state.applyToTextArea(textArea, false); + let state = new IENarratorTextAreaState(null, 'Hi world!', 2, 2, false, 0); + state.applyToTextArea('test', textArea, false); assert.equal(textArea._value, 'Hi world!'); assert.equal(textArea._selectionStart, 9); assert.equal(textArea._selectionEnd, 9); - state = new TextAreaState(null, 'Hi world!', 3, 3, false, 0); - state.applyToTextArea(textArea, false); + state = new IENarratorTextAreaState(null, 'Hi world!', 3, 3, false, 0); + state.applyToTextArea('test', textArea, false); assert.equal(textArea._value, 'Hi world!'); assert.equal(textArea._selectionStart, 9); assert.equal(textArea._selectionEnd, 9); - state = new TextAreaState(null, 'Hi world!', 0, 2, false, 0); - state.applyToTextArea(textArea, true); + state = new IENarratorTextAreaState(null, 'Hi world!', 0, 2, false, 0); + state.applyToTextArea('test', textArea, true); assert.equal(textArea._value, 'Hi world!'); assert.equal(textArea._selectionStart, 0); @@ -74,7 +74,7 @@ suite('TextAreaState', () => { textArea._selectionEnd = selectionEnd; textArea._isInOverwriteMode = isInOverwriteMode; - let newState = (prevState || TextAreaState.EMPTY).fromTextArea(textArea); + let newState = (prevState || IENarratorTextAreaState.EMPTY).fromTextArea(textArea); let actual = newState.extractNewText(); @@ -103,7 +103,7 @@ suite('TextAreaState', () => { test('extractNewText - typing does not cause a selection', () => { testExtractNewText( - new TextAreaState(null, '', 0, 0, false, 0), + new IENarratorTextAreaState(null, '', 0, 0, false, 0), 'a', 0, 1, false, '' @@ -112,7 +112,7 @@ suite('TextAreaState', () => { test('extractNewText - had the textarea empty', () => { testExtractNewText( - new TextAreaState(null, '', 0, 0, false, 0), + new IENarratorTextAreaState(null, '', 0, 0, false, 0), 'a', 1, 1, false, 'a' @@ -121,7 +121,7 @@ suite('TextAreaState', () => { test('extractNewText - had the entire line selected', () => { testExtractNewText( - new TextAreaState(null, 'Hello world!', 0, 12, false, 0), + new IENarratorTextAreaState(null, 'Hello world!', 0, 12, false, 0), 'H', 1, 1, false, 'H' @@ -130,7 +130,7 @@ suite('TextAreaState', () => { test('extractNewText - had previous text 1', () => { testExtractNewText( - new TextAreaState(null, 'Hello world!', 12, 12, false, 0), + new IENarratorTextAreaState(null, 'Hello world!', 12, 12, false, 0), 'Hello world!a', 13, 13, false, 'a' @@ -139,7 +139,7 @@ suite('TextAreaState', () => { test('extractNewText - had previous text 2', () => { testExtractNewText( - new TextAreaState(null, 'Hello world!', 0, 0, false, 0), + new IENarratorTextAreaState(null, 'Hello world!', 0, 0, false, 0), 'aHello world!', 1, 1, false, 'a' @@ -148,7 +148,7 @@ suite('TextAreaState', () => { test('extractNewText - had previous text 3', () => { testExtractNewText( - new TextAreaState(null, 'Hello world!', 6, 11, false, 0), + new IENarratorTextAreaState(null, 'Hello world!', 6, 11, false, 0), 'Hello other!', 11, 11, false, 'other' @@ -157,7 +157,7 @@ suite('TextAreaState', () => { test('extractNewText - IME', () => { testExtractNewText( - new TextAreaState(null, '', 0, 0, false, 0), + new IENarratorTextAreaState(null, '', 0, 0, false, 0), 'これは', 3, 3, false, 'これは' @@ -166,7 +166,7 @@ suite('TextAreaState', () => { test('extractNewText - isInOverwriteMode', () => { testExtractNewText( - new TextAreaState(null, 'Hello world!', 0, 0, false, 0), + new IENarratorTextAreaState(null, 'Hello world!', 0, 0, false, 0), 'Aello world!', 1, 1, true, 'A' @@ -180,7 +180,7 @@ suite('TextAreaState', () => { textArea._selectionEnd = selectionEnd; textArea._isInOverwriteMode = isInOverwriteMode; - let newState = (prevState || TextAreaState.EMPTY).fromTextArea(textArea); + let newState = (prevState || IENarratorTextAreaState.EMPTY).fromTextArea(textArea); let actual = newState.extractMacReplacedText(); @@ -191,7 +191,7 @@ suite('TextAreaState', () => { test('extractMacReplacedText - does nothing if there is selection', () => { testExtractMacReplacedText( - new TextAreaState(null, 'Hello world!', 0, 0, false, 0), + new IENarratorTextAreaState(null, 'Hello world!', 0, 0, false, 0), 'Hellö world!', 4, 5, false, '' @@ -200,7 +200,7 @@ suite('TextAreaState', () => { test('extractMacReplacedText - does nothing if there is more than one extra char', () => { testExtractMacReplacedText( - new TextAreaState(null, 'Hello world!', 0, 0, false, 0), + new IENarratorTextAreaState(null, 'Hello world!', 0, 0, false, 0), 'Hellöö world!', 6, 6, false, '' @@ -209,7 +209,7 @@ suite('TextAreaState', () => { test('extractMacReplacedText - does nothing if there is more than one changed char', () => { testExtractMacReplacedText( - new TextAreaState(null, 'Hello world!', 0, 0, false, 0), + new IENarratorTextAreaState(null, 'Hello world!', 0, 0, false, 0), 'Helöö world!', 6, 6, false, '' @@ -218,7 +218,7 @@ suite('TextAreaState', () => { test('extractMacReplacedText', () => { testExtractMacReplacedText( - new TextAreaState(null, 'Hello world!', 0, 0, false, 0), + new IENarratorTextAreaState(null, 'Hello world!', 0, 0, false, 0), 'Hellö world!', 5, 5, false, 'ö' @@ -227,7 +227,7 @@ suite('TextAreaState', () => { function testFromEditorSelectionAndPreviousState(eol:string, lines:string[], range:Range, prevSelectionToken:number): TextAreaState { let model = new SimpleModel(lines, eol); - let previousState = new TextAreaState(null, '', 0, 0, false, prevSelectionToken); + let previousState = new IENarratorTextAreaState(null, '', 0, 0, false, prevSelectionToken); return previousState.fromEditorSelection(model, range); } diff --git a/src/vs/languages/css/common/parser/cssScanner.ts b/src/vs/languages/css/common/parser/cssScanner.ts index c65a130da56..104a1b31073 100644 --- a/src/vs/languages/css/common/parser/cssScanner.ts +++ b/src/vs/languages/css/common/parser/cssScanner.ts @@ -277,6 +277,8 @@ export class Scanner { } } + let tokenType: TokenType = void 0; + // Comment - CSS if (this._comment()) { if (!this.ignoreComment) { @@ -302,7 +304,7 @@ export class Scanner { } // URL - var tokenType = this._url(); + tokenType = this._url(); if (tokenType !== null) { return this.finishToken(result, tokenType); } @@ -349,8 +351,8 @@ export class Scanner { // Percentage 43% return this.finishToken(result, TokenType.Percentage); } else if (this.ident(content)) { - var dim = this.stream.substring(pos).toLowerCase(); - var tokenType = staticUnitTable[dim]; + let dim = this.stream.substring(pos).toLowerCase(); + tokenType = staticUnitTable[dim]; if (typeof tokenType !== 'undefined') { // Known dimension 43px return this.finishToken(result, tokenType, content.join('')); @@ -365,13 +367,13 @@ export class Scanner { // String, BadString content = []; - var tokenType = this._string(content); + tokenType = this._string(content); if (tokenType !== null) { return this.finishToken(result, tokenType, content.join('')); } // single character tokens - var tokenType = staticTokenTable[this.stream.peekChar()]; + tokenType = staticTokenTable[this.stream.peekChar()]; if (typeof tokenType !== 'undefined') { this.stream.advance(1); return this.finishToken(result, tokenType); diff --git a/src/vs/languages/css/common/parser/cssSymbols.ts b/src/vs/languages/css/common/parser/cssSymbols.ts index 61af2712a45..19ef312278e 100644 --- a/src/vs/languages/css/common/parser/cssSymbols.ts +++ b/src/vs/languages/css/common/parser/cssSymbols.ts @@ -40,7 +40,7 @@ export class Scope { return this.findInScope(offset, length); } return null; - } + } private findInScope(offset: number, length: number = 0): Scope { // find the first scope child that has an offset larger than offset + length @@ -67,7 +67,7 @@ export class Scope { var symbol = this.symbols[index]; if (symbol.name === name && symbol.type === type) { return symbol; - } + } } return null; } @@ -141,33 +141,35 @@ export class ScopeBuilder implements nodes.IVisitor { return true; case nodes.NodeType.VariableDeclaration: this.addSymbol(node, ( node).getName(), nodes.ReferenceType.Variable); - return true; + return true; case nodes.NodeType.Ruleset: return this.visitRuleSet( node); case nodes.NodeType.MixinDeclaration: this.addSymbol(node, ( node).getName(), nodes.ReferenceType.Mixin); - return true; + return true; case nodes.NodeType.FunctionDeclaration: this.addSymbol(node, ( node).getName(), nodes.ReferenceType.Function); - return true; - case nodes.NodeType.FunctionParameter: + return true; + case nodes.NodeType.FunctionParameter: { // parameters are part of the body scope - var scopeNode = ( node.getParent()).getDeclarations(); + let scopeNode = ( node.getParent()).getDeclarations(); if (scopeNode) { this.addSymbolToChildScope(scopeNode, node, ( node).getName(), nodes.ReferenceType.Variable); } - return true; + return true; + } case nodes.NodeType.Declarations: this.addScope(node); return true; case nodes.NodeType.For: - case nodes.NodeType.Each: + case nodes.NodeType.Each: { var forOrEachNode = node; - var scopeNode = forOrEachNode.getDeclarations(); + let scopeNode = forOrEachNode.getDeclarations(); if (scopeNode) { this.addSymbolToChildScope(scopeNode, forOrEachNode.variable, forOrEachNode.variable.getName(), nodes.ReferenceType.Variable); } - return true; + return true; + } } return true; } @@ -245,7 +247,7 @@ export class Symbols { return null; } - private evaluateReferenceTypes(node: nodes.Node) : nodes.ReferenceType[] { + private evaluateReferenceTypes(node: nodes.Node) : nodes.ReferenceType[] { if (node instanceof nodes.Identifier) { var referenceTypes = ( node).referenceTypes; if (referenceTypes) { @@ -258,7 +260,7 @@ export class Symbols { if ((propertyName === 'animation' || propertyName === 'animation-name') && decl.getValue() && decl.getValue().offset === node.offset) { return [ nodes.ReferenceType.Keyframe ]; - } + } } } } else if (node instanceof nodes.Variable) { @@ -285,7 +287,7 @@ export class Symbols { var referenceTypes = this.evaluateReferenceTypes(node); if (referenceTypes) { - return this.internalFindSymbol(node, referenceTypes); + return this.internalFindSymbol(node, referenceTypes); } return null; } @@ -299,7 +301,7 @@ export class Symbols { } if (symbol.name.length !== node.length || symbol.name !== node.getText()) { return false; - } + } var referenceTypes = this.evaluateReferenceTypes(node); if (!referenceTypes || referenceTypes.indexOf(symbol.type) === -1) { @@ -320,6 +322,6 @@ export class Symbols { } scope = scope.parent; } - return null; + return null; } } diff --git a/src/vs/languages/css/common/services/lint.ts b/src/vs/languages/css/common/services/lint.ts index 5ab8b608c47..fc585988fc8 100644 --- a/src/vs/languages/css/common/services/lint.ts +++ b/src/vs/languages/css/common/services/lint.ts @@ -287,7 +287,7 @@ export class LintVisitor implements nodes.IVisitor { ///////////////////////////////////////////////////////////// // With 'display: inline', the width, height, margin-top, margin-bottom, and float properties have no effect - var displayElems = this.fetchWithValue(propertyTable, 'display', 'inline'); + let displayElems = this.fetchWithValue(propertyTable, 'display', 'inline'); if (displayElems.length > 0) { [ 'width', 'height', 'margin-top', 'margin-bottom', 'float'].forEach(function(prop) { var elem = self.fetch(propertyTable, prop); @@ -298,7 +298,7 @@ export class LintVisitor implements nodes.IVisitor { } // With 'display: inline-block', 'float' has no effect - var displayElems = this.fetchWithValue(propertyTable, 'display', 'inline-block'); + displayElems = this.fetchWithValue(propertyTable, 'display', 'inline-block'); if (displayElems.length > 0) { var elem = this.fetch(propertyTable, 'float'); for (var index = 0; index < elem.length; index++) { @@ -307,7 +307,7 @@ export class LintVisitor implements nodes.IVisitor { } // With 'display: block', 'vertical-align' has no effect - var displayElems = this.fetchWithValue(propertyTable, 'display', 'block'); + displayElems = this.fetchWithValue(propertyTable, 'display', 'block'); if (displayElems.length > 0) { var elem = this.fetch(propertyTable, 'vertical-align'); for (var index = 0; index < elem.length; index++) { diff --git a/src/vs/languages/json/common/features/tokenization.ts b/src/vs/languages/json/common/features/tokenization.ts index 9d73f109f42..dd2f041867e 100644 --- a/src/vs/languages/json/common/features/tokenization.ts +++ b/src/vs/languages/json/common/features/tokenization.ts @@ -157,7 +157,7 @@ function tokenize(mode:Modes.IMode, comments:boolean, line:string, state:JSONSta lastWasColon = false; break; case json.SyntaxKind.StringLiteral: - type = lastWasColon ? jsonTokenTypes.TOKEN_VALUE_STRING : jsonTokenTypes.TOKEN_PROPERTY_NAME, + type = lastWasColon ? jsonTokenTypes.TOKEN_VALUE_STRING : jsonTokenTypes.TOKEN_PROPERTY_NAME; lastWasColon = false; break; case json.SyntaxKind.NumericLiteral: diff --git a/src/vs/languages/markdown/common/markdown.css b/src/vs/languages/markdown/common/markdown.css index 2007607704f..a8dfa62e665 100644 --- a/src/vs/languages/markdown/common/markdown.css +++ b/src/vs/languages/markdown/common/markdown.css @@ -20,6 +20,14 @@ a { text-decoration: none; } +a:focus, +input:focus, +select:focus, +textarea:focus { + outline: 2px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + hr { border: 0; height: 2px; diff --git a/src/vs/languages/razor/common/csharpTokenization.ts b/src/vs/languages/razor/common/csharpTokenization.ts index 381d1988604..937619fb236 100644 --- a/src/vs/languages/razor/common/csharpTokenization.ts +++ b/src/vs/languages/razor/common/csharpTokenization.ts @@ -535,8 +535,8 @@ class CSSimpleHTML extends CSState { this.state = htmlMode.States.OpeningStartTag; return { type: htmlTokenTypes.DELIM_START, bracket: Modes.Bracket.Open }; - case htmlMode.States.OpeningEndTag: - var tagName = this.nextName(stream); + case htmlMode.States.OpeningEndTag: { + let tagName = this.nextName(stream); if (tagName.length > 0) { return { type: htmlTokenTypes.getTag(tagName), @@ -549,9 +549,10 @@ class CSSimpleHTML extends CSState { } stream.advanceUntil('>', false); return { type: '' }; + } - case htmlMode.States.OpeningStartTag: - var tagName = this.nextName(stream); + case htmlMode.States.OpeningStartTag: { + let tagName = this.nextName(stream); if (tagName.length > 0) { this.state = htmlMode.States.WithinTag; return { @@ -560,6 +561,7 @@ class CSSimpleHTML extends CSState { }; } break; + } case htmlMode.States.WithinTag: if (stream.skipWhitespace().length > 0) { diff --git a/src/vs/languages/typescript/common/features/quickFix.ts b/src/vs/languages/typescript/common/features/quickFix.ts index b7ebdcd22fd..bad02118928 100644 --- a/src/vs/languages/typescript/common/features/quickFix.ts +++ b/src/vs/languages/typescript/common/features/quickFix.ts @@ -29,16 +29,16 @@ export function evaluate(languageService: ts.LanguageService, resource: URI, ran var [command] = quickFix.command.arguments; switch (command.type) { case 'rename': { - var start = sourceFile.getLineAndCharacterOfPosition(token.getStart()); - var end = sourceFile.getLineAndCharacterOfPosition(token.getEnd()); - var renameRange: EditorCommon.IRange = { startLineNumber: start.line + 1, startColumn: start.character + 1, endLineNumber: end.line + 1, endColumn: end.character + 1 }; + let start = sourceFile.getLineAndCharacterOfPosition(token.getStart()); + let end = sourceFile.getLineAndCharacterOfPosition(token.getEnd()); + let renameRange: EditorCommon.IRange = { startLineNumber: start.line + 1, startColumn: start.character + 1, endLineNumber: end.line + 1, endColumn: end.character + 1 }; return { edits: [{ resource, range: renameRange, newText: command.name }] }; } case 'addglobal': { - var content = strings.format('/* global {0} */\n', command.name); - var renameRange: EditorCommon.IRange = { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }; + let content = strings.format('/* global {0} */\n', command.name); + let renameRange: EditorCommon.IRange = { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }; return { edits: [{ resource, range: renameRange, newText: content }] }; diff --git a/src/vs/languages/typescript/common/js/rewriting.ts b/src/vs/languages/typescript/common/js/rewriting.ts index ef203761c16..1f9e491312e 100644 --- a/src/vs/languages/typescript/common/js/rewriting.ts +++ b/src/vs/languages/typescript/common/js/rewriting.ts @@ -85,8 +85,10 @@ export function translate(_rewriter: ISyntaxRewriter[], snapshot:string|ts.IScri var result = textEdits.apply(context.edits, value); $perf['_apply'] = Date.now() - $t1; $perf['_total'] = Date.now() - $perf['_total']; - $measurePerf && console.info($perf); - $measurePerf && console.info(result.value); + if ($measurePerf) { + console.info($perf); + console.info(result.value); + } return result; } @@ -174,7 +176,9 @@ export function decodeVariableNames(name: string|htmlContent.IHTMLContentElement while (stack.length) { var element = stack.shift(); element.text = element.text && _doDecodeVariableName(element.text, sourceFile); - element.children && stack.push.apply(stack, element.children); + if(element.children){ + stack.push.apply(stack, element.children); + } } } } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index d7db4c329e4..021264c4cf1 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -509,6 +509,20 @@ declare namespace vscode { options: TextEditorOptions; } + /** + * Represents an event describing the change of a [text editor's view column](#TextEditor.viewColumn). + */ + export interface TextEditorViewColumnChangeEvent { + /** + * The [text editor](#TextEditor) for which the options have changed. + */ + textEditor: TextEditor; + /** + * The new value for the [text editor's view column](#TextEditor.viewColumn). + */ + viewColumn: ViewColumn; + } + /** * Represents a [text editor](#TextEditor)'s [options](#TextEditor.options). */ @@ -725,6 +739,12 @@ declare namespace vscode { */ options: TextEditorOptions; + /** + * The column in which this editor shows. Will be `undefined` in case this + * isn't one of the three main editors, e.g an embedded editor. + */ + viewColumn: ViewColumn; + /** * Perform an edit on the document associated with this text editor. * @@ -1453,7 +1473,10 @@ declare namespace vscode { String, Number, Boolean, - Array + Array, + Object, + Key, + Null } /** @@ -2761,6 +2784,11 @@ declare namespace vscode { */ export const onDidChangeTextEditorOptions: Event; + /** + * An [event](#Event) which fires when the view column of an editor das changed. + */ + export const onDidChangeTextEditorViewColumn: Event + /** * Show the given document in a text editor. A [column](#ViewColumn) can be provided * to control where the editor is being shown. Might change the [active editor](#window.activeTextEditor). diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 2b10f6a4add..b4be91bac63 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -191,6 +191,9 @@ export class ExtHostAPIImplementation { onDidChangeTextEditorOptions: (listener: (e: vscode.TextEditorOptionsChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => { return pluginHostEditors.onDidChangeTextEditorOptions(listener, thisArgs, disposables); }, + onDidChangeTextEditorViewColumn(listener, thisArg?, disposables?) { + return pluginHostEditors.onDidChangeTextEditorViewColumn(listener, thisArg, disposables); + }, showInformationMessage: (message, ...items) => { return pluginHostMessageService.showMessage(Severity.Info, message, items); }, diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index d812c1d59c1..1ba756d9da3 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -22,7 +22,7 @@ import {IThreadService} from 'vs/platform/thread/common/thread'; export function registerApiCommands(threadService: IThreadService) { const commands = threadService.getRemotable(ExtHostCommands); - new ExtHostApiCommands(commands); + new ExtHostApiCommands(commands).registerCommands(); } class ExtHostApiCommands { @@ -32,7 +32,9 @@ class ExtHostApiCommands { constructor(commands: ExtHostCommands) { this._commands = commands; + } + registerCommands() { this._register('vscode.executeWorkspaceSymbolProvider', this._executeWorkspaceSymbolProvider, { description: 'Execute all workspace symbol provider.', args: [{ name: 'query', constraint: String }], @@ -145,6 +147,19 @@ class ExtHostApiCommands { ], returns: 'A promise that resolves to an array of TextEdits.' }); + + + this._register('vscode.previewHtml', (uri: URI, position?: vscode.ViewColumn) => { + return this._commands.executeCommand('_workbench.previewHtml', uri, + typeof position === 'number' ? typeConverters.fromViewColumn(position) : void 0); + + }, { + description: 'Preview an html document.', + args: [ + { name: 'uri', description: 'Uri of the document to preview.', constraint: URI }, + { name: 'column', description: '(optional) Column in which to preview.' }, + ] + }); } // --- command impl diff --git a/src/vs/workbench/api/node/extHostEditors.ts b/src/vs/workbench/api/node/extHostEditors.ts index 9bda4f767c5..56b623de3c4 100644 --- a/src/vs/workbench/api/node/extHostEditors.ts +++ b/src/vs/workbench/api/node/extHostEditors.ts @@ -18,17 +18,23 @@ import {Position as EditorPosition} from 'vs/platform/editor/common/editor'; import {IModelService} from 'vs/editor/common/services/modelService'; import {MainThreadEditorsTracker, TextEditorRevealType, MainThreadTextEditor, ITextEditorConfiguration} from 'vs/workbench/api/node/mainThreadEditors'; import * as TypeConverters from './extHostTypeConverters'; -import {TextDocument, TextEditorSelectionChangeEvent, TextEditorOptionsChangeEvent, TextEditorOptions, ViewColumn} from 'vscode'; +import {TextDocument, TextEditorSelectionChangeEvent, TextEditorOptionsChangeEvent, TextEditorOptions, TextEditorViewColumnChangeEvent, ViewColumn} from 'vscode'; import {EventType} from 'vs/workbench/common/events'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {IEventService} from 'vs/platform/event/common/event'; import {equals as arrayEquals} from 'vs/base/common/arrays'; +import {equals as objectEquals} from 'vs/base/common/objects'; export interface ITextEditorAddData { id: string; document: URI; options: ITextEditorConfiguration; selections: ISelection[]; + editorPosition: EditorPosition; +} + +export interface ITextEditorPositionData { + [id: string]: EditorPosition; } @Remotable.PluginHostContext('ExtHostEditors') @@ -40,6 +46,9 @@ export class ExtHostEditors { public onDidChangeTextEditorOptions: Event; private _onDidChangeTextEditorOptions: Emitter; + public onDidChangeTextEditorViewColumn: Event; + private _onDidChangeTextEditorViewColumn: Emitter; + private _editors: { [id: string]: ExtHostTextEditor }; private _proxy: MainThreadEditors; private _onDidChangeActiveTextEditor: Emitter; @@ -56,6 +65,9 @@ export class ExtHostEditors { this._onDidChangeTextEditorOptions = new Emitter(); this.onDidChangeTextEditorOptions = this._onDidChangeTextEditorOptions.event; + this._onDidChangeTextEditorViewColumn = new Emitter(); + this.onDidChangeTextEditorViewColumn = this._onDidChangeTextEditorViewColumn.event; + this._modelService = threadService.getRemotable(ExtHostModelService); this._proxy = threadService.getRemotable(MainThreadEditors); this._onDidChangeActiveTextEditor = new Emitter(); @@ -95,7 +107,7 @@ export class ExtHostEditors { _acceptTextEditorAdd(data: ITextEditorAddData): void { let document = this._modelService.getDocumentData(data.document); - let newEditor = new ExtHostTextEditor(this._proxy, data.id, document, data.selections.map(TypeConverters.toSelection), data.options); + let newEditor = new ExtHostTextEditor(this._proxy, data.id, document, data.selections.map(TypeConverters.toSelection), data.options, TypeConverters.toViewColumn(data.editorPosition)); this._editors[data.id] = newEditor; } @@ -129,6 +141,17 @@ export class ExtHostEditors { this._onDidChangeActiveTextEditor.fire(this.getActiveTextEditor()); } + _acceptEditorPositionData(data: ITextEditorPositionData): void { + for (let id in data) { + let textEditor = this._editors[id]; + let viewColumn = TypeConverters.toViewColumn(data[id]); + if (textEditor.viewColumn !== viewColumn) { + textEditor._acceptViewColumn(viewColumn); + this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn }); + } + } + } + _acceptTextEditorRemove(id: string): void { // make sure the removed editor is not visible let newVisibleEditors = this._visibleEditorIds.filter(visibleEditorId => visibleEditorId !== id); @@ -271,13 +294,15 @@ class ExtHostTextEditor implements vscode.TextEditor { private _documentData: ExtHostDocumentData; private _selections: Selection[]; private _options: TextEditorOptions; + private _viewColumn: vscode.ViewColumn; - constructor(proxy: MainThreadEditors, id: string, document: ExtHostDocumentData, selections: Selection[], options: EditorOptions) { + constructor(proxy: MainThreadEditors, id: string, document: ExtHostDocumentData, selections: Selection[], options: EditorOptions, viewColumn: vscode.ViewColumn) { this._proxy = proxy; this._id = id; this._documentData = document; this._selections = selections; this._options = options; + this._viewColumn = viewColumn; } dispose() { @@ -319,6 +344,20 @@ class ExtHostTextEditor implements vscode.TextEditor { this._options = options; } + // ---- view column + + get viewColumn(): vscode.ViewColumn { + return this._viewColumn; + } + + set viewColumn(value) { + throw readonly('viewColumn'); + } + + _acceptViewColumn(value: vscode.ViewColumn) { + this._viewColumn = value; + } + // ---- selections get selection(): Selection { @@ -423,6 +462,7 @@ export class MainThreadEditors { private _textEditorsMap: { [editorId: string]: MainThreadTextEditor; }; private _activeTextEditor: string; private _visibleEditors: string[]; + private _editorPositionData: ITextEditorPositionData; constructor( @IThreadService threadService: IThreadService, @@ -440,6 +480,7 @@ export class MainThreadEditors { this._textEditorsMap = Object.create(null); this._activeTextEditor = null; this._visibleEditors = []; + this._editorPositionData = null; this._editorTracker = new MainThreadEditorsTracker(editorService, modelService); this._toDispose.push(this._editorTracker); @@ -450,6 +491,7 @@ export class MainThreadEditors { this._toDispose.push(this._editorTracker.onDidUpdateTextEditors(() => this._updateActiveAndVisibleTextEditors())); this._toDispose.push(this._editorTracker.onChangedFocusedTextEditor((focusedTextEditorId) => this._updateActiveAndVisibleTextEditors())); this._toDispose.push(eventService.addListener2(EventType.EDITOR_INPUT_CHANGED, () => this._updateActiveAndVisibleTextEditors())); + this._toDispose.push(eventService.addListener2(EventType.EDITOR_POSITION_CHANGED, () => this._updateActiveAndVisibleTextEditors())); } public dispose(): void { @@ -473,7 +515,8 @@ export class MainThreadEditors { id: id, document: textEditor.getModel().getAssociatedResource(), options: textEditor.getConfiguration(), - selections: textEditor.getSelections() + selections: textEditor.getSelections(), + editorPosition: this._findEditorPosition(textEditor) }); this._textEditorsListenersMap[id] = toDispose; @@ -489,16 +532,22 @@ export class MainThreadEditors { } private _updateActiveAndVisibleTextEditors(): void { + + // active and visible editors let visibleEditors = this._editorTracker.getVisibleTextEditorIds(); let activeEditor = this._findActiveTextEditorId(); - - if (activeEditor === this._activeTextEditor && arrayEquals(this._visibleEditors, visibleEditors, (a, b) => a === b)) { - // no change - return; + if (activeEditor !== this._activeTextEditor || !arrayEquals(this._visibleEditors, visibleEditors, (a, b) => a === b)) { + this._activeTextEditor = activeEditor; + this._visibleEditors = visibleEditors; + this._proxy._acceptActiveEditorAndVisibleEditors(this._activeTextEditor, this._visibleEditors); + } + + // editor columns + let editorPositionData = this._getTextEditorPositionData(); + if (!objectEquals(this._editorPositionData, editorPositionData)) { + this._editorPositionData = editorPositionData; + this._proxy._acceptEditorPositionData(this._editorPositionData); } - this._activeTextEditor = activeEditor; - this._visibleEditors = visibleEditors; - this._proxy._acceptActiveEditorAndVisibleEditors(this._activeTextEditor, this._visibleEditors); } private _findActiveTextEditorId(): string { @@ -527,6 +576,33 @@ export class MainThreadEditors { return this._editorTracker.findTextEditorIdFor((editor).getModifiedEditor()); } + private _findEditorPosition(editor: MainThreadTextEditor): EditorPosition { + for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) { + if (editor.matches(workbenchEditor)) { + return workbenchEditor.position; + } + } + } + + private _getTextEditorPositionData(): ITextEditorPositionData { + let result: ITextEditorPositionData = Object.create(null); + for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) { + let editor = workbenchEditor.getControl(); + // Substitute for (editor instanceof ICodeEditor) + if (!editor || typeof editor.getEditorType !== 'function') { + // Not a text editor... + continue; + } + if (editor.getEditorType() === EditorType.ICodeEditor) { + let id = this._editorTracker.findTextEditorIdFor(editor); + if (id) { + result[id] = workbenchEditor.position; + } + } + } + return result; + } + // --- from plugin host process _tryShowTextDocument(resource: URI, position: EditorPosition, preserveFocus: boolean): TPromise { diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 2fc4aaff599..0e38c558af8 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -111,6 +111,18 @@ export function fromViewColumn(column?: vscode.ViewColumn): EditorPosition { return editorColumn; } +export function toViewColumn(position?: EditorPosition): vscode.ViewColumn { + if (typeof position !== 'number') { + return; + } + if (position === EditorPosition.LEFT) { + return types.ViewColumn.One; + } else if (position === EditorPosition.CENTER) { + return types.ViewColumn.Two; + } else if (position === EditorPosition.RIGHT) { + return types.ViewColumn.Three; + } +} export function fromFormattedString(value: vscode.MarkedString): IHTMLContentElement { if (typeof value === 'string') { @@ -198,9 +210,11 @@ export namespace SymbolKind { return 'class'; case types.SymbolKind.Interface: return 'interface'; - case types.SymbolKind.Module: case types.SymbolKind.Namespace: + return 'namespace'; case types.SymbolKind.Package: + return 'package'; + case types.SymbolKind.Module: return 'module'; case types.SymbolKind.Property: return 'property'; @@ -216,6 +230,12 @@ export namespace SymbolKind { return 'number'; case types.SymbolKind.Boolean: return 'boolean'; + case types.SymbolKind.Object: + return 'object'; + case types.SymbolKind.Key: + return 'key'; + case types.SymbolKind.Null: + return 'null'; } return 'property'; } @@ -234,9 +254,11 @@ export namespace SymbolKind { return types.SymbolKind.Class; case 'interface': return types.SymbolKind.Interface; + case 'namespace': + return types.SymbolKind.Namespace; + case 'package': + return types.SymbolKind.Package; case 'module': - // case types.SymbolKind.Namespace: - // case types.SymbolKind.Package: return types.SymbolKind.Module; case 'property': return types.SymbolKind.Property; @@ -252,6 +274,12 @@ export namespace SymbolKind { return types.SymbolKind.Number; case 'boolean': return types.SymbolKind.Boolean; + case 'object': + return types.SymbolKind.Object; + case 'key': + return types.SymbolKind.Key; + case 'null': + return types.SymbolKind.Null; } return types.SymbolKind.Property; } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index caabab72738..6dffeec6958 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -536,6 +536,9 @@ export enum SymbolKind { Number, Boolean, Array, + Object, + Key, + Null } export class SymbolInformation { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 0c423d11c09..fc0875eec05 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -118,7 +118,7 @@ export class ActivitybarPart extends Part implements IActivityService { // Viewlet switcher is on top this.viewletSwitcherBar = new ActionBar(div, { actionItemProvider: (action: Action) => this.activityActionItems[action.id], - disableTabIndex: true // we handle this + orientation: ActionsOrientation.VERTICAL }); this.viewletSwitcherBar.getContainer().addClass('position-top'); @@ -159,7 +159,6 @@ export class ActivitybarPart extends Part implements IActivityService { actionItemProvider: (action: Action) => this.activityActionItems[action.id], orientation: ActionsOrientation.VERTICAL }); - this.globalToolBar.getContainer().removeAttribute('tabindex'); this.globalToolBar.getContainer().addClass('global'); this.globalToolBar.actionRunner.addListener(events.EventType.RUN, (e: any) => { diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index 4916dda4ab3..5c91ac95da4 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -29,6 +29,35 @@ text-shadow: 0 1px 0 rgba(0, 0, 0, 0.8); } +.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item .action-label:focus:before { + content: ""; + position: absolute; + top: 4px; + height: 32px; + width: 0; + border-left: 2px solid; +} + +.monaco-workbench.vs > .activitybar > .content .monaco-action-bar .action-item .action-label:focus:before { + border-left-color: #1E8AE5; +} + +.monaco-workbench.vs-dark > .activitybar > .content .monaco-action-bar .action-item .action-label:focus:before { + border-left-color: #1E8AE5; +} + +.monaco-workbench.hc-black > .activitybar > .content .monaco-action-bar .action-item .action-label:focus:before { + border-left-color: #DF740C; +} + +.monaco-workbench > .activitybar.left > .content .monaco-action-bar .action-item .action-label:focus:before { + left: 1px; +} + +.monaco-workbench > .activitybar.right > .content .monaco-action-bar .action-item .action-label:focus:before { + right: 1px; +} + .monaco-workbench > .activitybar > .content .monaco-action-bar .action-label > .label { -ms-flex: 1 1 auto; flex: 1 1 auto; @@ -82,12 +111,11 @@ } .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item .action-label.active, -.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover .action-label, -.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item .action-label:focus { +.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover .action-label { opacity: 1; } -.monaco-workbench.vs > .activitybar.left:not(.help) > .content > .monaco-action-bar.position-top .action-label.active:after { +.monaco-workbench.vs > .activitybar.left > .content > .monaco-action-bar.position-top .action-label.active:after { content: ''; position: absolute; top: 15px; @@ -99,7 +127,7 @@ border-right: 5px solid #F6F6F6; } -.monaco-workbench.vs-dark > .activitybar.left:not(.help) > .content > .monaco-action-bar.position-top .action-label.active:after { +.monaco-workbench.vs-dark > .activitybar.left > .content > .monaco-action-bar.position-top .action-label.active:after { content: ''; position: absolute; top: 15px; @@ -111,7 +139,7 @@ border-right: 5px solid #252526; } -.monaco-workbench.vs > .activitybar.right:not(.help) > .content > .monaco-action-bar.position-top .action-label.active:before { +.monaco-workbench.vs > .activitybar.right > .content > .monaco-action-bar.position-top .action-label.active:before { content: ''; position: absolute; top: 15px; @@ -123,7 +151,7 @@ border-left: 5px solid #F6F6F6; } -.monaco-workbench.vs-dark > .activitybar.right:not(.help) > .content > .monaco-action-bar.position-top .action-label.active:before { +.monaco-workbench.vs-dark > .activitybar.right > .content > .monaco-action-bar.position-top .action-label.active:before { content: ''; position: absolute; top: 15px; @@ -160,7 +188,6 @@ /* High Contrast Theming */ .monaco-workbench.hc-black > .activitybar > .content .monaco-action-bar .action-label, .monaco-workbench.hc-black > .activitybar > .content .monaco-action-bar .action-label.active { - background: none; opacity: 1; } diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index 8ed927cf65c..7cc94906671 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -9,6 +9,7 @@ import {Action, IAction} from 'vs/base/common/actions'; import {ActionBarContributor} from 'vs/workbench/browser/actionBarRegistry'; import types = require('vs/base/common/types'); import {Builder} from 'vs/base/browser/builder'; +import {EventType, EditorEvent} from 'vs/workbench/common/events'; import {Registry} from 'vs/platform/platform'; import {Panel} from 'vs/workbench/browser/panel'; import {EditorInput, IFileEditorInput, EditorOptions} from 'vs/workbench/common/editor'; @@ -113,6 +114,7 @@ export abstract class BaseEditor extends Panel implements IEditor { */ public changePosition(position: Position): void { this._position = position; + this.emit(EventType.EDITOR_POSITION_CHANGED, new EditorEvent(this, this.getId(), this.input, this.options, this.position)); } /** diff --git a/src/vs/workbench/browser/parts/editor/iframeEditor.ts b/src/vs/workbench/browser/parts/editor/iframeEditor.ts index c900154f01d..607601ae9d0 100644 --- a/src/vs/workbench/browser/parts/editor/iframeEditor.ts +++ b/src/vs/workbench/browser/parts/editor/iframeEditor.ts @@ -51,7 +51,6 @@ export class IFrameEditor extends BaseEditor { let iframeContainerElement = document.createElement('div'); iframeContainerElement.className = 'iframe-container monaco-editor-background'; // Inherit the background color from selected theme this.iframeContainer = $(iframeContainerElement); - this.iframeContainer.tabindex(0); // enable focus support // IFrame this.iframeBuilder = $(this.iframeContainer).element('iframe').addClass('iframe'); @@ -139,6 +138,7 @@ export class IFrameEditor extends BaseEditor { '