diff --git a/extensions/clojure/clojure.configuration.json b/extensions/clojure/clojure.configuration.json index d83f1ac8e9bdff9d95ac66a72cbab595e4ae43e1..f0d6b7f37b848b0bbb78f04d2514e8e5cb627876 100644 --- a/extensions/clojure/clojure.configuration.json +++ b/extensions/clojure/clojure.configuration.json @@ -1,7 +1,6 @@ { "comments": { - "lineComment": ";", - "blockComment": [ "(comment", ")" ] + "lineComment": ";" }, "brackets": [ ["{", "}"], diff --git a/extensions/csharp/package.json b/extensions/csharp/package.json index 180fba39a2e014cd3afd050be2e1ce58e1c2d5be..82e1a8d701aee8bbf252739e48df6dda4876904f 100644 --- a/extensions/csharp/package.json +++ b/extensions/csharp/package.json @@ -14,6 +14,10 @@ "language": "csharp", "scopeName": "source.cs", "path": "./syntaxes/csharp.json" + }], + "jsonValidation": [{ + "fileMatch": "project.json", + "url": "http://json.schemastore.org/project" }] } } \ No newline at end of file diff --git a/extensions/javascript/package.json b/extensions/javascript/package.json index 17834d8ee293f79e2f8ce1928b398e1a747e7636..a1e458939b1667e868e5c36024a1c9b9ebf94219 100644 --- a/extensions/javascript/package.json +++ b/extensions/javascript/package.json @@ -2,37 +2,86 @@ "name": "javascript", "version": "0.1.0", "publisher": "vscode", - "engines": { "vscode": "*" }, + "engines": { + "vscode": "*" + }, "contributes": { - "languages": [{ - "id": "javascriptreact", - "aliases": ["JavaScript React","jsx"], - "extensions": [".jsx"], - "configuration": "./javascript.configuration.json" - },{ - "id": "javascript", - "aliases": ["JavaScript", "javascript", "js"], - "extensions": [".js", ".es6"], - "filenames": ["jakefile"], - "firstLine": "^#!.*\\bnode", - "mimetypes": ["text/javascript"] - }], - "grammars": [{ - "language": "javascriptreact", - "scopeName": "source.jsx", - "path": "./syntaxes/JavaScriptReact.tmLanguage" - },{ - "language": "javascript", - "scopeName": "source.js", - "path": "./syntaxes/JavaScript.tmLanguage" - },{ - // referenced by other grammars - "scopeName": "source.js.regexp", - "path": "./syntaxes/Regular Expressions (JavaScript).tmLanguage" - }], - "snippets": [{ - "language": "javascript", - "path": "./snippets/javascript.json" - }] + "languages": [ + { + "id": "javascriptreact", + "aliases": [ + "JavaScript React", + "jsx" + ], + "extensions": [ + ".jsx" + ], + "configuration": "./javascript.configuration.json" + }, + { + "id": "javascript", + "aliases": [ + "JavaScript", + "javascript", + "js" + ], + "extensions": [ + ".js", + ".es6" + ], + "filenames": [ + "jakefile" + ], + "firstLine": "^#!.*\\bnode", + "mimetypes": [ + "text/javascript" + ] + } + ], + "grammars": [ + { + "language": "javascriptreact", + "scopeName": "source.jsx", + "path": "./syntaxes/JavaScriptReact.tmLanguage" + }, + { + "language": "javascript", + "scopeName": "source.js", + "path": "./syntaxes/JavaScript.tmLanguage" + }, + { + // referenced by other grammars + "scopeName": "source.js.regexp", + "path": "./syntaxes/Regular Expressions (JavaScript).tmLanguage" + } + ], + "snippets": [ + { + "language": "javascript", + "path": "./snippets/javascript.json" + } + ], + "jsonValidation": [ + { + "fileMatch": "package.json", + "url": "http://json.schemastore.org/project" + }, + { + "fileMatch": "bower.json", + "url": "http://json.schemastore.org/bower" + }, + { + "fileMatch": ".bower.json", + "url": "http://json.schemastore.org/bower" + }, + { + "fileMatch": ".bowerrc", + "url": "http://json.schemastore.org/bowerrc" + }, + { + "fileMatch": "jsconfig.json", + "url": "./schemas/jsconfig.schema.json" + } + ] } } \ No newline at end of file diff --git a/extensions/javascript/schemas/jsconfig.schema.json b/extensions/javascript/schemas/jsconfig.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..ad1b317e7074bbe1fc7bee939234da7d6550f577 --- /dev/null +++ b/extensions/javascript/schemas/jsconfig.schema.json @@ -0,0 +1,79 @@ +{ + "title": "JSON schema for the JavaScript configuration file", + "type": "object", + "default": { + "compilerOptions": { + "target": "ES5" + } + }, + "properties": { + "compilerOptions": { + "type": "object", + "description": "Instructs the JavaScript language service how to validate .js files", + "properties": { + "charset": { + "description": "The character set of the input files", + "type": "string" + }, + "diagnostics": { + "description": "Show diagnostic information.", + "type": "boolean" + }, + "locale": { + "description": "The locale to use to show error messages, e.g. en-us.", + "type": "string" + }, + "mapRoot": { + "description": "Specifies the location where debugger should locate map files instead of generated locations", + "type": "string", + "format": "uri" + }, + "module": { + "description": "Module code generation to resolve against: 'commonjs', 'amd', 'system', or 'umd'.", + "enum": [ + "commonjs", + "amd", + "system", + "umd" + ] + }, + "noLib": { + "description": "Do not include the default library file (lib.d.ts).", + "type": "boolean" + }, + "target": { + "description": "Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6' (experimental).", + "enum": [ + "ES3", + "ES5", + "ES6", + "es3", + "es5", + "es6" + ], + "default": "ES3" + }, + "experimentalDecorators": { + "description": "Enables experimental support for ES7 decorators.", + "type": "boolean" + } + } + }, + "files": { + "type": "array", + "description": "If no 'files' property is present in a jsconfig.json, the language service defaults to including all files the containing directory and subdirectories. When a 'files' property is specified, only those files are included.", + "items": { + "type": "string", + "format": "uri" + } + }, + "exclude": { + "type": "array", + "description": "List files and folders that should not be included. This property is not honored when the 'files' property is present.", + "items": { + "type": "string", + "format": "uri" + } + } + } +} \ No newline at end of file diff --git a/extensions/json/package.json b/extensions/json/package.json index 224e446f8a02898131a832e11d6b423876809079..0ba31d875a6f04f33a1989624fccee8a3a7f23f5 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -2,18 +2,41 @@ "name": "json", "version": "0.1.0", "publisher": "vscode", - "engines": { "vscode": "*" }, + "engines": { + "vscode": "*" + }, "contributes": { - "languages": [{ - "id": "json", - "aliases": ["JSON", "json"], - "extensions": [".json", ".bowerrc", ".jshintrc", ".jscsrc", ".eslintrc"], - "mimetypes": ["application/json"] - }], - "grammars": [{ - "language": "json", - "scopeName": "source.json", - "path": "./syntaxes/JSON.tmLanguage" - }] + "languages": [ + { + "id": "json", + "aliases": [ + "JSON", + "json" + ], + "extensions": [ + ".json", + ".bowerrc", + ".jshintrc", + ".jscsrc", + ".eslintrc" + ], + "mimetypes": [ + "application/json" + ] + } + ], + "grammars": [ + { + "language": "json", + "scopeName": "source.json", + "path": "./syntaxes/JSON.tmLanguage" + } + ], + "jsonValidation": [ + { + "fileMatch": "*.schema.json", + "url": "http://json-schema.org/draft-04/schema#" + } + ] } } \ No newline at end of file diff --git a/extensions/node-debug/node-debug.azure.json b/extensions/node-debug/node-debug.azure.json index 370a510e1338c7c4178fa84301717b362c2dc1a1..b59836d7e62ca3a313880497326a3995caec514e 100644 --- a/extensions/node-debug/node-debug.azure.json +++ b/extensions/node-debug/node-debug.azure.json @@ -1,6 +1,6 @@ { "account": "monacobuild", "container": "debuggers", - "zip": "32575e3/node-debug.zip", + "zip": "3efab9b/node-debug.zip", "output": "" } diff --git a/extensions/ruby/package.json b/extensions/ruby/package.json index 2dd3847cd347f0e7e4b10f97d4a237ed18f56818..7b1bf81aac678c1b6e30937e95e361b678549491 100644 --- a/extensions/ruby/package.json +++ b/extensions/ruby/package.json @@ -6,7 +6,7 @@ "contributes": { "languages": [{ "id": "ruby", - "extensions": [ ".rb", ".rbx", ".rjs", ".gemspec", ".pp" ], + "extensions": [ ".rb", ".rbx", ".rjs", ".gemspec", ".pp", ".rake" ], "filenames": [ "rakefile", "gemfile" ], "aliases": [ "Ruby", "rb" ], "configuration": "./ruby.configuration.json" diff --git a/extensions/typescript/package.json b/extensions/typescript/package.json index 962e0a13f4d6aaabed81fc535099f11b96d61710..162ac035bd2c5db126dc475afa7ec6a62c9f2e88 100644 --- a/extensions/typescript/package.json +++ b/extensions/typescript/package.json @@ -101,6 +101,12 @@ "language": "typescriptreact", "path": "./snippets/typescriptreact.json" } + ], + "jsonValidation": [ + { + "fileMatch": "tsconfig.json", + "url": "http://json.schemastore.org/tsconfig" + } ] } } \ No newline at end of file diff --git a/extensions/vb/vb.configuration.json b/extensions/vb/vb.configuration.json index f05c7584ecb57bd0e31dc00ee9619cc404c1f895..fceea4405f800e29eadfe6974cf04bb48a784e2e 100644 --- a/extensions/vb/vb.configuration.json +++ b/extensions/vb/vb.configuration.json @@ -1,7 +1,6 @@ { "comments": { - "lineComment": "'", - "blockComment": [ "/*", "*/" ] + "lineComment": "'" }, "brackets": [ ["{", "}"], diff --git a/extensions/xml/package.json b/extensions/xml/package.json index e3e9e0474c204aab0e05dc561e55c40c693620bd..e471964623a65613a7461acab146ee2c0183d727 100644 --- a/extensions/xml/package.json +++ b/extensions/xml/package.json @@ -8,6 +8,7 @@ "id": "xml", "extensions": [ ".ascx", + ".atom", ".axml", ".bpmn", ".config", diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 50278a352f7d9e83fca730f28838b7d0dd18c6f5..7d053110f7c833f3cd1f6de486414115de144262 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -414,6 +414,11 @@ "version": "4.3.6", "from": "semver@>=4.2.0 <5.0.0", "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" + }, + "vscode-debugprotocol": { + "version": "1.0.1", + "from": "vscode-debugprotocol@>=1.0.1", + "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.0.1.tgz" }, "vscode-textmate": { "version": "1.0.9", diff --git a/package.json b/package.json index 3dc33652ccbfc3f4091fae547bee015b962d24dc..2e2bddcdf60d1964da4b6c4126e398e1bdabe175 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "iconv-lite": "^0.4.13", "sax": "^1.1.1", "semver": "^4.2.0", + "vscode-debugprotocol": "^1.0.0", "vscode-textmate": "^1.0.9", "native-keymap": "^0.0.2", "winreg": "0.0.12", diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 970d9943b4a48a68efbdebd0976cdbf5264730a9..8f2914b7ca475c487547fa20a935ccc2195b1811 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -36,13 +36,11 @@ export class ArrayIterator implements IIterator { export class MappedIterator implements IIterator { - constructor(private iterator: IIterator, private fn: (item:T)=>R) { + constructor(protected iterator: IIterator, protected fn: (item:T)=>R) { // noop } - public next(): R { - return this.fn(this.iterator.next()); - } + next() { return this.fn(this.iterator.next()); } } export interface INavigator extends IIterator { @@ -52,3 +50,16 @@ export interface INavigator extends IIterator { first(): T; last(): T; } + +export class MappedNavigator extends MappedIterator implements INavigator { + + constructor(protected navigator: INavigator, fn: (item:T)=>R) { + super(navigator, fn); + } + + current() { return this.fn(this.navigator.current()); } + previous() { return this.fn(this.navigator.previous()); } + parent() { return this.fn(this.navigator.parent()); } + first() { return this.fn(this.navigator.first()); } + last() { return this.fn(this.navigator.last()); } +} diff --git a/src/vs/base/common/service.ts b/src/vs/base/common/service.ts index 023a9a010960c0d3d677f11026c78360b6399283..a7dc6ef09b5a9db0d30bae37b349f6d95e87b6fa 100644 --- a/src/vs/base/common/service.ts +++ b/src/vs/base/common/service.ts @@ -131,7 +131,11 @@ export class Server { if (!method) { promise = Promise.wrapError(new Error(`${ request.name } is not a valid method on ${ request.serviceName }`)); } else { - promise = method.call(service, ...request.args) + try { + promise = method.call(service, ...request.args); + } catch (err) { + promise = Promise.wrapError(err); + } } if (!Promise.is(promise)) { diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index e6f4a01e9f0e0ff4e88923860dbf2ae0517104b7..dbb0922ba2edb51afcdf1c709e575b060f73d235 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -11,6 +11,7 @@ import Events = require('vs/base/common/eventEmitter'); import Model = require('vs/base/parts/tree/common/treeModel'); import View = require('./treeView'); import _ = require('vs/base/parts/tree/common/tree'); +import { INavigator, MappedNavigator } from 'vs/base/common/iterator'; export class TreeContext implements _.ITreeContext { @@ -165,6 +166,10 @@ export class Tree extends Events.EventEmitter implements _.ITree { this.view.setScrollPosition(pos); } + getContentHeight(): number { + return this.view.getTotalHeight(); + } + public setHighlight(element?:any, eventPayload?:any):void { this.model.setHighlight(element, eventPayload); } @@ -306,6 +311,10 @@ export class Tree extends Events.EventEmitter implements _.ITree { return this.view.withFakeRow(fn); } + getNavigator(): INavigator { + return new MappedNavigator(this.model.getNavigator(), i => i && i.getElement()); + } + public dispose(): void { if (this.model !== null) { this.model.dispose(); diff --git a/src/vs/base/parts/tree/common/tree.ts b/src/vs/base/parts/tree/common/tree.ts index a331f11b4747d2fc8235e1bb9692125d60af3f20..03169b39817a2d242f4ae26b89617c9899f803e5 100644 --- a/src/vs/base/parts/tree/common/tree.ts +++ b/src/vs/base/parts/tree/common/tree.ts @@ -9,6 +9,7 @@ import Touch = require('vs/base/browser/touch'); import Events = require('vs/base/common/eventEmitter'); import Mouse = require('vs/base/browser/mouseEvent'); import Keyboard = require('vs/base/browser/keyboardEvent'); +import { INavigator } from 'vs/base/common/iterator'; export interface ITree extends Events.IEventEmitter { @@ -132,6 +133,11 @@ export interface ITree extends Events.IEventEmitter { */ setScrollPosition(pos: number): void; + /** + * Returns the total height of the tree's content. + */ + getContentHeight(): number; + /** * Sets the tree's highlight to be the given element. * Provide no arguments and it clears the tree's highlight. @@ -309,6 +315,12 @@ export interface ITree extends Events.IEventEmitter { */ withFakeRow(fn:(container:HTMLElement)=>any):any; + /** + * Returns a navigator which allows to discover the visible and + * expanded elements in the tree. + */ + getNavigator(): INavigator; + /** * Disposes the tree */ diff --git a/src/vs/base/parts/tree/common/treeModel.ts b/src/vs/base/parts/tree/common/treeModel.ts index 0152bcab3a26d2272f5dbf50887db0975779aed3..df148ba45d75ff99a0fd4a24240cb5db134fdb5d 100644 --- a/src/vs/base/parts/tree/common/treeModel.ts +++ b/src/vs/base/parts/tree/common/treeModel.ts @@ -458,7 +458,7 @@ export class Item extends Events.EventEmitter { public getHierarchy(): Item[] { var result: Item[] = []; - var node = this; + var node: Item = this; do { result.push(node); diff --git a/src/vs/editor/browser/standalone/simpleServices.ts b/src/vs/editor/browser/standalone/simpleServices.ts index 033df7f943b9c554f782da16626658fae05e69ec..93c7202721f0ac00d4fced43e3f0dcc6f636cd38 100644 --- a/src/vs/editor/browser/standalone/simpleServices.ts +++ b/src/vs/editor/browser/standalone/simpleServices.ts @@ -272,4 +272,8 @@ export class SimplePluginService extends AbstractPluginService { console.log(msg); } } + + public deactivate(pluginId:string): void { + // nothing to do + } } diff --git a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts index 17a8867a0cb51b2afc93e93753d805e8d9a104ed..25b476ab27627785042f2d07b36b78fb459b1273 100644 --- a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts @@ -40,6 +40,8 @@ import {createAsyncDescriptor0} from 'vs/platform/instantiation/common/descripto import {LanguageExtensions, ILanguageExtensionPoint} from 'vs/editor/common/modes/languageExtensionPoint'; import {AbstractKeybindingService} from 'vs/platform/keybinding/browser/keybindingServiceImpl'; import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService'; +import {IJSONSchema} from 'vs/base/common/jsonSchema'; +import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; // Set defaults for standalone editor DefaultConfig.editor.wrappingIndent = 'none'; @@ -451,6 +453,11 @@ export function registerStandaloneLanguage(language:ILanguageExtensionPoint, def }); } +export function registerStandaloneSchema(uri:string, schema:IJSONSchema) { + let schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); + schemaRegistry.registerSchema(uri, schema); +} + export function colorizeElement(domNode:HTMLElement, options:colorizer.IColorizerElementOptions): TPromise { startup.initStaticServicesIfNecessary(); var modeService = standaloneServices.ensureStaticPlatformServices(null).modeService; diff --git a/src/vs/editor/browser/standalone/standaloneEditor.ts b/src/vs/editor/browser/standalone/standaloneEditor.ts index aeb925c49da36a4ecfeba6fa1bd63806fb7d5298..ee5ffb40b37c3944ebc6009212a763fa7349bf8b 100644 --- a/src/vs/editor/browser/standalone/standaloneEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneEditor.ts @@ -5,12 +5,14 @@ 'use strict'; import 'vs/editor/standalone-languages/all'; +import './standaloneSchemas'; import Colorizer = require('vs/editor/browser/standalone/colorizer'); import standaloneCodeEditor = require('vs/editor/browser/standalone/standaloneCodeEditor'); import EditorCommon = require('vs/editor/common/editorCommon'); import EditorBrowser = require('vs/editor/browser/editorBrowser'); import {ILanguageDef} from 'vs/editor/standalone-languages/types'; +import {IJSONSchema} from 'vs/base/common/jsonSchema'; var global:any = self; if (!global.Monaco) { @@ -67,4 +69,10 @@ Monaco.Editor.OverlayWidgetPositionPreference = EditorBrowser.OverlayWidgetPosit let MonacoEditorLanguages: ILanguageDef[] = this.MonacoEditorLanguages || []; MonacoEditorLanguages.forEach((language) => { standaloneCodeEditor.registerStandaloneLanguage(language, language.defModule); -}); \ No newline at end of file +}); + +// Register all built-in standalone JSON schemas +let MonacoEditorSchemas: { [url:string]: IJSONSchema } = this.MonacoEditorSchemas || {}; +for (var uri in MonacoEditorSchemas) { + standaloneCodeEditor.registerStandaloneSchema(uri, MonacoEditorSchemas[uri]); +}; \ No newline at end of file diff --git a/src/vs/editor/browser/standalone/standaloneSchemas.ts b/src/vs/editor/browser/standalone/standaloneSchemas.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4e85fa5d3421e89dd4fc3813c7efa81bb8d9def --- /dev/null +++ b/src/vs/editor/browser/standalone/standaloneSchemas.ts @@ -0,0 +1,1077 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import nls = require('vs/nls'); +import {IJSONSchema} from 'vs/base/common/jsonSchema'; + +this.MonacoEditorSchemas = this.MonacoEditorSchemas || {}; +let MonacoEditorSchemas: { [uri:string]:IJSONSchema } = this.MonacoEditorSchemas; + +MonacoEditorSchemas['http://json-schema.org/draft-04/schema#'] = { + 'id': 'http://json-schema.org/draft-04/schema#', + 'title': nls.localize('schema.json', 'Describes a JSON file using a schema. See json-schema.org for more info.'), + '$schema': 'http://json-schema.org/draft-04/schema#', + 'definitions': { + 'schemaArray': { + 'type': 'array', + 'minItems': 1, + 'items': { '$ref': '#' } + }, + 'positiveInteger': { + 'type': 'integer', + 'minimum': 0 + }, + 'positiveIntegerDefault0': { + 'allOf': [{ '$ref': '#/definitions/positiveInteger' }, { 'default': 0 }] + }, + 'simpleTypes': { + 'type': 'string', + 'enum': ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'] + }, + 'stringArray': { + 'type': 'array', + 'items': { 'type': 'string' }, + 'minItems': 1, + 'uniqueItems': true + } + }, + 'type': 'object', + 'properties': { + 'id': { + 'type': 'string', + 'format': 'uri', + 'description': nls.localize('schema.json.id', 'A unique identifier for the schema.') + }, + '$schema': { + 'type': 'string', + 'format': 'uri', + 'description': nls.localize('schema.json.$schema', 'The schema to verify this document against ') + }, + 'title': { + 'type': 'string', + 'description': nls.localize('schema.json.title', 'A descriptive title of the element') + }, + 'description': { + 'type': 'string', + 'description': nls.localize('schema.json.description', 'A long description of the element. Used in hover menus and suggestions.') + }, + 'default': { + 'description': nls.localize('schema.json.default', 'A default value. Used by suggestions.') + }, + 'multipleOf': { + 'type': 'number', + 'minimum': 0, + 'exclusiveMinimum': true, + 'description': nls.localize('schema.json.multipleOf', 'A number that should cleanly divide the current value (i.e. have no remainder)') + }, + 'maximum': { + 'type': 'number', + 'description': nls.localize('schema.json.maximum', 'The maximum numerical value, inclusive by default.') + }, + 'exclusiveMaximum': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('schema.json.exclusiveMaximum', 'Makes the maximum property exclusive.') + }, + 'minimum': { + 'type': 'number', + 'description': nls.localize('schema.json.minimum', 'The minimum numerical value, inclusive by default.') + }, + 'exclusiveMinimum': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('schema.json.exclusiveMininum', 'Makes the minimum property exclusive.') + }, + 'maxLength': { + 'allOf': [ + { '$ref': '#/definitions/positiveInteger' } + ], + 'description': nls.localize('schema.json.maxLength', 'The maximum length of a string.') + }, + 'minLength': { + 'allOf': [ + { '$ref': '#/definitions/positiveIntegerDefault0' } + ], + 'description': nls.localize('schema.json.minLength', 'The minimum length of a string.') + }, + 'pattern': { + 'type': 'string', + 'format': 'regex', + 'description': nls.localize('schema.json.pattern', 'A regular expression to match the string against. It is not implicitly anchored.') + }, + 'additionalItems': { + 'anyOf': [ + { 'type': 'boolean' }, + { '$ref': '#' } + ], + 'default': {}, + 'description': nls.localize('schema.json.additionalItems', 'For arrays, only when items is set as an array. If it is a schema, then this schema validates items after the ones specified by the items array. If it is false, then additional items will cause validation to fail.') + }, + 'items': { + 'anyOf': [ + { '$ref': '#' }, + { '$ref': '#/definitions/schemaArray' } + ], + 'default': {}, + 'description': nls.localize('schema.json.items', 'For arrays. Can either be a schema to validate every element against or an array of schemas to validate each item against in order (the first schema will validate the first element, the second schema will validate the second element, and so on.') + }, + 'maxItems': { + 'allOf': [ + { '$ref': '#/definitions/positiveInteger' } + ], + 'description': nls.localize('schema.json.maxItems', 'The maximum number of items that can be inside an array. Inclusive.') + }, + 'minItems': { + 'allOf': [ + { '$ref': '#/definitions/positiveIntegerDefault0' } + ], + 'description': nls.localize('schema.json.minItems', 'The minimum number of items that can be inside an array. Inclusive.') + }, + 'uniqueItems': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('schema.json.uniqueItems', 'If all of the items in the array must be unique. Defaults to false.') + }, + 'maxProperties': { + 'allOf': [ + { '$ref': '#/definitions/positiveInteger' } + ], + 'description': nls.localize('schema.json.maxProperties', 'The maximum number of properties an object can have. Inclusive.') + }, + 'minProperties': { + 'allOf': [ + { '$ref': '#/definitions/positiveIntegerDefault0' }, + ], + 'description': nls.localize('schema.json.minProperties', 'The minimum number of properties an object can have. Inclusive.') + }, + 'required': { + 'allOf': [ + { '$ref': '#/definitions/stringArray' } + ], + 'description': nls.localize('schema.json.required', 'An array of strings that lists the names of all properties required on this object.') + }, + 'additionalProperties': { + 'anyOf': [ + { 'type': 'boolean' }, + { '$ref': '#' } + ], + 'default': {}, + 'description': nls.localize('schema.json.additionalProperties', 'Either a schema or a boolean. If a schema, then used to validate all properties not matched by \'properties\' or \'patternProperties\'. If false, then any properties not matched by either will cause this schema to fail.') + }, + 'definitions': { + 'type': 'object', + 'additionalProperties': { '$ref': '#' }, + 'default': {}, + 'description': nls.localize('schema.json.definitions', 'Not used for validation. Place subschemas here that you wish to reference inline with $ref') + }, + 'properties': { + 'type': 'object', + 'additionalProperties': { '$ref': '#' }, + 'default': {}, + 'description': nls.localize('schema.json.properties', 'A map of property names to schemas for each property.') + }, + 'patternProperties': { + 'type': 'object', + 'additionalProperties': { '$ref': '#' }, + 'default': {}, + 'description': nls.localize('schema.json.patternProperties', 'A map of regular expressions on property names to schemas for matching properties.') + }, + 'dependencies': { + 'type': 'object', + 'additionalProperties': { + 'anyOf': [ + { '$ref': '#' }, + { '$ref': '#/definitions/stringArray' } + ] + }, + 'description': nls.localize('schema.json.dependencies', 'A map of property names to either an array of property names or a schema. An array of property names means the property named in the key depends on the properties in the array being present in the object in order to be valid. If the value is a schema, then the schema is only applied to the object if the property in the key exists on the object.') + }, + 'enum': { + 'type': 'array', + 'minItems': 1, + 'uniqueItems': true, + 'description': nls.localize('schema.json.enum', 'The set of literal values that are valid') + }, + 'type': { + 'anyOf': [ + { '$ref': '#/definitions/simpleTypes' }, + { + 'type': 'array', + 'items': { '$ref': '#/definitions/simpleTypes' }, + 'minItems': 1, + 'uniqueItems': true + } + ], + 'description': nls.localize('schema.json.type', 'Either a string of one of the basic schema types (number, integer, null, array, object, boolean, string) or an array of strings specifying a subset of those types.') + }, + 'allOf': { + 'allOf': [ + { '$ref': '#/definitions/schemaArray' } + ], + 'description': nls.localize('schema.json.allOf', 'An array of schemas, all of which must match.') + }, + 'anyOf': { + 'allOf': [ + { '$ref': '#/definitions/schemaArray' } + ], + 'description': nls.localize('schema.json.anyOf', 'An array of schemas, where at least one must match.') + }, + 'oneOf': { + 'allOf': [ + { '$ref': '#/definitions/schemaArray' } + ], + 'description': nls.localize('schema.json.oneOf', 'An array of schemas, exactly one of which must match.') + }, + 'not': { + 'allOf': [ + { '$ref': '#' } + ], + 'description': nls.localize('schema.json.not', 'A schema which must not match.') + } + }, + 'dependencies': { + 'exclusiveMaximum': ['maximum'], + 'exclusiveMinimum': ['minimum'] + }, + 'default': {} +}; +MonacoEditorSchemas['http://json.schemastore.org/project'] = { + 'title': nls.localize('project.json.title', 'JSON schema for ASP.NET project.json files'), + '$schema': 'http://json-schema.org/draft-04/schema#', + 'id': 'http://json.schemastore.org/project', + 'type': 'object', + + 'definitions': { + 'compilationOptions': { + 'description': nls.localize('project.json.compilationOptions', 'Compilation options that are passed through to Roslyn'), + 'type': 'object', + 'properties': { + 'define': { + 'type': 'array', + 'items': { + 'type': 'string', + 'uniqueItems': true + } + }, + 'warningsAsErrors': { + 'type': 'boolean', + 'default': false + }, + 'allowUnsafe': { + 'type': 'boolean', + 'default': false + }, + 'optimize': { + 'type': 'boolean', + 'default': false + }, + 'languageVersion': { + 'type': 'string', + 'enum': ['csharp1', 'csharp2', 'csharp3', 'csharp4', 'csharp5', 'csharp6', 'experimental'] + } + } + }, + 'configType': { + 'type': 'object', + 'properties': { + 'dependencies': { '$ref': '#/definitions/dependencies' }, + 'compilationOptions': { '$ref': '#/definitions/compilationOptions' }, + 'frameworkAssemblies': { '$ref': '#/definitions/dependencies' } + } + }, + 'dependencies': { + 'type': 'object', + 'additionalProperties': { + 'type': ['string', 'object'], + 'properties': { + 'version': { + 'type': 'string', + 'description': nls.localize('project.json.dependency.name', 'The version of the dependency.') + }, + 'type': { + 'type': 'string', + 'default': 'default', + 'enum': ['default', 'build'], + 'description': nls.localize('project.json.dependency.type', 'The type of the dependency. \'build\' dependencies only exist at build time') + } + }, + 'id': 'nugget-package' + + }, + 'description': nls.localize('project.json.dependencies', 'The dependencies of the application. Each entry specifes the name and the version of a Nuget package.'), + 'id': 'nugget-packages' + }, + 'script': { + 'type': ['string', 'array'], + 'items': { + 'type': 'string' + }, + 'description': nls.localize('project.json.script', 'A command line script or scripts.\r\rAvailable variables:\r%project:Directory% - The project directory\r%project:Name% - The project name\r%project:Version% - The project version') + } + }, + + 'properties': { + 'authors': { + 'description': nls.localize('project.json.authors', 'The author of the application'), + 'type': 'array', + 'items': { + 'type': 'string', + 'uniqueItems': true + } + }, + 'bundleExclude': { + 'description': nls.localize('project.json.bundleExclude', 'List of files to exclude from publish output (kpm bundle).'), + 'type': [ 'string', 'array' ], + 'items': { + 'type': 'string' + }, + 'default': '' + }, + 'code': { + 'description': nls.localize('project.json.code', 'Glob pattern to specify all the code files that needs to be compiled. (data type: string or array with glob pattern(s)). Example: [ \'Folder1\\*.cs\', \'Folder2\\*.cs\' ]'), + 'type': ['string', 'array'], + 'items': { + 'type': 'string' + }, + 'default': '**\\*.cs' + }, + 'commands': { + 'description': nls.localize('project.json.commands', 'Commands that are available for this application'), + 'type': 'object', + 'additionalProperties': { + 'type': 'string' + } + }, + 'compilationOptions': { '$ref': '#/definitions/compilationOptions' }, + 'configurations': { + 'type': 'object', + 'description': nls.localize('project.json.configurations', 'Configurations are named groups of compilation settings. There are 2 defaults built into the runtime namely \'Debug\' and \'Release\'.'), + 'additionalProperties': { + 'type': 'object', + 'properties': { + 'compilationOptions': { '$ref': '#/definitions/compilationOptions' } + } + } + }, + 'dependencies': { '$ref': '#/definitions/dependencies' }, + 'description': { + 'description': nls.localize('project.json.description', 'The description of the application'), + 'type': 'string' + }, + 'exclude': { + 'description': nls.localize('project.json.exclude', 'Glob pattern to indicate all the code files to be excluded from compilation. (data type: string or array with glob pattern(s)).'), + 'type': ['string', 'array'], + 'items': { + 'type': 'string' + }, + 'default': ['bin/**/*.*', 'obj/**/*.*'] + }, + 'frameworks': { + 'description': nls.localize('project.json.frameworks', 'Target frameworks that will be built, and dependencies that are specific to the configuration.'), + 'type': 'object', + 'additionalProperties': { '$ref': '#/definitions/configType' } + }, + 'preprocess': { + 'description': nls.localize('project.json.preprocess', 'Glob pattern to indicate all the code files to be preprocessed. (data type: string with glob pattern).'), + 'type': 'string', + 'default': 'Compiler\\Preprocess\\**\\*.cs' + }, + 'resources': { + 'description': nls.localize('project.json.resources', 'Glob pattern to indicate all the files that need to be compiled as resources.'), + 'type': ['string', 'array'], + 'items': { + 'type': 'string' + }, + 'default': 'Compiler\\Resources\\**\\*.cs' + }, + 'scripts': { + 'type': 'object', + 'description': nls.localize('project.json.scripts', 'Scripts to execute during the various stages.'), + 'properties': { + 'prepack': { '$ref': '#/definitions/script' }, + 'postpack': { '$ref': '#/definitions/script' }, + + 'prebundle': { '$ref': '#/definitions/script' }, + 'postbundle': { '$ref': '#/definitions/script' }, + + 'prerestore': { '$ref': '#/definitions/script' }, + 'postrestore': { '$ref': '#/definitions/script' }, + 'prepare': { '$ref': '#/definitions/script' } + } + }, + 'shared': { + 'description': nls.localize('project.json.shared', 'Glob pattern to specify the code files to share with dependent projects. Example: [ \'Folder1\\*.cs\', \'Folder2\\*.cs\' ]'), + 'type': ['string', 'array'], + 'items': { + 'type': 'string' + }, + 'default': 'Compiler\\Shared\\**\\*.cs' + }, + 'version': { + 'description': nls.localize('project.json.version', 'The version of the application. Example: 1.2.0.0'), + 'type': 'string' + }, + 'webroot': { + 'description': nls.localize('project.json.webroot', 'Specifying the webroot property in the project.json file specifies the web server root (aka public folder). In visual studio, this folder will be used to root IIS. Static files should be put in here.'), + 'type': 'string' + } + } + +}; +MonacoEditorSchemas['http://json.schemastore.org/bower'] = { + + 'title': nls.localize('bower.json.title', 'JSON schema for Bower configuration files'), + '$schema': 'http://json-schema.org/draft-04/schema#', + 'id': 'http://json.schemastore.org/bower', + + 'type': 'object', + 'required': ['name'], + + 'patternProperties': { + '^_': { + 'description': nls.localize('bower.json.invalidPatternName', 'Any property starting with _ is valid.'), + 'additionalProperties': true, + 'additionalItems': true + } + }, + + 'properties': { + 'name': { + 'description': nls.localize('bower.json.packagename', 'The name of your package.'), + 'type': 'string', + 'maxLength': 50 + }, + 'description': { + 'description': nls.localize('bower.json.description', 'Help users identify and search for your package with a brief description.'), + 'type': 'string' + }, + 'version': { + 'description': nls.localize('bower.json.version', 'A semantic version number.'), + 'type': 'string' + }, + 'main': { + 'description': nls.localize('bower.json.main', 'The primary acting files necessary to use your package.'), + 'type': ['string', 'array'] + }, + 'license': { + 'description': nls.localize('bower.json.license', 'SPDX license identifier or path/url to a license.'), + 'type': ['string', 'array'], + 'maxLength': 140 + }, + 'ignore': { + 'description': nls.localize('bower.json.ignore', 'A list of files for Bower to ignore when installing your package.'), + 'type': ['string', 'array'] + }, + 'keywords': { + 'description': nls.localize('bower.json.keywords', 'Used for search by keyword. Helps make your package easier to discover without people needing to know its name.'), + 'type': 'array', + 'items': { + 'type': 'string', + 'maxLength': 50 + } + }, + 'authors': { + 'description': nls.localize('bower.json.authors', 'A list of people that authored the contents of the package.'), + 'type': 'array', + 'items': { + 'type': ['string', 'object'] + } + }, + 'homepage': { + 'description': nls.localize('bower.json.homepage', 'URL to learn more about the package. Falls back to GitHub project if not specified and it\'s a GitHub endpoint.'), + 'type': 'string' + }, + 'repository': { + 'description': nls.localize('bower.json.repository', 'The repository in which the source code can be found.'), + 'type': 'object', + 'properties': { + 'type': { + 'type': 'string', + 'enum': ['git'] + }, + 'url': { + 'type': 'string' + } + } + }, + 'dependencies': { + 'id': 'bower-packages', + 'description': nls.localize('bower.json.dependencies', 'Dependencies are specified with a simple hash of package name to a semver compatible identifier or URL.'), + 'type': 'object', + 'additionalProperties': { + 'id': 'bower-package', + 'type': 'string' + } + }, + 'devDependencies': { + 'id': 'bower-packages', + 'description': nls.localize('bower.json.devDependencies', 'Dependencies that are only needed for development of the package, e.g., test framework or building documentation.'), + 'type': 'object', + 'additionalProperties': { + 'id': 'bower-package', + 'type': 'string' + } + }, + 'resolutions': { + 'description': nls.localize('bower.json.resolutions', 'Dependency versions to automatically resolve with if conflicts occur between packages.'), + 'type': 'object' + }, + 'private': { + 'description': nls.localize('bower.json.private', 'If you set it to true it will refuse to publish it. This is a way to prevent accidental publication of private repositories.'), + 'type': 'boolean' + }, + 'exportsOverride': { + 'description': nls.localize('bower.json.exportsOverride', 'Used by grunt-bower-task to specify custom install locations.'), + 'type': 'object', + 'additionalProperties': { + 'type': 'object', + 'additionalProperties': { + 'type': 'string' + } + } + }, + 'moduleType': { + 'description': nls.localize('bower.json.moduleType', 'The types of modules this package exposes'), + 'type': 'array', + 'items': { + 'enum': ['amd', 'es6', 'globals', 'node', 'yui'] + } + } + } +}; +MonacoEditorSchemas['http://json.schemastore.org/package'] = { + 'id': 'http://json.schemastore.org/package', + 'description': nls.localize('package.json.description', 'NPM configuration for this package.'), + 'type': 'object', + 'required': ['name', 'version'], + 'definitions': { + 'person': { + 'description': nls.localize('package.json.person', 'A person who has been involved in creating or maintaining this package'), + 'type': [ 'object', 'string' ], + 'required': [ 'name' ], + 'properties': { + 'name': { + 'type': 'string' + }, + 'url': { + 'type': 'string', + 'format': 'uri' + }, + 'email': { + 'type': 'string', + 'format': 'email' + } + } + }, + 'dependency': { + 'id': 'npm-packages', + 'description': nls.localize('package.json.dependency', 'Dependencies are specified with a simple hash of package name to version range. The version range is a string which has one or more space-separated descriptors. Dependencies can also be identified with a tarball or git URL.'), + 'type': 'object', + 'additionalProperties': { + 'type': 'string' + } + } + }, + + 'patternProperties': { + '^_': { + 'description': nls.localize('package.json.underscore', 'Any property starting with _ is valid.'), + 'additionalProperties': true, + 'additionalItems': true + } + }, + + 'properties': { + 'name': { + 'description': nls.localize('package.json.name', 'The name of the package.'), + 'type': 'string' + }, + 'version': { + 'description': nls.localize('package.json.version', 'Version must be parseable by node-semver, which is bundled with npm as a dependency.'), + 'type': 'string' + }, + 'description': { + 'description': nls.localize('package.json.descr', 'This helps people discover your package, as it\'s listed in \'npm search\'.'), + 'type': 'string' + }, + 'icon': { + 'description': nls.localize('package.json.icon', 'The relative path to the icon of the package.'), + 'type': 'string' + }, + 'keywords': { + 'description': nls.localize('package.json.keywords', 'This helps people discover your package as it\'s listed in \'npm search\'.'), + 'type': 'array' + }, + 'homepage': { + 'description': nls.localize('package.json.homepage', 'The url to the project homepage.'), + 'type': 'string' + }, + 'bugs': { + 'description': nls.localize('package.json.bugs', 'The url to your project\'s issue tracker and / or the email address to which issues should be reported. These are helpful for people who encounter issues with your package.'), + 'type': [ 'object', 'string' ], + 'properties': { + 'url': { + 'type': 'string', + 'description': nls.localize('package.json.bugs.url', 'The url to your project\'s issue tracker.'), + 'format': 'uri' + }, + 'email': { + 'type': 'string', + 'description': nls.localize('package.json.bugs.email', 'The email address to which issues should be reported.') + } + } + }, + 'license': { + 'type': 'string', + 'description': nls.localize('package.json.license', 'You should specify a license for your package so that people know how they are permitted to use it, and any restrictions you\'re placing on it.') + }, + 'licenses': { + 'description': nls.localize('package.json.licenses', 'You should specify a license for your package so that people know how they are permitted to use it, and any restrictions you\'re placing on it.'), + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'type': { + 'type': 'string' + }, + 'url': { + 'type': 'string', + 'format': 'uri' + } + } + } + }, + 'author': { + '$ref': '#/definitions/person' + }, + 'contributors': { + 'description': nls.localize('package.json.contributors', 'A list of people who contributed to this package.'), + 'type': 'array', + 'items': { + '$ref': '#/definitions/person' + } + }, + 'maintainers': { + 'description': nls.localize('package.json.maintainers', 'A list of people who maintains this package.'), + 'type': 'array', + 'items': { + '$ref': '#/definitions/person' + } + }, + 'files': { + 'description': nls.localize('package.json.files', 'The \'files\' field is an array of files to include in your project. If you name a folder in the array, then it will also include the files inside that folder.'), + 'type': 'array', + 'items': { + 'type': 'string' + } + }, + 'main': { + 'description': nls.localize('package.json.main', 'The main field is a module ID that is the primary entry point to your program.'), + 'type': 'string' + }, + 'bin': { + 'type': [ 'string', 'object' ], + 'additionalProperties': { + 'type': 'string' + } + }, + 'man': { + 'type': [ 'array', 'string' ], + 'description': nls.localize('package.json.man', 'Specify either a single file or an array of filenames to put in place for the man program to find.'), + 'items': { + 'type': 'string' + } + }, + 'directories': { + 'type': 'object', + 'properties': { + 'bin': { + 'description': nls.localize('package.json.directories.bin', 'If you specify a \'bin\' directory, then all the files in that folder will be used as the \'bin\' hash.'), + 'type': 'string' + }, + 'doc': { + 'description': nls.localize('package.json.directories.doc', 'Put markdown files in here. Eventually, these will be displayed nicely, maybe, someday.'), + 'type': 'string' + }, + 'example': { + 'description': nls.localize('package.json.directories.example', 'Put example scripts in here. Someday, it might be exposed in some clever way.'), + 'type': 'string' + }, + 'lib': { + 'description': nls.localize('package.json.directories.lib', 'Tell people where the bulk of your library is. Nothing special is done with the lib folder in any way, but it\'s useful meta info.'), + 'type': 'string' + }, + 'man': { + 'description': nls.localize('package.json.directories.man', 'A folder that is full of man pages. Sugar to generate a \'man\' array by walking the folder.'), + 'type': 'string' + }, + 'test': { + 'type': 'string' + } + } + }, + 'repository': { + 'description': nls.localize('package.json.repository', 'Specify the place where your code lives. This is helpful for people who want to contribute.'), + 'type': 'object', + 'properties': { + 'type': { + 'type': 'string' + }, + 'url': { + 'type': 'string' + } + } + }, + 'scripts': { + 'description': nls.localize('package.json.scripts', 'The \'scripts\' member is an object hash of script commands that are run at various times in the lifecycle of your package. The key is the lifecycle event, and the value is the command to run at that point.'), + 'type': 'object', + 'additionalProperties': { + 'type': 'string' + } + }, + 'config': { + 'description': nls.localize('package.json.config', 'A \'config\' hash can be used to set configuration parameters used in package scripts that persist across upgrades.'), + 'type': 'object', + 'additionalProperties': true + }, + 'dependencies': { + '$ref': '#/definitions/dependency' + }, + 'devDependencies': { + '$ref': '#/definitions/dependency' + }, + 'bundleDependencies': { + 'type': 'array', + 'description': nls.localize('package.json.bundleDependencies', 'Array of package names that will be bundled when publishing the package.'), + 'items': { + 'type': 'string' + } + }, + 'bundledDependencies': { + 'type': 'array', + 'description': nls.localize('package.json.bundledDependencies', 'Array of package names that will be bundled when publishing the package.'), + 'items': { + 'type': 'string' + } + }, + 'optionalDependencies': { + '$ref': '#/definitions/dependency' + }, + 'peerDependencies': { + '$ref': '#/definitions/dependency' + }, + 'engines': { + 'type': 'object', + 'additionalProperties': { + 'type': 'string' + } + }, + 'engineStrict': { + 'type': 'boolean' + }, + 'os': { + 'type': 'array', + 'items': { + 'type': 'string' + } + }, + 'cpu': { + 'type': 'array', + 'items': { + 'type': 'string' + } + }, + 'preferGlobal': { + 'type': 'boolean', + 'description': nls.localize('package.json.preferGlobal', 'If your package is primarily a command-line application that should be installed globally, then set this value to true to provide a warning if it is installed locally.') + }, + 'private': { + 'type': 'boolean', + 'description': nls.localize('package.json.private', 'If set to true, then npm will refuse to publish it.') + }, + 'publishConfig': { + 'type': 'object', + 'additionalProperties': true + }, + 'dist': { + 'type': 'object', + 'properties': { + 'shasum': { + 'type': 'string' + }, + 'tarball': { + 'type': 'string' + } + } + }, + 'readme': { + 'type': 'string' + } + } +}; +MonacoEditorSchemas['http://json.schemastore.org/global'] = { + 'title': nls.localize('global.json.title', 'JSON schema for the ASP.NET global configuration files'), + 'type': 'object', + 'additionalProperties': true, + 'required': [ 'projects' ], + + 'properties': { + 'projects': { + 'type': 'array', + 'description': nls.localize('global.json.projects', 'A list of project folders relative to this file.'), + 'items': { + 'type': 'string' + } + }, + 'sources': { + 'type': 'array', + 'description': nls.localize('global.json.sources', 'A list of source folders relative to this file.'), + 'items': { + 'type': 'string' + } + }, + 'sdk': { + 'type': 'object', + 'description': nls.localize('global.json.sdk', 'The runtime to use.'), + 'properties': { + 'version': { + 'type': 'string', + 'description': nls.localize('global.json.sdk.version', 'The runtime version to use.') + }, + 'runtime': { + 'type': 'string', + 'description': nls.localize('global.json.sdk.runtime', 'The runtime to use, e.g. coreclr'), + }, + 'architecture': { + 'type': 'string', + 'description': nls.localize('global.json.sdk.architecture', 'The runtime architecture to use, e.g. x64.') + } + } + } + } +}; +MonacoEditorSchemas['http://json.schemastore.org/tsconfig'] = { + 'title': nls.localize('tsconfig.json.title', "JSON schema for the TypeScript compiler's configuration file"), + '$schema': 'http://json-schema.org/draft-04/schema#', + + 'type': 'object', + 'default': { 'compilerOptions': { 'target': 'ES5', 'module': 'commonjs'} }, + 'properties': { + 'compilerOptions': { + 'type': 'object', + 'description': nls.localize('tsconfig.json.compilerOptions', 'Instructs the TypeScript compiler how to compile .ts files'), + 'properties': { + 'charset': { + 'description': nls.localize('tsconfig.json.compilerOptions.charset', 'The character set of the input files'), + 'type': 'string' + }, + 'declaration': { + 'description': nls.localize('tsconfig.json.compilerOptions.declaration', 'Generates corresponding d.ts files.'), + 'type': 'boolean' + }, + 'diagnostics': { + 'description': nls.localize('tsconfig.json.compilerOptions.diagnostics', 'Show diagnostic information.'), + 'type': 'boolean' + }, + 'emitBOM': { + 'description': nls.localize('tsconfig.json.compilerOptions.emitBOM', 'Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files.'), + 'type': 'boolean' + }, + 'inlineSourceMap': { + 'description': nls.localize('tsconfig.json.compilerOptions.inlineSourceMap', 'Emit a single file with source maps instead of having a separate file.'), + 'type': 'boolean' + }, + 'inlineSources': { + 'description': nls.localize('tsconfig.json.compilerOptions.inlineSources', 'Emit the source alongside the sourcemaps within a single file; requires --inlineSourceMap to be set.'), + 'type': 'boolean' + }, + 'listFiles': { + 'description': nls.localize('tsconfig.json.compilerOptions.listFiles', 'Print names of files part of the compilation.'), + 'type': 'boolean' + }, + 'locale': { + 'description': nls.localize('tsconfig.json.compilerOptions.locale', 'The locale to use to show error messages, e.g. en-us.'), + 'type': 'string' + }, + 'mapRoot': { + 'description': nls.localize('tsconfig.json.compilerOptions.mapRoot', 'Specifies the location where debugger should locate map files instead of generated locations'), + 'type': 'string', + 'format': 'uri' + }, + 'module': { + 'description': nls.localize('tsconfig.json.compilerOptions.module', "Specify module code generation: 'CommonJS', 'Amd', 'System', or 'UMD'."), + 'enum': ['commonjs', 'amd', 'umd', 'system'] + }, + 'newLine': { + 'description': nls.localize('tsconfig.json.compilerOptions.newLine', "Specifies the end of line sequence to be used when emitting files: 'CRLF' (dos) or 'LF' (unix).)"), + 'enum': [ 'CRLF', 'LF' ] + }, + 'noEmit': { + 'description': nls.localize('tsconfig.json.compilerOptions.noEmit', 'Do not emit output.'), + 'type': 'boolean' + }, + 'noEmitOnError': { + 'description': nls.localize('tsconfig.json.compilerOptions.noEmitOnError', 'Do not emit outputs if any type checking errors were reported.'), + 'type': 'boolean' + }, + 'noEmitHelpers': { + 'description': nls.localize('tsconfig.json.compilerOptions.noEmitHelpers', 'Do not generate custom helper functions like __extends in compiled output.'), + 'type': 'boolean' + }, + 'noImplicitAny': { + 'description': nls.localize('tsconfig.json.compilerOptions.noImplicitAny', "Warn on expressions and declarations with an implied 'any' type."), + 'type': 'boolean' + }, + 'noLib': { + 'description': nls.localize('tsconfig.json.compilerOptions.noLib', "Do not include the default library file (lib.d.ts)."), + 'type': 'boolean' + }, + 'noResolve': { + 'description': nls.localize('tsconfig.json.compilerOptions.noResolve', "Do not add triple-slash references or module import targets to the list of compiled files."), + 'type': 'boolean' + }, + 'out': { + 'description': nls.localize('tsconfig.json.compilerOptions.out', 'Concatenate and emit output to single file.'), + 'type': 'string', + 'format': 'uri' + }, + 'outDir': { + 'description': nls.localize('tsconfig.json.compilerOptions.outDir', 'Redirect output structure to the directory.'), + 'type': 'string', + 'format': 'uri' + }, + 'preserveConstEnums': { + 'description': nls.localize('tsconfig.json.compilerOptions.preserveConstEnums', 'Do not erase const enum declarations in generated code.'), + 'type': 'boolean' + }, + 'removeComments': { + 'description': nls.localize('tsconfig.json.compilerOptions.removeComments', 'Do not emit comments to output.'), + 'type': 'boolean' + }, + 'rootDir': { + 'description': nls.localize('tsconfig.json.compilerOptions.rootDir', 'Specifies the root directory of input files. Use to control the output directory structure with --outDir.'), + 'type': 'string' + }, + 'sourceMap': { + 'description': nls.localize('tsconfig.json.compilerOptions.sourceMap', "Generates corresponding '.map' file."), + 'type': 'boolean' + }, + 'sourceRoot': { + 'description': nls.localize('tsconfig.json.compilerOptions.sourceRoot', 'Specifies the location where debugger should locate TypeScript files instead of source locations.'), + 'type': 'string', + 'format': 'uri' + }, + 'suppressImplicitAnyIndexErrors': { + 'description': nls.localize('tsconfig.json.compilerOptions.suppressImplicitAnyIndexErrors', 'Suppress noImplicitAny errors for indexing objects lacking index signatures.'), + 'type': 'boolean' + }, + 'target': { + 'description': nls.localize('tsconfig.json.compilerOptions.target', "Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6' (experimental)."), + 'enum': ['ES3', 'ES5', 'ES6', 'es3', 'es5', 'es6'], + 'default': 'ES3' + }, + 'watch': { + 'description': nls.localize('tsconfig.json.compilerOptions.watch', "Watch input files."), + "type": 'boolean' + }, + 'jsx': { + 'description': nls.localize('tsconfig.json.compilerOptions.jsx', "Enable the JSX option (requires TypeScript 1.6): 'preserve', 'react'."), + 'enum': ['react', 'preserve'], + 'default': 'react' + }, + 'emitDecoratorMetadata': { + 'description': nls.localize('tsconfig.json.compilerOptions.emitDecoratorMetadata', 'Emits meta data.for ES7 decorators.'), + 'type': 'boolean' + }, + 'isolatedModules': { + 'description': nls.localize('tsconfig.json.compilerOptions.isolatedModules', 'Supports transpiling single TS files into JS files.'), + 'type': 'boolean' + }, + 'experimentalDecorators': { + 'description': nls.localize('tsconfig.json.compilerOptions.experimentalDecorators', 'Enables experimental support for ES7 decorators.'), + 'type': 'boolean' + }, + 'experimentalAsyncFunctions': { + 'description': nls.localize('tsconfig.json.compilerOptions.experimentalAsynFunctions', 'Enables experimental support for async functions (requires TypeScript 1.6).'), + 'type': 'boolean' + } + } + }, + 'files': { + 'type': 'array', + 'description': nls.localize('tsconfig.json.files', "If no 'files' property is present in a tsconfig.json, the compiler defaults to including all files the containing directory and subdirectories. When a 'files' property is specified, only those files are included."), + 'items': { + 'type': 'string', + 'format': 'uri' + } + } + } +}; + +MonacoEditorSchemas['http://opentools.azurewebsites.net/jsconfig'] = { + 'title': nls.localize('jsconfig.json.title', "JSON schema for the JavaScript configuration file"), + 'type': 'object', + 'default': { 'compilerOptions': { 'target': 'ES5' } }, + 'properties': { + 'compilerOptions': { + 'type': 'object', + 'description': nls.localize('jsconfig.json.compilerOptions', 'Instructs the JavaScript language service how to validate .js files'), + 'properties': { + 'charset': { + 'description': nls.localize('jsconfig.json.compilerOptions.charset', 'The character set of the input files'), + 'type': 'string' + }, + 'diagnostics': { + 'description': nls.localize('jsconfig.json.compilerOptions.diagnostics', 'Show diagnostic information.'), + 'type': 'boolean' + }, + 'locale': { + 'description': nls.localize('jsconfig.json.compilerOptions.locale', 'The locale to use to show error messages, e.g. en-us.'), + 'type': 'string' + }, + 'mapRoot': { + 'description': nls.localize('jsconfig.json.compilerOptions.mapRoot', 'Specifies the location where debugger should locate map files instead of generated locations'), + 'type': 'string', + 'format': 'uri' + }, + 'module': { + 'description': nls.localize('jsconfig.json.compilerOptions.module', "Module code generation to resolve against: 'commonjs', 'amd', 'system', or 'umd'."), + 'enum': ['commonjs', 'amd', 'system', 'umd'] + }, + 'noLib': { + 'description': nls.localize('jsconfig.json.compilerOptions.noLib', "Do not include the default library file (lib.d.ts)."), + 'type': 'boolean' + }, + 'target': { + 'description': nls.localize('jsconfig.json.compilerOptions.target', "Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6' (experimental)."), + 'enum': ['ES3', 'ES5', 'ES6', 'es3', 'es5', 'es6'], + 'default': 'ES3' + }, + 'experimentalDecorators': { + 'description': nls.localize('jsconfig.json.compilerOptions.decorators', "Enables experimental support for ES7 decorators."), + 'type': 'boolean' + } + } + }, + 'files': { + 'type': 'array', + 'description': nls.localize('jsconfig.json.files', "If no 'files' property is present in a jsconfig.json, the language service defaults to including all files the containing directory and subdirectories. When a 'files' property is specified, only those files are included."), + 'items': { + 'type': 'string', + 'format': 'uri' + } + }, + 'exclude': { + 'type': 'array', + 'description': nls.localize('jsconfig.json.exclude', "List files and folders that should not be included. This property is not honored when the 'files' property is present."), + 'items': { + 'type': 'string', + 'format': 'uri' + } + } + } +}; diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index 36943ff08ecec539403d00fdb67b6b4f0057fdbb..957f9bc999b6bf7bed49d939944fd90c0578a267 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -9,6 +9,7 @@ import {Range} from 'vs/editor/common/core/range'; import {Selection} from 'vs/editor/common/core/selection'; import EditorCommon = require('vs/editor/common/editorCommon'); import {CursorMoveHelper} from 'vs/editor/common/controller/cursorMoveHelper'; +import {getRawEnterActionAtPosition} from 'vs/editor/common/modes/supports/onEnter'; export interface IShiftCommandOpts { isUnshift: boolean; @@ -50,19 +51,21 @@ export class ShiftCommand implements EditorCommon.ICommand { } public getEditOperations(model: EditorCommon.ITokenizedModel, builder: EditorCommon.IEditOperationBuilder): void { - var startLine = this._selection.startLineNumber, - endLine = this._selection.endLineNumber; + let startLine = this._selection.startLineNumber, + endLine = this._selection.endLineNumber, + _SPACE = ' '.charCodeAt(0); if (this._selection.endColumn === 1 && startLine !== endLine) { endLine = endLine - 1; } - var lineNumber:number, + let lineNumber:number, tabSize = this._opts.tabSize, - oneIndent = this._opts.oneIndent; + oneIndent = this._opts.oneIndent, + shouldIndentEmptyLines = (startLine === endLine); // indents[i] represents i * oneIndent - var indents: string[] = ['', oneIndent]; + let indents: string[] = ['', oneIndent]; // if indenting or outdenting on a whitespace only line if (this._selection.isEmpty()) { @@ -71,15 +74,21 @@ export class ShiftCommand implements EditorCommon.ICommand { } } - for (lineNumber = startLine; lineNumber <= endLine; lineNumber++) { - var lineText = model.getLineContent(lineNumber); - var indentationEndIndex = Strings.firstNonWhitespaceIndex(lineText); + // keep track of previous line's "miss-alignment" + let previousLineExtraSpaces = 0, extraSpaces = 0; + for (lineNumber = startLine; lineNumber <= endLine; lineNumber++, previousLineExtraSpaces = extraSpaces) { + extraSpaces = 0; + let lineText = model.getLineContent(lineNumber); + let indentationEndIndex = Strings.firstNonWhitespaceIndex(lineText); - if (this._opts.isUnshift) { - if (lineText.length === 0 || indentationEndIndex === 0) { - // empty line or line with no leading whitespace => nothing to do - continue; - } + if (this._opts.isUnshift && (lineText.length === 0 || indentationEndIndex === 0)) { + // empty line or line with no leading whitespace => nothing to do + continue; + } + + if (!shouldIndentEmptyLines && !this._opts.isUnshift && lineText.length === 0) { + // do not indent empty lines => nothing to do + continue; } if (indentationEndIndex === -1) { @@ -87,7 +96,45 @@ export class ShiftCommand implements EditorCommon.ICommand { indentationEndIndex = lineText.length; } - var desiredIndentCount: number; + if (lineNumber > 1) { + let contentStartVisibleColumn = CursorMoveHelper.visibleColumnFromColumn2(lineText, indentationEndIndex + 1, tabSize); + if (contentStartVisibleColumn % tabSize !== 0) { + // The current line is "miss-aligned", so let's see if this is expected... + // This can only happen when it has trailing commas in the indent + let enterAction = getRawEnterActionAtPosition(model, lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); + if (enterAction) { + extraSpaces = previousLineExtraSpaces; + if (enterAction.appendText) { + for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < tabSize; j++) { + if (enterAction.appendText.charCodeAt(j) === _SPACE) { + extraSpaces++; + } else { + break; + } + } + } + if (enterAction.removeText) { + extraSpaces = Math.max(0, extraSpaces - enterAction.removeText); + } + + // Act as if `prefixSpaces` is not part of the indentation + for (let j = 0; j < extraSpaces; j++) { + if (indentationEndIndex === 0 || lineText.charCodeAt(indentationEndIndex - 1) !== _SPACE) { + break; + } + indentationEndIndex--; + } + } + } + } + + + if (this._opts.isUnshift && indentationEndIndex === 0) { + // line with no leading whitespace => nothing to do + continue; + } + + let desiredIndentCount: number; if (this._opts.isUnshift) { desiredIndentCount = ShiftCommand.unshiftIndentCount(lineText, indentationEndIndex + 1, tabSize); } else { @@ -95,7 +142,7 @@ export class ShiftCommand implements EditorCommon.ICommand { } // Fill `indents`, as needed - for (var j = indents.length; j <= desiredIndentCount; j++) { + for (let j = indents.length; j <= desiredIndentCount; j++) { indents[j] = indents[j-1] + oneIndent; } diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index b36b57d5ce67c297e4bba7c38492b1cdc498e130..d75140b5201c9f0e13dea26d06dc7978c94a1181 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -505,7 +505,7 @@ export class CommonEditorConfiguration extends EventEmitter implements EditorCom tabSizeIsAuto: false, tabSize: 4, insertSpacesIsAuto: false, - insertSpaces: false + insertSpaces: true }; if (opts.tabSize === 'auto') { diff --git a/src/vs/editor/common/config/defaultConfig.ts b/src/vs/editor/common/config/defaultConfig.ts index 28748ec48681545e2092e8dc0f9c4c5de05feffc..51361684a405abd52baa1fe1927898c105fd859c 100644 --- a/src/vs/editor/common/config/defaultConfig.ts +++ b/src/vs/editor/common/config/defaultConfig.ts @@ -63,8 +63,8 @@ class ConfigClass implements IConfiguration { referenceInfos: true, renderWhitespace: false, - tabSize: 'auto', - insertSpaces: 'auto', + tabSize: 4, + insertSpaces: true, fontFamily: '', fontSize: 0, lineHeight: 0 diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 57b1488c0a91011e9c42600516667a3dcceb9183..4ca3774e40167e4ba1415dca9173e360c9909183 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -15,6 +15,7 @@ import {IEnterAction,IndentAction,IElectricAction} from 'vs/editor/common/modes' import {CursorMoveHelper, ICursorMoveHelperModel, IMoveResult} from 'vs/editor/common/controller/cursorMoveHelper'; import EditorCommon = require('vs/editor/common/editorCommon'); import Errors = require('vs/base/common/errors'); +import {getEnterActionAtPosition} from 'vs/editor/common/modes/supports/onEnter'; export interface IPostOperationRunnable { (ctx: IOneCursorOperationContext): void; @@ -491,23 +492,25 @@ export class OneCursorOp { // -------------------- START handlers that simply change cursor state public static jumpToBracket(cursor:OneCursor, ctx: IOneCursorOperationContext): boolean { - var bracketDecorations = cursor.getBracketsDecorations(); - var len = bracketDecorations.length; + let bracketDecorations = cursor.getBracketsDecorations(); - if (len !== 2) { + if (bracketDecorations.length !== 2) { return false; } - var position = cursor.getPosition(); + let firstBracket = cursor.model.getDecorationRange(bracketDecorations[0]); + let secondBracket = cursor.model.getDecorationRange(bracketDecorations[1]); - for (var i = 0; i < 2; i++) { - var range = cursor.model.getDecorationRange(bracketDecorations[i]); - var otherRange = cursor.model.getDecorationRange(bracketDecorations[1 - i]); + let position = cursor.getPosition(); - if (Utils.isPositionAtRangeEdges(position, range) || Utils.isPositionInsideRange(position, range)) { - cursor.moveModelPosition(false, otherRange.startLineNumber, otherRange.startColumn, 0, false); - return true; - } + if (Utils.isPositionAtRangeEdges(position, firstBracket) || Utils.isPositionInsideRange(position, firstBracket)) { + cursor.moveModelPosition(false, secondBracket.endLineNumber, secondBracket.endColumn, 0, false); + return true; + } + + if (Utils.isPositionAtRangeEdges(position, secondBracket) || Utils.isPositionInsideRange(position, secondBracket)) { + cursor.moveModelPosition(false, firstBracket.endLineNumber, firstBracket.endColumn, 0, false); + return true; } return false; @@ -950,64 +953,6 @@ export class OneCursorOp { return this._enter(cursor, true, ctx); } - private static _getEnterActionAtPosition(model:EditorCommon.IModel, lineNumber:number, column:number): { enterAction: IEnterAction; indentation: string; } { - var lineText = model.getLineContent(lineNumber); - var lineContext = model.getLineContext(lineNumber); - var enterAction:IEnterAction; - - if (model.getMode().onEnterSupport) { - try { - enterAction = model.getMode().onEnterSupport.onEnter(model, new Position(lineNumber, column)); - } catch (e) { - Errors.onUnexpectedError(e); - } - } - - if (!enterAction) { - if (model.getMode().electricCharacterSupport) { - try { - enterAction = model.getMode().electricCharacterSupport.onEnter(lineContext, column - 1); - } catch(e) { - Errors.onUnexpectedError(e); - } - } - } else { - // console.log('USING NEW INDENTATION LOGIC!'); - } - - var indentation = Strings.getLeadingWhitespace(lineText); - if (indentation.length > column - 1) { - indentation = indentation.substring(0, column - 1); - } - - if (!enterAction) { - enterAction = { - indentAction: IndentAction.None, - appendText: '', - }; - } else { - if(!enterAction.appendText) { - if ( - (enterAction.indentAction === IndentAction.Indent) || - (enterAction.indentAction === IndentAction.IndentOutdent) - ) { - enterAction.appendText = '\t'; - } else { - enterAction.appendText = ''; - } - } - } - - if (enterAction.removeText) { - indentation = indentation.substring(0, indentation.length - 1); - } - - return { - enterAction: enterAction, - indentation: indentation - }; - } - private static _enter(cursor:OneCursor, keepPosition: boolean, ctx: IOneCursorOperationContext, position?: EditorCommon.IEditorPosition, range?: EditorCommon.IEditorRange): boolean { if (typeof position === 'undefined') { position = cursor.getPosition(); @@ -1017,7 +962,7 @@ export class OneCursorOp { } ctx.shouldPushStackElementBefore = true; - var r = this._getEnterActionAtPosition(cursor.model, position.lineNumber, position.column); + var r = getEnterActionAtPosition(cursor.model, position.lineNumber, position.column); var enterAction = r.enterAction; var indentation = r.indentation; @@ -1308,7 +1253,7 @@ export class OneCursorOp { return '\t'; } - var r = this._getEnterActionAtPosition(cursor.model, lastLineNumber, cursor.model.getLineMaxColumn(lastLineNumber)); + var r = getEnterActionAtPosition(cursor.model, lastLineNumber, cursor.model.getLineMaxColumn(lastLineNumber)); var indentation: string; if (r.enterAction.indentAction === IndentAction.Outdent) { diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index da54897c2e23917697ea46f8d03f5b23ba371294..599213e39029e60d9283303a97e7f52add9b6db0 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -490,12 +490,14 @@ export interface ICommonEditorOptions { export interface IEditorOptions extends ICommonEditorOptions { /** * Tab size in spaces. This is used for rendering and for editing. - * Defaults to 'auto', meaning the model attached to the editor will be scanned and this property will be guessed. + * 'auto' means the model attached to the editor will be scanned and this property will be guessed. + * Defaults to 4. */ tabSize?:any; /** * Insert spaces instead of tabs when indenting or when auto-indenting. - * Defaults to 'auto', meaning the model attached to the editor will be scanned and this property will be guessed. + * 'auto' means the model attached to the editor will be scanned and this property will be guessed. + * Defaults to true. */ insertSpaces?:any; /** diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 46718fbec8cb1a55ed4001747cc32f5fff3807c7..027dcf91ee7393b83a5f5dd24bd97640c141b1c9 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -486,11 +486,11 @@ export class TextModel extends OrderGuaranteeEventEmitter implements EditorCommo linesIndentedWithSpaces += (absoluteSpaceCounts[i] || 0); } - // Give preference to tabs over spaces (when evidence is the same) + // Give preference to spaces over tabs (when evidence is the same) // or when there are not enough clues (too little indentation in the file) if (linesIndentedWithTabs >= linesIndentedWithSpaces) { return { - insertSpaces: false, + insertSpaces: true, tabSize: defaultTabSize }; } @@ -498,7 +498,7 @@ export class TextModel extends OrderGuaranteeEventEmitter implements EditorCommo if (linesWithIndentationCount < 6 && linesIndentedWithTabs > 0) { // Making a guess with 6 indented lines, of which tabs are used besides spaces is very difficult return { - insertSpaces: false, + insertSpaces: true, tabSize: defaultTabSize }; } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 4bd0910b8ffcb5dfa03fcf1138e120a5fcc17fcf..5ef3da01819844722a9d5c5eaf6d3c3fd45e32b5 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -409,7 +409,7 @@ export interface ISuggestionFilter { (word: string, suggestion: ISuggestion): IMatch[]; } -export interface ISuggestionSorter { +export interface ISuggestionCompare { (one: ISuggestion, other: ISuggestion): number; } @@ -429,7 +429,7 @@ export interface ISuggestSupport { getSuggestionDetails?:(resource:URI, position:EditorCommon.IPosition, suggestion:ISuggestion)=>TPromise; getFilter():ISuggestionFilter; - getSorter?():ISuggestionSorter; + getSorter?():ISuggestionCompare; getTriggerCharacters():string[]; shouldShowEmptySuggestionList():boolean; shouldAutotriggerSuggest(context:ILineContext, offset:number, triggeredByCharacter:string):boolean; diff --git a/src/vs/editor/common/modes/modesFilters.ts b/src/vs/editor/common/modes/modesFilters.ts index d9276237c18c5a6537065e41b2eb9ae4cd68d397..040afe57232850121d14e8db53c939487774755a 100644 --- a/src/vs/editor/common/modes/modesFilters.ts +++ b/src/vs/editor/common/modes/modesFilters.ts @@ -25,15 +25,11 @@ export var ContiguousSubString: ISuggestionFilter = wrapBaseFilter(Filters.match // Combined Filters export function or(first: ISuggestionFilter, second: ISuggestionFilter): ISuggestionFilter { - return (word: string, suggestion: ISuggestion): Filters.IMatch[] => { - return first(word, suggestion) || second(word, suggestion); - }; + return (word, suggestion) => first(word, suggestion) || second(word, suggestion); } export function and(first: ISuggestionFilter, second: ISuggestionFilter): ISuggestionFilter { - return (word: string, suggestion: ISuggestion): Filters.IMatch[] => { - return first(word, suggestion) && second(word, suggestion); - }; + return (word, suggestion) => first(word, suggestion) && second(word, suggestion); } export var DefaultFilter = or(or(Prefix, CamelCase), ContiguousSubString); \ No newline at end of file diff --git a/src/vs/editor/common/modes/supports.ts b/src/vs/editor/common/modes/supports.ts index ba20bd853fab4595078091b1500369d3b9cd76ec..3fffc97ee130378755759fa1e97eee8da1cce005 100644 --- a/src/vs/editor/common/modes/supports.ts +++ b/src/vs/editor/common/modes/supports.ts @@ -776,7 +776,7 @@ export class SuggestSupport extends AbstractSupport implements Modes.ISuggestSup return DefaultFilter; } - public getSorter(): Modes.ISuggestionSorter { + public getSorter(): Modes.ISuggestionCompare { return (one, other) => { if (this.sortByType.length > 0) { var oneTypeIndex = this.sortByType.indexOf(one.type); diff --git a/src/vs/editor/common/modes/supports/onEnter.ts b/src/vs/editor/common/modes/supports/onEnter.ts index c83ad326f118a37976c99b0fe81c1f0dd5e75219..b4cab652153693b34e5f75e3f8b36be30f4ae509 100644 --- a/src/vs/editor/common/modes/supports/onEnter.ts +++ b/src/vs/editor/common/modes/supports/onEnter.ts @@ -9,6 +9,7 @@ import {IEnterAction, IndentAction, IOnEnterSupport, ILineContext, IMode} from ' import EditorCommon = require('vs/editor/common/editorCommon'); import Errors = require('vs/base/common/errors'); import Strings = require('vs/base/common/strings'); +import {Position} from 'vs/editor/common/core/position'; export interface IBracketPair { open: string; @@ -179,3 +180,66 @@ export class OnEnterSupport implements IOnEnterSupport { } } } + +export function getRawEnterActionAtPosition(model:EditorCommon.ITokenizedModel, lineNumber:number, column:number): IEnterAction { + let enterAction:IEnterAction; + + if (model.getMode().onEnterSupport) { + try { + enterAction = model.getMode().onEnterSupport.onEnter(model, new Position(lineNumber, column)); + } catch (e) { + Errors.onUnexpectedError(e); + } + } + + if (!enterAction) { + if (model.getMode().electricCharacterSupport) { + let lineContext = model.getLineContext(lineNumber); + try { + enterAction = model.getMode().electricCharacterSupport.onEnter(lineContext, column - 1); + } catch(e) { + Errors.onUnexpectedError(e); + } + } + } else { + // console.log('USING NEW INDENTATION LOGIC!'); + } + + return enterAction; +} + +export function getEnterActionAtPosition(model:EditorCommon.ITokenizedModel, lineNumber:number, column:number): { enterAction: IEnterAction; indentation: string; } { + let lineText = model.getLineContent(lineNumber); + let indentation = Strings.getLeadingWhitespace(lineText); + if (indentation.length > column - 1) { + indentation = indentation.substring(0, column - 1); + } + + let enterAction = getRawEnterActionAtPosition(model, lineNumber, column); + if (!enterAction) { + enterAction = { + indentAction: IndentAction.None, + appendText: '', + }; + } else { + if(!enterAction.appendText) { + if ( + (enterAction.indentAction === IndentAction.Indent) || + (enterAction.indentAction === IndentAction.IndentOutdent) + ) { + enterAction.appendText = '\t'; + } else { + enterAction.appendText = ''; + } + } + } + + if (enterAction.removeText) { + indentation = indentation.substring(0, indentation.length - 1); + } + + return { + enterAction: enterAction, + indentation: indentation + }; +} diff --git a/src/vs/editor/common/worker/editorWorkerServer.ts b/src/vs/editor/common/worker/editorWorkerServer.ts index 0bcd42cb2ab472fbbbbcc6cd82d504caf634ec03..e054afee0df90be85bba593626ed0166d0e54938 100644 --- a/src/vs/editor/common/worker/editorWorkerServer.ts +++ b/src/vs/editor/common/worker/editorWorkerServer.ts @@ -73,6 +73,10 @@ class WorkerPluginService extends AbstractPluginService { } } + public deactivate(pluginId:string): void { + // nothing to do + } + } export class EditorWorkerServer { diff --git a/src/vs/editor/contrib/find/browser/find.ts b/src/vs/editor/contrib/find/browser/find.ts index 772c4ac78ab38afedbfc222d87a89a74b5e4e70e..d75681934f4f8856c5f5b1b8b6303c3295eef25a 100644 --- a/src/vs/editor/contrib/find/browser/find.ts +++ b/src/vs/editor/contrib/find/browser/find.ts @@ -516,6 +516,16 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution { var matches = this.editor.getModel().findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord); + // do not overlap with selection (issue #64) + let editorSelection = this.editor.getSelection(); + + matches = matches.filter((m) => { + if (editorSelection.equalsRange(m)) { + return false; + } + return true; + }); + var decorations = matches.map(r => { return { range: r, diff --git a/src/vs/editor/contrib/suggest/browser/suggest.css b/src/vs/editor/contrib/suggest/browser/suggest.css index 710b85633f1154db745818a321025f23e6f9eab3..6dbf2e00d83a39d748065edf0b2335e082e64186 100644 --- a/src/vs/editor/contrib/suggest/browser/suggest.css +++ b/src/vs/editor/contrib/suggest/browser/suggest.css @@ -26,43 +26,31 @@ line-height: 1.3em; } +.monaco-editor .suggest-widget > .message { + padding-left: 22px; + opacity: 0.7; +} + +.monaco-editor .suggest-widget > .tree { + height: 100%; + width: 100%; +} + .monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content { -mox-box-sizing: border-box; box-sizing: border-box; line-height: 1.2em; - padding: 2px 10px 2px 2px; + padding: 2px 10px 2px 22px; background-repeat: no-repeat; background-position: 2px 2px; white-space: nowrap; } -.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content > * { - float: left; -} - -.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content:after { - content:""; - clear: both; - display: block; - visibility: hidden; - height: 0; -} - .monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .text { - width: -webkit-calc(100% - 16px); - width: -moz-calc(100% - 16px); - width: -ms-calc(100% - 16px); - width: -o-calc(100% - 16px); - width: calc(100% - 16px); overflow: hidden; text-overflow: ellipsis; - padding-left: 6px; - -webkit-box-sizing: border-box; - -o-box-sizing: border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; } + .monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .docs { display: none; max-height: 3.4em; @@ -79,15 +67,11 @@ opacity: 1; } -.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight { +.monaco-editor .suggest-widget:not(.frozen) .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight { font-weight: bold; color: #186B9E; } -.monaco-editor .suggest-widget.empty .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight { - background-color: inherit; -} - .monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .type-label { display: none; margin-left: 0.8em; @@ -95,17 +79,19 @@ color: #0035DD; } -.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row.fake > .content .type-label, .monaco-editor .suggest-widget .monaco-tree .monaco-tree-row.focused > .content .type-label { display: inline; } -.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row.fake > .content .docs, .monaco-editor .suggest-widget .monaco-tree .monaco-tree-row.focused > .content .docs { display: block; } .monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .icon { + position: absolute; + display: block; + left: 1px; + top: 2px; background-image: url('symbol-sprite.svg'); background-repeat: no-repeat; height: 16px; @@ -150,14 +136,10 @@ border: 1px solid rgb(69, 69, 69); } -.monaco-editor.vs-dark .suggest-widget .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight { +.monaco-editor.vs-dark .suggest-widget:not(.frozen) .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight { color: #219AE4; } -.monaco-editor.vs-dark .suggest-widget.empty .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight { - color: inherit; -} - .monaco-editor.vs-dark .suggest-widget .monaco-tree .monaco-tree-row > .content .docs { color: #C07A7A; } @@ -195,14 +177,10 @@ border: 2px solid #6FC3DF; } -.monaco-editor.hc-black .suggest-widget .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight { +.monaco-editor.hc-black .suggest-widget:not(.frozen) .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight { color: #219AE4; } -.monaco-editor.hc-black .suggest-widget.empty .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight { - color: inherit; -} - .monaco-editor.hc-black .suggest-widget .monaco-tree .monaco-tree-row > .content .docs { color: #C07A7A; } diff --git a/src/vs/editor/contrib/suggest/browser/suggest.ts b/src/vs/editor/contrib/suggest/browser/suggest.ts index eff2c3669c4ea4881602d139d4efa5852ef78d43..cc3e286dc2084ef914c02367797034bd5e2a9051 100644 --- a/src/vs/editor/contrib/suggest/browser/suggest.ts +++ b/src/vs/editor/contrib/suggest/browser/suggest.ts @@ -7,22 +7,22 @@ import nls = require('vs/nls'); import Lifecycle = require('vs/base/common/lifecycle'); import Snippet = require('vs/editor/contrib/snippet/common/snippet'); -import SuggestWidget = require('./suggestWidget'); -import SuggestModel = require('./suggestModel'); +import { SuggestWidget } from './suggestWidget'; +import { SuggestModel } from './suggestModel'; import Errors = require('vs/base/common/errors'); -import {TPromise} from 'vs/base/common/winjs.base'; -import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions'; -import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions'; -import {EditorAction, Behaviour} from 'vs/editor/common/editorAction'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorBrowserRegistry } from 'vs/editor/browser/editorBrowserExtensions'; +import { CommonEditorRegistry, ContextKey, EditorActionDescriptor } from 'vs/editor/common/editorCommonExtensions'; +import { EditorAction, Behaviour } from 'vs/editor/common/editorAction'; import EditorBrowser = require('vs/editor/browser/editorBrowser'); import EditorCommon = require('vs/editor/common/editorCommon'); import Modes = require('vs/editor/common/modes'); import EventEmitter = require('vs/base/common/eventEmitter'); -import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService'; -import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; -import {SuggestRegistry, ACCEPT_SELECTED_SUGGESTION_CMD, CONTEXT_SUGGEST_WIDGET_VISIBLE} from 'vs/editor/contrib/suggest/common/suggest'; -import {INullService} from 'vs/platform/instantiation/common/instantiation'; -import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; +import { IKeybindingService, IKeybindingContextKey } from 'vs/platform/keybinding/common/keybindingService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { SuggestRegistry, ACCEPT_SELECTED_SUGGESTION_CMD, CONTEXT_SUGGEST_WIDGET_VISIBLE } from 'vs/editor/contrib/suggest/common/suggest'; +import { IInstantiationService, INullService } from 'vs/platform/instantiation/common/instantiation'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; export class SuggestController implements EditorCommon.IEditorContribution { static ID = 'editor.contrib.suggestController'; @@ -31,32 +31,25 @@ export class SuggestController implements EditorCommon.IEditorContribution { return editor.getContribution(SuggestController.ID); } - private editor:EditorBrowser.ICodeEditor; - private model:SuggestModel.SuggestModel; - private suggestWidget: SuggestWidget.SuggestWidget; - private toDispose: Lifecycle.IDisposable[]; - + private model: SuggestModel; + private widget: SuggestWidget; private triggerCharacterListeners: Function[]; private suggestWidgetVisible: IKeybindingContextKey; + private toDispose: Lifecycle.IDisposable[]; - constructor(editor:EditorBrowser.ICodeEditor, @IKeybindingService keybindingService: IKeybindingService, @ITelemetryService telemetryService: ITelemetryService) { - this.editor = editor; + constructor( + private editor:EditorBrowser.ICodeEditor, + @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService instantiationService: IInstantiationService + ) { this.suggestWidgetVisible = keybindingService.createKey(CONTEXT_SUGGEST_WIDGET_VISIBLE, false); - - this.model = new SuggestModel.SuggestModel(this.editor, (snippet:Snippet.CodeSnippet, overwriteBefore:number, overwriteAfter:number) => { - Snippet.get(this.editor).run(snippet, overwriteBefore, overwriteAfter); - }); - - this.suggestWidget = new SuggestWidget.SuggestWidget(this.editor, telemetryService, keybindingService, () => { - this.suggestWidgetVisible.set(true); - }, () => { - this.suggestWidgetVisible.reset(); - }); - this.suggestWidget.setModel(this.model); + this.model = new SuggestModel(this.editor); + this.widget = instantiationService.createInstance(SuggestWidget, this.editor, this.model); this.triggerCharacterListeners = []; this.toDispose = []; + this.toDispose.push(this.widget.onDidVisibilityChange(visible => visible ? this.suggestWidgetVisible.set(true) : this.suggestWidgetVisible.reset())); this.toDispose.push(editor.addListener2(EditorCommon.EventType.ConfigurationChanged, () => this.update())); this.toDispose.push(editor.addListener2(EditorCommon.EventType.ModelChanged, () => this.update())); this.toDispose.push(editor.addListener2(EditorCommon.EventType.ModelModeChanged, () => this.update())); @@ -67,6 +60,9 @@ export class SuggestController implements EditorCommon.IEditorContribution { })); this.toDispose.push(SuggestRegistry.onDidChange(this.update, this)); + // TODO@Joao: what is this? + this.toDispose.push(this.model.onDidAccept(e => Snippet.get(this.editor).run(e.snippet, e.overwriteBefore, e.overwriteAfter))); + this.update(); } @@ -78,12 +74,12 @@ export class SuggestController implements EditorCommon.IEditorContribution { this.toDispose = Lifecycle.disposeAll(this.toDispose); this.triggerCharacterListeners = Lifecycle.cAll(this.triggerCharacterListeners); - if (this.suggestWidget) { - this.suggestWidget.destroy(); - this.suggestWidget = null; + if (this.widget) { + this.widget.dispose(); + this.widget = null; } if (this.model) { - this.model.destroy(); + this.model.dispose(); this.model = null; } } @@ -160,38 +156,38 @@ export class SuggestController implements EditorCommon.IEditorContribution { } public acceptSelectedSuggestion(): void { - if (this.suggestWidget) { - this.suggestWidget.acceptSelectedSuggestion(); + if (this.widget) { + this.widget.acceptSelectedSuggestion(); } } public hideSuggestWidget(): void { - if (this.suggestWidget) { - this.suggestWidget.cancel(); + if (this.widget) { + this.widget.cancel(); } } public selectNextSuggestion(): void { - if (this.suggestWidget) { - this.suggestWidget.selectNext(); + if (this.widget) { + this.widget.selectNext(); } } public selectNextPageSuggestion(): void { - if (this.suggestWidget) { - this.suggestWidget.selectNextPage(); + if (this.widget) { + this.widget.selectNextPage(); } } public selectPrevSuggestion(): void { - if (this.suggestWidget) { - this.suggestWidget.selectPrevious(); + if (this.widget) { + this.widget.selectPrevious(); } } public selectPrevPageSuggestion(): void { - if (this.suggestWidget) { - this.suggestWidget.selectPreviousPage(); + if (this.widget) { + this.widget.selectPreviousPage(); } } } diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index 9aba39b6f35a0e5cb97b57b75f39bdcf05546422..d4c64abd8c703244ae69c19c4676c91283e05858 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -7,164 +7,64 @@ import { TPromise } from 'vs/base/common/winjs.base'; import {sequence} from 'vs/base/common/async'; import { assign } from 'vs/base/common/objects'; -import { EventEmitter, ListenerUnbind } from 'vs/base/common/eventEmitter'; -import {onUnexpectedError, isPromiseCanceledError} from 'vs/base/common/errors'; +import Event, { Emitter } from 'vs/base/common/event'; +import { onUnexpectedError } from 'vs/base/common/errors'; import strings = require('vs/base/common/strings'); -import URI from 'vs/base/common/uri'; -import {isFalsyOrEmpty} from 'vs/base/common/arrays'; import timer = require('vs/base/common/timer'); import { getSnippets } from 'vs/editor/common/modes/modesRegistry'; import EditorCommon = require('vs/editor/common/editorCommon'); -import { ISuggestSupport, ISuggestResult, ISuggestion, ISuggestionSorter } from 'vs/editor/common/modes'; -import {DefaultFilter, IMatch} from 'vs/editor/common/modes/modesFilters'; +import { ISuggestSupport, ISuggestResult, ISuggestion, ISuggestionCompare, ISuggestionFilter } from 'vs/editor/common/modes'; import { CodeSnippet } from 'vs/editor/contrib/snippet/common/snippet'; import { IDisposable, disposeAll } from 'vs/base/common/lifecycle'; -import {SuggestRegistry, ISuggestResult2, suggest} from '../common/suggest'; +import { SuggestRegistry, ISuggestResult2, suggest } from '../common/suggest'; -enum SuggestState { - NOT_ACTIVE = 0, - MANUAL_TRIGGER = 1, - AUTO_TRIGGER = 2 +export interface ICancelEvent { + retrigger: boolean; } -export interface SuggestDataEvent { - suggestions: { completionItems: CompletionItem[]; currentWord: string; }; +export interface ITriggerEvent { auto: boolean; + characterTriggered: boolean; + retrigger: boolean; } -export class CompletionItem { - - private static _idPool = 0; - - public id: string; - public suggestion: ISuggestion; - public highlights: IMatch[]; - public support: ISuggestSupport; - public container: ISuggestResult; - - private _resolveDetails:TPromise - - constructor(support: ISuggestSupport, suggestion: ISuggestion, container:ISuggestResult) { - this.id = '_completion_item_#' + CompletionItem._idPool++; - this.support = support; - this.suggestion = suggestion; - this.container = container; - } - - resolveDetails(resource:URI, position:EditorCommon.IPosition): TPromise { - if (!this._resolveDetails) { - if (!this.support || typeof this.support.getSuggestionDetails !== 'function') { - this._resolveDetails = TPromise.as(this); - } else { - this._resolveDetails = this.support.getSuggestionDetails(resource, position, this.suggestion).then(value => { - this.suggestion = assign(this.suggestion, value); - return this; - }, err => { - if (isPromiseCanceledError(err)) { - this._resolveDetails = undefined; - } else { - onUnexpectedError(err); - } - return this; - }); - } - } - return this._resolveDetails; - } +export interface ISuggestEvent { + suggestions: ISuggestResult2[][]; + currentWord: string; + auto: boolean; } -class RawModel { - - private _items: CompletionItem[][] = []; - - public size: number = 0; - public incomplete: boolean = false; - - insertSuggestions(rank: number, suggestions: ISuggestResult2[]): boolean { - if (suggestions) { - let items: CompletionItem[] = []; - for (let _suggestions of suggestions) { - - for (let suggestionItem of _suggestions.suggestions) { - items.push(new CompletionItem(_suggestions.support, suggestionItem, _suggestions)); - } - - this.size += _suggestions.suggestions.length; - this.incomplete = this.incomplete || _suggestions.incomplete; - } - this._items[rank] = items; - return true; - } - } - - select(ctx: SuggestionContext): CompletionItem[] { - let result: CompletionItem[] = []; - for (let item of this._items) { - RawModel._sortAndFilter(item, ctx, result); - } - return result; - } - - private static _sortAndFilter(items: CompletionItem[], ctx: SuggestionContext, bucket: CompletionItem[]): void { - if (isFalsyOrEmpty(items)) { - return; - } - - // all items have the same (origin) support. derive sorter and filter - // from first - const [first] = items; - let compare = RawModel._compare; - let filter = DefaultFilter; - if (first.support) { - compare = first.support.getSorter && first.support.getSorter() || compare; - filter = first.support.getFilter && first.support.getFilter() || filter; - } - - items = items.filter(item => { - // set hightlight and filter those that have none - item.highlights = filter(ctx.wordBefore, item.suggestion); - return !isFalsyOrEmpty(item.highlights); - }).sort((a, b) => { - // sort suggestions by custom strategy - return compare(a.suggestion, b.suggestion) - }); - - bucket.push(...items); - } - - private static _compare(a: ISuggestion, b: ISuggestion):number { - return a.label.localeCompare(b.label); - } +export interface IAcceptEvent { + snippet: CodeSnippet; + overwriteBefore: number; + overwriteAfter: number; } -class SuggestionContext { +class Context { public lineNumber:number; public column:number; public isInEditableRange:boolean; - private auto: boolean; - private shouldAuto: boolean; + private isAutoTriggerEnabled: boolean; private lineContentBefore:string; private lineContentAfter:string; public wordBefore:string; public wordAfter:string; - constructor(editor:EditorCommon.ICommonCodeEditor, auto: boolean) { - this.auto = auto; - - var model = editor.getModel(); - var position = editor.getPosition(); - - var lineContent = model.getLineContent(position.lineNumber); - this.wordBefore = ''; - this.wordAfter = ''; - var wordUnderCursor = model.getWordAtPosition(position); + constructor(editor:EditorCommon.ICommonCodeEditor, private auto: boolean) { + const model = editor.getModel(); + const position = editor.getPosition(); + const lineContent = model.getLineContent(position.lineNumber); + const wordUnderCursor = model.getWordAtPosition(position); if (wordUnderCursor) { this.wordBefore = lineContent.substring(wordUnderCursor.startColumn - 1, position.column - 1); this.wordAfter = lineContent.substring(position.column - 1, wordUnderCursor.endColumn - 1); + } else { + this.wordBefore = ''; + this.wordAfter = ''; } this.lineNumber = position.lineNumber; @@ -173,69 +73,77 @@ class SuggestionContext { this.lineContentAfter = lineContent.substr(position.column - 1); this.isInEditableRange = true; + if (model.hasEditableRange()) { - var editableRange = model.getEditableRange(); + const editableRange = model.getEditableRange(); + if (!editableRange.containsPosition(position)) { this.isInEditableRange = false; } } - var lineContext = model.getLineContext(position.lineNumber); - var character = model.getLineContent(position.lineNumber).charAt(position.column - 1); - this.shouldAuto = SuggestRegistry.all(model) - .some(support => support.shouldAutotriggerSuggest(lineContext, position.column - 1, character)); + const lineContext = model.getLineContext(position.lineNumber); + const character = model.getLineContent(position.lineNumber).charAt(position.column - 1); + const supports = SuggestRegistry.all(model); + this.isAutoTriggerEnabled = supports.some(s => s.shouldAutotriggerSuggest(lineContext, position.column - 1, character)); } - public isValidForAutoSuggest(): boolean { + public shouldAutoTrigger(): boolean { + if (!this.isAutoTriggerEnabled) { + // Support disallows it + return false; + } + if (this.wordBefore.length === 0) { // Word before position is empty return false; } + if (this.wordAfter.length > 0) { // Word after position is non empty return false; } - if (!this.shouldAuto) { - // ISuggestSupport#shouldAutotriggerSuggest is againt this - return false; - } + return true; } - public isValidForNewContext(newCtx:SuggestionContext):boolean { - if (this.lineNumber !== newCtx.lineNumber) { + public isDifferentContext(context: Context):boolean { + if (this.lineNumber !== context.lineNumber) { // Line number has changed - return false; + return true; } - if (newCtx.column < this.column - this.wordBefore.length) { + if (context.column < this.column - this.wordBefore.length) { // column went before word start - return false; + return true; } - if (!strings.startsWith(newCtx.lineContentBefore, this.lineContentBefore) || this.lineContentAfter !== newCtx.lineContentAfter) { + if (!strings.startsWith(context.lineContentBefore, this.lineContentBefore) || this.lineContentAfter !== context.lineContentAfter) { // Line has changed before position - return false; + return true; } - if (newCtx.wordBefore === '' && newCtx.lineContentBefore !== this.lineContentBefore) { + if (context.wordBefore === '' && context.lineContentBefore !== this.lineContentBefore) { // Most likely a space has been typed - return false; + return true; } - return true; + return false; } - public isValidForRetrigger(newCtx:SuggestionContext):boolean { - if (!strings.startsWith(this.lineContentBefore, newCtx.lineContentBefore) || this.lineContentAfter !== newCtx.lineContentAfter) { + public shouldRetrigger(context: Context):boolean { + if (!strings.startsWith(this.lineContentBefore, context.lineContentBefore) || this.lineContentAfter !== context.lineContentAfter) { + // Doesn't look like the same line return false; } - if (this.lineContentBefore.length > newCtx.lineContentBefore.length && this.wordBefore.length === 0) { + if (this.lineContentBefore.length > context.lineContentBefore.length && this.wordBefore.length === 0) { + // Text was deleted and previous current word was empty return false; } - if (this.auto && newCtx.wordBefore.length === 0) { + if (this.auto && context.wordBefore.length === 0) { + // Currently in auto mode and new current word is empty return false; } @@ -243,57 +151,55 @@ class SuggestionContext { } } -export class SuggestModel extends EventEmitter { +enum State { + Idle = 0, + Manual = 1, + Auto = 2 +} + +export class SuggestModel implements IDisposable { - private editor: EditorCommon.ICommonCodeEditor; - private onAccept:(snippet: CodeSnippet, overwriteBefore:number, overwriteAfter:number)=>void; private toDispose: IDisposable[]; private autoSuggestDelay:number; private triggerAutoSuggestPromise:TPromise; - private state:SuggestState; + private state:State; - // Members which make sense when state is active private requestPromise:TPromise; - private requestContext:SuggestionContext; - private raw:RawModel; + private context:Context; - constructor(editor:EditorCommon.ICommonCodeEditor, onAccept:(snippet: CodeSnippet, overwriteBefore:number, overwriteAfter:number)=>void) { - super(); - this.editor = editor; - this.onAccept = onAccept; - this.toDispose = []; + private raw: ISuggestResult2[][]; + private incomplete: boolean; - this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.ConfigurationChanged, () => this.onEditorConfigurationChange())); - this.onEditorConfigurationChange(); + private _onDidCancel: Emitter = new Emitter(); + public get onDidCancel(): Event { return this._onDidCancel.event; } - this.triggerAutoSuggestPromise = null; - this.state = SuggestState.NOT_ACTIVE; + private _onDidTrigger: Emitter = new Emitter(); + public get onDidTrigger(): Event { return this._onDidTrigger.event; } - this.requestPromise = null; - this.raw = null; - this.requestContext = null; + private _onDidSuggest: Emitter = new Emitter(); + public get onDidSuggest(): Event { return this._onDidSuggest.event; } - this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.CursorSelectionChanged, (e: EditorCommon.ICursorSelectionChangedEvent) => { - if (!e.selection.isEmpty()) { - this.cancel(); - return; - } + private _onDidAccept: Emitter = new Emitter(); + public get onDidAccept(): Event { return this._onDidAccept.event; } - if (e.source !== 'keyboard' || e.reason !== '') { - this.cancel(); - return; - } + constructor(private editor: EditorCommon.ICommonCodeEditor) { + this.state = State.Idle; + this.triggerAutoSuggestPromise = null; + this.requestPromise = null; + this.raw = null; + this.incomplete = false; + this.context = null; - this.onCursorChange(); - })); - this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.ModelChanged, (e) => { - this.cancel(); - })); + this.toDispose = []; + this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.ConfigurationChanged, () => this.onEditorConfigurationChange())); + this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.CursorSelectionChanged, e => this.onCursorChange(e))); + this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.ModelChanged, () => this.cancel())); + this.onEditorConfigurationChange(); } public cancel(silent:boolean = false, retrigger:boolean = false):boolean { - var actuallyCanceled = (this.state !== SuggestState.NOT_ACTIVE); + const actuallyCanceled = this.state !== State.Idle; if (this.triggerAutoSuggestPromise) { this.triggerAutoSuggestPromise.cancel(); @@ -305,49 +211,61 @@ export class SuggestModel extends EventEmitter { this.requestPromise = null; } - this.state = SuggestState.NOT_ACTIVE; + this.state = State.Idle; this.raw = null; - this.requestContext = null; + this.incomplete = false; + this.context = null; if (!silent) { - this.emit('cancel', { retrigger: retrigger }); + this._onDidCancel.fire({ retrigger }); } return actuallyCanceled; } public getRequestPosition():EditorCommon.IPosition { - if(!this.requestContext) { + if(!this.context) { return null; } + return { - lineNumber: this.requestContext.lineNumber, - column: this.requestContext.column + lineNumber: this.context.lineNumber, + column: this.context.column }; } private isAutoSuggest():boolean { - return this.state === SuggestState.AUTO_TRIGGER; + return this.state === State.Auto; } - private onCursorChange():void { + private onCursorChange(e: EditorCommon.ICursorSelectionChangedEvent):void { + if (!e.selection.isEmpty()) { + this.cancel(); + return; + } + + if (e.source !== 'keyboard' || e.reason !== '') { + this.cancel(); + return; + } + if (!SuggestRegistry.has(this.editor.getModel())) { return; } - var isInactive = this.state === SuggestState.NOT_ACTIVE; + const isInactive = this.state === State.Idle; if (isInactive && !this.editor.getConfiguration().quickSuggestions) { return; } - var ctx = new SuggestionContext(this.editor, false); + const ctx = new Context(this.editor, false); if (isInactive) { // trigger was not called or it was canceled this.cancel(); - if (ctx.isValidForAutoSuggest()) { + if (ctx.shouldAutoTrigger()) { this.triggerAutoSuggestPromise = TPromise.timeout(this.autoSuggestDelay); this.triggerAutoSuggestPromise.then(() => { this.triggerAutoSuggestPromise = null; @@ -355,79 +273,56 @@ export class SuggestModel extends EventEmitter { }); } - } else if (this.raw && this.raw.incomplete) { - this.trigger(this.state === SuggestState.AUTO_TRIGGER, undefined, true); + } else if (this.raw && this.incomplete) { + this.trigger(this.state === State.Auto, undefined, true); } else { this.onNewContext(ctx); } } public trigger(auto: boolean, triggerCharacter?: string, retrigger: boolean = false, groups?: ISuggestSupport[][]): void { - var model = this.editor.getModel(); - var characterTriggered = !!triggerCharacter; - if (!groups) { - groups = SuggestRegistry.orderedGroups(model); - } + const model = this.editor.getModel(); + const characterTriggered = !!triggerCharacter; + groups = groups || SuggestRegistry.orderedGroups(model); + if (groups.length === 0) { return; } - var ctx = new SuggestionContext(this.editor, auto); + + const ctx = new Context(this.editor, auto); + if (!ctx.isInEditableRange) { return; } - var $tTrigger = timer.start(timer.Topic.EDITOR, 'suggest/TRIGGER'); - // Cancel previous requests, change state & update UI this.cancel(false, retrigger); - this.state = (auto || characterTriggered) ? SuggestState.AUTO_TRIGGER : SuggestState.MANUAL_TRIGGER; - this.emit('loading', { auto: this.isAutoSuggest(), characterTriggered: characterTriggered, retrigger: retrigger }); + this.state = (auto || characterTriggered) ? State.Auto : State.Manual; + this._onDidTrigger.fire({ auto: this.isAutoSuggest(), characterTriggered, retrigger }); // Capture context when request was sent - this.requestContext = ctx; + this.context = ctx; - // Send mode request - var $tRequest = timer.start(timer.Topic.EDITOR, 'suggest/REQUEST'); - var position = this.editor.getPosition(); + const position = this.editor.getPosition(); - let raw = new RawModel(); - let rank = 0; this.requestPromise = suggest(model, position, triggerCharacter, groups).then(all => { - for (let suggestions of all) { - if (raw.insertSuggestions(rank, suggestions)) { - rank++; - } - } - }); - - this.requestPromise.then(() => { - $tRequest.stop(); this.requestPromise = null; - if (this.state === SuggestState.NOT_ACTIVE) { + if (this.state === State.Idle) { return; } - var snippets = getSnippets(model, position); - if (snippets && snippets.suggestions && snippets.suggestions.length > 0) { - raw.insertSuggestions(rank, [snippets]); - } + this.raw = all.concat([[getSnippets(model, position)]]); + this.incomplete = all.reduce((r, s) => r || s.reduce((r, s) => r || s.incomplete, false), false); - if(raw.size > 0) { - this.raw = raw; - this.onNewContext(new SuggestionContext(this.editor, auto)); - } else { - this.emit('empty', { auto: this.isAutoSuggest() }); - } - }, () => { - $tRequest.stop(); - }).done(() => $tTrigger.stop()); + this.onNewContext(new Context(this.editor, auto)); + }).then(null, onUnexpectedError); } - private onNewContext(ctx:SuggestionContext):void { - if (this.requestContext && !this.requestContext.isValidForNewContext(ctx)) { - if (this.requestContext.isValidForRetrigger(ctx)) { - this.trigger(this.state === SuggestState.AUTO_TRIGGER, undefined, true); + private onNewContext(ctx: Context):void { + if (this.context && this.context.isDifferentContext(ctx)) { + if (this.context.shouldRetrigger(ctx)) { + this.trigger(this.state === State.Auto, undefined, true); } else { this.cancel(); } @@ -436,42 +331,22 @@ export class SuggestModel extends EventEmitter { } if (this.raw) { - - let completionItems = this.raw.select(ctx); - - if (completionItems.length > 0) { - this.emit('suggest', { - suggestions: { - completionItems, - currentWord: ctx.wordBefore - }, - auto: this.isAutoSuggest() - }); - } else { - this.emit('empty', { auto: this.isAutoSuggest() }); - } + this._onDidSuggest.fire({ suggestions: this.raw, currentWord: ctx.wordBefore, auto: this.isAutoSuggest() }); } } - public accept(item: CompletionItem): boolean { + public accept(suggestion: ISuggestion, overwriteBefore: number, overwriteAfter: number): boolean { if (this.raw === null) { return false; } - var parentSuggestions = item.container; - var offsetFromInvocation = this.editor.getPosition().column - this.requestContext.column; - - var overwriteBefore = ((typeof parentSuggestions.overwriteBefore === 'undefined') - ? parentSuggestions.currentWord.length - : parentSuggestions.overwriteBefore) + offsetFromInvocation; - - var overwriteAfter = (typeof parentSuggestions.overwriteAfter === 'undefined') - ? 0 - : Math.max(0, parentSuggestions.overwriteAfter); + this._onDidAccept.fire({ + snippet: new CodeSnippet(suggestion.codeSnippet), + overwriteBefore: overwriteBefore + (this.editor.getPosition().column - this.context.column), + overwriteAfter + }); this.cancel(); - this.onAccept(new CodeSnippet(item.suggestion.codeSnippet), overwriteBefore, overwriteAfter); - return true; } @@ -483,9 +358,8 @@ export class SuggestModel extends EventEmitter { } } - public destroy():void { + public dispose():void { this.cancel(true); this.toDispose = disposeAll(this.toDispose); - this.emit('destroy', null); } } \ 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 3c75ee04a5c3d71ef5c6e66c42356e234968c907..bcc77271849448daffc1329bc20d1a4e7ca94a00 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -7,25 +7,141 @@ import 'vs/css!./suggest'; import nls = require('vs/nls'); -import {TPromise} from 'vs/base/common/winjs.base'; -import Errors = require('vs/base/common/errors'); -import dom = require('vs/base/browser/dom'); +import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, disposeAll } from 'vs/base/common/lifecycle'; +import { assign } from 'vs/base/common/objects'; +import Event, { Emitter } from 'vs/base/common/event'; +import { append, addClass, removeClass, emmet as $ } from 'vs/base/browser/dom'; import Tree = require('vs/base/parts/tree/common/tree'); import TreeImpl = require('vs/base/parts/tree/browser/treeImpl'); import TreeDefaults = require('vs/base/parts/tree/browser/treeDefaults'); import HighlightedLabel = require('vs/base/browser/ui/highlightedlabel/highlightedLabel'); -import {SuggestModel, SuggestDataEvent, CompletionItem} from './suggestModel'; +import { SuggestModel, ICancelEvent, ISuggestEvent, ITriggerEvent } from './suggestModel'; import Mouse = require('vs/base/browser/mouseEvent'); import EditorBrowser = require('vs/editor/browser/editorBrowser'); import EditorCommon = require('vs/editor/common/editorCommon'); import EventEmitter = require('vs/base/common/eventEmitter'); import Timer = require('vs/base/common/timer'); -import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; -import {SuggestRegistry, CONTEXT_SUGGESTION_SUPPORTS_ACCEPT_ON_KEY} from '../common/suggest'; -import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { SuggestRegistry, CONTEXT_SUGGESTION_SUPPORTS_ACCEPT_ON_KEY } from '../common/suggest'; +import { IKeybindingService, IKeybindingContextKey } from 'vs/platform/keybinding/common/keybindingService'; +import { ISuggestSupport, ISuggestResult, ISuggestion, ISuggestionCompare, ISuggestionFilter } from 'vs/editor/common/modes'; +import { DefaultFilter, IMatch } from 'vs/editor/common/modes/modesFilters'; +import { ISuggestResult2 } from '../common/suggest'; +import URI from 'vs/base/common/uri'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { onUnexpectedError, isPromiseCanceledError, illegalArgument } from 'vs/base/common/errors'; +const DefaultCompare: ISuggestionCompare = (a, b) => a.label.localeCompare(b.label); + +class CompletionItem { + + private static _idPool = 0; + + id: string; + suggestion: ISuggestion; + highlights: IMatch[]; + support: ISuggestSupport; + container: ISuggestResult; + + private _resolveDetails:TPromise + + constructor(public group: CompletionGroup, suggestion: ISuggestion, container:ISuggestResult2) { + this.id = '_completion_item_#' + CompletionItem._idPool++; + this.support = container.support; + this.suggestion = suggestion; + this.container = container; + } + + resolveDetails(resource:URI, position:EditorCommon.IPosition): TPromise { + if (this._resolveDetails) { + return this._resolveDetails; + } + + if (!this.support || typeof this.support.getSuggestionDetails !== 'function') { + return this._resolveDetails = TPromise.as(this); + } + + return this._resolveDetails = this.support + .getSuggestionDetails(resource, position, this.suggestion) + .then( + value => this.suggestion = assign(this.suggestion, value), + err => isPromiseCanceledError(err) ? this._resolveDetails = null : onUnexpectedError(err) + ) + .then(() => this); + } +} + +class CompletionGroup { + + incomplete: boolean; + items: CompletionItem[]; + size: number; + compare: ISuggestionCompare; + filter: ISuggestionFilter; -var $ = dom.emmet; + constructor(public model: CompletionModel, public index: number, raw: ISuggestResult2[]) { + this.incomplete = false; + this.size = 0; + this.items = raw.reduce((items, result) => { + this.incomplete = result.incomplete || this.incomplete; + this.size += result.suggestions.length; + + return items.concat( + result.suggestions + .map(suggestion => new CompletionItem(this, suggestion, result)) + ); + }, []); + + this.compare = DefaultCompare; + this.filter = DefaultFilter; + + if (this.items.length > 0) { + const [first] = this.items; + + if (first.support) { + this.compare = first.support.getSorter && first.support.getSorter() || this.compare; + this.filter = first.support.getFilter && first.support.getFilter() || this.filter; + } + } + } + + get visibleCount(): number { + return this.items.reduce((r, i) => r + (isFalsyOrEmpty(this.filter(this.model.currentWord, i.suggestion)) ? 0 : 1), 0); + } +} + +class CompletionModel { + + incomplete: boolean; + size: number; + + private groups: CompletionGroup[]; + + constructor(public raw: ISuggestResult2[][], public currentWord: string) { + this.incomplete = false; + this.size = 0; + + this.groups = raw + .filter(s => !!s) + .map((suggestResults, index) => { + const group = new CompletionGroup(this, index, suggestResults); + + this.incomplete = group.incomplete || this.incomplete; + this.size += group.size; + + return group; + }); + } + + get items(): CompletionItem[] { + return this.groups.reduce((r, groups) => r.concat(groups.items), []); + } + + get visibleCount(): number { + return this.groups.reduce((r, g) => r + g.visibleCount, 0); + } +} // To be used as a tree element when we want to show a message export class Message { @@ -42,69 +158,48 @@ export class MessageRoot { } } -class DataSource implements Tree.IDataSource { - - private static _IdPool:number = 0; - private root: SuggestDataEvent; - - constructor() { - this.root = null; - } +function isRoot(element: any) : boolean { + return element instanceof MessageRoot || element instanceof CompletionModel; +} - private isRoot(element: any) : boolean { - if (element instanceof MessageRoot) { - return true; - } else if (element instanceof Message) { - return false; - } else if (element instanceof CompletionItem) { - return false; - } else if (Array.isArray((element).suggestions.completionItems)) { - this.root = element; - return true; - } else { - return false; - } - } +class DataSource implements Tree.IDataSource { public getId(tree: Tree.ITree, element: any): string { if (element instanceof MessageRoot) { return 'messageroot'; } else if (element instanceof Message) { return 'message' + element.message; - } else if (!!element.suggestions) { + } else if (element instanceof CompletionModel) { return 'root'; } else if (element instanceof CompletionItem) { return (element).id; - } else { - throw Errors.illegalArgument('element'); } + + throw illegalArgument('element'); } public getParent(tree: Tree.ITree, element: any): TPromise { - if (element instanceof MessageRoot) { + if (isRoot(element)) { return TPromise.as(null); } else if (element instanceof Message) { return TPromise.as(element.parent); } - return TPromise.as(this.isRoot(element) - ? null - : this.root); + + return TPromise.as((element).group.model); } public getChildren(tree: Tree.ITree, element: any): TPromise { if (element instanceof MessageRoot) { return TPromise.as([element.child]); - } else if (element instanceof Message) { - return TPromise.as([]); + } else if (element instanceof CompletionModel) { + return TPromise.as((element).items); } - return TPromise.as(this.isRoot(element) - ? this.root.suggestions.completionItems - : []); + return TPromise.as([]); } public hasChildren(tree: Tree.ITree, element: any): boolean { - return this.isRoot(element); + return isRoot(element); } } @@ -122,6 +217,40 @@ class Controller extends TreeDefaults.DefaultController { } } +class Filter implements Tree.IFilter { + + constructor(private getState: () => State) {} + + isVisible(tree: Tree.ITree, element: any): boolean { + if (isRoot(element)) { + return false; + } else if (element instanceof Message) { + return true; + } + + const item: CompletionItem = element; + const filter = item.group.filter; + const currentWord = item.group.model.currentWord; + item.highlights = filter(currentWord, item.suggestion); + return !isFalsyOrEmpty(item.highlights); + } +} + +class Sorter implements Tree.ISorter { + + compare(tree: Tree.ITree, item: CompletionItem, otherItem: CompletionItem): number { + const group = item.group; + const otherGroup = otherItem.group; + const result = group.index - otherGroup.index + + if (result !== 0) { + return result; + } + + return group.compare(item.suggestion, otherItem.suggestion); + } +} + interface IMessageTemplateData { element: HTMLElement; } @@ -132,20 +261,12 @@ interface ISuggestionTemplateData { colorspan: HTMLElement; highlightedLabel: HighlightedLabel.HighlightedLabel; typeLabel: HTMLElement; - documentationLabel: HTMLElement; + documentation: HTMLElement; } class Renderer implements Tree.IRenderer { - private editor: EditorBrowser.ICodeEditor; - - constructor(editor: EditorBrowser.ICodeEditor) { - this.editor = editor; - } - public getHeight(tree:Tree.ITree, element:any):number { - // var suggestion = element; - if (element instanceof CompletionItem) { if ((element).suggestion.documentationLabel && tree.isFocused(element)) { return 35; @@ -161,25 +282,23 @@ class Renderer implements Tree.IRenderer { public renderTemplate(tree: Tree.ITree, templateId: string, container: HTMLElement): any { if (templateId === 'message') { - var span = $('span'); + const span = $('span'); span.style.opacity = '0.7'; - span.style.paddingLeft = '12px'; container.appendChild(span); return { element: span }; } - var data = Object.create(null); + const data = Object.create(null); data.root = container; - data.icon = dom.append(container, $('.icon')); - data.colorspan = dom.append(data.icon, $('span.colorspan')); + data.icon = append(container, $('.icon')); + data.colorspan = append(data.icon, $('span.colorspan')); - var text = dom.append(container, $('.text')); - var main = dom.append(text, $('.main')); + const text = append(container, $('.text')); + const main = append(text, $('.main')); data.highlightedLabel = new HighlightedLabel.HighlightedLabel(main); - data.typeLabel = dom.append(main, $('span.type-label')); - var documentation = dom.append(text, $('.docs')); - data.documentationLabel = dom.append(documentation, $('.docs-label')); + data.typeLabel = append(main, $('span.type-label')); + data.documentation = append(text, $('.docs')); return data; } @@ -190,8 +309,8 @@ class Renderer implements Tree.IRenderer { return; } - var data = templateData; - var suggestion = ( element).suggestion; + const data = templateData; + const suggestion = ( element).suggestion; if (suggestion.type && suggestion.type.charAt(0) === '#') { data.root.setAttribute('aria-label', 'color'); @@ -205,7 +324,7 @@ class Renderer implements Tree.IRenderer { data.highlightedLabel.set(suggestion.label, ( element).highlights); data.typeLabel.textContent = suggestion.typeLabel || ''; - data.documentationLabel.textContent = suggestion.documentationLabel || ''; + data.documentation.textContent = suggestion.documentationLabel || ''; } public disposeTemplate(tree: Tree.ITree, templateId: string, templateData: any): void { @@ -213,16 +332,16 @@ class Renderer implements Tree.IRenderer { return; } - var data = templateData; + const data = templateData; data.highlightedLabel.dispose(); } } function computeScore(suggestion:string, currentWord:string, currentWordLowerCase:string) : number { - var suggestionLowerCase = suggestion.toLowerCase(); - var score = 0; + const suggestionLowerCase = suggestion.toLowerCase(); + let score = 0; - for (var i = 0; i < currentWord.length && i < suggestion.length; i++) { + for (let i = 0; i < currentWord.length && i < suggestion.length; i++) { if (currentWord[i] === suggestion[i]) { score += 2; } else if (currentWordLowerCase[i] === suggestionLowerCase[i]) { @@ -244,411 +363,490 @@ interface ITelemetryData { wasAutomaticallyTriggered?: boolean; } -export class SuggestWidget implements EditorBrowser.IContentWidget { +enum State { + Hidden, + Triggered, + Loading, + Empty, + Open, + Frozen +} + +export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable { + static ID = 'editor.widget.suggestWidget'; static WIDTH = 438; - static LOADING_MESSAGE = new MessageRoot(nls.localize('suggestWidget.loading', "Loading...")); - static NO_SUGGESTIONS_MESSAGE = new MessageRoot(nls.localize('suggestWidget.noSuggestions', "No suggestions.")); + static LOADING_MESSAGE = nls.localize('suggestWidget.loading', "Loading..."); + static NO_SUGGESTIONS_MESSAGE = nls.localize('suggestWidget.noSuggestions', "No suggestions."); - private editor: EditorBrowser.ICodeEditor; - private shouldShowEmptySuggestionList: boolean; - private isActive: boolean; - private isLoading: boolean; + public allowEditorOverflow = true; // Editor.IContentWidget.allowEditorOverflow + + private state: State; private isAuto: boolean; - private listenersToRemove: EventEmitter.ListenerUnbind[]; - private modelListenersToRemove: EventEmitter.ListenerUnbind[]; - private model: SuggestModel; + private shouldShowEmptySuggestionList: boolean; private suggestionSupportsAutoAccept: IKeybindingContextKey; + private loadingTimeout: number; + private currentSuggestionDetails:TPromise; + private oldFocus: CompletionItem; private telemetryData: ITelemetryData; private telemetryService: ITelemetryService; + private telemetryTimer: Timer.ITimerEvent; private element: HTMLElement; + private messageElement: HTMLElement; + private treeElement: HTMLElement; private tree: Tree.ITree; private renderer: Renderer; - // Editor.IContentWidget.allowEditorOverflow - public allowEditorOverflow = true; + private toDispose: IDisposable[]; - private _onShown: () => void; - private _onHidden: () => void; + private _onDidVisibilityChange: Emitter = new Emitter(); + public get onDidVisibilityChange(): Event { return this._onDidVisibilityChange.event; } - constructor(editor: EditorBrowser.ICodeEditor, telemetryService:ITelemetryService, keybindingService:IKeybindingService, onShown: () => void, onHidden: () => void) { - this.editor = editor; - this._onShown = onShown; - this._onHidden = onHidden; - - this.isActive = false; - this.isLoading = false; + constructor( + private editor: EditorBrowser.ICodeEditor, + private model: SuggestModel, + @IKeybindingService keybindingService: IKeybindingService, + @ITelemetryService telemetryService: ITelemetryService + ) { this.isAuto = false; - this.modelListenersToRemove = []; - this.model = null; - this.suggestionSupportsAutoAccept = keybindingService.createKey(CONTEXT_SUGGESTION_SUPPORTS_ACCEPT_ON_KEY, true); + this.oldFocus = null; + this.suggestionSupportsAutoAccept = keybindingService.createKey(CONTEXT_SUGGESTION_SUPPORTS_ACCEPT_ON_KEY, true); this.telemetryData = null; this.telemetryService = telemetryService; - var onModelModeChanged = () => { - var model = this.editor.getModel(); - this.shouldShowEmptySuggestionList = SuggestRegistry.all(model) - .some(support => support.shouldShowEmptySuggestionList()); - }; - - onModelModeChanged(); - this.listenersToRemove = []; - this.listenersToRemove.push(editor.addListener(EditorCommon.EventType.ModelChanged, onModelModeChanged)); - this.listenersToRemove.push(editor.addListener(EditorCommon.EventType.ModelModeChanged, onModelModeChanged)); - this.listenersToRemove.push(editor.addListener(EditorCommon.EventType.ModelModeSupportChanged, (e: EditorCommon.IModeSupportChangedEvent) => { - if (e.suggestSupport) { - onModelModeChanged(); - } - })); - let subscription = SuggestRegistry.onDidChange(onModelModeChanged); - this.listenersToRemove.push(() => subscription.dispose()); - this.element = $('.editor-widget.suggest-widget.monaco-editor-background'); this.element.style.width = SuggestWidget.WIDTH + 'px'; this.element.style.top = '0'; this.element.style.left = '0'; if (!this.editor.getConfiguration().iconsInSuggestions) { - dom.addClass(this.element, 'no-icons'); + addClass(this.element, 'no-icons'); } - var dataSource = new DataSource(); - this.renderer = new Renderer(this.editor); + this.messageElement = append(this.element, $('.message')); + this.messageElement.style.display = 'none'; + this.treeElement = append(this.element, $('.tree')); + + const configuration = { + renderer: this.renderer = new Renderer(), + dataSource: new DataSource(), + controller: new Controller(), + filter: new Filter(() => this.state), + sorter: new Sorter() + }; - this.tree = new TreeImpl.Tree(this.element, { - dataSource: dataSource, - renderer: this.renderer, - controller: new Controller() - }, { + const options = { twistiePixels: 0, alwaysFocused: true, verticalScrollMode: 'visible', useShadows: false - }); + }; - this.listenersToRemove.push(editor.addListener(EditorCommon.EventType.EditorTextBlur, () => { - TPromise.timeout(150).done(() => { - if (this.tree && !this.tree.isDOMFocused()) { - this.hide(); - } - }); - })); + this.tree = new TreeImpl.Tree(this.treeElement, configuration, options); + + this.toDispose = [ + editor.addListener2(EditorCommon.EventType.ModelChanged, () => this.onModelModeChanged()), + editor.addListener2(EditorCommon.EventType.ModelModeChanged, () => this.onModelModeChanged()), + editor.addListener2(EditorCommon.EventType.ModelModeSupportChanged, (e: EditorCommon.IModeSupportChangedEvent) => e.suggestSupport && this.onModelModeChanged()), + SuggestRegistry.onDidChange(() => this.onModelModeChanged()), + editor.addListener2(EditorCommon.EventType.EditorTextBlur, () => this.onEditorBlur()), + this.tree.addListener2('selection', e => this.onTreeSelection(e)), + this.tree.addListener2('focus', e => this.onTreeFocus(e)), + this.editor.addListener2(EditorCommon.EventType.CursorSelectionChanged, () => this.onCursorSelectionChanged()), + this.model.onDidTrigger(e => this.onDidTrigger(e)), + this.model.onDidSuggest(e => this.onDidSuggest(e)), + this.model.onDidCancel(e => this.onDidCancel(e)) + ]; + + this.onModelModeChanged(); + this.editor.addContentWidget(this); + this.setState(State.Hidden); + } - this.listenersToRemove.push(this.tree.addListener('selection', (e:Tree.ISelectionEvent) => { - if (e.selection && e.selection.length > 0) { - var element = e.selection[0]; - if (!element.hasOwnProperty('suggestions') && !(element instanceof MessageRoot) && !(element instanceof Message)) { + private onCursorSelectionChanged(): void { + if (this.state === State.Hidden) { + return; + } - this.telemetryData.selectedIndex = ( this.tree.getInput()).suggestions.completionItems.indexOf(element); - this.telemetryData.wasCancelled = false; - this.submitTelemetryData(); + this.editor.layoutContentWidget(this); + } - this.model.accept(element); - this.editor.focus(); - } + private onEditorBlur(): void { + TPromise.timeout(150).done(() => { + if (this.tree && !this.tree.isDOMFocused()) { + this.setState(State.Hidden); } - })); + }); + } - var oldFocus: any = null; + private onTreeSelection(e:Tree.ISelectionEvent): void { + if (!e.selection || e.selection.length === 0) { + return; + } - this.listenersToRemove.push(this.tree.addListener('focus', (e:Tree.IFocusEvent) => { - var focus = e.focus; - var payload = e.payload; + const element = e.selection[0]; - if(focus instanceof CompletionItem) { - this.resolveDetails(focus); - this.suggestionSupportsAutoAccept.set(!(focus).suggestion.noAutoAccept); - } + if (!element.hasOwnProperty('suggestions') && !(element instanceof MessageRoot) && !(element instanceof Message)) { + const item: CompletionItem = element; + const navigator = this.tree.getNavigator(); - if (focus === oldFocus) { - return; + this.telemetryData.selectedIndex = 0; + this.telemetryData.wasCancelled = false; + + while (navigator.next() !== item) { + this.telemetryData.selectedIndex++; } - var elementsToRefresh: any[] = []; + this.submitTelemetryData(); - if (oldFocus) { - elementsToRefresh.push(oldFocus); - } + const container = item.container; + const overwriteBefore = (typeof container.overwriteBefore === 'undefined') ? container.currentWord.length : container.overwriteBefore; + const overwriteAfter = (typeof container.overwriteAfter === 'undefined') ? 0 : Math.max(0, container.overwriteAfter); + this.model.accept(item.suggestion, overwriteBefore, overwriteAfter); + + this.editor.focus(); + } + } + + private onTreeFocus(e:Tree.IFocusEvent): void { + const focus = e.focus; + const payload = e.payload; + + if(focus instanceof CompletionItem) { + this.resolveDetails(focus); + this.suggestionSupportsAutoAccept.set(!(focus).suggestion.noAutoAccept); + } + + if (focus === this.oldFocus) { + return; + } + + const elementsToRefresh: any[] = []; + + if (this.oldFocus) { + elementsToRefresh.push(this.oldFocus); + } + + if (focus) { + elementsToRefresh.push(focus); + } + + this.oldFocus = focus; + + this.tree.refreshAll(elementsToRefresh).done(() => { + this.updateWidgetHeight(); if (focus) { - elementsToRefresh.push(focus); + return this.tree.reveal(focus, (payload && payload.firstSuggestion) ? 0 : null); } + }, onUnexpectedError); + } + + private onModelModeChanged(): void { + const model = this.editor.getModel(); + const supports = SuggestRegistry.all(model); + this.shouldShowEmptySuggestionList = supports.some(s => s.shouldShowEmptySuggestionList()); + } - oldFocus = focus; + private setState(state: State): void { + this.state = state; - this.tree.refreshAll(elementsToRefresh).done(() => { - this.updateWidgetHeight(); + switch (state) { + case State.Hidden: + this.messageElement.style.display = 'none'; + this.treeElement.style.display = 'block'; + this.hide(); + return; + case State.Triggered: + this.messageElement.style.display = 'none'; + this.treeElement.style.display = 'block'; + this.hide(); + return; + case State.Loading: + this.messageElement.innerText = SuggestWidget.LOADING_MESSAGE; + this.messageElement.style.display = 'block'; + this.treeElement.style.display = 'none'; + removeClass(this.element, 'frozen'); + break; + case State.Empty: + this.messageElement.innerText = SuggestWidget.NO_SUGGESTIONS_MESSAGE; + this.messageElement.style.display = 'block'; + this.treeElement.style.display = 'none'; + removeClass(this.element, 'frozen'); + break; + case State.Open: + this.messageElement.style.display = 'none'; + this.treeElement.style.display = 'block'; + removeClass(this.element, 'frozen'); + break; + case State.Frozen: + this.messageElement.style.display = 'none'; + this.treeElement.style.display = 'block'; + addClass(this.element, 'frozen'); + break; + } - if (focus) { - return this.tree.reveal(focus, (payload && payload.firstSuggestion) ? 0 : null); - } - }, Errors.onUnexpectedError); - })); + this.updateWidgetHeight(); + this.show(); + } - this.editor.addContentWidget(this); + private onDidTrigger(e: ITriggerEvent) { + if (this.state !== State.Hidden) { + return; + } - this.listenersToRemove.push(this.editor.addListener(EditorCommon.EventType.CursorSelectionChanged, (e: EditorCommon.ICursorSelectionChangedEvent) => { - if (this.isActive) { - this.editor.layoutContentWidget(this); - } - })); + this.telemetryTimer = this.telemetryService.start('suggestWidgetLoadingTime'); + this.isAuto = !!e.auto; + + if (!this.isAuto) { + this.loadingTimeout = setTimeout(() => { + this.loadingTimeout = null; + this.setState(State.Loading); + }, 50); + } - this.hide(); + if (!e.retrigger) { + this.telemetryData = { + wasAutomaticallyTriggered: e.characterTriggered + }; + } } - public setModel(newModel: SuggestModel) : void { - this.releaseModel(); - this.model = newModel; + private onDidSuggest(e: ISuggestEvent): void { + clearTimeout(this.loadingTimeout); - var timer : Timer.ITimerEvent = null, - loadingHandle:number; + let model: CompletionModel = this.tree.getInput(); + let promise = TPromise.as(null); + let visibleCount: number; - this.modelListenersToRemove.push(this.model.addListener('loading', (e: any) => { - if (!this.isActive) { - timer = this.telemetryService.start('suggestWidgetLoadingTime'); - this.isLoading = true; - this.isAuto = !!e.auto; + if (model && model.raw === e.suggestions) { + const oldCurrentWord = model.currentWord; + model.currentWord = e.currentWord; + visibleCount = model.visibleCount; - if (!this.isAuto) { - loadingHandle = setTimeout(() => { - dom.removeClass(this.element, 'empty'); - this.tree.setInput(SuggestWidget.LOADING_MESSAGE).done(null, Errors.onUnexpectedError); - this.updateWidgetHeight(); - this.show(); - }, 50); - } + if (!e.auto && visibleCount === 0) { + model.currentWord = oldCurrentWord; + this.setState(State.Frozen); + return; + } else { + promise = this.tree.refresh(); + } + } else { + model = new CompletionModel(e.suggestions, e.currentWord); + visibleCount = model.visibleCount; + promise = this.tree.setInput(model); + } - if (!e.retrigger) { - this.telemetryData = { - wasAutomaticallyTriggered: e.characterTriggered - }; + if (visibleCount === 0) { + if (e.auto) { + this.setState(State.Hidden); + } else { + if (this.shouldShowEmptySuggestionList) { + this.setState(State.Empty); + } else { + this.setState(State.Hidden); } } - })); - - this.modelListenersToRemove.push(this.model.addListener('suggest', (e: SuggestDataEvent) => { - this.isLoading = false; - if(typeof loadingHandle !== 'undefined') { - clearTimeout(loadingHandle); + if(this.telemetryTimer) { + this.telemetryTimer.data = { reason: 'empty' }; + this.telemetryTimer.stop(); + this.telemetryTimer = null; } - var currentWord = e.suggestions.currentWord; - var currentWordLowerCase = currentWord.toLowerCase(); - var suggestions = e.suggestions.completionItems; + return; + } + + promise.done(() => { + const navigator = this.tree.getNavigator(); + const currentWord = e.currentWord; + const currentWordLowerCase = currentWord.toLowerCase(); + const suggestions = model.items; + + let index = 0; + let bestSuggestionIndex = -1; + let bestSuggestion = suggestions[0]; + let bestScore = -1; + let item: CompletionItem; - var bestSuggestionIndex = -1; - var bestSuggestion = e.suggestions.completionItems[0]; - var bestScore = -1; + while (item = navigator.next()) { + const score = computeScore(item.suggestion.label, currentWord, currentWordLowerCase); - for (var i = 0, len = suggestions.length; i < len; i++) { - var score = computeScore(suggestions[i].suggestion.label, currentWord, currentWordLowerCase); if (score > bestScore) { bestScore = score; - bestSuggestion = suggestions[i]; - bestSuggestionIndex = i; + bestSuggestion = item; + bestSuggestionIndex = index; } } - dom.removeClass(this.element, 'empty'); - this.tree.setInput(e).done(null, Errors.onUnexpectedError); this.tree.setFocus(bestSuggestion, { firstSuggestion: true }); - this.updateWidgetHeight(); - this.show(); this.telemetryData = this.telemetryData || {}; this.telemetryData.suggestionCount = suggestions.length; this.telemetryData.suggestedIndex = bestSuggestionIndex; this.telemetryData.hintLength = currentWord.length; - if(timer) { - timer.data = { reason: 'results'}; - timer.stop(); - timer = null; - } - })); - - this.modelListenersToRemove.push(this.model.addListener('empty', (e: { auto:boolean; }) => { - var wasLoading = this.isLoading; - this.isLoading = false; - - if(typeof loadingHandle !== 'undefined') { - clearTimeout(loadingHandle); - } - - if (e.auto) { - this.hide(); - } else if (wasLoading) { - if (this.shouldShowEmptySuggestionList) { - dom.removeClass(this.element, 'empty'); - this.tree.setInput(SuggestWidget.NO_SUGGESTIONS_MESSAGE).done(null, Errors.onUnexpectedError); - this.updateWidgetHeight(); - this.show(); - } else { - this.hide(); - } - } else { - dom.addClass(this.element, 'empty'); - } + this.setState(State.Open); - if(timer) { - timer.data = { reason: 'empty'}; - timer.stop(); - timer = null; + if(this.telemetryTimer) { + this.telemetryTimer.data = { reason: 'results' }; + this.telemetryTimer.stop(); + this.telemetryTimer = null; } - })); + }, onUnexpectedError); + } - this.modelListenersToRemove.push(this.model.addListener('cancel', (e:any) => { - this.isLoading = false; + private onDidCancel(e: ICancelEvent) { + clearTimeout(this.loadingTimeout); - if (!e.retrigger) { - this.hide(); + if (!e.retrigger) { + this.setState(State.Hidden); - if (this.telemetryData) { - this.telemetryData.selectedIndex = -1; - this.telemetryData.wasCancelled = true; - this.submitTelemetryData(); - } + if (this.telemetryData) { + this.telemetryData.selectedIndex = -1; + this.telemetryData.wasCancelled = true; + this.submitTelemetryData(); } + } - if (timer) { - timer.data = { reason: 'cancel' }; - timer.stop(); - timer = null; - } - })); + if (this.telemetryTimer) { + this.telemetryTimer.data = { reason: 'cancel' }; + this.telemetryTimer.stop(); + this.telemetryTimer = null; + } } - private currentSuggestionDetails:TPromise; - private resolveDetails(item: CompletionItem): void { - if (item) { - if(this.currentSuggestionDetails) { - this.currentSuggestionDetails.cancel(); - } - - this.currentSuggestionDetails = item.resolveDetails(this.editor.getModel().getAssociatedResource(), - this.model.getRequestPosition() || this.editor.getPosition()); + if (!item) { + return; + } - this.currentSuggestionDetails.then(() => { - this.currentSuggestionDetails = undefined; - return this.tree.refresh(item).then(() => this.updateWidgetHeight()); - }, (err) => { - return Errors.isPromiseCanceledError(err) ? null : err; - }).done(undefined, Errors.onUnexpectedError); + if(this.currentSuggestionDetails) { + this.currentSuggestionDetails.cancel(); } + + this.currentSuggestionDetails = item.resolveDetails( + this.editor.getModel().getAssociatedResource(), + this.model.getRequestPosition() || this.editor.getPosition() + ); + + this.currentSuggestionDetails.then(() => { + this.currentSuggestionDetails = undefined; + return this.tree.refresh(item).then(() => this.updateWidgetHeight()); + }) + .done(null, err => !isPromiseCanceledError(err) && onUnexpectedError(err)); } public selectNextPage(): boolean { - if (this.isLoading) { - return !this.isAuto; + switch (this.state) { + case State.Hidden: + return false; + case State.Triggered: + case State.Loading: + return !this.isAuto; + default: + this.tree.focusNextPage(); + return true; } - if (this.isActive) { - this.tree.focusNextPage(); - return true; - } - return false; } public selectNext(): boolean { - if (this.isLoading) { - return !this.isAuto; - } - if (this.isActive) { - var focus = this.tree.getFocus(); - this.tree.focusNext(1); - if (focus === this.tree.getFocus()) { - this.tree.focusFirst(); - } - return true; + switch (this.state) { + case State.Hidden: + return false; + case State.Triggered: + case State.Loading: + return !this.isAuto; + default: + const focus = this.tree.getFocus(); + this.tree.focusNext(1); + if (focus === this.tree.getFocus()) { + this.tree.focusFirst(); + } + return true; } - return false; } public selectPreviousPage(): boolean { - if (this.isLoading) { - return !this.isAuto; - } - if (this.isActive) { - this.tree.focusPreviousPage(); - return true; + switch (this.state) { + case State.Hidden: + return false; + case State.Triggered: + case State.Loading: + return !this.isAuto; + default: + this.tree.focusPreviousPage(); + return true; } - return false; } public selectPrevious(): boolean { - if (this.isLoading) { - return !this.isAuto; - } - if (this.isActive) { - var focus = this.tree.getFocus(); - this.tree.focusPrevious(1); - if (focus === this.tree.getFocus()) { - this.tree.focusLast(); - } - return true; + switch (this.state) { + case State.Hidden: + return false; + case State.Triggered: + case State.Loading: + return !this.isAuto; + default: + const focus = this.tree.getFocus(); + this.tree.focusPrevious(1); + if (focus === this.tree.getFocus()) { + this.tree.focusLast(); + } + return true; } - return false; } public acceptSelectedSuggestion() : boolean { - if (this.isLoading) { - return !this.isAuto; - } - if (this.isActive) { - var focus = this.tree.getFocus(); - if (focus) { - this.tree.setSelection([focus]); - } else { - this.model.cancel(); - } - return true; - } - return false; - } - - private releaseModel() : void { - var listener:()=>void; - while (listener = this.modelListenersToRemove.pop()) { - listener(); + switch (this.state) { + case State.Hidden: + return false; + case State.Triggered: + case State.Loading: + return !this.isAuto; + default: + const focus = this.tree.getFocus(); + if (focus) { + this.tree.setSelection([focus]); + } else { + this.model.cancel(); + } + return true; } - this.model = null; } public show(): void { - this._onShown(); - this.isActive = true; + this._onDidVisibilityChange.fire(true); this.tree.layout(); this.editor.layoutContentWidget(this); TPromise.timeout(100).done(() => { - dom.addClass(this.element, 'visible'); + addClass(this.element, 'visible'); }); } - public cancel(): void { - this.model.cancel(); - } - public hide(): void { - this._onHidden(); - - this.isActive = false; - dom.removeClass(this.element, 'visible'); + this._onDidVisibilityChange.fire(false); + removeClass(this.element, 'visible'); this.editor.layoutContentWidget(this); } + public cancel(): void { + this.model.cancel(); + } + public getPosition():EditorBrowser.IContentWidgetPosition { - if (this.isActive) { - return { - position: this.editor.getPosition(), - preference: [EditorBrowser.ContentWidgetPositionPreference.BELOW, EditorBrowser.ContentWidgetPositionPreference.ABOVE] - }; + if (this.state === State.Hidden) { + return null; } - return null; + + return { + position: this.editor.getPosition(), + preference: [EditorBrowser.ContentWidgetPositionPreference.BELOW, EditorBrowser.ContentWidgetPositionPreference.ABOVE] + }; } public getDomNode() : HTMLElement { @@ -665,37 +863,43 @@ export class SuggestWidget implements EditorBrowser.IContentWidget { } private updateWidgetHeight(): void { - var input = this.tree.getInput(); - var maxHeight = 1000; - var height = 0; + const input = this.tree.getInput(); + const maxHeight = 1000; + let height = 0; - if (input === SuggestWidget.LOADING_MESSAGE || input === SuggestWidget.NO_SUGGESTIONS_MESSAGE) { + if (this.state === State.Empty || this.state === State.Loading) { height = 19; } else { - var focus = this.tree.getFocus(); - var focusHeight = focus ? this.renderer.getHeight(this.tree, focus) : 19; + const focus = this.tree.getFocus(); + const focusHeight = focus ? this.renderer.getHeight(this.tree, focus) : 19; height += focusHeight; - var maxSuggestions = Math.floor((maxHeight - focusHeight) / 19); - var data = input; - height += Math.min(data.suggestions.completionItems.length - 1, 11, maxSuggestions) * 19; + const suggestionCount = (this.tree.getContentHeight() - focusHeight) / 19; + const maxSuggestions = Math.floor((maxHeight - focusHeight) / 19); + height += Math.min(suggestionCount, 11, maxSuggestions) * 19; } this.element.style.height = height + 'px'; - this.tree.layout(height); this.editor.layoutContentWidget(this); } - public destroy() : void { - this.releaseModel(); + public dispose() : void { + this.state = null; + this.suggestionSupportsAutoAccept = null; + this.currentSuggestionDetails = null; + this.oldFocus = null; + this.telemetryData = null; + this.telemetryService = null; + this.telemetryTimer = null; + this.element = null; + this.messageElement = null; + this.treeElement = null; this.tree.dispose(); this.tree = null; - this.element = null; - - this.listenersToRemove.forEach((element) => { - element(); - }); - this.listenersToRemove = null; + this.renderer = null; + this.toDispose = disposeAll(this.toDispose); + this._onDidVisibilityChange.dispose(); + this._onDidVisibilityChange = null; } } \ No newline at end of file diff --git a/src/vs/editor/test/common/commands/shiftCommand.test.ts b/src/vs/editor/test/common/commands/shiftCommand.test.ts index 680ec749efa6ce9a1d29a11c567c023f543fdb63..1eb460be73ad4b8bc5eef6716336ba8fcdf47be3 100644 --- a/src/vs/editor/test/common/commands/shiftCommand.test.ts +++ b/src/vs/editor/test/common/commands/shiftCommand.test.ts @@ -11,6 +11,8 @@ import EditorCommon = require('vs/editor/common/editorCommon'); import {withEditorModel} from 'vs/editor/test/common/editorTestUtils'; import {Selection} from 'vs/editor/common/core/selection'; import {Cursor} from 'vs/editor/common/controller/cursor'; +import * as Modes from 'vs/editor/common/modes'; +import {OnEnterSupport} from 'vs/editor/common/modes/supports/onEnter'; function testShiftCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { TU.testCommand(lines, null, selection, (sel) => new ShiftCommand(sel, { @@ -28,6 +30,69 @@ function testUnshiftCommand(lines: string[], selection: Selection, expectedLines }), expectedLines, expectedSelection); } +class DocBlockCommentMode implements Modes.IMode { + + public onEnterSupport: Modes.IOnEnterSupport; + + constructor() { + this.onEnterSupport = new OnEnterSupport(this.getId(), { + brackets: [ + { open: '(', close: ')' }, + { open: '{', close: '}' }, + { open: '[', close: ']' } + ], + regExpRules: [ + { + // e.g. /** | */ + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + afterText: /^\s*\*\/$/, + action: { indentAction: Modes.IndentAction.IndentOutdent, appendText: ' * ' } + }, + { + // e.g. /** ...| + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + action: { indentAction: Modes.IndentAction.None, appendText: ' * ' } + }, + { + // e.g. * ...| + beforeText: /^(\t|(\ \ ))*\ \*\ ([^\*]|\*(?!\/))*$/, + action: { indentAction: Modes.IndentAction.None, appendText: '* ' } + }, + { + // e.g. */| + beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/, + action: { indentAction: Modes.IndentAction.None, removeText: 1 } + } + ] + }); + } + + public getId(): string { + return 'docBlockCommentMode'; + } + + public toSimplifiedMode(): Modes.IMode { + return this; + } + +} + +function testShiftCommandInDocBlockCommentMode(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + TU.testCommand(lines, new DocBlockCommentMode(), selection, (sel) => new ShiftCommand(sel, { + isUnshift: false, + tabSize: 4, + oneIndent: '\t' + }), expectedLines, expectedSelection); +} + +function testUnshiftCommandInDocBlockCommentMode(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + TU.testCommand(lines, new DocBlockCommentMode(), selection, (sel) => new ShiftCommand(sel, { + isUnshift: true, + tabSize: 4, + oneIndent: '\t' + }), expectedLines, expectedSelection); +} + suite('Editor Commands - ShiftCommand', () => { // --------- shift @@ -212,10 +277,50 @@ suite('Editor Commands - ShiftCommand', () => { 'My First Line', '\t\tMy Second Line', ' Third Line', - '\t', + '', '\t123' ], - new Selection(4, 2, 5, 3) + new Selection(4, 1, 5, 3) + ); + }); + + test('issue #1120 TAB should not indent empty lines in a multi-line selection', () => { + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(1, 1, 5, 2), + [ + '\tMy First Line', + '\t\t\tMy Second Line', + '\t\tThird Line', + '', + '\t123' + ], + new Selection(1, 2, 5, 3) + ); + + testShiftCommand( + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '', + '123' + ], + new Selection(4, 1, 5, 1), + [ + 'My First Line', + '\t\tMy Second Line', + ' Third Line', + '\t', + '123' + ], + new Selection(4, 2, 5, 1) ); }); @@ -399,7 +504,7 @@ suite('Editor Commands - ShiftCommand', () => { '\tMy First Line', '\tMy Second Line', '\tThird Line', - '\t', + '', '\t123' ], new Selection(1, 2, 5, 5) @@ -427,6 +532,65 @@ suite('Editor Commands - ShiftCommand', () => { ); }); + test('issue #348: indenting around doc block comments', () => { + testShiftCommandInDocBlockCommentMode( + [ + '', + '/**', + ' * a doc comment', + ' */', + 'function hello() {}' + ], + new Selection(1,1,5,20), + [ + '', + '\t/**', + '\t * a doc comment', + '\t */', + '\tfunction hello() {}' + ], + new Selection(1,1,5,21) + ); + + testUnshiftCommandInDocBlockCommentMode( + [ + '', + '/**', + ' * a doc comment', + ' */', + 'function hello() {}' + ], + new Selection(1,1,5,20), + [ + '', + '/**', + ' * a doc comment', + ' */', + 'function hello() {}' + ], + new Selection(1,1,5,20) + ); + + testUnshiftCommandInDocBlockCommentMode( + [ + '\t', + '\t/**', + '\t * a doc comment', + '\t */', + '\tfunction hello() {}' + ], + new Selection(1,1,5,21), + [ + '', + '/**', + ' * a doc comment', + ' */', + 'function hello() {}' + ], + new Selection(1,1,5,20) + ); + }); + test('bug #16815:Shift+Tab doesn\'t go back to tabstop', () => { var repeatStr = (str:string, cnt:number): string => { diff --git a/src/vs/editor/test/common/controller/cursor.test.ts b/src/vs/editor/test/common/controller/cursor.test.ts index b65cb9d01691b65023167fc915cd91b8ff2dcfc6..490fd0663f40790229a9ca6f5eda4e59457ea247 100644 --- a/src/vs/editor/test/common/controller/cursor.test.ts +++ b/src/vs/editor/test/common/controller/cursor.test.ts @@ -756,7 +756,10 @@ suite('Editor Controller - Cursor', () => { test('Bug 9121: Auto indent + undo + redo is funky', () => { var model = new Model.Model('', thisHighlighter); - var cursor = new Cursor.Cursor(1, new MockConfiguration(null), model, null, false); + var cursor = new Cursor.Cursor(1, new MockConfiguration({ + tabSize: 4, + insertSpaces: false + }), model, null, false); cursorCommand(cursor, H.Type, { text: '\n' }, null, 'keyboard'); assert.equal(model.getValue(EditorCommon.EndOfLinePreference.LF), '\n', 'assert1'); @@ -811,6 +814,31 @@ suite('Editor Controller - Cursor Configuration', () => { var thisHighlighter = new ModelModes.CursorMode(); + test('issue #183: jump to matching bracket position', () => { + let mode = new ModelModes.BracketMode(); + let model = new Model.Model([ + 'var x = (3 + (5-7));' + ].join('\n'), mode); + let cursor = new Cursor.Cursor(1, new MockConfiguration(null), model, null, false); + + // ensure is tokenized + model.getLineContext(1); + + moveTo(cursor, 1, 20); + + cursorCommand(cursor, H.JumpToBracket, null, null, 'keyboard'); + cursorEqual(cursor, 1, 10); + + cursorCommand(cursor, H.JumpToBracket, null, null, 'keyboard'); + cursorEqual(cursor, 1, 20); + + cursorCommand(cursor, H.JumpToBracket, null, null, 'keyboard'); + cursorEqual(cursor, 1, 10); + + cursor.dispose(); + model.dispose(); + }); + test('Cursor honors insertSpaces configuration on new line', () => { var text = ' \tMy First Line\t \n' + '\tMy Second Line\n' + ' Third Line\n' + '\n' + '1'; var model = new Model.Model(text, thisHighlighter); diff --git a/src/vs/editor/test/common/model/textModel.test.ts b/src/vs/editor/test/common/model/textModel.test.ts index 463ad75fc3ed6d226543c5a69723146a5f6e6434..cd6b8e86c9e09bdfe061eae030caf43d7ba0ae68 100644 --- a/src/vs/editor/test/common/model/textModel.test.ts +++ b/src/vs/editor/test/common/model/textModel.test.ts @@ -11,14 +11,14 @@ import Position = require('vs/editor/common/core/position'); function testGuessIndentation(expectedInsertSpaces:boolean, expectedTabSize:number, text:string[], msg?:string): void { var m = new TextModel.TextModel([], TextModel.TextModel.toRawText(text.join('\n'))); - var r = m.guessIndentation(1773); + var r = m.guessIndentation(1337); m.dispose(); assert.equal(r.insertSpaces, expectedInsertSpaces, msg); if (expectedInsertSpaces) { assert.equal(r.tabSize, expectedTabSize, msg); } else { - assert.equal(r.tabSize, 1773, msg); + assert.equal(r.tabSize, 1337, msg); } } @@ -64,26 +64,26 @@ suite('Editor Model - TextModel', () => { test('guess indentation 1', () => { // Defaults to tabs - guessesTabs([ + guessesSpaces(1337, [ 'x', 'x' ]); // Gives preference to tabs - guessesTabs([ + guessesSpaces(1337, [ '\tx', 'x' ]); - guessesTabs([ + guessesSpaces(1337, [ '\tx', ' x' ]); - guessesTabs([ + guessesSpaces(1337, [ '\tx', ' x' ]); - guessesTabs([ + guessesSpaces(1337, [ 'x', ' x', ' x', @@ -93,7 +93,7 @@ suite('Editor Model - TextModel', () => { ' x', ' x' ], '7x1 - 1 space is never guessed as an indentation'); - guessesTabs([ + guessesSpaces(1337, [ '', ' ', ' ', diff --git a/src/vs/editor/test/common/servicesTestUtils.ts b/src/vs/editor/test/common/servicesTestUtils.ts index 935277a623886d60d1ebc63c33a3413ad8498b4a..eea049ef7e9e3f8893b4b07c940fd10b0f3f0fe0 100644 --- a/src/vs/editor/test/common/servicesTestUtils.ts +++ b/src/vs/editor/test/common/servicesTestUtils.ts @@ -123,6 +123,10 @@ class MockPluginService extends AbstractPluginService { console.log(msg); } } + + public deactivate(pluginId:string): void { + // nothing to do + } } class MockModelService extends ModelServiceImpl {} diff --git a/src/vs/languages/json/common/json.contribution.ts b/src/vs/languages/json/common/json.contribution.ts index ee702feb700a76bac29fb2775e1af3416b5419ef..2d4d01be9ea8c799a4bc3177dd5ac981c9b468b7 100644 --- a/src/vs/languages/json/common/json.contribution.ts +++ b/src/vs/languages/json/common/json.contribution.ts @@ -51,19 +51,7 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('jsonConfiguration.schema', "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL."), }, } - }, - 'default' : [ - { 'fileMatch': [ '/bower.json', '/.bower.json' ], 'url': 'http://json.schemastore.org/bower' }, - { 'fileMatch': [ '/package.json' ], 'url': 'http://json.schemastore.org/package' }, - { 'fileMatch': [ '/project.json' ], 'url': 'http://json.schemastore.org/project' }, - { 'fileMatch': [ '*.schema.json' ], 'url': 'http://json-schema.org/draft-04/schema#' }, - { 'fileMatch': [ '/global.json' ], 'url': 'http://json.schemastore.org/global' }, - { 'fileMatch': [ '/tsconfig.json'], 'url': 'http://json.schemastore.org/tsconfig' }, - { 'fileMatch': [ '/jsconfig.json' ], 'url': 'http://opentools.azurewebsites.net/jsconfig' }, - { 'fileMatch': [ '/.bowerrc' ], 'url': 'http://json.schemastore.org/bowerrc' }, - { 'fileMatch': [ '/.jshintrc' ], 'url': 'http://json.schemastore.org/jshintrc' }, - { 'fileMatch': [ '/.jscsrc' ], 'url': 'http://json.schemastore.org/jscsrc' } - ] + } } } }); \ No newline at end of file diff --git a/src/vs/languages/json/common/json.ts b/src/vs/languages/json/common/json.ts index 518b89a36cab86953f9f4e6def32c0feb6272329..5a7b0c0f1dbb58acc266bd4275436c78848d933a 100644 --- a/src/vs/languages/json/common/json.ts +++ b/src/vs/languages/json/common/json.ts @@ -20,7 +20,7 @@ import {OneWorkerAttr, AllWorkersAttr} from 'vs/platform/thread/common/threadSer import {IThreadService, IThreadSynchronizableObject} from 'vs/platform/thread/common/thread'; import {AsyncDescriptor2, createAsyncDescriptor2} from 'vs/platform/instantiation/common/descriptors'; import {OnEnterSupport} from 'vs/editor/common/modes/supports/onEnter'; -import {IJSONContributionRegistry, Extensions, ISchemaContributions} from 'vs/languages/json/common/jsonContributionRegistry'; +import {IJSONContributionRegistry, Extensions, ISchemaContributions} from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; export class JSONMode extends AbstractMode implements Modes.IExtraInfoSupport, Modes.IOutlineSupport, IThreadSynchronizableObject { diff --git a/src/vs/languages/json/common/jsonSchemaService.ts b/src/vs/languages/json/common/jsonSchemaService.ts index 24be1adce977a4097508e804071448646ebc5cb4..620b1b9ea46c2f4edd8bca3c2f505846fe1ee9cd 100644 --- a/src/vs/languages/json/common/jsonSchemaService.ts +++ b/src/vs/languages/json/common/jsonSchemaService.ts @@ -17,7 +17,7 @@ import EventEmitter = require('vs/base/common/eventEmitter'); import {IResourceService, ResourceEvents, IResourceChangedEvent} from 'vs/editor/common/services/resourceService'; import {IRequestService} from 'vs/platform/request/common/request'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; -import {ISchemaContributions} from 'vs/languages/json/common/jsonContributionRegistry'; +import {ISchemaContributions} from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; 'use strict'; @@ -209,8 +209,6 @@ export class JSONSchemaService implements IJSONSchemaService { private contributionSchemas:{ [id:string]:SchemaHandle }; private contributionAssociations:{ [id:string]:string[] }; - private preloadedSchemas:{ [id:string]:SchemaHandle }; - private schemasById: { [id:string]:SchemaHandle }; private filePatternAssociations: FilePatternAssociation[]; private filePatternAssociationById: { [id:string]: FilePatternAssociation }; @@ -235,11 +233,9 @@ export class JSONSchemaService implements IJSONSchemaService { this.contributionSchemas = {}; this.contributionAssociations = {}; - this.preloadedSchemas = {}; this.schemasById = {}; this.filePatternAssociations = []; this.filePatternAssociationById = {}; - this.addPreloadedFileSchemas(); } public dispose(): void { @@ -325,9 +321,6 @@ export class JSONSchemaService implements IJSONSchemaService { this.filePatternAssociations = []; this.filePatternAssociationById = {}; - for (var id in this.preloadedSchemas) { - this.schemasById[id] = this.preloadedSchemas[id]; - } for (var id in this.contributionSchemas) { this.schemasById[id] = this.contributionSchemas[id]; } @@ -490,1081 +483,6 @@ export class JSONSchemaService implements IJSONSchemaService { return this.addSchemaHandle(combinedSchemaId, combinedSchema); } } - - public addPreloadedFileSchema(uri: string, schema : IJSONSchema): void { - var id = this.normalizeId(uri); - this.preloadedSchemas[id] = this.addSchemaHandle(id, schema); - } - - private addPreloadedFileSchemas(): void { - this.addPreloadedFileSchema('http://json-schema.org/draft-04/schema#', { - 'id': 'http://json-schema.org/draft-04/schema#', - 'title': nls.localize('schema.json', 'Describes a JSON file using a schema. See json-schema.org for more info.'), - '$schema': 'http://json-schema.org/draft-04/schema#', - 'definitions': { - 'schemaArray': { - 'type': 'array', - 'minItems': 1, - 'items': { '$ref': '#' } - }, - 'positiveInteger': { - 'type': 'integer', - 'minimum': 0 - }, - 'positiveIntegerDefault0': { - 'allOf': [{ '$ref': '#/definitions/positiveInteger' }, { 'default': 0 }] - }, - 'simpleTypes': { - 'type': 'string', - 'enum': ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'] - }, - 'stringArray': { - 'type': 'array', - 'items': { 'type': 'string' }, - 'minItems': 1, - 'uniqueItems': true - } - }, - 'type': 'object', - 'properties': { - 'id': { - 'type': 'string', - 'format': 'uri', - 'description': nls.localize('schema.json.id', 'A unique identifier for the schema.') - }, - '$schema': { - 'type': 'string', - 'format': 'uri', - 'description': nls.localize('schema.json.$schema', 'The schema to verify this document against ') - }, - 'title': { - 'type': 'string', - 'description': nls.localize('schema.json.title', 'A descriptive title of the element') - }, - 'description': { - 'type': 'string', - 'description': nls.localize('schema.json.description', 'A long description of the element. Used in hover menus and suggestions.') - }, - 'default': { - 'description': nls.localize('schema.json.default', 'A default value. Used by suggestions.') - }, - 'multipleOf': { - 'type': 'number', - 'minimum': 0, - 'exclusiveMinimum': true, - 'description': nls.localize('schema.json.multipleOf', 'A number that should cleanly divide the current value (i.e. have no remainder)') - }, - 'maximum': { - 'type': 'number', - 'description': nls.localize('schema.json.maximum', 'The maximum numerical value, inclusive by default.') - }, - 'exclusiveMaximum': { - 'type': 'boolean', - 'default': false, - 'description': nls.localize('schema.json.exclusiveMaximum', 'Makes the maximum property exclusive.') - }, - 'minimum': { - 'type': 'number', - 'description': nls.localize('schema.json.minimum', 'The minimum numerical value, inclusive by default.') - }, - 'exclusiveMinimum': { - 'type': 'boolean', - 'default': false, - 'description': nls.localize('schema.json.exclusiveMininum', 'Makes the minimum property exclusive.') - }, - 'maxLength': { - 'allOf': [ - { '$ref': '#/definitions/positiveInteger' } - ], - 'description': nls.localize('schema.json.maxLength', 'The maximum length of a string.') - }, - 'minLength': { - 'allOf': [ - { '$ref': '#/definitions/positiveIntegerDefault0' } - ], - 'description': nls.localize('schema.json.minLength', 'The minimum length of a string.') - }, - 'pattern': { - 'type': 'string', - 'format': 'regex', - 'description': nls.localize('schema.json.pattern', 'A regular expression to match the string against. It is not implicitly anchored.') - }, - 'additionalItems': { - 'anyOf': [ - { 'type': 'boolean' }, - { '$ref': '#' } - ], - 'default': {}, - 'description': nls.localize('schema.json.additionalItems', 'For arrays, only when items is set as an array. If it is a schema, then this schema validates items after the ones specified by the items array. If it is false, then additional items will cause validation to fail.') - }, - 'items': { - 'anyOf': [ - { '$ref': '#' }, - { '$ref': '#/definitions/schemaArray' } - ], - 'default': {}, - 'description': nls.localize('schema.json.items', 'For arrays. Can either be a schema to validate every element against or an array of schemas to validate each item against in order (the first schema will validate the first element, the second schema will validate the second element, and so on.') - }, - 'maxItems': { - 'allOf': [ - { '$ref': '#/definitions/positiveInteger' } - ], - 'description': nls.localize('schema.json.maxItems', 'The maximum number of items that can be inside an array. Inclusive.') - }, - 'minItems': { - 'allOf': [ - { '$ref': '#/definitions/positiveIntegerDefault0' } - ], - 'description': nls.localize('schema.json.minItems', 'The minimum number of items that can be inside an array. Inclusive.') - }, - 'uniqueItems': { - 'type': 'boolean', - 'default': false, - 'description': nls.localize('schema.json.uniqueItems', 'If all of the items in the array must be unique. Defaults to false.') - }, - 'maxProperties': { - 'allOf': [ - { '$ref': '#/definitions/positiveInteger' } - ], - 'description': nls.localize('schema.json.maxProperties', 'The maximum number of properties an object can have. Inclusive.') - }, - 'minProperties': { - 'allOf': [ - { '$ref': '#/definitions/positiveIntegerDefault0' }, - ], - 'description': nls.localize('schema.json.minProperties', 'The minimum number of properties an object can have. Inclusive.') - }, - 'required': { - 'allOf': [ - { '$ref': '#/definitions/stringArray' } - ], - 'description': nls.localize('schema.json.required', 'An array of strings that lists the names of all properties required on this object.') - }, - 'additionalProperties': { - 'anyOf': [ - { 'type': 'boolean' }, - { '$ref': '#' } - ], - 'default': {}, - 'description': nls.localize('schema.json.additionalProperties', 'Either a schema or a boolean. If a schema, then used to validate all properties not matched by \'properties\' or \'patternProperties\'. If false, then any properties not matched by either will cause this schema to fail.') - }, - 'definitions': { - 'type': 'object', - 'additionalProperties': { '$ref': '#' }, - 'default': {}, - 'description': nls.localize('schema.json.definitions', 'Not used for validation. Place subschemas here that you wish to reference inline with $ref') - }, - 'properties': { - 'type': 'object', - 'additionalProperties': { '$ref': '#' }, - 'default': {}, - 'description': nls.localize('schema.json.properties', 'A map of property names to schemas for each property.') - }, - 'patternProperties': { - 'type': 'object', - 'additionalProperties': { '$ref': '#' }, - 'default': {}, - 'description': nls.localize('schema.json.patternProperties', 'A map of regular expressions on property names to schemas for matching properties.') - }, - 'dependencies': { - 'type': 'object', - 'additionalProperties': { - 'anyOf': [ - { '$ref': '#' }, - { '$ref': '#/definitions/stringArray' } - ] - }, - 'description': nls.localize('schema.json.dependencies', 'A map of property names to either an array of property names or a schema. An array of property names means the property named in the key depends on the properties in the array being present in the object in order to be valid. If the value is a schema, then the schema is only applied to the object if the property in the key exists on the object.') - }, - 'enum': { - 'type': 'array', - 'minItems': 1, - 'uniqueItems': true, - 'description': nls.localize('schema.json.enum', 'The set of literal values that are valid') - }, - 'type': { - 'anyOf': [ - { '$ref': '#/definitions/simpleTypes' }, - { - 'type': 'array', - 'items': { '$ref': '#/definitions/simpleTypes' }, - 'minItems': 1, - 'uniqueItems': true - } - ], - 'description': nls.localize('schema.json.type', 'Either a string of one of the basic schema types (number, integer, null, array, object, boolean, string) or an array of strings specifying a subset of those types.') - }, - 'allOf': { - 'allOf': [ - { '$ref': '#/definitions/schemaArray' } - ], - 'description': nls.localize('schema.json.allOf', 'An array of schemas, all of which must match.') - }, - 'anyOf': { - 'allOf': [ - { '$ref': '#/definitions/schemaArray' } - ], - 'description': nls.localize('schema.json.anyOf', 'An array of schemas, where at least one must match.') - }, - 'oneOf': { - 'allOf': [ - { '$ref': '#/definitions/schemaArray' } - ], - 'description': nls.localize('schema.json.oneOf', 'An array of schemas, exactly one of which must match.') - }, - 'not': { - 'allOf': [ - { '$ref': '#' } - ], - 'description': nls.localize('schema.json.not', 'A schema which must not match.') - } - }, - 'dependencies': { - 'exclusiveMaximum': ['maximum'], - 'exclusiveMinimum': ['minimum'] - }, - 'default': {} - }); - this.addPreloadedFileSchema('http://json.schemastore.org/project', { - 'title': nls.localize('project.json.title', 'JSON schema for ASP.NET project.json files'), - '$schema': 'http://json-schema.org/draft-04/schema#', - 'id': 'http://json.schemastore.org/project', - 'type': 'object', - - 'definitions': { - 'compilationOptions': { - 'description': nls.localize('project.json.compilationOptions', 'Compilation options that are passed through to Roslyn'), - 'type': 'object', - 'properties': { - 'define': { - 'type': 'array', - 'items': { - 'type': 'string', - 'uniqueItems': true - } - }, - 'warningsAsErrors': { - 'type': 'boolean', - 'default': false - }, - 'allowUnsafe': { - 'type': 'boolean', - 'default': false - }, - 'optimize': { - 'type': 'boolean', - 'default': false - }, - 'languageVersion': { - 'type': 'string', - 'enum': ['csharp1', 'csharp2', 'csharp3', 'csharp4', 'csharp5', 'csharp6', 'experimental'] - } - } - }, - 'configType': { - 'type': 'object', - 'properties': { - 'dependencies': { '$ref': '#/definitions/dependencies' }, - 'compilationOptions': { '$ref': '#/definitions/compilationOptions' }, - 'frameworkAssemblies': { '$ref': '#/definitions/dependencies' } - } - }, - 'dependencies': { - 'type': 'object', - 'additionalProperties': { - 'type': ['string', 'object'], - 'properties': { - 'version': { - 'type': 'string', - 'description': nls.localize('project.json.dependency.name', 'The version of the dependency.') - }, - 'type': { - 'type': 'string', - 'default': 'default', - 'enum': ['default', 'build'], - 'description': nls.localize('project.json.dependency.type', 'The type of the dependency. \'build\' dependencies only exist at build time') - } - }, - 'id': 'nugget-package' - - }, - 'description': nls.localize('project.json.dependencies', 'The dependencies of the application. Each entry specifes the name and the version of a Nuget package.'), - 'id': 'nugget-packages' - }, - 'script': { - 'type': ['string', 'array'], - 'items': { - 'type': 'string' - }, - 'description': nls.localize('project.json.script', 'A command line script or scripts.\r\rAvailable variables:\r%project:Directory% - The project directory\r%project:Name% - The project name\r%project:Version% - The project version') - } - }, - - 'properties': { - 'authors': { - 'description': nls.localize('project.json.authors', 'The author of the application'), - 'type': 'array', - 'items': { - 'type': 'string', - 'uniqueItems': true - } - }, - 'bundleExclude': { - 'description': nls.localize('project.json.bundleExclude', 'List of files to exclude from publish output (kpm bundle).'), - 'type': [ 'string', 'array' ], - 'items': { - 'type': 'string' - }, - 'default': '' - }, - 'code': { - 'description': nls.localize('project.json.code', 'Glob pattern to specify all the code files that needs to be compiled. (data type: string or array with glob pattern(s)). Example: [ \'Folder1\\*.cs\', \'Folder2\\*.cs\' ]'), - 'type': ['string', 'array'], - 'items': { - 'type': 'string' - }, - 'default': '**\\*.cs' - }, - 'commands': { - 'description': nls.localize('project.json.commands', 'Commands that are available for this application'), - 'type': 'object', - 'additionalProperties': { - 'type': 'string' - } - }, - 'compilationOptions': { '$ref': '#/definitions/compilationOptions' }, - 'configurations': { - 'type': 'object', - 'description': nls.localize('project.json.configurations', 'Configurations are named groups of compilation settings. There are 2 defaults built into the runtime namely \'Debug\' and \'Release\'.'), - 'additionalProperties': { - 'type': 'object', - 'properties': { - 'compilationOptions': { '$ref': '#/definitions/compilationOptions' } - } - } - }, - 'dependencies': { '$ref': '#/definitions/dependencies' }, - 'description': { - 'description': nls.localize('project.json.description', 'The description of the application'), - 'type': 'string' - }, - 'exclude': { - 'description': nls.localize('project.json.exclude', 'Glob pattern to indicate all the code files to be excluded from compilation. (data type: string or array with glob pattern(s)).'), - 'type': ['string', 'array'], - 'items': { - 'type': 'string' - }, - 'default': ['bin/**/*.*', 'obj/**/*.*'] - }, - 'frameworks': { - 'description': nls.localize('project.json.frameworks', 'Target frameworks that will be built, and dependencies that are specific to the configuration.'), - 'type': 'object', - 'additionalProperties': { '$ref': '#/definitions/configType' } - }, - 'preprocess': { - 'description': nls.localize('project.json.preprocess', 'Glob pattern to indicate all the code files to be preprocessed. (data type: string with glob pattern).'), - 'type': 'string', - 'default': 'Compiler\\Preprocess\\**\\*.cs' - }, - 'resources': { - 'description': nls.localize('project.json.resources', 'Glob pattern to indicate all the files that need to be compiled as resources.'), - 'type': ['string', 'array'], - 'items': { - 'type': 'string' - }, - 'default': 'Compiler\\Resources\\**\\*.cs' - }, - 'scripts': { - 'type': 'object', - 'description': nls.localize('project.json.scripts', 'Scripts to execute during the various stages.'), - 'properties': { - 'prepack': { '$ref': '#/definitions/script' }, - 'postpack': { '$ref': '#/definitions/script' }, - - 'prebundle': { '$ref': '#/definitions/script' }, - 'postbundle': { '$ref': '#/definitions/script' }, - - 'prerestore': { '$ref': '#/definitions/script' }, - 'postrestore': { '$ref': '#/definitions/script' }, - 'prepare': { '$ref': '#/definitions/script' } - } - }, - 'shared': { - 'description': nls.localize('project.json.shared', 'Glob pattern to specify the code files to share with dependent projects. Example: [ \'Folder1\\*.cs\', \'Folder2\\*.cs\' ]'), - 'type': ['string', 'array'], - 'items': { - 'type': 'string' - }, - 'default': 'Compiler\\Shared\\**\\*.cs' - }, - 'version': { - 'description': nls.localize('project.json.version', 'The version of the application. Example: 1.2.0.0'), - 'type': 'string' - }, - 'webroot': { - 'description': nls.localize('project.json.webroot', 'Specifying the webroot property in the project.json file specifies the web server root (aka public folder). In visual studio, this folder will be used to root IIS. Static files should be put in here.'), - 'type': 'string' - } - } - - }); - this.addPreloadedFileSchema('http://json.schemastore.org/bower', { - - 'title': nls.localize('bower.json.title', 'JSON schema for Bower configuration files'), - '$schema': 'http://json-schema.org/draft-04/schema#', - 'id': 'http://json.schemastore.org/bower', - - 'type': 'object', - 'required': ['name'], - - 'patternProperties': { - '^_': { - 'description': nls.localize('bower.json.invalidPatternName', 'Any property starting with _ is valid.'), - 'additionalProperties': true, - 'additionalItems': true - } - }, - - 'properties': { - 'name': { - 'description': nls.localize('bower.json.packagename', 'The name of your package.'), - 'type': 'string', - 'maxLength': 50 - }, - 'description': { - 'description': nls.localize('bower.json.description', 'Help users identify and search for your package with a brief description.'), - 'type': 'string' - }, - 'version': { - 'description': nls.localize('bower.json.version', 'A semantic version number.'), - 'type': 'string' - }, - 'main': { - 'description': nls.localize('bower.json.main', 'The primary acting files necessary to use your package.'), - 'type': ['string', 'array'] - }, - 'license': { - 'description': nls.localize('bower.json.license', 'SPDX license identifier or path/url to a license.'), - 'type': ['string', 'array'], - 'maxLength': 140 - }, - 'ignore': { - 'description': nls.localize('bower.json.ignore', 'A list of files for Bower to ignore when installing your package.'), - 'type': ['string', 'array'] - }, - 'keywords': { - 'description': nls.localize('bower.json.keywords', 'Used for search by keyword. Helps make your package easier to discover without people needing to know its name.'), - 'type': 'array', - 'items': { - 'type': 'string', - 'maxLength': 50 - } - }, - 'authors': { - 'description': nls.localize('bower.json.authors', 'A list of people that authored the contents of the package.'), - 'type': 'array', - 'items': { - 'type': ['string', 'object'] - } - }, - 'homepage': { - 'description': nls.localize('bower.json.homepage', 'URL to learn more about the package. Falls back to GitHub project if not specified and it\'s a GitHub endpoint.'), - 'type': 'string' - }, - 'repository': { - 'description': nls.localize('bower.json.repository', 'The repository in which the source code can be found.'), - 'type': 'object', - 'properties': { - 'type': { - 'type': 'string', - 'enum': ['git'] - }, - 'url': { - 'type': 'string' - } - } - }, - 'dependencies': { - 'id': 'bower-packages', - 'description': nls.localize('bower.json.dependencies', 'Dependencies are specified with a simple hash of package name to a semver compatible identifier or URL.'), - 'type': 'object', - 'additionalProperties': { - 'id': 'bower-package', - 'type': 'string' - } - }, - 'devDependencies': { - 'id': 'bower-packages', - 'description': nls.localize('bower.json.devDependencies', 'Dependencies that are only needed for development of the package, e.g., test framework or building documentation.'), - 'type': 'object', - 'additionalProperties': { - 'id': 'bower-package', - 'type': 'string' - } - }, - 'resolutions': { - 'description': nls.localize('bower.json.resolutions', 'Dependency versions to automatically resolve with if conflicts occur between packages.'), - 'type': 'object' - }, - 'private': { - 'description': nls.localize('bower.json.private', 'If you set it to true it will refuse to publish it. This is a way to prevent accidental publication of private repositories.'), - 'type': 'boolean' - }, - 'exportsOverride': { - 'description': nls.localize('bower.json.exportsOverride', 'Used by grunt-bower-task to specify custom install locations.'), - 'type': 'object', - 'additionalProperties': { - 'type': 'object', - 'additionalProperties': { - 'type': 'string' - } - } - }, - 'moduleType': { - 'description': nls.localize('bower.json.moduleType', 'The types of modules this package exposes'), - 'type': 'array', - 'items': { - 'enum': ['amd', 'es6', 'globals', 'node', 'yui'] - } - } - } - }); - this.addPreloadedFileSchema('http://json.schemastore.org/package', { - 'id': 'http://json.schemastore.org/package', - 'description': nls.localize('package.json.description', 'NPM configuration for this package.'), - 'type': 'object', - 'required': ['name', 'version'], - 'definitions': { - 'person': { - 'description': nls.localize('package.json.person', 'A person who has been involved in creating or maintaining this package'), - 'type': [ 'object', 'string' ], - 'required': [ 'name' ], - 'properties': { - 'name': { - 'type': 'string' - }, - 'url': { - 'type': 'string', - 'format': 'uri' - }, - 'email': { - 'type': 'string', - 'format': 'email' - } - } - }, - 'dependency': { - 'id': 'npm-packages', - 'description': nls.localize('package.json.dependency', 'Dependencies are specified with a simple hash of package name to version range. The version range is a string which has one or more space-separated descriptors. Dependencies can also be identified with a tarball or git URL.'), - 'type': 'object', - 'additionalProperties': { - 'type': 'string' - } - } - }, - - 'patternProperties': { - '^_': { - 'description': nls.localize('package.json.underscore', 'Any property starting with _ is valid.'), - 'additionalProperties': true, - 'additionalItems': true - } - }, - - 'properties': { - 'name': { - 'description': nls.localize('package.json.name', 'The name of the package.'), - 'type': 'string' - }, - 'version': { - 'description': nls.localize('package.json.version', 'Version must be parseable by node-semver, which is bundled with npm as a dependency.'), - 'type': 'string' - }, - 'description': { - 'description': nls.localize('package.json.descr', 'This helps people discover your package, as it\'s listed in \'npm search\'.'), - 'type': 'string' - }, - 'icon': { - 'description': nls.localize('package.json.icon', 'The relative path to the icon of the package.'), - 'type': 'string' - }, - 'keywords': { - 'description': nls.localize('package.json.keywords', 'This helps people discover your package as it\'s listed in \'npm search\'.'), - 'type': 'array' - }, - 'homepage': { - 'description': nls.localize('package.json.homepage', 'The url to the project homepage.'), - 'type': 'string' - }, - 'bugs': { - 'description': nls.localize('package.json.bugs', 'The url to your project\'s issue tracker and / or the email address to which issues should be reported. These are helpful for people who encounter issues with your package.'), - 'type': [ 'object', 'string' ], - 'properties': { - 'url': { - 'type': 'string', - 'description': nls.localize('package.json.bugs.url', 'The url to your project\'s issue tracker.'), - 'format': 'uri' - }, - 'email': { - 'type': 'string', - 'description': nls.localize('package.json.bugs.email', 'The email address to which issues should be reported.') - } - } - }, - 'license': { - 'type': 'string', - 'description': nls.localize('package.json.license', 'You should specify a license for your package so that people know how they are permitted to use it, and any restrictions you\'re placing on it.') - }, - 'licenses': { - 'description': nls.localize('package.json.licenses', 'You should specify a license for your package so that people know how they are permitted to use it, and any restrictions you\'re placing on it.'), - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'type': { - 'type': 'string' - }, - 'url': { - 'type': 'string', - 'format': 'uri' - } - } - } - }, - 'author': { - '$ref': '#/definitions/person' - }, - 'contributors': { - 'description': nls.localize('package.json.contributors', 'A list of people who contributed to this package.'), - 'type': 'array', - 'items': { - '$ref': '#/definitions/person' - } - }, - 'maintainers': { - 'description': nls.localize('package.json.maintainers', 'A list of people who maintains this package.'), - 'type': 'array', - 'items': { - '$ref': '#/definitions/person' - } - }, - 'files': { - 'description': nls.localize('package.json.files', 'The \'files\' field is an array of files to include in your project. If you name a folder in the array, then it will also include the files inside that folder.'), - 'type': 'array', - 'items': { - 'type': 'string' - } - }, - 'main': { - 'description': nls.localize('package.json.main', 'The main field is a module ID that is the primary entry point to your program.'), - 'type': 'string' - }, - 'bin': { - 'type': [ 'string', 'object' ], - 'additionalProperties': { - 'type': 'string' - } - }, - 'man': { - 'type': [ 'array', 'string' ], - 'description': nls.localize('package.json.man', 'Specify either a single file or an array of filenames to put in place for the man program to find.'), - 'items': { - 'type': 'string' - } - }, - 'directories': { - 'type': 'object', - 'properties': { - 'bin': { - 'description': nls.localize('package.json.directories.bin', 'If you specify a \'bin\' directory, then all the files in that folder will be used as the \'bin\' hash.'), - 'type': 'string' - }, - 'doc': { - 'description': nls.localize('package.json.directories.doc', 'Put markdown files in here. Eventually, these will be displayed nicely, maybe, someday.'), - 'type': 'string' - }, - 'example': { - 'description': nls.localize('package.json.directories.example', 'Put example scripts in here. Someday, it might be exposed in some clever way.'), - 'type': 'string' - }, - 'lib': { - 'description': nls.localize('package.json.directories.lib', 'Tell people where the bulk of your library is. Nothing special is done with the lib folder in any way, but it\'s useful meta info.'), - 'type': 'string' - }, - 'man': { - 'description': nls.localize('package.json.directories.man', 'A folder that is full of man pages. Sugar to generate a \'man\' array by walking the folder.'), - 'type': 'string' - }, - 'test': { - 'type': 'string' - } - } - }, - 'repository': { - 'description': nls.localize('package.json.repository', 'Specify the place where your code lives. This is helpful for people who want to contribute.'), - 'type': 'object', - 'properties': { - 'type': { - 'type': 'string' - }, - 'url': { - 'type': 'string' - } - } - }, - 'scripts': { - 'description': nls.localize('package.json.scripts', 'The \'scripts\' member is an object hash of script commands that are run at various times in the lifecycle of your package. The key is the lifecycle event, and the value is the command to run at that point.'), - 'type': 'object', - 'additionalProperties': { - 'type': 'string' - } - }, - 'config': { - 'description': nls.localize('package.json.config', 'A \'config\' hash can be used to set configuration parameters used in package scripts that persist across upgrades.'), - 'type': 'object', - 'additionalProperties': true - }, - 'dependencies': { - '$ref': '#/definitions/dependency' - }, - 'devDependencies': { - '$ref': '#/definitions/dependency' - }, - 'bundleDependencies': { - 'type': 'array', - 'description': nls.localize('package.json.bundleDependencies', 'Array of package names that will be bundled when publishing the package.'), - 'items': { - 'type': 'string' - } - }, - 'bundledDependencies': { - 'type': 'array', - 'description': nls.localize('package.json.bundledDependencies', 'Array of package names that will be bundled when publishing the package.'), - 'items': { - 'type': 'string' - } - }, - 'optionalDependencies': { - '$ref': '#/definitions/dependency' - }, - 'peerDependencies': { - '$ref': '#/definitions/dependency' - }, - 'engines': { - 'type': 'object', - 'additionalProperties': { - 'type': 'string' - } - }, - 'engineStrict': { - 'type': 'boolean' - }, - 'os': { - 'type': 'array', - 'items': { - 'type': 'string' - } - }, - 'cpu': { - 'type': 'array', - 'items': { - 'type': 'string' - } - }, - 'preferGlobal': { - 'type': 'boolean', - 'description': nls.localize('package.json.preferGlobal', 'If your package is primarily a command-line application that should be installed globally, then set this value to true to provide a warning if it is installed locally.') - }, - 'private': { - 'type': 'boolean', - 'description': nls.localize('package.json.private', 'If set to true, then npm will refuse to publish it.') - }, - 'publishConfig': { - 'type': 'object', - 'additionalProperties': true - }, - 'dist': { - 'type': 'object', - 'properties': { - 'shasum': { - 'type': 'string' - }, - 'tarball': { - 'type': 'string' - } - } - }, - 'readme': { - 'type': 'string' - } - } - }); - this.addPreloadedFileSchema('http://json.schemastore.org/global', { - 'title': nls.localize('global.json.title', 'JSON schema for the ASP.NET global configuration files'), - 'type': 'object', - 'additionalProperties': true, - 'required': [ 'projects' ], - - 'properties': { - 'projects': { - 'type': 'array', - 'description': nls.localize('global.json.projects', 'A list of project folders relative to this file.'), - 'items': { - 'type': 'string' - } - }, - 'sources': { - 'type': 'array', - 'description': nls.localize('global.json.sources', 'A list of source folders relative to this file.'), - 'items': { - 'type': 'string' - } - }, - 'sdk': { - 'type': 'object', - 'description': nls.localize('global.json.sdk', 'The runtime to use.'), - 'properties': { - 'version': { - 'type': 'string', - 'description': nls.localize('global.json.sdk.version', 'The runtime version to use.') - }, - 'runtime': { - 'type': 'string', - 'description': nls.localize('global.json.sdk.runtime', 'The runtime to use, e.g. coreclr'), - }, - 'architecture': { - 'type': 'string', - 'description': nls.localize('global.json.sdk.architecture', 'The runtime architecture to use, e.g. x64.') - } - } - } - } - } - - ); - this.addPreloadedFileSchema('http://json.schemastore.org/tsconfig', { - 'title': nls.localize('tsconfig.json.title', "JSON schema for the TypeScript compiler's configuration file"), - '$schema': 'http://json-schema.org/draft-04/schema#', - - 'type': 'object', - 'default': { 'compilerOptions': { 'target': 'ES5', 'module': 'commonjs'} }, - 'properties': { - 'compilerOptions': { - 'type': 'object', - 'description': nls.localize('tsconfig.json.compilerOptions', 'Instructs the TypeScript compiler how to compile .ts files'), - 'properties': { - 'charset': { - 'description': nls.localize('tsconfig.json.compilerOptions.charset', 'The character set of the input files'), - 'type': 'string' - }, - 'declaration': { - 'description': nls.localize('tsconfig.json.compilerOptions.declaration', 'Generates corresponding d.ts files.'), - 'type': 'boolean' - }, - 'diagnostics': { - 'description': nls.localize('tsconfig.json.compilerOptions.diagnostics', 'Show diagnostic information.'), - 'type': 'boolean' - }, - 'emitBOM': { - 'description': nls.localize('tsconfig.json.compilerOptions.emitBOM', 'Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files.'), - 'type': 'boolean' - }, - 'inlineSourceMap': { - 'description': nls.localize('tsconfig.json.compilerOptions.inlineSourceMap', 'Emit a single file with source maps instead of having a separate file.'), - 'type': 'boolean' - }, - 'inlineSources': { - 'description': nls.localize('tsconfig.json.compilerOptions.inlineSources', 'Emit the source alongside the sourcemaps within a single file; requires --inlineSourceMap to be set.'), - 'type': 'boolean' - }, - 'listFiles': { - 'description': nls.localize('tsconfig.json.compilerOptions.listFiles', 'Print names of files part of the compilation.'), - 'type': 'boolean' - }, - 'locale': { - 'description': nls.localize('tsconfig.json.compilerOptions.locale', 'The locale to use to show error messages, e.g. en-us.'), - 'type': 'string' - }, - 'mapRoot': { - 'description': nls.localize('tsconfig.json.compilerOptions.mapRoot', 'Specifies the location where debugger should locate map files instead of generated locations'), - 'type': 'string', - 'format': 'uri' - }, - 'module': { - 'description': nls.localize('tsconfig.json.compilerOptions.module', "Specify module code generation: 'CommonJS', 'Amd', 'System', or 'UMD'."), - 'enum': ['commonjs', 'amd', 'umd', 'system'] - }, - 'newLine': { - 'description': nls.localize('tsconfig.json.compilerOptions.newLine', "Specifies the end of line sequence to be used when emitting files: 'CRLF' (dos) or 'LF' (unix).)"), - 'enum': [ 'CRLF', 'LF' ] - }, - 'noEmit': { - 'description': nls.localize('tsconfig.json.compilerOptions.noEmit', 'Do not emit output.'), - 'type': 'boolean' - }, - 'noEmitOnError': { - 'description': nls.localize('tsconfig.json.compilerOptions.noEmitOnError', 'Do not emit outputs if any type checking errors were reported.'), - 'type': 'boolean' - }, - 'noEmitHelpers': { - 'description': nls.localize('tsconfig.json.compilerOptions.noEmitHelpers', 'Do not generate custom helper functions like __extends in compiled output.'), - 'type': 'boolean' - }, - 'noImplicitAny': { - 'description': nls.localize('tsconfig.json.compilerOptions.noImplicitAny', "Warn on expressions and declarations with an implied 'any' type."), - 'type': 'boolean' - }, - 'noLib': { - 'description': nls.localize('tsconfig.json.compilerOptions.noLib', "Do not include the default library file (lib.d.ts)."), - 'type': 'boolean' - }, - 'noResolve': { - 'description': nls.localize('tsconfig.json.compilerOptions.noResolve', "Do not add triple-slash references or module import targets to the list of compiled files."), - 'type': 'boolean' - }, - 'out': { - 'description': nls.localize('tsconfig.json.compilerOptions.out', 'Concatenate and emit output to single file.'), - 'type': 'string', - 'format': 'uri' - }, - 'outDir': { - 'description': nls.localize('tsconfig.json.compilerOptions.outDir', 'Redirect output structure to the directory.'), - 'type': 'string', - 'format': 'uri' - }, - 'preserveConstEnums': { - 'description': nls.localize('tsconfig.json.compilerOptions.preserveConstEnums', 'Do not erase const enum declarations in generated code.'), - 'type': 'boolean' - }, - 'removeComments': { - 'description': nls.localize('tsconfig.json.compilerOptions.removeComments', 'Do not emit comments to output.'), - 'type': 'boolean' - }, - 'rootDir': { - 'description': nls.localize('tsconfig.json.compilerOptions.rootDir', 'Specifies the root directory of input files. Use to control the output directory structure with --outDir.'), - 'type': 'string' - }, - 'sourceMap': { - 'description': nls.localize('tsconfig.json.compilerOptions.sourceMap', "Generates corresponding '.map' file."), - 'type': 'boolean' - }, - 'sourceRoot': { - 'description': nls.localize('tsconfig.json.compilerOptions.sourceRoot', 'Specifies the location where debugger should locate TypeScript files instead of source locations.'), - 'type': 'string', - 'format': 'uri' - }, - 'suppressImplicitAnyIndexErrors': { - 'description': nls.localize('tsconfig.json.compilerOptions.suppressImplicitAnyIndexErrors', 'Suppress noImplicitAny errors for indexing objects lacking index signatures.'), - 'type': 'boolean' - }, - 'target': { - 'description': nls.localize('tsconfig.json.compilerOptions.target', "Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6' (experimental)."), - 'enum': ['ES3', 'ES5', 'ES6', 'es3', 'es5', 'es6'], - 'default': 'ES3' - }, - 'watch': { - 'description': nls.localize('tsconfig.json.compilerOptions.watch', "Watch input files."), - "type": 'boolean' - }, - 'jsx': { - 'description': nls.localize('tsconfig.json.compilerOptions.jsx', "Enable the JSX option (requires TypeScript 1.6): 'preserve', 'react'."), - 'enum': ['react', 'preserve'], - 'default': 'react' - }, - 'emitDecoratorMetadata': { - 'description': nls.localize('tsconfig.json.compilerOptions.emitDecoratorMetadata', 'Emits meta data.for ES7 decorators.'), - 'type': 'boolean' - }, - 'isolatedModules': { - 'description': nls.localize('tsconfig.json.compilerOptions.isolatedModules', 'Supports transpiling single TS files into JS files.'), - 'type': 'boolean' - }, - 'experimentalDecorators': { - 'description': nls.localize('tsconfig.json.compilerOptions.experimentalDecorators', 'Enables experimental support for ES7 decorators.'), - 'type': 'boolean' - }, - 'experimentalAsyncFunctions': { - 'description': nls.localize('tsconfig.json.compilerOptions.experimentalAsynFunctions', 'Enables experimental support for async functions (requires TypeScript 1.6).'), - 'type': 'boolean' - } - } - }, - 'files': { - 'type': 'array', - 'description': nls.localize('tsconfig.json.files', "If no 'files' property is present in a tsconfig.json, the compiler defaults to including all files the containing directory and subdirectories. When a 'files' property is specified, only those files are included."), - 'items': { - 'type': 'string', - 'format': 'uri' - } - } - } - }); - - this.addPreloadedFileSchema('http://opentools.azurewebsites.net/jsconfig', { - 'title': nls.localize('jsconfig.json.title', "JSON schema for the JavaScript configuration file"), - 'type': 'object', - 'default': { 'compilerOptions': { 'target': 'ES5' } }, - 'properties': { - 'compilerOptions': { - 'type': 'object', - 'description': nls.localize('jsconfig.json.compilerOptions', 'Instructs the JavaScript language service how to validate .js files'), - 'properties': { - 'charset': { - 'description': nls.localize('jsconfig.json.compilerOptions.charset', 'The character set of the input files'), - 'type': 'string' - }, - 'diagnostics': { - 'description': nls.localize('jsconfig.json.compilerOptions.diagnostics', 'Show diagnostic information.'), - 'type': 'boolean' - }, - 'locale': { - 'description': nls.localize('jsconfig.json.compilerOptions.locale', 'The locale to use to show error messages, e.g. en-us.'), - 'type': 'string' - }, - 'mapRoot': { - 'description': nls.localize('jsconfig.json.compilerOptions.mapRoot', 'Specifies the location where debugger should locate map files instead of generated locations'), - 'type': 'string', - 'format': 'uri' - }, - 'module': { - 'description': nls.localize('jsconfig.json.compilerOptions.module', "Module code generation to resolve against: 'commonjs', 'amd', 'system', or 'umd'."), - 'enum': ['commonjs', 'amd', 'system', 'umd'] - }, - 'noLib': { - 'description': nls.localize('jsconfig.json.compilerOptions.noLib', "Do not include the default library file (lib.d.ts)."), - 'type': 'boolean' - }, - 'target': { - 'description': nls.localize('jsconfig.json.compilerOptions.target', "Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6' (experimental)."), - 'enum': ['ES3', 'ES5', 'ES6', 'es3', 'es5', 'es6'], - 'default': 'ES3' - }, - 'experimentalDecorators': { - 'description': nls.localize('jsconfig.json.compilerOptions.decorators', "Enables experimental support for ES7 decorators."), - 'type': 'boolean' - } - } - }, - 'files': { - 'type': 'array', - 'description': nls.localize('jsconfig.json.files', "If no 'files' property is present in a jsconfig.json, the language service defaults to including all files the containing directory and subdirectories. When a 'files' property is specified, only those files are included."), - 'items': { - 'type': 'string', - 'format': 'uri' - } - }, - 'exclude': { - 'type': 'array', - 'description': nls.localize('jsconfig.json.exclude', "List files and folders that should not be included. This property is not honored when the 'files' property is present."), - 'items': { - 'type': 'string', - 'format': 'uri' - } - } - } - }); - } - } function toDisplayString(url:string) { diff --git a/src/vs/languages/json/common/jsonWorker.ts b/src/vs/languages/json/common/jsonWorker.ts index f146d2e62bf2234a0227b7e760babdb1150921bf..e811948f91fa770ebb194cb60468c2d4f367cec7 100644 --- a/src/vs/languages/json/common/jsonWorker.ts +++ b/src/vs/languages/json/common/jsonWorker.ts @@ -29,7 +29,7 @@ import errors = require('vs/base/common/errors'); import {IMarkerService, IMarkerData} from 'vs/platform/markers/common/markers'; import {IRequestService} from 'vs/platform/request/common/request'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; -import {ISchemaContributions} from 'vs/languages/json/common/jsonContributionRegistry'; +import {ISchemaContributions} from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import {IResourceService} from 'vs/editor/common/services/resourceService'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/languages/json/test/common/jsonworker.test.ts b/src/vs/languages/json/test/common/jsonworker.test.ts index 98c2705f6985a77a6c18027cbb124524f67033fc..e8fc1db1b608953714ae374209e5b5e35b59af14 100644 --- a/src/vs/languages/json/test/common/jsonworker.test.ts +++ b/src/vs/languages/json/test/common/jsonworker.test.ts @@ -50,8 +50,7 @@ suite('JSON - Worker', () => { if (schema) { var id = "http://myschemastore/test1"; var schemaService = (worker).schemaService; - schemaService.addPreloadedFileSchema(id, schema); - schemaService.registerExternalSchema(id, [ "*.json" ]); + schemaService.registerExternalSchema(id, [ "*.json" ], schema); } } diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 04344f928b318758fc9467552ea16d6289eafeae..9d27ad2b76960f4ba565c16b06d42e1fe2f35373 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -12,7 +12,7 @@ import objects = require('vs/base/common/objects'); import strings = require('vs/base/common/strings'); import {IPluginDescription} from 'vs/platform/plugins/common/plugins'; import {PluginsRegistry} from 'vs/platform/plugins/common/pluginsRegistry'; -import JSONContributionRegistry = require('vs/languages/json/common/jsonContributionRegistry'); +import JSONContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); export var Extensions = { diff --git a/src/vs/languages/json/common/jsonContributionRegistry.ts b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts similarity index 100% rename from src/vs/languages/json/common/jsonContributionRegistry.ts rename to src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts diff --git a/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.ts b/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.ts new file mode 100644 index 0000000000000000000000000000000000000000..65934eaf5d406d75f847773df363761cc8f3292e --- /dev/null +++ b/src/vs/platform/jsonschemas/common/jsonValidationExtensionPoint.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import nls = require('vs/nls'); +import {IJSONSchema} from 'vs/base/common/jsonSchema'; +import {PluginsRegistry} from 'vs/platform/plugins/common/pluginsRegistry'; +import {Registry} from 'vs/platform/platform'; +import JSONContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); +import strings = require('vs/base/common/strings'); +import paths = require('vs/base/common/paths'); +import {INullService} from 'vs/platform/instantiation/common/instantiation'; + +interface IJSONValidationExtensionPoint { + fileMatch: string, + url: string +} + +let schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); + +let configurationExtPoint = PluginsRegistry.registerExtensionPoint('jsonValidation', { + description: nls.localize('contributes.jsonValidation', 'Contributes json schema configuration.'), + type: 'array', + default: [ { fileMatch: "{{file.json}}", url: "{{url}}" } ], + items: { + type: 'object', + default: { fileMatch: "{{file.json}}", url: "{{url}}" }, + properties: { + fileMatch: { + type: 'string', + description: nls.localize('contributes.jsonValidation.fileMatch', 'The file pattern to match, for example "package.json" or "*.launch".'), + }, + url: { + description: nls.localize('contributes.jsonValidation.url', 'A schema URL (\'http:\', \'https:\') or relative path to the extension folder (\'./\').'), + type: 'string' + } + } + } +}); + +export class JSONValidationExtensionPoint { + + constructor( + @INullService modeService: INullService + ) { + configurationExtPoint.setHandler((extensions) => { + for (var i = 0; i < extensions.length; i++) { + var extensionValue = extensions[i].value; + var collector = extensions[i].collector; + var extensionPath = extensions[i].description.extensionFolderPath; + + if (!extensionValue || !Array.isArray(extensionValue)) { + collector.error(nls.localize('invalid.jsonValidation', "'configuration.jsonValidation' must be a array")); + return; + } + extensionValue.forEach(extension => { + if (typeof extension.fileMatch !== 'string') { + collector.error(nls.localize('invalid.fileMatch', "'configuration.jsonValidation.fileMatch' must be defined")); + return; + } + var uri = extension.url; + if (typeof extension.url !== 'string') { + collector.error(nls.localize('invalid.url', "'configuration.jsonValidation.url' must be a URL or relative path")); + return; + } + if (strings.startsWith(uri, './')) { + uri = paths.normalize(paths.join(extensionPath, uri)); + } else if (!strings.startsWith(uri, 'https:/') && strings.startsWith(uri, 'https:/')) { + collector.error(nls.localize('invalid.url.schema', "'configuration.jsonValidation.url' must start with 'http:', 'https:' or './' to reference schemas located in the extension")); + return; + } + var fileMatch = extension.fileMatch; + if (!strings.startsWith(extension.fileMatch, '/')) { + fileMatch = '/' + fileMatch; + } + schemaRegistry.addSchemaFileAssociation(fileMatch, uri); + }); + } + }); + } + +} diff --git a/src/vs/platform/keybinding/common/commands.ts b/src/vs/platform/keybinding/common/commands.ts deleted file mode 100644 index d3bfa2c93a76ee09fb0d7f93c9f5bbc538eba8f7..0000000000000000000000000000000000000000 --- a/src/vs/platform/keybinding/common/commands.ts +++ /dev/null @@ -1,37 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import {IDisposable} from 'vs/base/common/lifecycle'; -import Event, {Emitter} from 'vs/base/common/event'; - - -export class ApiCommands { - - static Instance = new ApiCommands(); - - private _commands: { [id: string]: [(...args: any[]) => any, any] } = Object.create(null); - private _onDidAddCommand = new Emitter<{ id: string; handler: (...args: any[]) => any; thisArg?: any }>(); - - add(id: string, handler: (...args: any[]) => any, thisArg?: any) { - if (this._commands[id]) { - throw new Error(); - } - this._commands[id] = [handler, thisArg]; - this._onDidAddCommand.fire({ id, handler, thisArg }); - } - - track(callback: (event: { id: string; handler: (...args: any[]) => any; thisArg?: any }) => any): IDisposable { - for (let id in this._commands) { - let [handler, thisArg] = this._commands[id]; - callback({ id, handler, thisArg }); - } - return this._onDidAddCommand.event(callback); - } -} - -export function addApiCommand(id: string, handler: (...args: any[]) => any, thisArg?: any) { - ApiCommands.Instance.add(id, handler, thisArg); -} \ No newline at end of file diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 7275deb876ecfa698b7936849f0fdf83c907a94d..65ac634a595b0a29e9e5f73b3363a8078e16ecc4 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -8,7 +8,6 @@ import {Registry} from 'vs/platform/platform'; import {ICommandHandler, ICommandsMap, IKeybindingItem, IKeybindings, IKeybindingContextRule} from 'vs/platform/keybinding/common/keybindingService'; import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils'; import {KeyMod, KeyCode, BinaryKeybindings} from 'vs/base/common/keyCodes'; -import {ApiCommands} from 'vs/platform/keybinding/common/commands'; import Platform = require('vs/base/common/platform'); export interface ICommandRule extends IKeybindings { @@ -68,17 +67,6 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { constructor() { this._keybindings = []; this._commands = Object.create(null); - ApiCommands.Instance.track(event => { - this.registerCommandDesc({ - id: event.id, - handler: function(accessor, args) { - event.handler.apply(event.thisArg, args); - }, - weight: this.WEIGHT.externalExtension(0), - context: undefined, - primary: undefined - }); - }); } public registerCommandRule(rule:ICommandRule): void { diff --git a/src/vs/platform/plugins/common/abstractPluginService.ts b/src/vs/platform/plugins/common/abstractPluginService.ts index 28cccff354f774efc9e1abeb4afb641c9b2bd4bb..a5d85100765413c3806ab205f061edfb48fdeb47 100644 --- a/src/vs/platform/plugins/common/abstractPluginService.ts +++ b/src/vs/platform/plugins/common/abstractPluginService.ts @@ -5,7 +5,7 @@ 'use strict'; import nls = require('vs/nls'); -import {IPluginDescription, IPluginService, IMessage, IPointListener, IActivationEventListener } from 'vs/platform/plugins/common/plugins'; +import {IPluginDescription, IPluginService, IMessage, IPointListener, IActivationEventListener, IPluginStatus } from 'vs/platform/plugins/common/plugins'; import WinJS = require('vs/base/common/winjs.base'); import {IDisposable} from 'vs/base/common/lifecycle'; import Errors = require('vs/base/common/errors'); @@ -21,8 +21,8 @@ export interface IPluginExports { } export interface IPluginModule { - activate(subscriptions: IDisposable[]): WinJS.TPromise; - deactivate(callback:(err:any, success:boolean)=>void): void; + activate(ctx: IPluginContext): WinJS.TPromise; + deactivate(): void; } export interface IPluginContext { @@ -83,6 +83,7 @@ export abstract class AbstractPluginService implements IPluginService { this.activatedPlugins = {}; } + public abstract deactivate(pluginId:string): void; protected abstract _showMessage(severity:Severity, message:string): void; protected showMessage(severity:Severity, source:string, message:string): void { @@ -111,6 +112,10 @@ export abstract class AbstractPluginService implements IPluginService { return this.activatedPlugins[pluginId].exports; } + public getPluginsStatus(): { [id: string]: IPluginStatus } { + return null; + } + public isActivated(pluginId:string): boolean { return hasOwnProperty.call(this.activatedPlugins, pluginId); } diff --git a/src/vs/platform/plugins/common/nativePluginService.ts b/src/vs/platform/plugins/common/nativePluginService.ts index d69012b650c6d138c822e2462f85cf8b0f4bda58..b7b07ae4a48774a05d72694ce8bea6970c537c74 100644 --- a/src/vs/platform/plugins/common/nativePluginService.ts +++ b/src/vs/platform/plugins/common/nativePluginService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {IPluginDescription, IMessage} from 'vs/platform/plugins/common/plugins'; +import {IPluginDescription, IMessage, IPluginStatus} from 'vs/platform/plugins/common/plugins'; import {PluginsRegistry} from 'vs/platform/plugins/common/pluginsRegistry'; import WinJS = require('vs/base/common/winjs.base'); import {Remotable, IThreadService} from 'vs/platform/thread/common/thread'; @@ -15,6 +15,7 @@ import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {PluginHostStorage} from 'vs/platform/storage/common/remotable.storage'; import * as paths from 'vs/base/common/paths'; import {IWorkspaceContextService, IConfiguration} from 'vs/platform/workspace/common/workspace'; +import {disposeAll} from 'vs/base/common/lifecycle'; class PluginMemento implements IPluginMemento { @@ -64,6 +65,7 @@ export class MainProcessPluginService extends AbstractPluginService { private _telemetryService: ITelemetryService; private _proxy: PluginHostPluginService; private _isDev: boolean; + private _pluginsStatus: { [id: string]: IPluginStatus }; /** * This class is constructed manually because it is a service, so it doesn't use any ctor injection @@ -83,8 +85,13 @@ export class MainProcessPluginService extends AbstractPluginService { this._threadService = threadService; this._telemetryService = telemetryService; this._proxy = this._threadService.getRemotable(PluginHostPluginService); + this._pluginsStatus = {}; PluginsRegistry.handleExtensionPoints((severity, source, message) => { + if (!this._pluginsStatus[source]) { + this._pluginsStatus[source] = { messages: [] }; + } + this._pluginsStatus[source].messages.push({ type: severity, source, message }); this.showMessage(severity, source, message); }); } @@ -158,6 +165,14 @@ export class MainProcessPluginService extends AbstractPluginService { } } + public getPluginsStatus(): { [id: string]: IPluginStatus } { + return this._pluginsStatus; + } + + public deactivate(pluginId:string): void { + this._proxy.deactivate(pluginId); + } + // -- overwriting AbstractPluginService protected _actualActivatePlugin(pluginDescription: IPluginDescription): WinJS.TPromise { @@ -222,6 +237,28 @@ export class PluginHostPluginService extends AbstractPluginService { } } + public deactivate(pluginId:string): void { + let plugin = this.activatedPlugins[pluginId]; + if (!plugin) { + return; + } + + // call deactivate if available + try { + if (typeof plugin.module.deactivate === 'function') { + plugin.module.deactivate(); + } + } catch(err) { + // TODO: Do something with err if this is not the shutdown case + } + + // clean up subscriptions + try { + disposeAll(plugin.subscriptions) + } catch(err) { + // TODO: Do something with err if this is not the shutdown case + } + } // -- overwriting AbstractPluginService diff --git a/src/vs/platform/plugins/common/plugins.ts b/src/vs/platform/plugins/common/plugins.ts index 3825edbea9cc794f3cea35703a70efe21cfa2af3..d4ae4848dfb4d88794eedf9d888e4059a7c2bb8e 100644 --- a/src/vs/platform/plugins/common/plugins.ts +++ b/src/vs/platform/plugins/common/plugins.ts @@ -40,18 +40,38 @@ export interface IMessage { source: string; } +export interface IPluginStatus { + messages: IMessage[]; +} + export interface IPluginService { serviceId: ServiceIdentifier; + activateByEvent(activationEvent:string): TPromise; activateAndGet(pluginId:string): TPromise; activateAndGet(pluginId:string): TPromise; isActivated(pluginId:string): boolean; + + /** + * This method should be called only on shutdown! + * More work is needed for this to be called any time! + */ + deactivate(pluginId:string): void; + + /** + * To be used only by the platform once on startup. + */ registrationDone(errors?:IMessage[]): void; registerOneTimeActivationEventListener(activationEvent: string, listener: IActivationEventListener): void; get(pluginId:string): any; + + /** + * Block on this signal any interactions with extensions. + */ onReady(): TPromise; + getPluginsStatus(): { [id: string]: IPluginStatus }; } export var INSTANCE:IPluginService = null; \ No newline at end of file diff --git a/src/vs/platform/plugins/common/pluginsRegistry.ts b/src/vs/platform/plugins/common/pluginsRegistry.ts index 2b4e5ad1b67775f3e602ad19cd43be0e80a97a17..137b4793d32c3e5c5b7fc859e0794e139d29d94a 100644 --- a/src/vs/platform/plugins/common/pluginsRegistry.ts +++ b/src/vs/platform/plugins/common/pluginsRegistry.ts @@ -8,7 +8,7 @@ import {IPluginDescription, IPointListener, IActivationEventListener, IMessage} import {Registry} from 'vs/platform/platform'; import Errors = require('vs/base/common/errors'); import env = require('vs/base/common/flags'); -import * as JSONContributionRegistry from 'vs/languages/json/common/jsonContributionRegistry'; +import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import {IJSONSchema} from 'vs/base/common/jsonSchema'; import nls = require('vs/nls'); import paths = require('vs/base/common/paths'); diff --git a/src/vs/workbench/api/common/extHostLanguageFeatureCommands.ts b/src/vs/workbench/api/common/extHostLanguageFeatureCommands.ts index 260dc0aaae63a0ebb83059ede675bc6efb3b8dc4..d8dc78673349c53e1becb47ae8dbf16d9fe332b7 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatureCommands.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatureCommands.ts @@ -35,7 +35,6 @@ import {NavigateTypesSupportRegistry, INavigateTypesSupport, ITypeBearing} from import {RenameRegistry} from 'vs/editor/contrib/rename/common/rename'; import {FormatRegistry, FormatOnTypeRegistry} from 'vs/editor/contrib/format/common/format'; import {ICodeLensData} from 'vs/editor/contrib/codelens/common/codelens'; -import {addApiCommand} from 'vs/platform/keybinding/common/commands'; export class ExtHostLanguageFeatureCommands { @@ -45,24 +44,28 @@ export class ExtHostLanguageFeatureCommands { constructor(commands: PluginHostCommands) { this._commands = commands; - addApiCommand('vscode.executeWorkspaceSymbolProvider', this._executeWorkspaceSymbolProvider, this); - addApiCommand('vscode.executeDefinitionProvider', this._executeDefinitionProvider, this); - addApiCommand('vscode.executeHoverProvider', this._executeHoverProvider, this); - addApiCommand('vscode.executeDocumentHighlights', this._executeDocumentHighlights, this); - addApiCommand('vscode.executeReferenceProvider', this._executeReferenceProvider, this); - addApiCommand('vscode.executeDocumentRenameProvider', this._executeDocumentRenameProvider, this); - addApiCommand('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider, this); - addApiCommand('vscode.executeDocumentSymbolProvider', this._executeDocumentSymbolProvider, this); - addApiCommand('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, this); - addApiCommand('vscode.executeCodeActionProvider', this._executeCodeActionProvider, this); - addApiCommand('vscode.executeCodeLensProvider', this._executeCodeLensProvider, this); - addApiCommand('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider, this); - addApiCommand('vscode.executeFormatRangeProvider', this._executeFormatRangeProvider, this); - addApiCommand('vscode.executeFormatOnTypeProvider', this._executeFormatOnTypeProvider, this); + this._register('vscode.executeWorkspaceSymbolProvider', this._executeWorkspaceSymbolProvider); + this._register('vscode.executeDefinitionProvider', this._executeDefinitionProvider); + this._register('vscode.executeHoverProvider', this._executeHoverProvider); + this._register('vscode.executeDocumentHighlights', this._executeDocumentHighlights); + this._register('vscode.executeReferenceProvider', this._executeReferenceProvider); + this._register('vscode.executeDocumentRenameProvider', this._executeDocumentRenameProvider); + this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider); + this._register('vscode.executeDocumentSymbolProvider', this._executeDocumentSymbolProvider); + this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider); + this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider); + this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider); + this._register('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider); + this._register('vscode.executeFormatRangeProvider', this._executeFormatRangeProvider); + this._register('vscode.executeFormatOnTypeProvider', this._executeFormatOnTypeProvider); } // --- command impl + private _register(id: string, handler: (...args: any[]) => any): void { + this._disposables.push(this._commands.registerCommand(id, handler, this)); + } + /** * Execute workspace symbol provider. * diff --git a/src/vs/workbench/api/common/pluginHostCommands.ts b/src/vs/workbench/api/common/pluginHostCommands.ts index 4c9defc22c5a1cf4ce326d592109fa0d333e3150..bc57ffe399258c63f60d40397bd3af88571bc5a0 100644 --- a/src/vs/workbench/api/common/pluginHostCommands.ts +++ b/src/vs/workbench/api/common/pluginHostCommands.ts @@ -15,7 +15,6 @@ import {PluginHostEditors} from 'vs/workbench/api/common/pluginHostEditors'; import {IMessageService, Severity} from 'vs/platform/message/common/message'; import {canSerialize} from 'vs/base/common/marshalling'; import {toErrorMessage} from 'vs/base/common/errors'; -import {ApiCommands} from 'vs/platform/keybinding/common/commands'; import * as vscode from 'vscode'; @Remotable.PluginHostContext('PluginHostCommands') @@ -28,11 +27,6 @@ export class PluginHostCommands { constructor(@IThreadService threadService: IThreadService) { this._pluginHostEditors = threadService.getRemotable(PluginHostEditors); this._proxy = threadService.getRemotable(MainThreadCommands); - - ApiCommands.Instance.track(event => { - let {id, handler, thisArg} = event; - this.registerCommand(id, handler, thisArg); - }); } registerCommand(id: string, command: (...args: any[]) => T | Thenable, thisArgs?: any): vscode.Disposable { diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 449b1d5ca6692b1285c36aa8f8379e65c31d71c2..76ad3678219d9ec9cdc5f3b9cfcf7af3a49886fa 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -55,6 +55,7 @@ import {MainProcessPluginService} from 'vs/platform/plugins/common/nativePluginS import {MainThreadDocuments} from 'vs/workbench/api/common/pluginHostDocuments'; import {MainProcessTextMateSyntax} from 'vs/editor/node/textMate/TMSyntax'; import {MainProcessTextMateSnippet} from 'vs/editor/node/textMate/TMSnippets'; +import {JSONValidationExtensionPoint} from 'vs/platform/jsonschemas/common/jsonValidationExtensionPoint'; import {LanguageConfigurationFileHandler} from 'vs/editor/node/languageConfiguration'; import {MainThreadFileSystemEventService} from 'vs/workbench/api/common/pluginHostFileSystemEventService'; import {MainThreadQuickOpen} from 'vs/workbench/api/browser/pluginHostQuickOpen'; @@ -355,6 +356,7 @@ export class WorkbenchShell { this.threadService.getRemotable(RemoteTelemetryServiceHelper); this.workbench.getInstantiationService().createInstance(MainProcessTextMateSyntax); this.workbench.getInstantiationService().createInstance(MainProcessTextMateSnippet); + this.workbench.getInstantiationService().createInstance(JSONValidationExtensionPoint); this.workbench.getInstantiationService().createInstance(LanguageConfigurationFileHandler); this.threadService.getRemotable(MainThreadConfiguration); this.threadService.getRemotable(MainThreadQuickOpen); diff --git a/src/vs/workbench/electron-main/windows.ts b/src/vs/workbench/electron-main/windows.ts index 1eb2da30c490168a3c7ed46544fd99b9cfbe1bf3..260d668cac6769929f057b77ac704f5d26d3fde2 100644 --- a/src/vs/workbench/electron-main/windows.ts +++ b/src/vs/workbench/electron-main/windows.ts @@ -822,8 +822,7 @@ export class WindowsManager { if (paths && paths.length > 0) { // Remember path in storage for next time - let pathToRemember = isFolder ? paths[0] : path.dirname(paths[0]); - storage.setItem(WindowsManager.workingDirPickerStorageKey, pathToRemember); + storage.setItem(WindowsManager.workingDirPickerStorageKey, path.dirname(paths[0])); // Return clb(paths); diff --git a/src/vs/workbench/node/pluginHostMain.ts b/src/vs/workbench/node/pluginHostMain.ts index 76a74bb1a428921b985a652babc59b032ac5c46e..d8f2b947243c63ea79c1540d4bbefc512faf118e 100644 --- a/src/vs/workbench/node/pluginHostMain.ts +++ b/src/vs/workbench/node/pluginHostMain.ts @@ -104,16 +104,45 @@ interface ITestRunner { export class PluginHostMain { + private _isTerminating: boolean; + constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService, @IPluginService private pluginService: IPluginService, @IInstantiationService instantiationService: IInstantiationService - ) {} + ) { + this._isTerminating = false; + } public start(): TPromise { return this.readPlugins(); } + public terminate(): void { + if (this._isTerminating) { + // we are already shutting down... + return; + } + this._isTerminating = true; + + try { + let allExtensions = PluginsRegistry.getAllPluginDescriptions(); + let allExtensionsIds = allExtensions.map(ext => ext.id); + let activatedExtensions = allExtensionsIds.filter(id => this.pluginService.isActivated(id)); + + activatedExtensions.forEach((extensionId) => { + this.pluginService.deactivate(extensionId); + }); + } catch(err) { + // TODO: write to log once we have one + } + + // Give extensions 1 second to wrap up any async dispose, then exit + setTimeout(() => { + exit() + }, 1000); + } + private readPlugins(): TPromise { let collector = new PluginsMessageCollector(); let env = this.contextService.getConfiguration().env; diff --git a/src/vs/workbench/node/pluginHostProcess.ts b/src/vs/workbench/node/pluginHostProcess.ts index 03b9c9bd74609daddf07b7fc477d51ae43352472..ee57a5c42e535d6212522d4087db1894f86ce68f 100644 --- a/src/vs/workbench/node/pluginHostProcess.ts +++ b/src/vs/workbench/node/pluginHostProcess.ts @@ -16,8 +16,14 @@ interface IRendererConnection { initData: IInitData; } +// This calls exit directly in case the initialization is not finished and we need to exit +// Otherwise, if initialization completed we go to pluginHostMain.terminate() +var onTerminate = function() { + exit(); +}; + function connectToRenderer(): TPromise { - return new TPromise((c, e) => { + return new TPromise((c, e) => { const stats: number[] = []; // Listen init data message @@ -28,7 +34,13 @@ function connectToRenderer(): TPromise { }); // Listen to all other messages - process.on('message', msg => remoteCom.handle(msg)); + process.on('message', (msg) => { + if (msg.type === '__$terminate') { + onTerminate(); + return; + } + remoteCom.handle(msg); + }); // Print a console message when rejection isn't handled. For details // see https://nodejs.org/api/process.html#process_event_unhandledrejection @@ -50,7 +62,7 @@ function connectToRenderer(): TPromise { try { process.kill(msg.parentPid, 0); // throws an exception if the main process doesn't exist anymore. } catch (e) { - exit(); + onTerminate(); } }, 5000); @@ -86,6 +98,10 @@ TPromise.join([connectToRenderer(), connectToSharedProcess()]) const instantiationService = createServices(renderer.remoteCom, renderer.initData, sharedProcessClient); const pluginHostMain = instantiationService.createInstance(PluginHostMain); + onTerminate = () => { + pluginHostMain.terminate(); + }; + pluginHostMain.start() .done(null, err => console.error(err)); }); \ No newline at end of file diff --git a/src/vs/workbench/node/proxy.ts b/src/vs/workbench/node/proxy.ts index afd60795aa596030990c721c4da65157e0478e33..4a4951e8db84ed2bb9e614e83b017151c27412bb 100644 --- a/src/vs/workbench/node/proxy.ts +++ b/src/vs/workbench/node/proxy.ts @@ -55,8 +55,6 @@ export interface IOptions { } export function getProxyAgent(rawRequestURL: string, options: IOptions = {}): any { - console.log(rawRequestURL, options); - if (!options.proxyUrl) { return getSystemProxyAgent(rawRequestURL); } diff --git a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts index 0957d59761b6cdabd60759170752ad4ab69dcc35..7539390ad9a70ecb020fa0d5d3f6aaf1b9c368f8 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts @@ -6,7 +6,7 @@ import lifecycle = require('vs/base/common/lifecycle'); import editorcommon = require('vs/editor/common/editorCommon'); import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IDebugService, ModelEvents, ViewModelEvents, IBreakpoint, IRawBreakpoint } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, ModelEvents, ViewModelEvents, IBreakpoint, IRawBreakpoint, State } from 'vs/workbench/parts/debug/common/debug'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -254,10 +254,11 @@ export class DebugEditorModelManager implements IWorkbenchContribution { private createBreakpointDecorations(breakpoints: IBreakpoint[]): editorcommon.IModelDeltaDecoration[] { const activated = this.debugService.getModel().areBreakpointsActivated(); + const debugActive = this.debugService.getState() === State.Running || this.debugService.getState() === State.Stopped; return breakpoints.map((breakpoint) => { return { options: (!breakpoint.enabled || !activated) ? DebugEditorModelManager.BREAKPOINT_DISABLED_DECORATION : - breakpoint.verified ? DebugEditorModelManager.BREAKPOINT_VERIFIED_DECORATION : DebugEditorModelManager.BREAKPOINT_DECORATION, + debugActive && !breakpoint.verified ? DebugEditorModelManager.BREAKPOINT_UNVERIFIED_DECORATION : DebugEditorModelManager.BREAKPOINT_DECORATION, range: createRange(breakpoint.lineNumber, 1, breakpoint.lineNumber, 2) }; }); @@ -275,8 +276,8 @@ export class DebugEditorModelManager implements IWorkbenchContribution { stickiness: editorcommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }; - private static BREAKPOINT_VERIFIED_DECORATION: editorcommon.IModelDecorationOptions = { - glyphMarginClassName: 'debug-breakpoint-glyph-verified', + private static BREAKPOINT_UNVERIFIED_DECORATION: editorcommon.IModelDecorationOptions = { + glyphMarginClassName: 'debug-breakpoint-glyph-unverified', stickiness: editorcommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }; diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled-dark.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled-dark.svg index 4a2575a30563f0cb5b5f14194a8d357d63fa8c90..bf58a18178f079cbb3946f2dba6110ffe81785b2 100644 --- a/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled-dark.svg +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled-dark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled.svg index 617e7ede7bd2c237d141f3e8ec2d13b891ca5e5d..9780270357d6ff0fc43e43c24f9fa2338dd21ae5 100644 --- a/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled.svg +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-disabled.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-hint.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-hint.svg new file mode 100644 index 0000000000000000000000000000000000000000..e397c3a2215375ea1da49a2ab0b44bb2b8bfade1 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-hint.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified-dark.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified-dark.svg new file mode 100644 index 0000000000000000000000000000000000000000..4a2575a30563f0cb5b5f14194a8d357d63fa8c90 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified.svg new file mode 100644 index 0000000000000000000000000000000000000000..617e7ede7bd2c237d141f3e8ec2d13b891ca5e5d --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint-unverified.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint-verified.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint-verified.svg deleted file mode 100644 index ab0515b23c901a809351cb4a82ff29f77496af12..0000000000000000000000000000000000000000 --- a/src/vs/workbench/parts/debug/browser/media/breakpoint-verified.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/breakpoint.svg b/src/vs/workbench/parts/debug/browser/media/breakpoint.svg index 67d458b2f761f34ecf421388c49ada5f2ec71253..ab0515b23c901a809351cb4a82ff29f77496af12 100644 --- a/src/vs/workbench/parts/debug/browser/media/breakpoint.svg +++ b/src/vs/workbench/parts/debug/browser/media/breakpoint.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css index cf226d5185266cd3804c3f3110e9fc2be0e06e4d..82659e645e67e659dd45bcbe7f527e75451fb72a 100644 --- a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css @@ -26,15 +26,15 @@ } .monaco-editor .debug-breakpoint-glyph-hint { - background: url('breakpoint.svg') center center no-repeat; + background: url('breakpoint-hint.svg') center center no-repeat; } .monaco-editor .debug-breakpoint-glyph-disabled { background: url('breakpoint-disabled.svg') center center no-repeat; } -.monaco-editor .debug-breakpoint-glyph-verified { - background: url('breakpoint-verified.svg') center center no-repeat; +.monaco-editor .debug-breakpoint-glyph-unverified { + background: url('breakpoint-unverified.svg') center center no-repeat; } .monaco-editor .debug-top-stack-frame-glyph { @@ -49,11 +49,11 @@ background: url('breakpoint.svg') center center no-repeat; } -.monaco-editor .debug-top-stack-frame-glyph.debug-breakpoint-glyph-verified { +.monaco-editor .debug-top-stack-frame-glyph.debug-breakpoint-glyph { background: url('current-and-breakpoint.svg') center center no-repeat; } -.monaco-editor .debug-focused-stack-frame-glyph.debug-breakpoint-glyph-verified { +.monaco-editor .debug-focused-stack-frame-glyph.debug-breakpoint-glyph { background: url('stackframe-and-breakpoint.svg') center center no-repeat; } @@ -271,6 +271,10 @@ background: url('breakpoint-disabled-dark.svg') center center no-repeat; } +.monaco-editor.vs-dark .debug-breakpoint-glyph-unverified { + background: url('breakpoint-unverified-dark.svg') center center no-repeat; +} + .monaco-editor.vs-dark .debug-top-stack-frame-glyph { background: url('current-arrow-dark.svg') center center no-repeat; } @@ -279,11 +283,11 @@ background: url('stackframe-arrow-dark.svg') center center no-repeat; } -.monaco-editor.vs-dark .debug-top-stack-frame-glyph.debug-breakpoint-glyph-verified { +.monaco-editor.vs-dark .debug-top-stack-frame-glyph.debug-breakpoint-glyph { background: url('current-and-breakpoint-dark.svg') center center no-repeat; } -.monaco-editor.vs-dark .debug-focused-stack-frame-glyph.debug-breakpoint-glyph-verified { +.monaco-editor.vs-dark .debug-focused-stack-frame-glyph.debug-breakpoint-glyph { background: url('stackframe-and-breakpoint-dark.svg') center center no-repeat; } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugActions.ts b/src/vs/workbench/parts/debug/electron-browser/debugActions.ts index 6e2e780d82fa1870e8e663a9d3e45fabe1e575fa..65a2482e75f2ee48d941acec7c1cc715939389cb 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugActions.ts @@ -290,7 +290,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction { export class ToggleEnablementAction extends AbstractDebugAction { static ID = 'workbench.debug.viewlet.action.toggleBreakpointEnablement'; - static LABEL = nls.localize('toggleEnablement', "Toggle Enablement"); + static LABEL = nls.localize('toggleEnablement', "Enable/Disable Breakpoint"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { super(id, label, 'debug-action toggle-enablement', debugService, keybindingService); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 5b4b2617c2a390049a76f648005f776f7a4df506..7e5529570ddf183db6e303d755b19142798e0be3 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -601,19 +601,17 @@ export class DebugService extends ee.EventEmitter implements debug.IDebugService } this.session = null; this.partService.removeClass('debugging'); - this.contextService.updateOptions('editor', { - hover: true - }); this.editorService.focusEditor(); + this.model.clearThreads(true); + this.setFocusedStackFrameAndEvaluate(null); + this.setStateAndEmit(debug.State.Inactive); + // Set breakpoints back to unverified since the session ended. const data: {[id: string]: { line: number, verified: boolean } } = { }; this.model.getBreakpoints().forEach(bp => data[bp.getId()] = { line: bp.lineNumber, verified: false }); this.model.updateBreakpoints(data); - this.model.clearThreads(true); - this.setFocusedStackFrameAndEvaluate(null); - this.setStateAndEmit(debug.State.Inactive); this.inDebugMode.reset(); } diff --git a/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts index 7ac928392c6b63d6b9cc3e6b07d3dfebb9833f51..2984969f2d809f847ff6ed179c145889865e94e3 100644 --- a/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts @@ -13,7 +13,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import platform = require('vs/platform/platform'); import pluginsRegistry = require('vs/platform/plugins/common/pluginsRegistry'); import editor = require('vs/editor/common/editorCommon'); -import jsonContributionRegistry = require('vs/languages/json/common/jsonContributionRegistry'); +import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); import debug = require('vs/workbench/parts/debug/common/debug'); import { SystemVariables } from 'vs/workbench/parts/lib/node/systemVariables'; import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter'; @@ -179,7 +179,7 @@ export class ConfigurationManager { } else if (duplicate[attribute] && attribute !== 'type') { // Give priority to the later registered extension. duplicate[attribute] = adapter[attribute]; - extension.collector.warn(nls.localize('duplicateDebuggerType', "Debug type '{0}' is already registered and has attribute '{1}', ignoring attribute '{1}'.", adapter.type, attribute)); + extension.collector.error(nls.localize('duplicateDebuggerType', "Debug type '{0}' is already registered and has attribute '{1}', ignoring attribute '{1}'.", adapter.type, attribute)); } else { duplicate[attribute] = adapter[attribute]; } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index 6ca000ca8e23226ff9e2011515e42ce0b5b0c74b..892ea370c055e66b8fbed751a9504c2c8db32630 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -5,6 +5,8 @@ import platform = require('vs/platform/platform'); import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import statusbar = require('vs/workbench/browser/parts/statusbar/statusbar'); +import { ExtensionsStatusbarItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets'; import { IGalleryService } from 'vs/workbench/parts/extensions/common/extensions'; import { GalleryService } from 'vs/workbench/parts/extensions/node/vsoGalleryService'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; @@ -17,3 +19,10 @@ registerSingleton(IGalleryService, GalleryService); (platform.Registry.as(WorkbenchExtensions.Workbench)).registerWorkbenchContribution( ExtensionsWorkbenchExtension ); + +// Register Statusbar item +(platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( + ExtensionsStatusbarItem, + statusbar.StatusbarAlignment.LEFT, + 10 /* Low Priority */ +)); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.css b/src/vs/workbench/parts/extensions/electron-browser/extensions.css index 87d0cc98a207e23edffac6341b06afde429f8082..ac981ecc570e17a3084a5d59c1ae44f5fd50b109 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.css @@ -119,4 +119,17 @@ -moz-transform: none; -o-transform: none; transform: none; +} + +/* Status bar */ +.monaco-shell .extensions-statusbar { + cursor: pointer; +} + +.monaco-shell .extensions-statusbar.warning::before { + color: yellow; +} + +.monaco-shell .extensions-statusbar.error::before { + color: #FF9D00; } \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 5d71a8d24bb97d0059e178cd4df46047dd08ecae..7abf87688ec9a5a78e0a83574a3b4dd7aae82415 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -6,6 +6,9 @@ import nls = require('vs/nls'); import { Promise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; +import Severity from 'vs/base/common/severity'; +import { IPluginService, IPluginStatus } from 'vs/platform/plugins/common/plugins'; +import { IMessageService } from 'vs/platform/message/common/message'; import { IExtensionsService } from 'vs/workbench/parts/extensions/common/extensions'; import { IQuickOpenService } from 'vs/workbench/services/quickopen/browser/quickOpenService'; @@ -32,6 +35,43 @@ export class ListExtensionsAction extends Action { } } +export class ShowExtensionsStatusAction extends Action { + + static ID = 'workbench.extensions.action.showExtensionsStatus'; + static LABEL = nls.localize('showInstalledExtensions', "Show Extensions Status"); + private status: { [id: string]: IPluginStatus }; + + constructor( + id: string, + label: string, + @IPluginService pluginService: IPluginService, + @IMessageService private messageService: IMessageService + + ) { + super(id, label, null, true); + + pluginService.onReady().then(() => { + this.status = pluginService.getPluginsStatus(); + }); + } + + public run(): Promise { + Object.keys(this.status).forEach(key => { + this.status[key].messages.forEach(m => { + if (m.type > Severity.Ignore) { + this.messageService.show(m.type, m.message); + } + }); + }); + + return Promise.as(null); + } + + protected isEnabled(): boolean { + return true; + } +} + export class InstallExtensionAction extends Action { static ID = 'workbench.extensions.action.installExtension'; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts new file mode 100644 index 0000000000000000000000000000000000000000..8d8baa625471b255ebefece62aa1ca9fdc94feb4 --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import nls = require('vs/nls'); +import Severity from 'vs/base/common/severity'; +import errors = require('vs/base/common/errors'); +import dom = require('vs/base/browser/dom'); +import lifecycle = require('vs/base/common/lifecycle'); +import statusbar = require('vs/workbench/browser/parts/statusbar/statusbar'); +import { IPluginService } from 'vs/platform/plugins/common/plugins'; +import { IQuickOpenService } from 'vs/workbench/services/quickopen/browser/quickOpenService'; + +var $ = dom.emmet; + +export class ExtensionsStatusbarItem implements statusbar.IStatusbarItem { + + private toDispose: lifecycle.IDisposable[]; + private domNode: HTMLElement; + + constructor( + @IPluginService pluginService: IPluginService, + @IQuickOpenService private quickOpenService: IQuickOpenService + ) { + this.toDispose = []; + + pluginService.onReady().then(() => { + const pluginsStatus = pluginService.getPluginsStatus(); + Object.keys(pluginsStatus).forEach(key => { + const severity = pluginsStatus[key].messages.reduce((maxSeverity, message) => Math.max(maxSeverity, message.type), Severity.Ignore); + this.domNode.classList.add(Severity[severity].toLowerCase()); + }); + }); + } + + public render(container: HTMLElement): lifecycle.IDisposable { + this.domNode = dom.append(container, $('.extensions-statusbar octicon octicon-package')); + this.domNode.title = nls.localize('extensions', "Extensions"), + this.toDispose.push(dom.addDisposableListener(this.domNode, 'click', () => { + this.quickOpenService.show('>extensions: ').done(null, errors.onUnexpectedError); + })); + + return { + dispose: () => lifecycle.disposeAll(this.toDispose) + }; + } +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts index f968b308e05bbcce789b9368d2cd1c37ed40ca60..fa3205a4d23ce6635123125eefc430715bdbd04f 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts @@ -16,7 +16,7 @@ import { IWorkspaceContextService } from 'vs/workbench/services/workspace/common import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; import wbaregistry = require('vs/workbench/browser/actionRegistry'); import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ListExtensionsAction, InstallExtensionAction, ListOutdatedExtensionsAction } from './extensionsActions'; +import { ListExtensionsAction, ShowExtensionsStatusAction, InstallExtensionAction, ListOutdatedExtensionsAction } from './extensionsActions'; import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import ipc = require('ipc'); @@ -45,6 +45,7 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution { const extensionsCategory = nls.localize('extensionsCategory', "Extensions"); const actionRegistry = ( platform.Registry.as(wbaregistry.Extensions.WorkbenchActions)); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ListExtensionsAction, ListExtensionsAction.ID, ListExtensionsAction.LABEL), extensionsCategory); + actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowExtensionsStatusAction, ShowExtensionsStatusAction.ID, ShowExtensionsStatusAction.LABEL), extensionsCategory); (platform.Registry.as(Extensions.Quickopen)).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( diff --git a/src/vs/workbench/parts/files/test/browser/fileEditorModel.test.ts b/src/vs/workbench/parts/files/test/browser/fileEditorModel.test.ts index aa7185799254452fc46ba4149b712a8d7d069a7c..8f70ed479424a06447fa61ca5db57bfe3c54fd8e 100644 --- a/src/vs/workbench/parts/files/test/browser/fileEditorModel.test.ts +++ b/src/vs/workbench/parts/files/test/browser/fileEditorModel.test.ts @@ -208,40 +208,6 @@ suite('Files - TextFileEditorModel', () => { }); }); - test("Change after auto save triggered will cause another autosave and twice the events", function(done) { - this.timeout(10000); // TODO@Ben test tends to need longer? - - let eventCounter = 0; - let m1 = baseInstantiationService.createInstance(TextFileEditorModel, toResource("/path/index.txt"), "utf8"); - - (m1).autoSaveDelay = 10; - (m1).autoSaveEnabled = true; - - eventService.addListener(EventType.FILE_DIRTY, (e: LocalFileChangeEvent) => { - eventCounter++; - }); - - eventService.addListener(EventType.FILE_SAVED, (e: LocalFileChangeEvent) => { - eventCounter++; - }); - - m1.load().then(() => { - m1.textEditorModel.setValue("foo"); - - return Promise.timeout(50).then(() => { - m1.textEditorModel.setValue("bar"); - - return Promise.timeout(20).then(() => { - m1.dispose(); - assert.ok(!m1.isDirty()); - assert.equal(eventCounter, 4); - - done(); - }); - }); - }); - }); - test("Dirty tracking", function(done) { let resource = toResource("/path/index_async.txt"); let i1 = baseInstantiationService.createInstance(FileEditorInput, resource, "text/plain", "utf8"); diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts index b6e1e4b45b0d4c4cab8e27adcfb32f5487f4071f..1867cd089a9befba0f2819b677b6572839e5093e 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts @@ -17,7 +17,7 @@ import modesExtensions = require('vs/editor/common/modes/modesRegistry'); import errors = require('vs/base/common/errors'); import {IQuickOpenService, IPickOpenEntry} from 'vs/workbench/services/quickopen/browser/quickOpenService'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; -import * as JSONContributionRegistry from 'vs/languages/json/common/jsonContributionRegistry'; +import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import {IJSONSchema} from 'vs/base/common/jsonSchema'; import ipc = require('ipc'); diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 4c2094e4d03d74b8c601e9e277d2e27d9cf6dbfa..5c2078677cc68666fc811e794bf752b2f49b706d 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -48,7 +48,7 @@ import { IModel } from 'vs/editor/common/editorCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import jsonContributionRegistry = require('vs/languages/json/common/jsonContributionRegistry'); +import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 4c7de277b5ecc6e1cf94d37b0599047ccb301cb4..2babf7f4924ff5c02f0668f3bde1aacf37d17174 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -13,7 +13,7 @@ import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; import {IKeybindingItem, IUserFriendlyKeybinding} from 'vs/platform/keybinding/common/keybindingService'; import {IOSupport} from 'vs/platform/keybinding/common/commonKeybindingResolver'; -import * as JSONContributionRegistry from 'vs/languages/json/common/jsonContributionRegistry'; +import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import {IJSONSchema} from 'vs/base/common/jsonSchema'; export abstract class WorkbenchKeybindingService extends KeybindingService { diff --git a/src/vs/workbench/services/thread/electron-browser/threadService.ts b/src/vs/workbench/services/thread/electron-browser/threadService.ts index baa8313712ba0edbada88046ede5cbe1caaa491e..d585a6e8acfe939f986151c8cf7d9a977dca3e9c 100644 --- a/src/vs/workbench/services/thread/electron-browser/threadService.ts +++ b/src/vs/workbench/services/thread/electron-browser/threadService.ts @@ -305,7 +305,9 @@ class PluginHostProcessManager { this.terminating = true; if (this.pluginHostProcessHandle) { - this.pluginHostProcessHandle.kill(); + this.pluginHostProcessHandle.send({ + type: '__$terminate' + }); } } } \ No newline at end of file