Merge branch 'master' into ben/electron

This commit is contained in:
Benjamin Pasero 2016-01-06 09:33:41 +01:00
commit 945d5d57e1
17 changed files with 123 additions and 666 deletions

View file

@ -1,6 +1,6 @@
{
"account": "monacobuild",
"container": "debuggers",
"zip": "d98733b/node-debug.zip",
"zip": "2985e20/node-debug.zip",
"output": ""
}

View file

@ -26,7 +26,6 @@ define([
'vs/editor/contrib/parameterHints/browser/parameterHints',
'vs/editor/contrib/quickFix/browser/quickFix',
'vs/editor/contrib/referenceSearch/browser/referenceSearch',
'vs/editor/contrib/rename/browser/rename',
'vs/editor/contrib/rename/browser/rename2',
'vs/editor/contrib/smartSelect/common/smartSelect',
'vs/editor/contrib/smartSelect/common/jumpToBracket',

View file

@ -35,7 +35,6 @@ export class AbstractMode<W extends AbstractModeWorker> implements Modes.IMode {
// adapters start
public autoValidateDelay:number;
public occurrencesSupport:Modes.IOccurrencesSupport;
public suggestSupport:Modes.ISuggestSupport;
public inplaceReplaceSupport:Modes.IInplaceReplaceSupport;
public diffSupport:Modes.IDiffSupport;
@ -63,7 +62,6 @@ export class AbstractMode<W extends AbstractModeWorker> implements Modes.IMode {
this._options = null;
this.autoValidateDelay = 500;
this.occurrencesSupport = this;
this.suggestSupport = this;
this.inplaceReplaceSupport = this;
this.diffSupport = this;
@ -194,11 +192,6 @@ export class AbstractMode<W extends AbstractModeWorker> implements Modes.IMode {
return true;
}
static $findOccurrences = OneWorkerAttr(AbstractMode, AbstractMode.prototype.findOccurrences);
public findOccurrences(resource:URI, position:EditorCommon.IPosition, strict:boolean = false): TPromise<Modes.IOccurence[]> {
return this._worker((w) => w.findOccurrences(resource, position, strict));
}
static $navigateValueSet = OneWorkerAttr(AbstractMode, AbstractMode.prototype.navigateValueSet);
public navigateValueSet(resource:URI, position:EditorCommon.IRange, up:boolean):TPromise<Modes.IInplaceReplaceSupportResult> {
return this._worker((w) => w.inplaceReplaceSupport.navigateValueSet(resource, position, up));

View file

@ -177,30 +177,6 @@ export class AbstractModeWorker {
return AbstractModeWorker.filter;
}
// ---- occurrences ---------------------------------------------------------------
public findOccurrences(resource:URI, position:EditorCommon.IPosition, strict?:boolean):TPromise<Modes.IOccurence[]> {
var model = this.resourceService.get(resource),
wordAtPosition = model.getWordAtPosition(position),
currentWord = (wordAtPosition ? wordAtPosition.word : ''),
result:Modes.IOccurence[] = [];
var words = model.getAllWordsWithRange(),
upperBound = Math.min(1000, words.length); // Limit find occurences to 1000 occurences
for(var i = 0; i < upperBound; i++) {
if(words[i].text === currentWord) {
result.push({
range: words[i].range,
kind: 'text'
});
}
}
return TPromise.as(result);
}
// ---- diff --------------------------------------------------------------------------
public computeDiff(original:URI, modified:URI, ignoreTrimWhitespace:boolean):TPromise<EditorCommon.ILineChange[]> {

View file

@ -22,6 +22,7 @@ import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
import {INullService} from 'vs/platform/instantiation/common/instantiation';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
import {Range} from 'vs/editor/common/core/range';
import {OccurrencesRegistry} from 'vs/editor/contrib/wordHighlighter/common/wordHighlighter';
/**
* The Find controller will survive an editor.setModel(..) call
@ -459,9 +460,18 @@ class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchAction {
class SelectHighlightsAction extends EditorAction {
static ID = 'editor.action.selectHighlights';
static COMPAT_ID = 'editor.action.changeAll';
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
let behaviour = Behaviour.WidgetFocus;
if (descriptor.id === SelectHighlightsAction.COMPAT_ID) {
behaviour |= Behaviour.ShowInContextMenu;
}
super(descriptor, editor, behaviour);
}
public getGroupId(): string {
return '2_change/1_changeAll';
}
public run(): TPromise<boolean> {
@ -484,20 +494,17 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
static ID = 'editor.contrib.selectionHighlighter';
private editor:EditorCommon.ICommonCodeEditor;
private model:EditorCommon.IModel;
private decorations:string[];
private toDispose:Lifecycle.IDisposable[];
constructor(editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
this.editor = editor;
this.model = this.editor.getModel();
this.decorations = [];
this.toDispose = [];
this.toDispose.push(editor.addListener2(EditorCommon.EventType.CursorPositionChanged, _ => this._update()));
this.toDispose.push(editor.addListener2(EditorCommon.EventType.ModelChanged, (e) => {
this.removeDecorations();
this.model = this.editor.getModel();
}));
this.toDispose.push(FindController.getFindController(editor).onStateChanged(() => this._update()));
}
@ -522,10 +529,15 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
this.removeDecorations();
return;
}
let model = this.editor.getModel();
if (r.nextMatch) {
// This is an empty selection
this.removeDecorations();
return;
if (OccurrencesRegistry.has(model)) {
// Do not interfere with semantic word highlighting in the no selection case
this.removeDecorations();
return;
}
}
if (/^[ \t]+$/.test(r.searchText)) {
// whitespace only selection
@ -538,7 +550,7 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
return;
}
let allMatches = this.editor.getModel().findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord);
let allMatches = model.findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord);
allMatches.sort(Range.compareRangesUsingStarts);
let selections = this.editor.getSelections();
@ -594,6 +606,11 @@ CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighl
context: ContextKey.EditorFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L
}));
// register SelectHighlightsAction again to replace the now removed Change All action
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighlightsAction, SelectHighlightsAction.COMPAT_ID, nls.localize('changeAll.label', "Change All Occurrences"), {
context: ContextKey.EditorTextFocus,
primary: KeyMod.CtrlCmd | KeyCode.F2
}));
var CONTEXT_FIND_WIDGET_VISIBLE = 'findWidgetVisible';

View file

@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-editor .linked-editing-placeholder {
}
.monaco-editor .word-level-progress {
font-style: italic;

View file

@ -1,328 +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 'vs/css!./rename';
import {TPromise} from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions';
import {EditorAction, Behaviour} from 'vs/editor/common/editorAction';
import EditorCommon = require('vs/editor/common/editorCommon');
import Modes = require('vs/editor/common/modes');
import Severity from 'vs/base/common/severity';
import EventEmitter = require('vs/base/common/eventEmitter');
import {Range} from 'vs/editor/common/core/range';
import {IMessageService} from 'vs/platform/message/common/message';
import {IProgressService, IProgressRunner} from 'vs/platform/progress/common/progress';
import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
class LinkedEditingController {
private editor: EditorCommon.ICommonCodeEditor;
private listenersToRemove:EventEmitter.ListenerUnbind[];
private decorations:string[];
private isDisposed: boolean;
private _onDispose: () => void;
constructor(editor: EditorCommon.ICommonCodeEditor, selections: EditorCommon.ISelection[], ranges: EditorCommon.IRange[], onDispose: () => void) {
this._onDispose = onDispose;
this.editor = editor;
this.isDisposed = false;
// Decorate editing ranges
this.editor.changeDecorations((changeAccessor:EditorCommon.IModelDecorationsChangeAccessor) => {
var newDecorations: EditorCommon.IModelDeltaDecoration[] = [];
for (var i = 0, len = selections.length; i < len; i++) {
var className = 'linked-editing-placeholder';
newDecorations.push({
range: ranges[i],
options: {
className: className
}
});
}
this.decorations = changeAccessor.deltaDecorations([], newDecorations);
});
// Begin linked editing (multi-cursor)
this.editor.setSelections(selections);
this.listenersToRemove = [];
this.listenersToRemove.push(this.editor.addListener(EditorCommon.EventType.CursorPositionChanged, (e:EditorCommon.ICursorPositionChangedEvent) => {
if (this.isDisposed) {
return;
}
var cursorCount = 1 + e.secondaryPositions.length;
if (cursorCount !== this.decorations.length) {
this.dispose();
}
}));
}
public onEnterOrEscape(): boolean {
if (this.isDisposed) {
return;
}
// Basically cancel multi-cursor
this.editor.setSelection(this.editor.getSelection());
this.dispose();
return true;
}
public dispose(): void {
if (this.isDisposed) {
return;
}
this.isDisposed = true;
this._onDispose();
this.decorations = this.editor.deltaDecorations(this.decorations, []);
this.listenersToRemove.forEach((element) => {
element();
});
this.listenersToRemove = [];
}
}
class LocalProgressService implements IProgressService {
public serviceId = IProgressService;
constructor(private _editor:EditorCommon.ICommonCodeEditor) {
//
}
showWhile<T>(promise:TPromise<T>, delay?:number):TPromise<T> {
var decoration: string,
delayHandle: number;
delayHandle = setTimeout(() => {
decoration = this._addDecoration();
}, delay || 0);
return promise.then((value) => {
clearTimeout(delayHandle);
this._removeDecoration(decoration);
return value;
}, (err) => {
clearTimeout(delayHandle);
this._removeDecoration(decoration);
throw err;
});
}
private _addDecoration():string {
var position = this._editor.getPosition(),
word = this._editor.getModel().getWordAtPosition(position),
decorationId:string;
var decorations = this._editor.deltaDecorations([], [{
range: {
startLineNumber: position.lineNumber,
startColumn: word.startColumn,
endLineNumber: position.lineNumber,
endColumn: word.endColumn
},
options: {
inlineClassName: 'word-level-progress'
}
}]);
return decorations[0];
}
private _removeDecoration(decorationId:string):void {
if(decorationId) {
this._editor.changeDecorations((accessor) => {
accessor.deltaDecorations([decorationId], []);
});
}
}
public show(...args:any[]):IProgressRunner {
throw new Error('not implemented');
}
}
export class ChangeAllAction extends EditorAction {
public static ID = 'editor.action.changeAll';
private _idPool:number;
private _messageService:IMessageService;
private _progressService: IProgressService;
private _currentController: LinkedEditingController;
private _changeAllMode: IKeybindingContextKey<boolean>;
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @IMessageService messageService: IMessageService, @IKeybindingService keybindingService: IKeybindingService) {
super(descriptor, editor, Behaviour.WidgetFocus | Behaviour.Writeable | Behaviour.ShowInContextMenu | Behaviour.UpdateOnCursorPositionChange);
this._idPool = 0;
this._messageService = messageService;
this._progressService = new LocalProgressService(this.editor);
this._currentController = null;
this._changeAllMode = keybindingService.createKey(CONTEXT_CHANGE_ALL_MODE, false);
}
public getGroupId(): string {
return '2_change/1_changeAll';
}
public isSupported():boolean {
var mode = this.editor.getModel().getMode();
return !!mode && !!mode.occurrencesSupport && super.isSupported();
}
public computeInfos(editor:EditorCommon.ICommonCodeEditor):TPromise<Modes.IOccurence[]> {
var selection = editor.getSelection();
var position = selection.getStartPosition();
var model = editor.getModel();
return this.editor.getModel().getMode().occurrencesSupport.findOccurrences(model.getAssociatedResource(), position);
}
public run():TPromise<boolean> {
var myId = ++this._idPool,
state = this.editor.captureState(EditorCommon.CodeEditorStateFlag.Position, EditorCommon.CodeEditorStateFlag.Value),
capturedSelection = this.editor.getSelection(),
infoPromise = this.computeInfos(this.editor);
if(this._progressService) {
this._progressService.showWhile(infoPromise, 500);
}
return infoPromise.then((infos:Modes.IOccurence[]) => {
if(myId !== this._idPool) {
return;
}
if(!state.validate(this.editor)) {
return;
}
if(infos.length === 0) {
return;
}
var ranges = infos.map((info) => {
return info.range;
});
this._beginLinkedEditing(ranges, capturedSelection);
return true;
}, (e) => {
this._messageService.show(Severity.Info, e);
});
}
private _indexOf(ranges:EditorCommon.IRange[], lineNumber: number, column: number): number {
var pos = {
lineNumber: lineNumber,
column: column
};
for (var i = 0; i < ranges.length; i++) {
if (ranges[i].startLineNumber !== lineNumber) {
// Only consider ranges that start on the same line as position
continue;
}
if (Range.containsPosition(ranges[i], pos)) {
return i;
}
}
return -1;
}
private _beginLinkedEditing(ranges: EditorCommon.IRange[], capturedSelection: EditorCommon.IEditorSelection): void {
if (this._currentController) {
this._currentController.dispose();
this._currentController = null;
}
var editorSelection = this.editor.getSelection();
// Try to find a suitable range for the current editor position
var foundRangeIndex = this._indexOf(ranges, editorSelection.positionLineNumber, editorSelection.positionColumn);
if (foundRangeIndex === -1) {
// Current editor position is outside of one of these ranges, try again with the original editor position
editorSelection = capturedSelection;
foundRangeIndex = this._indexOf(ranges, editorSelection.positionLineNumber, editorSelection.positionColumn);
if (foundRangeIndex === -1) {
// These ranges are bogus!
return;
}
}
var hasSelectionInFoundRange = false;
if (!editorSelection.isEmpty()) {
if (Range.containsPosition(ranges[foundRangeIndex], { lineNumber: editorSelection.selectionStartLineNumber, column: editorSelection.selectionStartColumn})) {
hasSelectionInFoundRange = true;
}
}
var deltaColumnForPosition: number, deltaColumnForStartSelection: number;
if (hasSelectionInFoundRange) {
deltaColumnForPosition = editorSelection.positionColumn - ranges[foundRangeIndex].startColumn;
deltaColumnForStartSelection = editorSelection.selectionStartColumn - ranges[foundRangeIndex].startColumn;
} else {
deltaColumnForPosition = ranges[foundRangeIndex].endColumn - ranges[foundRangeIndex].startColumn;
deltaColumnForStartSelection = 0;
}
var newEditorSelections: EditorCommon.ISelection[] = [];
newEditorSelections.push({
selectionStartLineNumber: ranges[foundRangeIndex].startLineNumber,
selectionStartColumn: ranges[foundRangeIndex].startColumn + deltaColumnForStartSelection,
positionLineNumber: ranges[foundRangeIndex].startLineNumber,
positionColumn: ranges[foundRangeIndex].startColumn + deltaColumnForPosition,
});
for (var i = 0; i < ranges.length; i++) {
if (i !== foundRangeIndex) {
newEditorSelections.push({
selectionStartLineNumber: ranges[i].startLineNumber,
selectionStartColumn: ranges[i].startColumn + deltaColumnForStartSelection,
positionLineNumber: ranges[i].startLineNumber,
positionColumn: ranges[i].startColumn + deltaColumnForPosition,
});
}
}
this._changeAllMode.set(true);
this._currentController = new LinkedEditingController(this.editor, newEditorSelections, ranges, () => {
this._changeAllMode.reset();
});
}
public leaveChangeAllMode(): void {
if (this._currentController) {
this._currentController.onEnterOrEscape();
this._currentController = null;
}
}
}
var CONTEXT_CHANGE_ALL_MODE = 'inChangeAllMode';
var weight = CommonEditorRegistry.commandWeight(30);
// register actions
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(ChangeAllAction, ChangeAllAction.ID, nls.localize('changeAll.label', "Change All Occurrences"), {
context: ContextKey.EditorTextFocus,
primary: KeyMod.CtrlCmd | KeyCode.F2
}));
CommonEditorRegistry.registerEditorCommand('leaveChangeAllMode', weight, { primary: KeyCode.Enter, secondary: [KeyCode.Escape] }, true, CONTEXT_CHANGE_ALL_MODE,(ctx, editor, args) => {
var action = <ChangeAllAction>editor.getAction(ChangeAllAction.ID);
action.leaveChangeAllMode();
});

View file

@ -285,6 +285,7 @@ export class CSSMode extends AbstractMode<cssWorker.CSSWorker> {
public referenceSupport: Modes.IReferenceSupport;
public logicalSelectionSupport: Modes.ILogicalSelectionSupport;
public extraInfoSupport:Modes.IExtraInfoSupport;
public occurrencesSupport:Modes.IOccurrencesSupport;
public outlineSupport: Modes.IOutlineSupport;
public declarationSupport: Modes.IDeclarationSupport;
public suggestSupport: Modes.ISuggestSupport;
@ -305,6 +306,7 @@ export class CSSMode extends AbstractMode<cssWorker.CSSWorker> {
{ tokenType: 'punctuation.bracket.css', open: '{', close: '}', isElectric: true }
] });
this.occurrencesSupport = this;
this.extraInfoSupport = this;
this.referenceSupport = new supports.ReferenceSupport(this, {
tokens: [cssTokenTypes.TOKEN_PROPERTY + '.css', cssTokenTypes.TOKEN_VALUE + '.css', cssTokenTypes.TOKEN_SELECTOR_TAG + '.css'],
@ -344,6 +346,11 @@ export class CSSMode extends AbstractMode<cssWorker.CSSWorker> {
return createAsyncDescriptor2('vs/languages/css/common/cssWorker', 'CSSWorker');
}
static $findOccurrences = OneWorkerAttr(CSSMode, CSSMode.prototype.findOccurrences);
public findOccurrences(resource:URI, position:EditorCommon.IPosition, strict:boolean = false): WinJS.TPromise<Modes.IOccurence[]> {
return this._worker((w) => w.findOccurrences(resource, position, strict));
}
static $findDeclaration = OneWorkerAttr(CSSMode, CSSMode.prototype.findDeclaration);
public findDeclaration(resource:URI, position:EditorCommon.IPosition):WinJS.TPromise<Modes.IReference> {
return this._worker((w) => w.findDeclaration(resource, position));

View file

@ -277,6 +277,7 @@ export class HTMLMode<W extends htmlWorker.HTMLWorker> extends AbstractMode<W> i
public characterPairSupport: Modes.ICharacterPairSupport;
public extraInfoSupport:Modes.IExtraInfoSupport;
public occurrencesSupport:Modes.IOccurrencesSupport;
public referenceSupport: Modes.IReferenceSupport;
public logicalSelectionSupport: Modes.ILogicalSelectionSupport;
public formattingSupport: Modes.IFormattingSupport;
@ -311,6 +312,7 @@ export class HTMLMode<W extends htmlWorker.HTMLWorker> extends AbstractMode<W> i
this.formattingSupport = this;
this.extraInfoSupport = this;
this.occurrencesSupport = this;
this.referenceSupport = new supports.ReferenceSupport(this, {
tokens: ['invalid'],
findReferences: (resource, position, includeDeclaration) => this.findReferences(resource, position, includeDeclaration)});

View file

@ -132,6 +132,7 @@ export class TypeScriptMode<W extends typescriptWorker.TypeScriptWorker2> extend
public characterPairSupport: Modes.ICharacterPairSupport;
public referenceSupport: Modes.IReferenceSupport;
public extraInfoSupport:Modes.IExtraInfoSupport;
public occurrencesSupport:Modes.IOccurrencesSupport;
public quickFixSupport:Modes.IQuickFixSupport;
public logicalSelectionSupport:Modes.ILogicalSelectionSupport;
public parameterHintsSupport:Modes.IParameterHintsSupport;
@ -185,6 +186,7 @@ export class TypeScriptMode<W extends typescriptWorker.TypeScriptWorker2> extend
}
this.extraInfoSupport = this;
this.occurrencesSupport = this;
this.formattingSupport = this;
this.quickFixSupport = this;
this.logicalSelectionSupport = this;

View file

@ -141,7 +141,6 @@ export interface IEnvironment {
};
sendASmile: {
submitUrl: string,
reportIssueUrl: string,
requestFeatureUrl: string
};

View file

@ -51,7 +51,6 @@ export interface IProductConfiguration {
asimovKey: string;
};
sendASmile: {
submitUrl: string,
reportIssueUrl: string,
requestFeatureUrl: string
};

View file

@ -123,7 +123,6 @@ export interface IWindowConfiguration extends env.ICommandLineArguments {
asimovKey: string;
},
sendASmile: {
submitUrl: string,
reportIssueUrl: string,
requestFeatureUrl: string
}

View file

@ -15,17 +15,17 @@ import {Dropdown} from 'vs/base/browser/ui/dropdown/dropdown';
import {IXHRResponse} from 'vs/base/common/http';
import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
import {IWorkspaceContextService} from 'vs/workbench/services/workspace/common/contextService';
const STATUS_TIMEOUT = 500;
export interface IFeedback {
feedback: string;
alias: string;
sentiment: number;
}
export interface IFeedbackService {
submitFeedback(feedback: IFeedback): Promise;
submitFeedback(feedback: IFeedback): void;
}
export interface IFeedbackDropdownOptions {
@ -40,9 +40,9 @@ enum FormEvent {
};
export class FeedbackDropdown extends Dropdown {
protected static MAX_FEEDBACK_CHARS: number = 140;
protected feedback: string;
protected alias: string;
protected sentiment: number;
protected aliasEnabled: boolean;
protected isSendingFeedback: boolean;
@ -52,15 +52,18 @@ export class FeedbackDropdown extends Dropdown {
protected feedbackForm: HTMLFormElement;
protected feedbackDescriptionInput: HTMLTextAreaElement;
protected feedbackAliasInput: HTMLInputElement;
protected smileyInput: Builder;
protected frownyInput: Builder;
protected sendButton: Builder;
protected requestFeatureLink: string;
protected reportIssueLink: string;
constructor(
container: HTMLElement,
options: IFeedbackDropdownOptions,
@ITelemetryService protected telemetryService: ITelemetryService
@ITelemetryService protected telemetryService: ITelemetryService,
@IWorkspaceContextService private contextService: IWorkspaceContextService
) {
super(container, {
contextViewProvider: options.contextViewProvider,
@ -76,18 +79,19 @@ export class FeedbackDropdown extends Dropdown {
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;
const env = contextService.getConfiguration().env;
this.reportIssueLink = env.sendASmile.reportIssueUrl;
this.requestFeatureLink = env.sendASmile.requestFeatureUrl;
}
public renderContents(container: HTMLElement): IDisposable {
@ -106,10 +110,12 @@ export class FeedbackDropdown extends Dropdown {
this.hide();
}).appendTo($form);
$('h3').text(nls.localize("sentiment", "How was your experience?")).appendTo($form);
let $content = $('div.content').appendTo($form);
let $feedbackSentiment = $('div.feedback-sentiment').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',
@ -134,44 +140,49 @@ export class FeedbackDropdown extends Dropdown {
this.frownyInput.addClass('checked').attr('aria-checked', 'true');
}
$('h3').text(nls.localize("commentsHeader", "Comments")).appendTo($form);
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.reportIssueLink).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('(' + FeedbackDropdown.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 = <HTMLTextAreaElement>$('textarea.feedback-description').attr({
rows: 8,
rows: 3,
maxlength: FeedbackDropdown.MAX_FEEDBACK_CHARS,
'aria-label': nls.localize("commentsHeader", "Comments")
})
.text(this.feedback).attr('required', 'required')
.on('keyup', () => {
$charCounter.text('(' + (FeedbackDropdown.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 = <HTMLInputElement>$('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() { });
this.onSubmit();
});
return {
dispose: () => {
this.feedbackForm = null;
this.feedbackDescriptionInput = null;
this.feedbackAliasInput = null;
this.smileyInput = null;
this.frownyInput = null;
}
@ -211,10 +222,6 @@ export class FeedbackDropdown extends Dropdown {
this.feedback = this.feedbackDescriptionInput.value;
}
if (this.feedbackAliasInput) {
this.alias = this.feedbackAliasInput.value;
}
if (this.autoHideTimeout) {
clearTimeout(this.autoHideTimeout);
this.autoHideTimeout = null;
@ -232,24 +239,19 @@ export class FeedbackDropdown extends Dropdown {
}
}
protected onSubmit(): Promise {
protected onSubmit(): void {
if ((this.feedbackForm.checkValidity && !this.feedbackForm.checkValidity())) {
return Promise.as(null);
return;
}
this.changeFormStatus(FormEvent.SENDING);
return this.feedbackService.submitFeedback({
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));
});
this.changeFormStatus(FormEvent.SENT);
}
@ -282,7 +284,6 @@ export class FeedbackDropdown extends Dropdown {
protected resetForm(): void {
this.feedbackDescriptionInput ? this.feedbackDescriptionInput.value = '' : null;
this.feedbackAliasInput ? this.feedbackAliasInput.value = '' : null;
this.sentiment = 1;
this.aliasEnabled = false;
}

View file

@ -7,7 +7,7 @@
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';
import {FeedbackStatusbarItem} from 'vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem';
// Register Statusbar item
if (Flags.enableSendASmile) {

View file

@ -1,251 +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 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 = <HTMLFormElement>$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 = <HTMLTextAreaElement>$('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 = <HTMLInputElement>$('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)
});
}
}

View file

@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* 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 {IStatusbarItem} from 'vs/workbench/browser/parts/statusbar/statusbar';
import {FeedbackDropdown, IFeedback, IFeedbackService} 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 shell = require('shell');
class TwitterFeedbackService implements IFeedbackService {
private serviceUrl: string;
private static TWITTER_URL: string = 'https://twitter.com/intent/tweet';
public submitFeedback(feedback: IFeedback): void {
var queryString = `?${feedback.sentiment === 1 ? 'hashtags=LoveVSCode&' : null}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${feedback.feedback}&tw_p=tweetbutton&via=code`;
var url = TwitterFeedbackService.TWITTER_URL + queryString;
shell.openExternal(url);
}
}
export class FeedbackStatusbarItem implements IStatusbarItem {
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@IContextViewService private contextViewService: IContextViewService
) {
}
public render(element: HTMLElement): IDisposable {
return this.instantiationService.createInstance(FeedbackDropdown, element, {
contextViewProvider: this.contextViewService,
feedbackService: this.instantiationService.createInstance(TwitterFeedbackService)
});
}
}