diff --git a/extensions/node-debug/node-debug.azure.json b/extensions/node-debug/node-debug.azure.json index c39753b19c0..687772cb60f 100644 --- a/extensions/node-debug/node-debug.azure.json +++ b/extensions/node-debug/node-debug.azure.json @@ -3,4 +3,4 @@ "container": "debuggers", "zip": "e6b9b29/node-debug.zip", "output": "" -} \ No newline at end of file +} diff --git a/src/typings/native-keymap.d.ts~HEAD b/src/typings/native-keymap.d.ts~HEAD deleted file mode 100644 index af80ddaeaf3..00000000000 --- a/src/typings/native-keymap.d.ts~HEAD +++ /dev/null @@ -1,18 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'native-keymap' { - - export interface INativeKeyMap { - key_code: string; - value: string; - withShift: string; - withAltGr: string; - withShiftAltGr: string; - } - - export function getKeyMap(): INativeKeyMap[]; - -} \ No newline at end of file diff --git a/src/typings/native-keymap.d.ts~Microsoft_master b/src/typings/native-keymap.d.ts~Microsoft_master deleted file mode 100644 index af80ddaeaf3..00000000000 --- a/src/typings/native-keymap.d.ts~Microsoft_master +++ /dev/null @@ -1,18 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'native-keymap' { - - export interface INativeKeyMap { - key_code: string; - value: string; - withShift: string; - withAltGr: string; - withShiftAltGr: string; - } - - export function getKeyMap(): INativeKeyMap[]; - -} \ No newline at end of file diff --git a/src/typings/windows-mutex.ts~HEAD b/src/typings/windows-mutex.ts~HEAD deleted file mode 100644 index 039dffcc2ad..00000000000 --- a/src/typings/windows-mutex.ts~HEAD +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'windows-mutex' { - export class Mutex { - constructor(name: string); - isActive(): boolean; - release(): void; - } -} \ No newline at end of file diff --git a/src/typings/windows-mutex.ts~Microsoft_master b/src/typings/windows-mutex.ts~Microsoft_master deleted file mode 100644 index 039dffcc2ad..00000000000 --- a/src/typings/windows-mutex.ts~Microsoft_master +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'windows-mutex' { - export class Mutex { - constructor(name: string); - isActive(): boolean; - release(): void; - } -} \ No newline at end of file diff --git a/src/vs/base/common/flags.ts b/src/vs/base/common/flags.ts index 5ccc72dd7fe..a442271d045 100644 --- a/src/vs/base/common/flags.ts +++ b/src/vs/base/common/flags.ts @@ -8,6 +8,7 @@ import { globals } from 'vs/base/common/platform'; export const workersCount = environment('workersCount', 2); export const enableTasks = environment('enableTasks'); +export const enableSendASmile = environment('enableSendASmile'); export const enableJavaScriptRewriting = environment('enableJavaScriptRewriting'); export const enableTypeScriptServiceMode = environment('enableTypeScriptServiceMode'); export const enableTypeScriptServiceModeForJS = environment('enableTypeScriptServiceModeForJS'); diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 4f85ffa0355..96ca0786824 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -138,5 +138,11 @@ export interface IEnvironment { aiConfig: { key: string; asimovKey: string; + }, + + sendASmile: { + submitUrl: string, + reportIssueUrl: string, + requestFeatureUrl: string } } \ No newline at end of file diff --git a/src/vs/workbench/api/common/extHostLanguageFeatureCommands.ts b/src/vs/workbench/api/common/extHostLanguageFeatureCommands.ts deleted file mode 100644 index 9c8b5e54693..00000000000 --- a/src/vs/workbench/api/common/extHostLanguageFeatureCommands.ts +++ /dev/null @@ -1,255 +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 URI from 'vs/base/common/uri'; -import Event, {Emitter} from 'vs/base/common/event'; -import Severity from 'vs/base/common/severity'; -import {DefaultFilter} from 'vs/editor/common/modes/modesFilters'; -import {TPromise} from 'vs/base/common/winjs.base'; -import {onUnexpectedError} from 'vs/base/common/errors'; -import {sequence} from 'vs/base/common/async'; -import {Range as EditorRange} from 'vs/editor/common/core/range'; -import {IDisposable} from 'vs/base/common/lifecycle'; -import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; -import {Remotable, IThreadService} from 'vs/platform/thread/common/thread'; -import * as vscode from 'vscode'; -import * as typeConverters from 'vs/workbench/api/common/pluginHostTypeConverters'; -import * as types from 'vs/workbench/api/common/pluginHostTypes'; -import {IPosition, IRange, ISingleEditOperation} from 'vs/editor/common/editorCommon'; -import * as modes from 'vs/editor/common/modes'; -import {CancellationTokenSource} from 'vs/base/common/cancellation'; -import {PluginHostModelService} from 'vs/workbench/api/common/pluginHostDocuments'; -import {IMarkerService, IMarker} from 'vs/platform/markers/common/markers'; -import {PluginHostCommands, MainThreadCommands} from 'vs/workbench/api/common/pluginHostCommands'; -import {DeclarationRegistry} from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration'; -import {ExtraInfoRegistry} from 'vs/editor/contrib/hover/common/hover'; -import {OccurrencesRegistry} from 'vs/editor/contrib/wordHighlighter/common/wordHighlighter'; -import {ReferenceRegistry} from 'vs/editor/contrib/referenceSearch/common/referenceSearch'; -import {IQuickFix2, QuickFixRegistry, getQuickFixes} from 'vs/editor/contrib/quickFix/common/quickFix'; -import {IOutline} from 'vs/editor/contrib/quickOpen/common/quickOpen'; -import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; -import {NavigateTypesSupportRegistry, INavigateTypesSupport, ITypeBearing} from 'vs/workbench/parts/search/common/search' -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'; - -export class ExtHostLanguageFeatureCommands { - - private _commands: PluginHostCommands; - private _disposables: IDisposable[] = []; - - constructor(commands: PluginHostCommands) { - this._commands = commands; - - 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); - } - - private _register(id: string, callback: (...args: any[]) => any): void { - this._disposables.push(this._commands.registerCommand(id, callback, this)); - } - - // --- command impl - - private _executeWorkspaceSymbolProvider(query: string): Thenable { - return this._commands.executeCommand('_executeWorkspaceSymbolProvider', { query }).then(value => { - if (Array.isArray(value)) { - return value.map(typeConverters.toSymbolInformation); - } - }); - } - - private _executeDefinitionProvider(resource: URI, position: types.Position): Thenable { - const args = { - resource, - position: position && typeConverters.fromPosition(position) - }; - return this._commands.executeCommand('_executeDefinitionProvider', args).then(value => { - if (Array.isArray(value)) { - return value.map(typeConverters.toLocation) - } - }); - } - - private _executeHoverProvider(resource: URI, position: types.Position): Thenable { - const args = { - resource, - position: position && typeConverters.fromPosition(position) - }; - return this._commands.executeCommand('_executeHoverProvider', args).then(value => { - if (Array.isArray(value)) { - return value.map(typeConverters.toHover) - } - }); - } - - private _executeDocumentHighlights(resource: URI, position: types.Position): Thenable { - const args = { - resource, - position: position && typeConverters.fromPosition(position) - }; - return this._commands.executeCommand('_executeDocumentHighlights', args).then(value => { - if (Array.isArray(value)) { - return value.map(typeConverters.toDocumentHighlight) - } - }); - } - - private _executeReferenceProvider(resource: URI, position: types.Position): Thenable { - const args = { - resource, - position: position && typeConverters.fromPosition(position) - }; - return this._commands.executeCommand('_executeDocumentHighlights', args).then(value => { - if (Array.isArray(value)) { - return value.map(typeConverters.toLocation) - } - }); - } - - private _executeDocumentRenameProvider(resource: URI, position: types.Position, newName: string): Thenable { - const args = { - resource, - position: position && typeConverters.fromPosition(position), - newName - }; - return this._commands.executeCommand('_executeDocumentRenameProvider', args).then(value => { - if (!value) { - return; - } - if (value.rejectReason) { - return TPromise.wrapError(value.rejectReason); - } - let workspaceEdit = new types.WorkspaceEdit(); - for (let edit of value.edits) { - workspaceEdit.replace(edit.resource, typeConverters.toRange(edit.range), edit.newText); - } - return workspaceEdit; - }); - } - - private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Thenable { - const args = { - resource, - position: position && typeConverters.fromPosition(position), - triggerCharacter - }; - return this._commands.executeCommand('_executeSignatureHelpProvider', args).then(value => { - if (value) { - return typeConverters.SignatureHelp.to(value); - } - }); - } - - private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string): Thenable { - const args = { - resource, - position: position && typeConverters.fromPosition(position), - triggerCharacter - }; - return this._commands.executeCommand('_executeCompletionItemProvider', args).then(value => { - if (value) { - let items: types.CompletionItem[] = []; - for (let group of value) { - for (let suggestions of group) { - for (let suggestion of suggestions.suggestions) { - const item = typeConverters.Suggest.to(suggestion); - items.push(item); - } - } - } - return items; - } - }); - } - - private _executeDocumentSymbolProvider(resource: URI): Thenable { - const args = { - resource - }; - return this._commands.executeCommand('_executeDocumentSymbolProvider', args).then(value => { - if (value && Array.isArray(value.entries)) { - return value.entries.map(typeConverters.SymbolInformation.fromOutlineEntry); - } - }); - } - - private _executeCodeActionProvider(resource: URI, range: types.Range): Thenable { - const args = { - resource, - range: typeConverters.fromRange(range) - }; - return this._commands.executeCommand('_executeCodeActionProvider', args).then(value => { - if (!Array.isArray(value)) { - return; - } - return value.map(quickFix => typeConverters.Command.to(quickFix.command)); - }); - } - - private _executeCodeLensProvider(resource: URI): Thenable{ - const args = { resource }; - return this._commands.executeCommand('_executeCodeLensProvider', args).then(value => { - if (Array.isArray(value)) { - return value.map(item => { - return new types.CodeLens(typeConverters.toRange(item.symbol.range), - typeConverters.Command.to(item.symbol.command)); - }); - } - }); - } - - private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Thenable { - const args = { - resource, - options - }; - return this._commands.executeCommand('_executeFormatDocumentProvider', args).then(value => { - if (Array.isArray(value)) { - return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text)); - } - }); - } - - private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Thenable { - const args = { - resource, - range: typeConverters.fromRange(range), - options - }; - return this._commands.executeCommand('_executeFormatRangeProvider', args).then(value => { - if (Array.isArray(value)) { - return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text)); - } - }); - } - - private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch:string, options: vscode.FormattingOptions): Thenable { - const args = { - resource, - position: typeConverters.fromPosition(position), - ch, - options - }; - return this._commands.executeCommand('_executeFormatOnTypeProvider', args).then(value => { - if (Array.isArray(value)) { - return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text)); - } - }); - } -} \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/index.html b/src/vs/workbench/electron-browser/index.html index ad533db5e8e..5afc32ca427 100644 --- a/src/vs/workbench/electron-browser/index.html +++ b/src/vs/workbench/electron-browser/index.html @@ -147,6 +147,7 @@ 'platform': remote.process.platform, 'enableTasks': hasWorkspaceContext, 'enableJavaScriptRewriting': true, + 'enableSendASmile' : !!configuration.sendASmile, 'enableTypeScriptServiceMode': true || !process.env['VSCODE_TSWORKER'], 'enableTypeScriptServiceModeForJS': !!process.env['CODE_TSJS'] }; diff --git a/src/vs/workbench/electron-main/env.ts b/src/vs/workbench/electron-main/env.ts index e1e4c28c9be..76096440054 100644 --- a/src/vs/workbench/electron-main/env.ts +++ b/src/vs/workbench/electron-main/env.ts @@ -50,6 +50,11 @@ export interface IProductConfiguration { key: string; asimovKey: string; }, + sendASmile: { + submitUrl: string, + reportIssueUrl: string, + requestFeatureUrl: string + }, documentationUrl: string, releaseNotesUrl: string, twitterUrl: string, diff --git a/src/vs/workbench/electron-main/window.ts b/src/vs/workbench/electron-main/window.ts index 3a070fac5de..7bd14c3c996 100644 --- a/src/vs/workbench/electron-main/window.ts +++ b/src/vs/workbench/electron-main/window.ts @@ -116,6 +116,11 @@ export interface IWindowConfiguration extends env.ICommandLineArguments { aiConfig: { key: string; asimovKey: string; + }, + sendASmile: { + submitUrl: string, + reportIssueUrl: string, + requestFeatureUrl: string } } diff --git a/src/vs/workbench/electron-main/windows.ts b/src/vs/workbench/electron-main/windows.ts index 260d668cac6..52c82f9471d 100644 --- a/src/vs/workbench/electron-main/windows.ts +++ b/src/vs/workbench/electron-main/windows.ts @@ -554,6 +554,7 @@ export class WindowsManager { configuration.updateChannel = UpdateManager.channel; configuration.recentPaths = this.getRecentlyOpenedPaths(workspacePath, filesToOpen); configuration.aiConfig = env.product.aiConfig; + configuration.sendASmile = env.product.sendASmile; configuration.enableTelemetry = env.product.enableTelemetry; configuration.userEnv = userEnv; diff --git a/src/vs/workbench/parts/debug/browser/debug.contribution.ts b/src/vs/workbench/parts/debug/browser/debug.contribution.ts deleted file mode 100644 index 243e77722bc..00000000000 --- a/src/vs/workbench/parts/debug/browser/debug.contribution.ts +++ /dev/null @@ -1,97 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./media/debug.contribution'; -import nls = require('vs/nls'); -import { CommonEditorRegistry, ContextKey, EditorActionDescriptor } from 'vs/editor/common/editorCommonExtensions'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import platform = require('vs/platform/platform'); -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IKeybindings } from 'vs/platform/keybinding/common/keybindingService'; -import { EditorBrowserRegistry } from 'vs/editor/browser/editorBrowserExtensions'; -import wbaregistry = require('vs/workbench/browser/actionRegistry'); -import actionbarregistry = require('vs/workbench/browser/actionBarRegistry'); -import viewlet = require('vs/workbench/browser/viewlet'); -import wbext = require('vs/workbench/common/contributions'); -import baseeditor = require('vs/workbench/browser/parts/editor/baseEditor'); -import * as debug from 'vs/workbench/parts/debug/common/debug'; -import { DebugEditorModelManager } from 'vs/workbench/parts/debug/browser/debugEditorModelManager' -import dbgactions = require('vs/workbench/parts/debug/browser/debugActions'); -import editorinputs = require('vs/workbench/parts/debug/browser/debugEditorInputs'); -import repleditor = require('vs/workbench/parts/debug/browser/replEditor'); -import debugwidget = require('vs/workbench/parts/debug/browser/debugActionsWidget'); -import { DebugEditorContribution } from 'vs/workbench/parts/debug/browser/debugEditorContribution'; -import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; - -import IDebugService = debug.IDebugService; - -class OpenDebugViewletAction extends viewlet.ToggleViewletAction { - public static ID = debug.VIEWLET_ID; - public static LABEL = nls.localize('toggleDebugViewlet', "Show Debug"); - - constructor( - id: string, - label: string, - @IViewletService viewletService: IViewletService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService - ) { - super(id, label, debug.VIEWLET_ID, viewletService, editorService); - } -} - -EditorBrowserRegistry.registerEditorContribution(DebugEditorContribution); -CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(dbgactions.ToggleBreakpointAction, dbgactions.ToggleBreakpointAction.ID, nls.localize('toggleBreakpointAction', "Debug: Toggle Breakpoint"), { - context: ContextKey.EditorTextFocus, - primary: KeyCode.F9 -})); -CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(dbgactions.SelectionToReplAction, dbgactions.SelectionToReplAction.ID, nls.localize('debugEvaluate', "Debug: Evaluate"))); -CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(dbgactions.SelectionToWatchExpressionsAction, dbgactions.SelectionToWatchExpressionsAction.ID, nls.localize('addToWatch', "Debug: Add to Watch"))); -CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(dbgactions.RunToCursorAction, dbgactions.RunToCursorAction.ID, nls.localize('runToCursor', "Debug: Run to Cursor"))); - -// Register Viewlet -(platform.Registry.as(viewlet.Extensions.Viewlets)).registerViewlet(new viewlet.ViewletDescriptor( - 'vs/workbench/parts/debug/browser/debugViewlet', - 'DebugViewlet', - debug.VIEWLET_ID, - nls.localize('debug', "Debug"), - 'debug', - 40 -)); - -var openViewletKb: IKeybindings = { - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_D -}; - -// Register repl editor -platform.Registry.as(baseeditor.Extensions.Editors).registerEditor( - new baseeditor.EditorDescriptor(repleditor.Repl.ID, 'Repl', 'vs/workbench/parts/debug/browser/replEditor', 'Repl'), - new SyncDescriptor(editorinputs.ReplEditorInput)); - -let actionBarRegistry = platform.Registry.as(actionbarregistry.Extensions.Actionbar); -actionBarRegistry.registerActionBarContributor(actionbarregistry.Scope.EDITOR, repleditor.ReplEditorActionContributor); -(platform.Registry.as(baseeditor.Extensions.Editors)).registerEditorInputFactory(editorinputs.ReplEditorInput.ID, repleditor.ReplInputFactory); - -// Register Action to Open Viewlet -var registry = ( platform.Registry.as(wbaregistry.Extensions.WorkbenchActions)); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), nls.localize('view', "View")); - -(platform.Registry.as(wbext.Extensions.Workbench)).registerWorkbenchContribution(DebugEditorModelManager); -(platform.Registry.as(wbext.Extensions.Workbench)).registerWorkbenchContribution(debugwidget.DebugActionsWidget); - -var debugCategory = nls.localize('debugCategory', "Debug"); -registry.registerWorkbenchAction(new SyncActionDescriptor( - dbgactions.StartDebugAction, dbgactions.StartDebugAction.ID, dbgactions.StartDebugAction.LABEL, { primary: KeyCode.F5 }, [{ key: debug.CONTEXT_IN_DEBUG_MODE, operator: KeybindingsRegistry.KEYBINDING_CONTEXT_OPERATOR_NOT_EQUAL, operand: true }]), debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(dbgactions.StepOverDebugAction, dbgactions.StepOverDebugAction.ID, dbgactions.StepOverDebugAction.LABEL, { primary: KeyCode.F10 }, [{ key: debug.CONTEXT_IN_DEBUG_MODE }]), debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(dbgactions.StepIntoDebugAction, dbgactions.StepIntoDebugAction.ID, dbgactions.StepIntoDebugAction.LABEL, { primary: KeyCode.F11 }, [{ key: debug.CONTEXT_IN_DEBUG_MODE }], KeybindingsRegistry.WEIGHT.workbenchContrib(1)), debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(dbgactions.StepOutDebugAction, dbgactions.StepOutDebugAction.ID, dbgactions.StepOutDebugAction.LABEL, { primary: KeyMod.Shift | KeyCode.F11 }, [{ key: debug.CONTEXT_IN_DEBUG_MODE }]), debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(dbgactions.RestartDebugAction, dbgactions.RestartDebugAction.ID, dbgactions.RestartDebugAction.LABEL), debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(dbgactions.StopDebugAction, dbgactions.StopDebugAction.ID, dbgactions.StopDebugAction.LABEL, { primary: KeyMod.Shift | KeyCode.F5 }, [{ key: debug.CONTEXT_IN_DEBUG_MODE }]), debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(dbgactions.ContinueAction, dbgactions.ContinueAction.ID, dbgactions.ContinueAction.LABEL, { primary: KeyCode.F5 }, [{ key: debug.CONTEXT_IN_DEBUG_MODE }]), debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(dbgactions.PauseAction, dbgactions.PauseAction.ID, dbgactions.PauseAction.LABEL), debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(dbgactions.ConfigureAction, dbgactions.ConfigureAction.ID, dbgactions.ConfigureAction.LABEL), debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(dbgactions.OpenReplAction, dbgactions.OpenReplAction.ID, dbgactions.OpenReplAction.LABEL), debugCategory); 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 ab0515b23c9..00000000000 --- 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/feedback/browser/feedback.ts b/src/vs/workbench/parts/feedback/browser/feedback.ts new file mode 100644 index 00000000000..f75ab870e0d --- /dev/null +++ b/src/vs/workbench/parts/feedback/browser/feedback.ts @@ -0,0 +1,291 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/css!./media/feedback'; +import nls = require('vs/nls'); +import types = require('vs/base/common/types'); +import {IDisposable} from 'vs/base/common/lifecycle'; +import {Builder, $} from 'vs/base/browser/builder'; +import errors = require('vs/base/common/errors'); +import DOM = require('vs/base/browser/dom'); +import {Promise} from 'vs/base/common/winjs.base'; +import {Dropdown} from 'vs/base/browser/ui/dropdown/dropdown'; +import {IXHRResponse} from 'vs/base/common/http'; +import {IContextViewService, IContextMenuService} from 'vs/platform/contextview/browser/contextView'; +import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; + +const STATUS_TIMEOUT = 500; + +export interface IFeedback { + feedback: string; + alias: string; + sentiment: number; +} + +export interface IFeedbackService { + submitFeedback(feedback: IFeedback): Promise; +} + +export interface IFeedbackDropdownOptions { + contextViewProvider: IContextViewService; + feedbackService?: IFeedbackService; +} + +enum FormEvent { + SENDING, + SENT, + SEND_ERROR +}; + +export class FeedbackDropdown extends Dropdown { + + protected feedback: string; + protected alias: string; + protected sentiment: number; + protected aliasEnabled: boolean; + protected isSendingFeedback: boolean; + protected autoHideTimeout: number; + + protected feedbackService: IFeedbackService; + + protected feedbackForm: HTMLFormElement; + protected feedbackDescriptionInput: HTMLTextAreaElement; + protected feedbackAliasInput: HTMLInputElement; + protected smileyInput: Builder; + protected frownyInput: Builder; + protected sendButton: Builder; + + constructor( + container: HTMLElement, + options: IFeedbackDropdownOptions, + @ITelemetryService protected telemetryService: ITelemetryService + ) { + super(container, { + contextViewProvider: options.contextViewProvider, + labelRenderer: (container: HTMLElement): IDisposable => { + $(container).addClass('send-feedback'); + return null; + } + }); + + this.$el.addClass('send-feedback'); + this.$el.title(nls.localize('sendFeedback', "Send Feedback")); + + this.feedbackService = options.feedbackService; + + this.feedback = ''; + this.alias = ''; + this.aliasEnabled = false; + this.sentiment = 1; + + this.feedbackForm = null; + this.feedbackDescriptionInput = null; + this.feedbackAliasInput = null; + + this.smileyInput = null; + this.frownyInput = null; + + this.sendButton = null; + } + + public renderContents(container: HTMLElement): IDisposable { + let $form = $('form.feedback-form').attr({ + action: 'javascript:void(0);', + tabIndex: '-1' + }).appendTo(container); + + $(container).addClass('monaco-menu-container'); + + this.feedbackForm = $form.getHTMLElement(); + + $('h2.title').text(nls.localize("label.sendASmile", "Let us know how we're doing")).appendTo($form); + + this.invoke($('div.cancel'), () => { + this.hide(); + }).appendTo($form); + + $('h3').text(nls.localize("sentiment", "How was your experience?")).appendTo($form); + + let $feedbackSentiment = $('div.feedback-sentiment').appendTo($form); + + + this.smileyInput = $('div').addClass('sentiment smile').attr({ + 'aria-checked': 'false', + 'aria-label': nls.localize('smileCaption', "Happy"), + 'tabindex': 0, + 'role': 'checkbox' + }); + this.invoke(this.smileyInput, () => { this.setSentiment(true); }).appendTo($feedbackSentiment); + + this.frownyInput = $('div').addClass('sentiment frown').attr({ + 'aria-checked': 'false', + 'aria-label': nls.localize('frownCaption', "Sad"), + 'tabindex': 0, + 'role': 'checkbox' + }); + + this.invoke(this.frownyInput, () => { this.setSentiment(false); }).appendTo($feedbackSentiment); + + if (this.sentiment === 1) { + this.smileyInput.addClass('checked').attr('aria-checked', 'true'); + } else { + this.frownyInput.addClass('checked').attr('aria-checked', 'true'); + } + + $('h3').text(nls.localize("commentsHeader", "Comments")).appendTo($form); + + this.feedbackDescriptionInput = $('textarea.feedback-description').attr({ + rows: 8, + 'aria-label': nls.localize("commentsHeader", "Comments") + }) + .text(this.feedback).attr('required', 'required') + .on('keyup', () => { + this.feedbackDescriptionInput.value ? this.sendButton.removeAttribute('disabled') : this.sendButton.attr('disabled', ''); + }) + .appendTo($form).domFocus().getHTMLElement(); + + let aliasHeaderText = nls.localize('aliasHeader', "Add e-mail address"); + + this.feedbackAliasInput = $('input.feedback-alias') + .type('text') + .text(aliasHeaderText) + .attr('type', 'email') + .attr('placeholder', nls.localize('aliasPlaceholder', "Optional e-mail address")) + .value(this.alias) + .attr('aria-label', aliasHeaderText) + .appendTo($form) + .getHTMLElement(); + + let $buttons = $('div.form-buttons').appendTo($form); + + this.sendButton = this.invoke($('input.send').type('submit').attr('disabled', '').value(nls.localize('send', "Send")).appendTo($buttons), () => { + if (this.isSendingFeedback) { + return; + } + this.onSubmit().then(null, function() { }); + }); + + return { + dispose: () => { + this.feedbackForm = null; + this.feedbackDescriptionInput = null; + this.feedbackAliasInput = null; + this.smileyInput = null; + this.frownyInput = null; + } + }; + } + + protected setSentiment(smile: boolean): void { + if (smile) { + this.smileyInput.addClass('checked'); + this.smileyInput.attr('aria-checked', 'true'); + this.frownyInput.removeClass('checked'); + this.frownyInput.attr('aria-checked', 'false'); + } else { + this.frownyInput.addClass('checked'); + this.frownyInput.attr('aria-checked', 'true'); + this.smileyInput.removeClass('checked'); + this.smileyInput.attr('aria-checked', 'false'); + } + this.sentiment = smile ? 1 : 0; + } + + protected invoke(element: Builder, callback: () => void): Builder { + element.on('click', callback); + element.on('keypress', (e) => { + if (e instanceof KeyboardEvent) { + let keyboardEvent = e; + if (keyboardEvent.keyCode === 13 || keyboardEvent.keyCode === 32) { // Enter or Spacebar + callback(); + } + } + }); + return element; + } + + public hide(): void { + if (this.feedbackDescriptionInput) { + this.feedback = this.feedbackDescriptionInput.value; + } + + if (this.feedbackAliasInput) { + this.alias = this.feedbackAliasInput.value; + } + + if (this.autoHideTimeout) { + clearTimeout(this.autoHideTimeout); + this.autoHideTimeout = null; + } + + super.hide(); + } + + public onEvent(e: Event, activeElement: HTMLElement): void { + if (e instanceof KeyboardEvent) { + let keyboardEvent = e; + if (keyboardEvent.keyCode === 27) { // Escape + this.hide(); + } + } + } + + protected onSubmit(): Promise { + if ((this.feedbackForm.checkValidity && !this.feedbackForm.checkValidity())) { + return Promise.as(null); + } + + this.changeFormStatus(FormEvent.SENDING); + + return this.feedbackService.submitFeedback({ + feedback: this.feedbackDescriptionInput.value, + alias: this.feedbackAliasInput.value, + sentiment: this.sentiment + }).then((response: IXHRResponse) => { + setTimeout(() => { this.changeFormStatus(FormEvent.SENT); }, STATUS_TIMEOUT); + return ''; + }, (xhr: IXHRResponse) => { + setTimeout(() => { this.changeFormStatus(FormEvent.SEND_ERROR); }, STATUS_TIMEOUT); + return Promise.wrapError(new errors.ConnectionError(xhr)); + }); + } + + + private changeFormStatus(event: FormEvent): void { + switch (event) { + case FormEvent.SENDING: + this.isSendingFeedback = true; + this.sendButton.setClass('send in-progress'); + this.sendButton.value(nls.localize('feedbackSending', "Sending...")); + break; + case FormEvent.SENT: + this.isSendingFeedback = false; + this.sendButton.setClass('send success').value(nls.localize('feedbackSent', "Thanks :)")); + this.resetForm(); + this.autoHideTimeout = setTimeout(() => { + this.hide(); + }, 1000); + this.sendButton.off(['click', 'keypress']); + this.invoke(this.sendButton, () => { + this.hide(); + this.sendButton.off(['click', 'keypress']); + }); + break; + case FormEvent.SEND_ERROR: + this.isSendingFeedback = false; + this.sendButton.setClass('send error').value(nls.localize('feedbackSendingError', "Try again")); + break; + } + } + + protected resetForm(): void { + this.feedbackDescriptionInput ? this.feedbackDescriptionInput.value = '' : null; + this.feedbackAliasInput ? this.feedbackAliasInput.value = '' : null; + this.sentiment = 1; + this.aliasEnabled = false; + } +} diff --git a/src/vs/workbench/parts/feedback/browser/media/close-dark.svg b/src/vs/workbench/parts/feedback/browser/media/close-dark.svg new file mode 100644 index 00000000000..ce0e5896405 --- /dev/null +++ b/src/vs/workbench/parts/feedback/browser/media/close-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/browser/media/close.svg b/src/vs/workbench/parts/feedback/browser/media/close.svg new file mode 100644 index 00000000000..58af8536799 --- /dev/null +++ b/src/vs/workbench/parts/feedback/browser/media/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/browser/media/feedback.css b/src/vs/workbench/parts/feedback/browser/media/feedback.css new file mode 100644 index 00000000000..c635b92ef3b --- /dev/null +++ b/src/vs/workbench/parts/feedback/browser/media/feedback.css @@ -0,0 +1,399 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +.monaco-shell .feedback-form { + width: 420px; + top: 30px; + right: 6px; + padding: 1em; +} + +.monaco-shell .feedback-form h2 { + margin: 0; + padding: 0; + font-family: "Segoe WPC", "Segoe UI", "SFUIDisplay-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-weight: normal; + font-size: 1.8em; +} + +.monaco-shell .feedback-form h3 { + margin: 1em 0 0; + padding: 0; + font-weight: normal; + font-size: 1.2em; +} + +.monaco-shell .feedback-form .content { + font-size: 1.2em; +} + +.monaco-shell .feedback-form .content > div { + display: inline-block; + vertical-align: top; + margin-top: 20px; +} + +.monaco-shell .feedback-form .content .contactus { + padding: 10px; + border: solid 1px #B4BABF; + background-color: #EBF2F9; + float: right; +} + +.monaco-shell.vs-dark .feedback-form .content .contactus { + border: solid 1px #44444C; + background-color: #333337; +} + + +.monaco-shell .feedback-form .content .channels { + margin-top: 5px; + font-size: 0.9em; +} + +.monaco-shell .char-counter { + padding-left: 3px; +} + +/* TODO @C5 review link color */ +.monaco-shell .feedback-form .content .channels a { + color: #007ACC; + padding: 2px 0; +} + +.monaco-shell.vs-dark .feedback-form .content .channels a { + color: #75BEFF; +} + +.monaco-shell .feedback-form .feedback-alias, .monaco-shell .feedback-form .feedback-description { + resize: none; + font-size: 1.1em; + margin: 6px 0 0; + width: 100%; + padding: 3px 2px; + -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-shell .feedback-form .feedback-empty { + margin-top: .2em; +} + +.monaco-shell .feedback-form .feedback-alias-checkbox { + display: inline-block; + vertical-align: text-top; + margin-left: 0; +} + +.monaco-shell .feedback-form .feedback-alias { + height: 26px; +} + +.monaco-shell .feedback-form .feedback-alias:disabled { + opacity: 0.6; +} + +.monaco-shell .feedback-form .cancel { + position: absolute; + top: 0; + right: 0; + margin: .5em 0 0 0; + padding: .5em; + width: 22px; + height: 22px; + border: none; + cursor: pointer; +} + +.monaco-shell .feedback-form .form-buttons { + margin-top: 18px; + text-align: right; +} + +.monaco-shell .feedback-form .send { + padding: .5em 1.5em; + font-size: 1.1em; + margin: 0; + width: 15%; + -webkit-box-sizing: border-box; + -o-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + + -webkit-transition: width 200ms ease-out; + transition: width 200ms ease-out; +} + +.monaco-shell .feedback-form .send.in-progress { + width: 22%; +} + +.monaco-shell .feedback-form .send.success, +.monaco-shell .feedback-form .send.error { + width: 20%; +} + +.monaco-shell .feedback-form .feedback-sentiment span { + color:rgb(111, 111, 111); +} + +.monaco-shell .feedback-form .sentiment { + height: 32px; + width: 32px; + display: inline-block; + margin: .5em 0; + cursor: pointer; + + -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-shell .feedback-form .sentiment:hover { + background-color: #eaeaea; +} + +.monaco-shell .feedback-form .sentiment.checked { + border: 1px solid #39F; +} + +/* Statusbar */ +.monaco-shell .statusbar-item > .dropdown.send-feedback { + display: inline-block; +} + +.monaco-shell .statusbar-item > .dropdown.send-feedback:hover { + background-color: rgba(255, 255, 255, 0.12); +} + +.monaco-shell .statusbar-item > .dropdown.send-feedback > .dropdown-label.send-feedback { + background: url('smiley.svg') center center no-repeat; + width: 26px; +} + +/* Theming */ +.monaco-shell.vs .feedback-form { + color: black; + background: #F6F6F6; +} + +.monaco-shell .feedback-form h2 { + color: #007ACC; +} + +.monaco-shell.vs .feedback-form h3 { + color: #646465; +} + + +.monaco-shell.vs .feedback-form .feedback-alias, .monaco-shell.vs .feedback-form .feedback-description { + font-family: inherit; + border: 1px solid transparent; +} + + +.monaco-shell.vs .feedback-form .cancel { + background: url('close.svg') center center no-repeat; +} + +.monaco-shell.vs .feedback-form .cancel:hover { + background-color: #eaeaea; +} + +.monaco-shell .feedback-form .form-buttons .send { + background-color: #007ACC; + color: white; + border: none; + cursor: pointer; +} + +.monaco-shell .feedback-form .form-buttons .send.in-progress, +.monaco-shell .feedback-form .form-buttons .send:hover { + background-color: #006BB3; +} + +.monaco-shell .feedback-form .form-buttons .send:disabled { + pointer-events: none; + cursor: not-allowed; + opacity: .65; +} + +.monaco-shell .feedback-form .form-buttons .send.success { + background-color: #2d883e; +} + +.monaco-shell .feedback-form .form-buttons .send.error { + background-color: #E51400; +} + +.monaco-shell.vs-dark .feedback-form { + background: #252526; + box-shadow: 0 2px 8px #000; +} + +.monaco-shell.vs-dark .feedback-form h2 { + color: #75BEFF; +} + +.monaco-shell.vs-dark .feedback-form h3 { + font-weight: normal; + font-size: 1.2em; +} + +.monaco-shell.vs-dark .feedback-form .sentiment:hover { + background-color: #1e1e1e; +} + +.monaco-shell.vs-dark .feedback-form .sentiment.checked { + border-color: #75BEFF; +} + +.monaco-shell.vs-dark .feedback-form .feedback-alias, .monaco-shell.vs-dark .feedback-form .feedback-description { + font-family: inherit; + border: 1px solid transparent; + background-color: #333337; + color: #D4D4D4; +} + +.monaco-shell.vs-dark .feedback-form .cancel { + background: url('close-dark.svg') center center no-repeat; +} + +.monaco-shell.vs-dark .feedback-form .cancel:hover { + background-color: #1e1e1e; +} + +.monaco-shell .feedback-form .sentiment.smile { + background-image: url('happy.svg'); + background-position: center; + background-repeat: no-repeat; +} + +.monaco-shell .feedback-form .sentiment.frown { + background-image: url('sad.svg'); + background-position: center; + background-repeat: no-repeat; +} + +.monaco-shell .feedback-form .infotip { + background-image: url('info.svg'); + background-position: center; + background-repeat: no-repeat; + + height: 16px; + width: 16px; + display: inline-block; + vertical-align: text-bottom; + box-sizing: border-box; + margin-left: 5px; +} + +.monaco-shell .feedback-form .privacyLink { + float: left; + color: #007ACC !Important; +} +.monaco-shell.vs-dark .feedback-form .privacyLink { + color: #75BEFF !Important; +} + +.monaco-shell .feedback-form .privacyLink:hover { + text-decoration: underline; +} + +/* High Contrast Theming */ +.monaco-shell .hc-black .feedback-form { + background-color: #0C141F; +} + +.monaco-shell.hc-black .feedback-form h2 { + color: white; +} + +.monaco-shell.hc-black .feedback-form .feedback-alias, .monaco-shell.hc-black .feedback-form .feedback-description { + border: 1px solid #6FC3DF; + background-color: #000; + color: #D4D4D4; +} + +.monaco-shell.hc-black .feedback-form .content .contactus { + padding: 10px; + border: solid 1px #B4BABF; + background-color: #0C141F; + float: right; +} +.monaco-shell.hc-black .feedback-form .cancel { + background: none; + opacity: 0.6; +} + +.monaco-shell.hc-black .feedback-form .cancel:hover { + opacity: 1; +} + +.monaco-shell.hc-black .feedback-form .cancel:before { + content: url('close-dark.svg'); + position: relative; + top: 4px; + left: 6px; + height: 32px; + width: 32px; +} + +.monaco-shell.hc-black .feedback-form .form-buttons .send, +.monaco-shell.hc-black .feedback-form .form-buttons .send.in-progress, +.monaco-shell.hc-black .feedback-form .form-buttons .send.success { + background-color: #0C141F; + color: #D4D4D4; + border: 1px solid #6FC3DF; +} + +.monaco-shell.hc-black .feedback-form .form-buttons .send:hover { + background-color: #0C141F; +} + +.monaco-shell.hc-black .feedback-form .sentiment { + background: none; +} + +.monaco-shell.hc-black .feedback-form .sentiment.checked { + border: 1px solid #DF740C; +} + +.monaco-shell.hc-black .feedback-form .sentiment:before { + height: 21px; + width: 21px; + display: inline-block; + position: relative; + top: 4px; + left: 4px; +} + +.monaco-shell.hc-black .feedback-form .sentiment.smile:before { + content: url('happy.svg'); +} + +.monaco-shell.hc-black .feedback-form .sentiment.frown:before { + content: url('sad.svg'); +} + +.monaco-shell .feedback-form .infotip { + background: none; +} + +.monaco-shell .feedback-form .infotip:before { + content: url('info.svg'); + height: 16px; + width: 16px; + display: inline-block; +} + +.monaco-shell.hc-black .feedback-form .privacyLink { + color: white !Important; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/browser/media/happy.svg b/src/vs/workbench/parts/feedback/browser/media/happy.svg new file mode 100644 index 00000000000..954c221de21 --- /dev/null +++ b/src/vs/workbench/parts/feedback/browser/media/happy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/browser/media/info.svg b/src/vs/workbench/parts/feedback/browser/media/info.svg new file mode 100644 index 00000000000..6578b81ea3f --- /dev/null +++ b/src/vs/workbench/parts/feedback/browser/media/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/browser/media/sad.svg b/src/vs/workbench/parts/feedback/browser/media/sad.svg new file mode 100644 index 00000000000..c424c268e45 --- /dev/null +++ b/src/vs/workbench/parts/feedback/browser/media/sad.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/browser/media/smiley.svg b/src/vs/workbench/parts/feedback/browser/media/smiley.svg new file mode 100644 index 00000000000..6bc95278904 --- /dev/null +++ b/src/vs/workbench/parts/feedback/browser/media/smiley.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts new file mode 100644 index 00000000000..dd944103392 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * 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 {Registry} from 'vs/platform/platform'; +import * as Flags from 'vs/base/common/flags'; +import {StatusbarAlignment, IStatusbarRegistry, Extensions, StatusbarItemDescriptor} from 'vs/workbench/browser/parts/statusbar/statusbar'; +import {FeedbackStatusbarItem} from 'vs/workbench/parts/feedback/electron-browser/feedback'; + +// Register Statusbar item +if (Flags.enableSendASmile) { + (Registry.as(Extensions.Statusbar)).registerStatusbarItem(new StatusbarItemDescriptor( + FeedbackStatusbarItem, + StatusbarAlignment.RIGHT, + -100 /* Low Priority */ + )); +} \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts new file mode 100644 index 00000000000..4e05a7a2ec5 --- /dev/null +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts @@ -0,0 +1,251 @@ +/*--------------------------------------------------------------------------------------------- + * 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 pal = require('vs/base/common/platform'); +import {Promise} from 'vs/base/common/winjs.base'; +import {IDisposable} from 'vs/base/common/lifecycle'; +import {$} from 'vs/base/browser/builder'; +import {IStatusbarItem} from 'vs/workbench/browser/parts/statusbar/statusbar'; +import {FeedbackDropdown, IFeedback, IFeedbackService, IFeedbackDropdownOptions} from 'vs/workbench/parts/feedback/browser/feedback'; +import {IContextViewService} from 'vs/platform/contextview/browser/contextView'; +import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; +import {IRequestService} from 'vs/platform/request/common/request'; +import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; +import {IWorkspaceContextService} from 'vs/workbench/services/workspace/common/contextService'; + +import os = require('os'); + +class NativeFeedbackService implements IFeedbackService { + + private serviceUrl: string; + private appName: string; + private appVersion: string; + + constructor( + @IRequestService private requestService: IRequestService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { + const env = contextService.getConfiguration().env; + if (env.sendASmile) { + this.serviceUrl = env.sendASmile.submitUrl; + } + + this.appName = env.appName; + this.appVersion = env.version; + } + + public submitFeedback(feedback: IFeedback): Promise { + let data = JSON.stringify({ + version: 1, + user: feedback.alias, + userType: 'External', + text: feedback.feedback, + source: 'Send a smile', + sentiment: feedback.sentiment, + tags: [ + { type: 'product', value: this.appName }, + { type: 'product-version', value: this.appVersion } + ] + }); + + return this.requestService.makeRequest({ + type: 'POST', + url: this.serviceUrl, + data: data, + headers: { + 'Content-Type': 'application/json; charset=utf8', + 'Content-Length': data.length + } + }); + } +} + +class NativeFeedbackDropdown extends FeedbackDropdown { + private static MAX_FEEDBACK_CHARS: number = 140; + + private appVersion: string; + private requestFeatureLink: string; + private reportIssueLink: string; + + constructor( + container: HTMLElement, + options: IFeedbackDropdownOptions, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { + super(container, options, telemetryService); + + const env = contextService.getConfiguration().env; + this.appVersion = env.version; + this.requestFeatureLink = env.sendASmile.requestFeatureUrl; + this.reportIssueLink = env.sendASmile.reportIssueUrl; + } + + public renderContents(container: HTMLElement): IDisposable { + let $form = $('form.feedback-form').attr({ + action: 'javascript:void(0);', + tabIndex: '-1' + }).appendTo(container); + + $(container).addClass('monaco-menu-container'); + + this.feedbackForm = $form.getHTMLElement(); + + $('h2.title').text(nls.localize("label.sendASmile", "Let us know how we're doing")).appendTo($form); + + this.invoke($('div.cancel'), () => { + this.hide(); + }).appendTo($form); + + let $content = $('div.content').appendTo($form); + + let $sentimentContainer = $('div').appendTo($content); + $('span').text(nls.localize("sentiment", "How was your experience?")).appendTo($sentimentContainer); + + let $feedbackSentiment = $('div.feedback-sentiment').appendTo($sentimentContainer); + + + this.smileyInput = $('div').addClass('sentiment smile').attr({ + 'aria-checked': 'false', + 'aria-label': nls.localize('smileCaption', "Happy"), + 'tabindex': 0, + 'role': 'checkbox' + }); + this.invoke(this.smileyInput, () => { this.setSentiment(true); }).appendTo($feedbackSentiment); + + this.frownyInput = $('div').addClass('sentiment frown').attr({ + 'aria-checked': 'false', + 'aria-label': nls.localize('frownCaption', "Sad"), + 'tabindex': 0, + 'role': 'checkbox' + }); + + this.invoke(this.frownyInput, () => { this.setSentiment(false); }).appendTo($feedbackSentiment); + + if (this.sentiment === 1) { + this.smileyInput.addClass('checked').attr('aria-checked', 'true'); + } else { + this.frownyInput.addClass('checked').attr('aria-checked', 'true'); + } + + let $contactUs = $('div.contactus').appendTo($content); + + $('span').text(nls.localize("other ways to contact us", "Other ways to contact us")).appendTo($contactUs); + + let $contactUsContainer = $('div.channels').appendTo($contactUs); + + $('div').append($('a').attr('target', '_blank').attr('href', this.getReportIssueLink()).text(nls.localize("submit a bug", "Submit a bug"))) + .appendTo($contactUsContainer); + + $('div').append($('a').attr('target', '_blank').attr('href', this.requestFeatureLink).text(nls.localize("request a missing feature", "Request a missing feature"))) + .appendTo($contactUsContainer); + + let $charCounter = $('span.char-counter').text('(' + NativeFeedbackDropdown.MAX_FEEDBACK_CHARS + ' ' + nls.localize("characters left", "characters left") + ')'); + + $('h3').text(nls.localize("tell us why?", "Tell us why?")) + .append($charCounter) + .appendTo($form); + + this.feedbackDescriptionInput = $('textarea.feedback-description').attr({ + rows: 3, + maxlength: NativeFeedbackDropdown.MAX_FEEDBACK_CHARS, + 'aria-label': nls.localize("commentsHeader", "Comments") + }) + .text(this.feedback).attr('required', 'required') + .on('keyup', () => { + $charCounter.text('(' + (NativeFeedbackDropdown.MAX_FEEDBACK_CHARS - this.feedbackDescriptionInput.value.length) + ' ' + nls.localize("characters left", "characters left") + ')'); + this.feedbackDescriptionInput.value ? this.sendButton.removeAttribute('disabled') : this.sendButton.attr('disabled', ''); + }) + .appendTo($form).domFocus().getHTMLElement(); + + let aliasHeaderText = nls.localize('aliasHeader', "Add e-mail address"); + + this.feedbackAliasInput = $('input.feedback-alias') + .type('text') + .text(aliasHeaderText) + .attr('type', 'email') + .attr('placeholder', nls.localize('aliasPlaceholder', "Optional e-mail address")) + .value(this.alias) + .attr('aria-label', aliasHeaderText) + .appendTo($form) + .getHTMLElement(); + + let $buttons = $('div.form-buttons').appendTo($form); + + this.sendButton = this.invoke($('input.send').type('submit').attr('disabled', '').value(nls.localize('send', "Send")).appendTo($buttons), () => { + if (this.isSendingFeedback) { + return; + } + this.onSubmit().then(null, function() { }); + }); + + return { + dispose: () => { + this.feedbackForm = null; + this.feedbackDescriptionInput = null; + this.feedbackAliasInput = null; + this.smileyInput = null; + this.frownyInput = null; + } + }; + } + + private getReportIssueLink(): string { + let reportIssueLink = this.reportIssueLink; + let result = reportIssueLink + '&' + this.getReportIssuesQueryString() + '#vscode'; + return result; + } + + private getReportIssuesQueryString(): string { + + let queryString: { [key: string]: any; } = Object.create(null); + queryString['version'] = this.appVersion; + let platform: number; + switch (pal.platform) { + case pal.Platform.Windows: + platform = 3; + break; + case pal.Platform.Mac: + platform = 2; + break; + case pal.Platform.Linux: + platform = 1; + break; + default: + platform = 0; + } + + queryString['platform'] = platform; + queryString['osversion'] = os.release(); + queryString['sessionid'] = this.telemetryService.getSessionId(); + + let queryStringArray: string[] = []; + for (let p in queryString) { + queryStringArray.push(encodeURIComponent(p) + '=' + encodeURIComponent(queryString[p])); + } + + let result = queryStringArray.join('&'); + return result; + } +} + +export class FeedbackStatusbarItem implements IStatusbarItem { + + constructor( + @IInstantiationService private instantiationService: IInstantiationService, + @IContextViewService private contextViewService: IContextViewService + ) { + } + + public render(element: HTMLElement): IDisposable { + return this.instantiationService.createInstance(NativeFeedbackDropdown, element, { + contextViewProvider: this.contextViewService, + feedbackService: this.instantiationService.createInstance(NativeFeedbackService) + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/git/common/gitModel.ts b/src/vs/workbench/parts/git/common/gitModel.ts index 5403f23aca1..1f2b0b546f5 100644 --- a/src/vs/workbench/parts/git/common/gitModel.ts +++ b/src/vs/workbench/parts/git/common/gitModel.ts @@ -334,10 +334,6 @@ export class Model extends EventEmitter.EventEmitter implements Git.IModel { return this.repositoryRoot; } - public getRepositoryRoot(): string { - return this.repositoryRoot; - } - public getStatus(): Git.IStatusModel { return this.status; } diff --git a/src/vs/workbench/test/common/api/extHostLanguageFeatureCommands.test.ts b/src/vs/workbench/test/common/api/extHostLanguageFeatureCommands.test.ts deleted file mode 100644 index 3d5b78dfb75..00000000000 --- a/src/vs/workbench/test/common/api/extHostLanguageFeatureCommands.test.ts +++ /dev/null @@ -1,311 +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 * as assert from 'assert'; -import {setUnexpectedErrorHandler, errorHandler} from 'vs/base/common/errors'; -import {create} from 'vs/base/common/types'; -import URI from 'vs/base/common/uri'; -import {TPromise} from 'vs/base/common/winjs.base'; -import {PluginHostDocument} from 'vs/workbench/api/common/pluginHostDocuments'; -import * as types from 'vs/workbench/api/common/pluginHostTypes'; -import {Range as CodeEditorRange} from 'vs/editor/common/core/range'; -import * as EditorCommon from 'vs/editor/common/editorCommon'; -import {Model as EditorModel} from 'vs/editor/common/model/model'; -import threadService from './testThreadService' -import {create as createInstantiationService} from 'vs/platform/instantiation/common/instantiationService'; -import {MarkerService} from 'vs/platform/markers/common/markerService'; -import {IMarkerService} from 'vs/platform/markers/common/markers'; -import {IThreadService} from 'vs/platform/thread/common/thread'; -import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; -import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; -import {IModelService} from 'vs/editor/common/services/modelService'; -import {ExtHostLanguageFeatures, MainThreadLanguageFeatures} from 'vs/workbench/api/common/extHostLanguageFeatures'; -import {ExtHostLanguageFeatureCommands} from 'vs/workbench/api/common/extHostLanguageFeatureCommands'; -import {PluginHostCommands, MainThreadCommands} from 'vs/workbench/api/common/pluginHostCommands'; -import {PluginHostModelService} from 'vs/workbench/api/common/pluginHostDocuments'; -import {SyncDescriptor0} from 'vs/platform/instantiation/common/descriptors'; -import {LanguageSelector} from 'vs/editor/common/modes/languageSelector'; -import {OutlineRegistry, getOutlineEntries} from 'vs/editor/contrib/quickOpen/common/quickOpen'; -import {CodeLensRegistry, getCodeLensData} from 'vs/editor/contrib/codelens/common/codelens'; -import {DeclarationRegistry, getDeclarationsAtPosition} from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration'; -import {ExtraInfoRegistry, getExtraInfoAtPosition} from 'vs/editor/contrib/hover/common/hover'; -import {OccurrencesRegistry, getOccurrencesAtPosition} from 'vs/editor/contrib/wordHighlighter/common/wordHighlighter'; -import {ReferenceRegistry, findReferences} from 'vs/editor/contrib/referenceSearch/common/referenceSearch'; -import {getQuickFixes} from 'vs/editor/contrib/quickFix/common/quickFix'; -import {getNavigateToItems} from 'vs/workbench/parts/search/common/search'; -import {rename} from 'vs/editor/contrib/rename/common/rename'; -import {getParameterHints} from 'vs/editor/contrib/parameterHints/common/parameterHints'; - -const defaultSelector = { scheme: 'far' }; -const model: EditorCommon.IModel = new EditorModel( - [ - 'This is the first line', - 'This is the second line', - 'This is the third line', - ].join('\n'), - undefined, - URI.parse('far://testing/file.b')); - -let extHost: ExtHostLanguageFeatures; -let mainThread: MainThreadLanguageFeatures; -let commands: PluginHostCommands; -let disposables: vscode.Disposable[] = []; - -suite('ExtHostLanguageFeatureCommands', function() { - - suiteSetup(() => { - - let instantiationService = createInstantiationService(); - threadService.setInstantiationService(instantiationService); - instantiationService.addSingleton(IMarkerService, new MarkerService(threadService)); - instantiationService.addSingleton(IThreadService, threadService); - instantiationService.addSingleton(IModelService, { - serviceId: IModelService, - getModel():any { return model; }, - createModel():any { throw new Error(); }, - destroyModel():any { throw new Error(); }, - getModels():any { throw new Error(); }, - onModelAdded: undefined, - onModelModeChanged: undefined, - onModelRemoved: undefined - }); - instantiationService.addSingleton(IKeybindingService, { - executeCommand(id, args):any { - let handler = KeybindingsRegistry.getCommands()[id]; - return TPromise.as(instantiationService.invokeFunction(handler, args)); - } - }) - - threadService.getRemotable(PluginHostModelService)._acceptModelAdd({ - isDirty: false, - versionId: model.getVersionId(), - modeId: model.getModeId(), - url: model.getAssociatedResource(), - value: { - EOL: model.getEOL(), - lines: model.getValue().split(model.getEOL()), - BOM: '', - length: -1 - }, - }); - - threadService.getRemotable(MainThreadCommands); - commands = threadService.getRemotable(PluginHostCommands); - new ExtHostLanguageFeatureCommands(commands); - mainThread = threadService.getRemotable(MainThreadLanguageFeatures); - extHost = threadService.getRemotable(ExtHostLanguageFeatures); - }); - - suiteTeardown(() => { - model.dispose(); - }); - - teardown(function(done) { - while (disposables.length) { - disposables.pop().dispose(); - } - threadService.sync() - .then(() => done(), err => done(err)); - }); - - // --- workspace symbols - - test('WorkspaceSymbols, invalid arguments', function(done) { - let promises = [ - commands.executeCommand('vscode.executeWorkspaceSymbolProvider'), - commands.executeCommand('vscode.executeWorkspaceSymbolProvider', null), - commands.executeCommand('vscode.executeWorkspaceSymbolProvider', undefined), - commands.executeCommand('vscode.executeWorkspaceSymbolProvider', true) - ]; - - threadService.sync().then(() => { - TPromise.join(promises).then(undefined, (err: any[]) => { - assert.equal(err.length, 4); - done(); - return []; - }); - }); - }); - - test('WorkspaceSymbols, ⇔ back and forth', function(done) { - - disposables.push(extHost.registerWorkspaceSymbolProvider({ - provideWorkspaceSymbols(query): any { - return [ - new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first')), - new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/second')) - ] - } - })); - - disposables.push(extHost.registerWorkspaceSymbolProvider({ - provideWorkspaceSymbols(query): any { - return [ - new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first')) - ] - } - })); - - threadService.sync().then(() => { - commands.executeCommand('vscode.executeWorkspaceSymbolProvider', 'testing').then(value => { - - for (let info of value) { - assert.ok(info instanceof types.SymbolInformation); - assert.equal(info.name, 'testing'); - assert.equal(info.kind, types.SymbolKind.Array); - } - assert.equal(value.length, 3); - done(); - }); - }); - }); - - - // --- definition - - test('Definition, invalid arguments', function(done) { - let promises = [ - commands.executeCommand('vscode.executeDefinitionProvider'), - commands.executeCommand('vscode.executeDefinitionProvider', null), - commands.executeCommand('vscode.executeDefinitionProvider', undefined), - commands.executeCommand('vscode.executeDefinitionProvider', true, false) - ]; - - threadService.sync().then(() => { - TPromise.join(promises).then(undefined, (err: any[]) => { - assert.equal(err.length, 4); - done(); - return []; - }); - }); - }); - - test('Definition, ⇔ back and forth', function(done) { - - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { - provideDefinition(doc: any): any { - return new types.Location(doc.uri, new types.Range(0, 0, 0, 0)); - } - })); - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { - provideDefinition(doc: any): any { - return [ - new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), - new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), - new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), - ] - } - })); - - threadService.sync().then(() => { - commands.executeCommand('vscode.executeDefinitionProvider', model.getAssociatedResource(), new types.Position(0, 0)).then(values => { - assert.equal(values.length, 4); - done(); - }); - }); - }); - - // --- outline - - test('Outline, back and forth', function(done) { - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { - provideDocumentSymbols(): any { - return [ - new types.SymbolInformation('testing1', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0)), - new types.SymbolInformation('testing2', types.SymbolKind.Enum, new types.Range(0, 1, 0, 3)), - ] - } - })); - - threadService.sync().then(() => { - commands.executeCommand('vscode.executeDocumentSymbolProvider', model.getAssociatedResource()).then(values => { - assert.equal(values.length, 2); - let [first, second] = values; - assert.equal(first.name, 'testing2'); - assert.equal(second.name, 'testing1'); - done(); - }); - }); - }); - - // --- suggest - - test('Suggest, back and forth', function(done) { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { - provideCompletionItems(doc, pos): any { - let a = new types.CompletionItem('item1'); - let b = new types.CompletionItem('item2'); - b.textEdit = types.TextEdit.replace(new types.Range(0, 0, 0, 4), 'foo'); - return [a, b]; - } - }, [])); - - threadService.sync().then(() => { - commands.executeCommand('vscode.executeCompletionItemProvider', model.getAssociatedResource(), new types.Position(0, 0)).then(values => { - assert.equal(values.length, 2); - let [first, second] = values; - assert.equal(first.label, 'item1'); - assert.equal(first.textEdit.newText, 'item1'); - assert.equal(first.textEdit.range.start.line, 0); - assert.equal(first.textEdit.range.start.character, 0); - assert.equal(first.textEdit.range.end.line, 0); - assert.equal(first.textEdit.range.end.character, 0); - - assert.equal(second.label, 'item2'); - assert.equal(second.textEdit.newText, 'foo'); - assert.equal(second.textEdit.range.start.line, 0); - assert.equal(second.textEdit.range.start.character, 0); - assert.equal(second.textEdit.range.end.line, 0); - assert.equal(second.textEdit.range.end.character, 4); - done(); - }); - }); - }); - - // --- quickfix - - test('QuickFix, back and forth', function(done) { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { - provideCodeActions(): any { - return [{ command: 'testing', title: 'Title', arguments: [1, 2, true] }]; - } - })); - - threadService.sync().then(() => { - commands.executeCommand('vscode.executeCodeActionProvider', model.getAssociatedResource(), new types.Range(0, 0, 1, 1)).then(value => { - assert.equal(value.length, 1); - let [first] = value; - assert.equal(first.title, 'Title'); - assert.equal(first.command, 'testing'); - assert.deepEqual(first.arguments, [1, 2, true]); - done(); - }); - }); - }); - - // --- code lens - - test('CodeLens, back and forth', function(done) { - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { - provideCodeLenses(): any { - return [new types.CodeLens(new types.Range(0, 0, 1, 1), { title: 'Title', command: 'cmd', arguments: [1, 2, true] })]; - } - })); - - threadService.sync().then(() => { - commands.executeCommand('vscode.executeCodeLensProvider', model.getAssociatedResource()).then(value => { - assert.equal(value.length, 1); - let [first] = value; - - assert.equal(first.command.title, 'Title'); - assert.equal(first.command.command, 'cmd'); - assert.deepEqual(first.command.arguments, [1, 2, true]); - done(); - }); - }); - }); -}); \ No newline at end of file diff --git a/src/vs/workbench/workbench.main.js b/src/vs/workbench/workbench.main.js index 4544520d105..7c1762cdec9 100644 --- a/src/vs/workbench/workbench.main.js +++ b/src/vs/workbench/workbench.main.js @@ -71,6 +71,8 @@ define([ 'vs/workbench/parts/themes/electron-browser/themes.contribution', + 'vs/workbench/parts/feedback/electron-browser/feedback.contribution', + 'vs/workbench/electron-browser/main.contribution', 'vs/workbench/electron-browser/main'