Merge branch 'master' into joh/commandsAsApi
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"comments": {
|
||||
"lineComment": ";",
|
||||
"blockComment": [ "(comment", ")" ]
|
||||
"lineComment": ";"
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"account": "monacobuild",
|
||||
"container": "debuggers",
|
||||
"zip": "32575e3/node-debug.zip",
|
||||
"zip": "3efab9b/node-debug.zip",
|
||||
"output": ""
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"contributes": {
|
||||
"languages": [{
|
||||
"id": "ruby",
|
||||
"extensions": [ ".rb", ".rbx", ".rjs", ".gemspec", ".pp" ],
|
||||
"extensions": [ ".rb", ".rbx", ".rjs", ".gemspec", ".pp", ".rake" ],
|
||||
"filenames": [ "rakefile", "gemfile" ],
|
||||
"aliases": [ "Ruby", "rb" ],
|
||||
"configuration": "./ruby.configuration.json"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"comments": {
|
||||
"lineComment": "'",
|
||||
"blockComment": [ "/*", "*/" ]
|
||||
"lineComment": "'"
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"id": "xml",
|
||||
"extensions": [
|
||||
".ascx",
|
||||
".atom",
|
||||
".axml",
|
||||
".bpmn",
|
||||
".config",
|
||||
|
|
5
npm-shrinkwrap.json
generated
|
@ -414,6 +414,11 @@
|
|||
"version": "4.3.6",
|
||||
"from": "semver@>=4.2.0 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz"
|
||||
},
|
||||
"vscode-debugprotocol": {
|
||||
"version": "1.0.0",
|
||||
"from": "vscode-debugprotocol@1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.0.0.tgz"
|
||||
},
|
||||
"vscode-textmate": {
|
||||
"version": "1.0.9",
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"iconv-lite": "^0.4.13",
|
||||
"sax": "^1.1.1",
|
||||
"semver": "^4.2.0",
|
||||
"vscode-debugprotocol": "^1.0.0",
|
||||
"vscode-textmate": "^1.0.9",
|
||||
"native-keymap": "^0.0.2",
|
||||
"winreg": "0.0.12",
|
||||
|
|
|
@ -36,13 +36,11 @@ export class ArrayIterator<T> implements IIterator<T> {
|
|||
|
||||
export class MappedIterator<T, R> implements IIterator<R> {
|
||||
|
||||
constructor(private iterator: IIterator<T>, private fn: (item:T)=>R) {
|
||||
constructor(protected iterator: IIterator<T>, protected fn: (item:T)=>R) {
|
||||
// noop
|
||||
}
|
||||
|
||||
public next(): R {
|
||||
return this.fn(this.iterator.next());
|
||||
}
|
||||
next() { return this.fn(this.iterator.next()); }
|
||||
}
|
||||
|
||||
export interface INavigator<T> extends IIterator<T> {
|
||||
|
@ -52,3 +50,16 @@ export interface INavigator<T> extends IIterator<T> {
|
|||
first(): T;
|
||||
last(): T;
|
||||
}
|
||||
|
||||
export class MappedNavigator<T, R> extends MappedIterator<T, R> implements INavigator<R> {
|
||||
|
||||
constructor(protected navigator: INavigator<T>, fn: (item:T)=>R) {
|
||||
super(navigator, fn);
|
||||
}
|
||||
|
||||
current() { return this.fn(this.navigator.current()); }
|
||||
previous() { return this.fn(this.navigator.previous()); }
|
||||
parent() { return this.fn(this.navigator.parent()); }
|
||||
first() { return this.fn(this.navigator.first()); }
|
||||
last() { return this.fn(this.navigator.last()); }
|
||||
}
|
||||
|
|
|
@ -131,7 +131,11 @@ export class Server {
|
|||
if (!method) {
|
||||
promise = Promise.wrapError(new Error(`${ request.name } is not a valid method on ${ request.serviceName }`));
|
||||
} else {
|
||||
promise = method.call(service, ...request.args)
|
||||
try {
|
||||
promise = method.call(service, ...request.args);
|
||||
} catch (err) {
|
||||
promise = Promise.wrapError(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Promise.is(promise)) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import Events = require('vs/base/common/eventEmitter');
|
|||
import Model = require('vs/base/parts/tree/common/treeModel');
|
||||
import View = require('./treeView');
|
||||
import _ = require('vs/base/parts/tree/common/tree');
|
||||
import { INavigator, MappedNavigator } from 'vs/base/common/iterator';
|
||||
|
||||
export class TreeContext implements _.ITreeContext {
|
||||
|
||||
|
@ -165,6 +166,10 @@ export class Tree extends Events.EventEmitter implements _.ITree {
|
|||
this.view.setScrollPosition(pos);
|
||||
}
|
||||
|
||||
getContentHeight(): number {
|
||||
return this.view.getTotalHeight();
|
||||
}
|
||||
|
||||
public setHighlight(element?:any, eventPayload?:any):void {
|
||||
this.model.setHighlight(element, eventPayload);
|
||||
}
|
||||
|
@ -306,6 +311,10 @@ export class Tree extends Events.EventEmitter implements _.ITree {
|
|||
return this.view.withFakeRow(fn);
|
||||
}
|
||||
|
||||
getNavigator(): INavigator<any> {
|
||||
return new MappedNavigator(this.model.getNavigator(), i => i && i.getElement());
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.model !== null) {
|
||||
this.model.dispose();
|
||||
|
|
|
@ -9,6 +9,7 @@ import Touch = require('vs/base/browser/touch');
|
|||
import Events = require('vs/base/common/eventEmitter');
|
||||
import Mouse = require('vs/base/browser/mouseEvent');
|
||||
import Keyboard = require('vs/base/browser/keyboardEvent');
|
||||
import { INavigator } from 'vs/base/common/iterator';
|
||||
|
||||
export interface ITree extends Events.IEventEmitter {
|
||||
|
||||
|
@ -132,6 +133,11 @@ export interface ITree extends Events.IEventEmitter {
|
|||
*/
|
||||
setScrollPosition(pos: number): void;
|
||||
|
||||
/**
|
||||
* Returns the total height of the tree's content.
|
||||
*/
|
||||
getContentHeight(): number;
|
||||
|
||||
/**
|
||||
* Sets the tree's highlight to be the given element.
|
||||
* Provide no arguments and it clears the tree's highlight.
|
||||
|
@ -309,6 +315,12 @@ export interface ITree extends Events.IEventEmitter {
|
|||
*/
|
||||
withFakeRow(fn:(container:HTMLElement)=>any):any;
|
||||
|
||||
/**
|
||||
* Returns a navigator which allows to discover the visible and
|
||||
* expanded elements in the tree.
|
||||
*/
|
||||
getNavigator(): INavigator<any>;
|
||||
|
||||
/**
|
||||
* Disposes the tree
|
||||
*/
|
||||
|
|
|
@ -458,7 +458,7 @@ export class Item extends Events.EventEmitter {
|
|||
|
||||
public getHierarchy(): Item[] {
|
||||
var result: Item[] = [];
|
||||
var node = this;
|
||||
var node: Item = this;
|
||||
|
||||
do {
|
||||
result.push(node);
|
||||
|
|
|
@ -272,4 +272,8 @@ export class SimplePluginService extends AbstractPluginService {
|
|||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public deactivate(pluginId:string): void {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ import {createAsyncDescriptor0} from 'vs/platform/instantiation/common/descripto
|
|||
import {LanguageExtensions, ILanguageExtensionPoint} from 'vs/editor/common/modes/languageExtensionPoint';
|
||||
import {AbstractKeybindingService} from 'vs/platform/keybinding/browser/keybindingServiceImpl';
|
||||
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
|
||||
import {IJSONSchema} from 'vs/base/common/jsonSchema';
|
||||
import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
|
||||
// Set defaults for standalone editor
|
||||
DefaultConfig.editor.wrappingIndent = 'none';
|
||||
|
@ -451,6 +453,11 @@ export function registerStandaloneLanguage(language:ILanguageExtensionPoint, def
|
|||
});
|
||||
}
|
||||
|
||||
export function registerStandaloneSchema(uri:string, schema:IJSONSchema) {
|
||||
let schemaRegistry = <JSONContributionRegistry.IJSONContributionRegistry>Registry.as(JSONContributionRegistry.Extensions.JSONContribution);
|
||||
schemaRegistry.registerSchema(uri, schema);
|
||||
}
|
||||
|
||||
export function colorizeElement(domNode:HTMLElement, options:colorizer.IColorizerElementOptions): TPromise<void> {
|
||||
startup.initStaticServicesIfNecessary();
|
||||
var modeService = standaloneServices.ensureStaticPlatformServices(null).modeService;
|
||||
|
|
|
@ -5,12 +5,14 @@
|
|||
'use strict';
|
||||
|
||||
import 'vs/editor/standalone-languages/all';
|
||||
import './standaloneSchemas';
|
||||
|
||||
import Colorizer = require('vs/editor/browser/standalone/colorizer');
|
||||
import standaloneCodeEditor = require('vs/editor/browser/standalone/standaloneCodeEditor');
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import EditorBrowser = require('vs/editor/browser/editorBrowser');
|
||||
import {ILanguageDef} from 'vs/editor/standalone-languages/types';
|
||||
import {IJSONSchema} from 'vs/base/common/jsonSchema';
|
||||
|
||||
var global:any = self;
|
||||
if (!global.Monaco) {
|
||||
|
@ -67,4 +69,10 @@ Monaco.Editor.OverlayWidgetPositionPreference = EditorBrowser.OverlayWidgetPosit
|
|||
let MonacoEditorLanguages: ILanguageDef[] = this.MonacoEditorLanguages || [];
|
||||
MonacoEditorLanguages.forEach((language) => {
|
||||
standaloneCodeEditor.registerStandaloneLanguage(language, language.defModule);
|
||||
});
|
||||
});
|
||||
|
||||
// Register all built-in standalone JSON schemas
|
||||
let MonacoEditorSchemas: { [url:string]: IJSONSchema } = this.MonacoEditorSchemas || {};
|
||||
for (var uri in MonacoEditorSchemas) {
|
||||
standaloneCodeEditor.registerStandaloneSchema(uri, MonacoEditorSchemas[uri]);
|
||||
};
|
1077
src/vs/editor/browser/standalone/standaloneSchemas.ts
Normal file
|
@ -9,6 +9,7 @@ import {Range} from 'vs/editor/common/core/range';
|
|||
import {Selection} from 'vs/editor/common/core/selection';
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import {CursorMoveHelper} from 'vs/editor/common/controller/cursorMoveHelper';
|
||||
import {getRawEnterActionAtPosition} from 'vs/editor/common/modes/supports/onEnter';
|
||||
|
||||
export interface IShiftCommandOpts {
|
||||
isUnshift: boolean;
|
||||
|
@ -50,19 +51,21 @@ export class ShiftCommand implements EditorCommon.ICommand {
|
|||
}
|
||||
|
||||
public getEditOperations(model: EditorCommon.ITokenizedModel, builder: EditorCommon.IEditOperationBuilder): void {
|
||||
var startLine = this._selection.startLineNumber,
|
||||
endLine = this._selection.endLineNumber;
|
||||
let startLine = this._selection.startLineNumber,
|
||||
endLine = this._selection.endLineNumber,
|
||||
_SPACE = ' '.charCodeAt(0);
|
||||
|
||||
if (this._selection.endColumn === 1 && startLine !== endLine) {
|
||||
endLine = endLine - 1;
|
||||
}
|
||||
|
||||
var lineNumber:number,
|
||||
let lineNumber:number,
|
||||
tabSize = this._opts.tabSize,
|
||||
oneIndent = this._opts.oneIndent;
|
||||
oneIndent = this._opts.oneIndent,
|
||||
shouldIndentEmptyLines = (startLine === endLine);
|
||||
|
||||
// indents[i] represents i * oneIndent
|
||||
var indents: string[] = ['', oneIndent];
|
||||
let indents: string[] = ['', oneIndent];
|
||||
|
||||
// if indenting or outdenting on a whitespace only line
|
||||
if (this._selection.isEmpty()) {
|
||||
|
@ -71,15 +74,21 @@ export class ShiftCommand implements EditorCommon.ICommand {
|
|||
}
|
||||
}
|
||||
|
||||
for (lineNumber = startLine; lineNumber <= endLine; lineNumber++) {
|
||||
var lineText = model.getLineContent(lineNumber);
|
||||
var indentationEndIndex = Strings.firstNonWhitespaceIndex(lineText);
|
||||
// keep track of previous line's "miss-alignment"
|
||||
let previousLineExtraSpaces = 0, extraSpaces = 0;
|
||||
for (lineNumber = startLine; lineNumber <= endLine; lineNumber++, previousLineExtraSpaces = extraSpaces) {
|
||||
extraSpaces = 0;
|
||||
let lineText = model.getLineContent(lineNumber);
|
||||
let indentationEndIndex = Strings.firstNonWhitespaceIndex(lineText);
|
||||
|
||||
if (this._opts.isUnshift) {
|
||||
if (lineText.length === 0 || indentationEndIndex === 0) {
|
||||
// empty line or line with no leading whitespace => nothing to do
|
||||
continue;
|
||||
}
|
||||
if (this._opts.isUnshift && (lineText.length === 0 || indentationEndIndex === 0)) {
|
||||
// empty line or line with no leading whitespace => nothing to do
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!shouldIndentEmptyLines && !this._opts.isUnshift && lineText.length === 0) {
|
||||
// do not indent empty lines => nothing to do
|
||||
continue;
|
||||
}
|
||||
|
||||
if (indentationEndIndex === -1) {
|
||||
|
@ -87,7 +96,45 @@ export class ShiftCommand implements EditorCommon.ICommand {
|
|||
indentationEndIndex = lineText.length;
|
||||
}
|
||||
|
||||
var desiredIndentCount: number;
|
||||
if (lineNumber > 1) {
|
||||
let contentStartVisibleColumn = CursorMoveHelper.visibleColumnFromColumn2(lineText, indentationEndIndex + 1, tabSize);
|
||||
if (contentStartVisibleColumn % tabSize !== 0) {
|
||||
// The current line is "miss-aligned", so let's see if this is expected...
|
||||
// This can only happen when it has trailing commas in the indent
|
||||
let enterAction = getRawEnterActionAtPosition(model, lineNumber - 1, model.getLineMaxColumn(lineNumber - 1));
|
||||
if (enterAction) {
|
||||
extraSpaces = previousLineExtraSpaces;
|
||||
if (enterAction.appendText) {
|
||||
for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < tabSize; j++) {
|
||||
if (enterAction.appendText.charCodeAt(j) === _SPACE) {
|
||||
extraSpaces++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (enterAction.removeText) {
|
||||
extraSpaces = Math.max(0, extraSpaces - enterAction.removeText);
|
||||
}
|
||||
|
||||
// Act as if `prefixSpaces` is not part of the indentation
|
||||
for (let j = 0; j < extraSpaces; j++) {
|
||||
if (indentationEndIndex === 0 || lineText.charCodeAt(indentationEndIndex - 1) !== _SPACE) {
|
||||
break;
|
||||
}
|
||||
indentationEndIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this._opts.isUnshift && indentationEndIndex === 0) {
|
||||
// line with no leading whitespace => nothing to do
|
||||
continue;
|
||||
}
|
||||
|
||||
let desiredIndentCount: number;
|
||||
if (this._opts.isUnshift) {
|
||||
desiredIndentCount = ShiftCommand.unshiftIndentCount(lineText, indentationEndIndex + 1, tabSize);
|
||||
} else {
|
||||
|
@ -95,7 +142,7 @@ export class ShiftCommand implements EditorCommon.ICommand {
|
|||
}
|
||||
|
||||
// Fill `indents`, as needed
|
||||
for (var j = indents.length; j <= desiredIndentCount; j++) {
|
||||
for (let j = indents.length; j <= desiredIndentCount; j++) {
|
||||
indents[j] = indents[j-1] + oneIndent;
|
||||
}
|
||||
|
||||
|
|
|
@ -505,7 +505,7 @@ export class CommonEditorConfiguration extends EventEmitter implements EditorCom
|
|||
tabSizeIsAuto: false,
|
||||
tabSize: 4,
|
||||
insertSpacesIsAuto: false,
|
||||
insertSpaces: false
|
||||
insertSpaces: true
|
||||
};
|
||||
|
||||
if (opts.tabSize === 'auto') {
|
||||
|
|
|
@ -63,8 +63,8 @@ class ConfigClass implements IConfiguration {
|
|||
referenceInfos: true,
|
||||
renderWhitespace: false,
|
||||
|
||||
tabSize: 'auto',
|
||||
insertSpaces: 'auto',
|
||||
tabSize: 4,
|
||||
insertSpaces: true,
|
||||
fontFamily: '',
|
||||
fontSize: 0,
|
||||
lineHeight: 0
|
||||
|
|
|
@ -15,6 +15,7 @@ import {IEnterAction,IndentAction,IElectricAction} from 'vs/editor/common/modes'
|
|||
import {CursorMoveHelper, ICursorMoveHelperModel, IMoveResult} from 'vs/editor/common/controller/cursorMoveHelper';
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import Errors = require('vs/base/common/errors');
|
||||
import {getEnterActionAtPosition} from 'vs/editor/common/modes/supports/onEnter';
|
||||
|
||||
export interface IPostOperationRunnable {
|
||||
(ctx: IOneCursorOperationContext): void;
|
||||
|
@ -950,64 +951,6 @@ export class OneCursorOp {
|
|||
return this._enter(cursor, true, ctx);
|
||||
}
|
||||
|
||||
private static _getEnterActionAtPosition(model:EditorCommon.IModel, lineNumber:number, column:number): { enterAction: IEnterAction; indentation: string; } {
|
||||
var lineText = model.getLineContent(lineNumber);
|
||||
var lineContext = model.getLineContext(lineNumber);
|
||||
var enterAction:IEnterAction;
|
||||
|
||||
if (model.getMode().onEnterSupport) {
|
||||
try {
|
||||
enterAction = model.getMode().onEnterSupport.onEnter(model, new Position(lineNumber, column));
|
||||
} catch (e) {
|
||||
Errors.onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!enterAction) {
|
||||
if (model.getMode().electricCharacterSupport) {
|
||||
try {
|
||||
enterAction = model.getMode().electricCharacterSupport.onEnter(lineContext, column - 1);
|
||||
} catch(e) {
|
||||
Errors.onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// console.log('USING NEW INDENTATION LOGIC!');
|
||||
}
|
||||
|
||||
var indentation = Strings.getLeadingWhitespace(lineText);
|
||||
if (indentation.length > column - 1) {
|
||||
indentation = indentation.substring(0, column - 1);
|
||||
}
|
||||
|
||||
if (!enterAction) {
|
||||
enterAction = {
|
||||
indentAction: IndentAction.None,
|
||||
appendText: '',
|
||||
};
|
||||
} else {
|
||||
if(!enterAction.appendText) {
|
||||
if (
|
||||
(enterAction.indentAction === IndentAction.Indent) ||
|
||||
(enterAction.indentAction === IndentAction.IndentOutdent)
|
||||
) {
|
||||
enterAction.appendText = '\t';
|
||||
} else {
|
||||
enterAction.appendText = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enterAction.removeText) {
|
||||
indentation = indentation.substring(0, indentation.length - 1);
|
||||
}
|
||||
|
||||
return {
|
||||
enterAction: enterAction,
|
||||
indentation: indentation
|
||||
};
|
||||
}
|
||||
|
||||
private static _enter(cursor:OneCursor, keepPosition: boolean, ctx: IOneCursorOperationContext, position?: EditorCommon.IEditorPosition, range?: EditorCommon.IEditorRange): boolean {
|
||||
if (typeof position === 'undefined') {
|
||||
position = cursor.getPosition();
|
||||
|
@ -1017,7 +960,7 @@ export class OneCursorOp {
|
|||
}
|
||||
ctx.shouldPushStackElementBefore = true;
|
||||
|
||||
var r = this._getEnterActionAtPosition(cursor.model, position.lineNumber, position.column);
|
||||
var r = getEnterActionAtPosition(cursor.model, position.lineNumber, position.column);
|
||||
var enterAction = r.enterAction;
|
||||
var indentation = r.indentation;
|
||||
|
||||
|
@ -1308,7 +1251,7 @@ export class OneCursorOp {
|
|||
return '\t';
|
||||
}
|
||||
|
||||
var r = this._getEnterActionAtPosition(cursor.model, lastLineNumber, cursor.model.getLineMaxColumn(lastLineNumber));
|
||||
var r = getEnterActionAtPosition(cursor.model, lastLineNumber, cursor.model.getLineMaxColumn(lastLineNumber));
|
||||
|
||||
var indentation: string;
|
||||
if (r.enterAction.indentAction === IndentAction.Outdent) {
|
||||
|
|
|
@ -490,12 +490,14 @@ export interface ICommonEditorOptions {
|
|||
export interface IEditorOptions extends ICommonEditorOptions {
|
||||
/**
|
||||
* Tab size in spaces. This is used for rendering and for editing.
|
||||
* Defaults to 'auto', meaning the model attached to the editor will be scanned and this property will be guessed.
|
||||
* 'auto' means the model attached to the editor will be scanned and this property will be guessed.
|
||||
* Defaults to 4.
|
||||
*/
|
||||
tabSize?:any;
|
||||
/**
|
||||
* Insert spaces instead of tabs when indenting or when auto-indenting.
|
||||
* Defaults to 'auto', meaning the model attached to the editor will be scanned and this property will be guessed.
|
||||
* 'auto' means the model attached to the editor will be scanned and this property will be guessed.
|
||||
* Defaults to true.
|
||||
*/
|
||||
insertSpaces?:any;
|
||||
/**
|
||||
|
|
|
@ -486,11 +486,11 @@ export class TextModel extends OrderGuaranteeEventEmitter implements EditorCommo
|
|||
linesIndentedWithSpaces += (absoluteSpaceCounts[i] || 0);
|
||||
}
|
||||
|
||||
// Give preference to tabs over spaces (when evidence is the same)
|
||||
// Give preference to spaces over tabs (when evidence is the same)
|
||||
// or when there are not enough clues (too little indentation in the file)
|
||||
if (linesIndentedWithTabs >= linesIndentedWithSpaces) {
|
||||
return {
|
||||
insertSpaces: false,
|
||||
insertSpaces: true,
|
||||
tabSize: defaultTabSize
|
||||
};
|
||||
}
|
||||
|
@ -498,7 +498,7 @@ export class TextModel extends OrderGuaranteeEventEmitter implements EditorCommo
|
|||
if (linesWithIndentationCount < 6 && linesIndentedWithTabs > 0) {
|
||||
// Making a guess with 6 indented lines, of which tabs are used besides spaces is very difficult
|
||||
return {
|
||||
insertSpaces: false,
|
||||
insertSpaces: true,
|
||||
tabSize: defaultTabSize
|
||||
};
|
||||
}
|
||||
|
|
|
@ -409,7 +409,7 @@ export interface ISuggestionFilter {
|
|||
(word: string, suggestion: ISuggestion): IMatch[];
|
||||
}
|
||||
|
||||
export interface ISuggestionSorter {
|
||||
export interface ISuggestionCompare {
|
||||
(one: ISuggestion, other: ISuggestion): number;
|
||||
}
|
||||
|
||||
|
@ -429,7 +429,7 @@ export interface ISuggestSupport {
|
|||
getSuggestionDetails?:(resource:URI, position:EditorCommon.IPosition, suggestion:ISuggestion)=>TPromise<ISuggestion>;
|
||||
|
||||
getFilter():ISuggestionFilter;
|
||||
getSorter?():ISuggestionSorter;
|
||||
getSorter?():ISuggestionCompare;
|
||||
getTriggerCharacters():string[];
|
||||
shouldShowEmptySuggestionList():boolean;
|
||||
shouldAutotriggerSuggest(context:ILineContext, offset:number, triggeredByCharacter:string):boolean;
|
||||
|
|
|
@ -25,15 +25,11 @@ export var ContiguousSubString: ISuggestionFilter = wrapBaseFilter(Filters.match
|
|||
// Combined Filters
|
||||
|
||||
export function or(first: ISuggestionFilter, second: ISuggestionFilter): ISuggestionFilter {
|
||||
return (word: string, suggestion: ISuggestion): Filters.IMatch[] => {
|
||||
return first(word, suggestion) || second(word, suggestion);
|
||||
};
|
||||
return (word, suggestion) => first(word, suggestion) || second(word, suggestion);
|
||||
}
|
||||
|
||||
export function and(first: ISuggestionFilter, second: ISuggestionFilter): ISuggestionFilter {
|
||||
return (word: string, suggestion: ISuggestion): Filters.IMatch[] => {
|
||||
return first(word, suggestion) && second(word, suggestion);
|
||||
};
|
||||
return (word, suggestion) => first(word, suggestion) && second(word, suggestion);
|
||||
}
|
||||
|
||||
export var DefaultFilter = or(or(Prefix, CamelCase), ContiguousSubString);
|
|
@ -776,7 +776,7 @@ export class SuggestSupport extends AbstractSupport implements Modes.ISuggestSup
|
|||
return DefaultFilter;
|
||||
}
|
||||
|
||||
public getSorter(): Modes.ISuggestionSorter {
|
||||
public getSorter(): Modes.ISuggestionCompare {
|
||||
return (one, other) => {
|
||||
if (this.sortByType.length > 0) {
|
||||
var oneTypeIndex = this.sortByType.indexOf(one.type);
|
||||
|
|
|
@ -9,6 +9,7 @@ import {IEnterAction, IndentAction, IOnEnterSupport, ILineContext, IMode} from '
|
|||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import Errors = require('vs/base/common/errors');
|
||||
import Strings = require('vs/base/common/strings');
|
||||
import {Position} from 'vs/editor/common/core/position';
|
||||
|
||||
export interface IBracketPair {
|
||||
open: string;
|
||||
|
@ -179,3 +180,66 @@ export class OnEnterSupport implements IOnEnterSupport {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getRawEnterActionAtPosition(model:EditorCommon.ITokenizedModel, lineNumber:number, column:number): IEnterAction {
|
||||
let enterAction:IEnterAction;
|
||||
|
||||
if (model.getMode().onEnterSupport) {
|
||||
try {
|
||||
enterAction = model.getMode().onEnterSupport.onEnter(model, new Position(lineNumber, column));
|
||||
} catch (e) {
|
||||
Errors.onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!enterAction) {
|
||||
if (model.getMode().electricCharacterSupport) {
|
||||
let lineContext = model.getLineContext(lineNumber);
|
||||
try {
|
||||
enterAction = model.getMode().electricCharacterSupport.onEnter(lineContext, column - 1);
|
||||
} catch(e) {
|
||||
Errors.onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// console.log('USING NEW INDENTATION LOGIC!');
|
||||
}
|
||||
|
||||
return enterAction;
|
||||
}
|
||||
|
||||
export function getEnterActionAtPosition(model:EditorCommon.ITokenizedModel, lineNumber:number, column:number): { enterAction: IEnterAction; indentation: string; } {
|
||||
let lineText = model.getLineContent(lineNumber);
|
||||
let indentation = Strings.getLeadingWhitespace(lineText);
|
||||
if (indentation.length > column - 1) {
|
||||
indentation = indentation.substring(0, column - 1);
|
||||
}
|
||||
|
||||
let enterAction = getRawEnterActionAtPosition(model, lineNumber, column);
|
||||
if (!enterAction) {
|
||||
enterAction = {
|
||||
indentAction: IndentAction.None,
|
||||
appendText: '',
|
||||
};
|
||||
} else {
|
||||
if(!enterAction.appendText) {
|
||||
if (
|
||||
(enterAction.indentAction === IndentAction.Indent) ||
|
||||
(enterAction.indentAction === IndentAction.IndentOutdent)
|
||||
) {
|
||||
enterAction.appendText = '\t';
|
||||
} else {
|
||||
enterAction.appendText = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enterAction.removeText) {
|
||||
indentation = indentation.substring(0, indentation.length - 1);
|
||||
}
|
||||
|
||||
return {
|
||||
enterAction: enterAction,
|
||||
indentation: indentation
|
||||
};
|
||||
}
|
||||
|
|
|
@ -73,6 +73,10 @@ class WorkerPluginService extends AbstractPluginService {
|
|||
}
|
||||
}
|
||||
|
||||
public deactivate(pluginId:string): void {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class EditorWorkerServer {
|
||||
|
|
|
@ -516,6 +516,16 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
|
|||
|
||||
var matches = this.editor.getModel().findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord);
|
||||
|
||||
// do not overlap with selection (issue #64)
|
||||
let editorSelection = this.editor.getSelection();
|
||||
|
||||
matches = matches.filter((m) => {
|
||||
if (editorSelection.equalsRange(m)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
var decorations = matches.map(r => {
|
||||
return {
|
||||
range: r,
|
||||
|
|
|
@ -26,43 +26,31 @@
|
|||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget > .message {
|
||||
padding-left: 22px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget > .tree {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content {
|
||||
-mox-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
line-height: 1.2em;
|
||||
padding: 2px 10px 2px 2px;
|
||||
padding: 2px 10px 2px 22px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 2px 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content > * {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content:after {
|
||||
content:"";
|
||||
clear: both;
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .text {
|
||||
width: -webkit-calc(100% - 16px);
|
||||
width: -moz-calc(100% - 16px);
|
||||
width: -ms-calc(100% - 16px);
|
||||
width: -o-calc(100% - 16px);
|
||||
width: calc(100% - 16px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-left: 6px;
|
||||
-webkit-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .docs {
|
||||
display: none;
|
||||
max-height: 3.4em;
|
||||
|
@ -79,15 +67,11 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight {
|
||||
.monaco-editor .suggest-widget:not(.frozen) .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight {
|
||||
font-weight: bold;
|
||||
color: #186B9E;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.empty .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .type-label {
|
||||
display: none;
|
||||
margin-left: 0.8em;
|
||||
|
@ -95,17 +79,19 @@
|
|||
color: #0035DD;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row.fake > .content .type-label,
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row.focused > .content .type-label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row.fake > .content .docs,
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row.focused > .content .docs {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .icon {
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: 1px;
|
||||
top: 2px;
|
||||
background-image: url('symbol-sprite.svg');
|
||||
background-repeat: no-repeat;
|
||||
height: 16px;
|
||||
|
@ -150,14 +136,10 @@
|
|||
border: 1px solid rgb(69, 69, 69);
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .suggest-widget .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight {
|
||||
.monaco-editor.vs-dark .suggest-widget:not(.frozen) .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight {
|
||||
color: #219AE4;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .suggest-widget.empty .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .suggest-widget .monaco-tree .monaco-tree-row > .content .docs {
|
||||
color: #C07A7A;
|
||||
}
|
||||
|
@ -195,14 +177,10 @@
|
|||
border: 2px solid #6FC3DF;
|
||||
}
|
||||
|
||||
.monaco-editor.hc-black .suggest-widget .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight {
|
||||
.monaco-editor.hc-black .suggest-widget:not(.frozen) .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight {
|
||||
color: #219AE4;
|
||||
}
|
||||
|
||||
.monaco-editor.hc-black .suggest-widget.empty .monaco-tree .monaco-tree-row > .content .monaco-highlighted-label .highlight {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.monaco-editor.hc-black .suggest-widget .monaco-tree .monaco-tree-row > .content .docs {
|
||||
color: #C07A7A;
|
||||
}
|
||||
|
|
|
@ -7,22 +7,22 @@
|
|||
import nls = require('vs/nls');
|
||||
import Lifecycle = require('vs/base/common/lifecycle');
|
||||
import Snippet = require('vs/editor/contrib/snippet/common/snippet');
|
||||
import SuggestWidget = require('./suggestWidget');
|
||||
import SuggestModel = require('./suggestModel');
|
||||
import { SuggestWidget } from './suggestWidget';
|
||||
import { SuggestModel } from './suggestModel';
|
||||
import Errors = require('vs/base/common/errors');
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions';
|
||||
import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions';
|
||||
import {EditorAction, Behaviour} from 'vs/editor/common/editorAction';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorBrowserRegistry } from 'vs/editor/browser/editorBrowserExtensions';
|
||||
import { CommonEditorRegistry, ContextKey, EditorActionDescriptor } from 'vs/editor/common/editorCommonExtensions';
|
||||
import { EditorAction, Behaviour } from 'vs/editor/common/editorAction';
|
||||
import EditorBrowser = require('vs/editor/browser/editorBrowser');
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import Modes = require('vs/editor/common/modes');
|
||||
import EventEmitter = require('vs/base/common/eventEmitter');
|
||||
import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService';
|
||||
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
|
||||
import {SuggestRegistry, ACCEPT_SELECTED_SUGGESTION_CMD, CONTEXT_SUGGEST_WIDGET_VISIBLE} from 'vs/editor/contrib/suggest/common/suggest';
|
||||
import {INullService} from 'vs/platform/instantiation/common/instantiation';
|
||||
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
|
||||
import { IKeybindingService, IKeybindingContextKey } from 'vs/platform/keybinding/common/keybindingService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { SuggestRegistry, ACCEPT_SELECTED_SUGGESTION_CMD, CONTEXT_SUGGEST_WIDGET_VISIBLE } from 'vs/editor/contrib/suggest/common/suggest';
|
||||
import { IInstantiationService, INullService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
export class SuggestController implements EditorCommon.IEditorContribution {
|
||||
static ID = 'editor.contrib.suggestController';
|
||||
|
@ -31,32 +31,25 @@ export class SuggestController implements EditorCommon.IEditorContribution {
|
|||
return <SuggestController>editor.getContribution(SuggestController.ID);
|
||||
}
|
||||
|
||||
private editor:EditorBrowser.ICodeEditor;
|
||||
private model:SuggestModel.SuggestModel;
|
||||
private suggestWidget: SuggestWidget.SuggestWidget;
|
||||
private toDispose: Lifecycle.IDisposable[];
|
||||
|
||||
private model: SuggestModel;
|
||||
private widget: SuggestWidget;
|
||||
private triggerCharacterListeners: Function[];
|
||||
private suggestWidgetVisible: IKeybindingContextKey<boolean>;
|
||||
private toDispose: Lifecycle.IDisposable[];
|
||||
|
||||
constructor(editor:EditorBrowser.ICodeEditor, @IKeybindingService keybindingService: IKeybindingService, @ITelemetryService telemetryService: ITelemetryService) {
|
||||
this.editor = editor;
|
||||
constructor(
|
||||
private editor:EditorBrowser.ICodeEditor,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService instantiationService: IInstantiationService
|
||||
) {
|
||||
this.suggestWidgetVisible = keybindingService.createKey(CONTEXT_SUGGEST_WIDGET_VISIBLE, false);
|
||||
|
||||
this.model = new SuggestModel.SuggestModel(this.editor, (snippet:Snippet.CodeSnippet, overwriteBefore:number, overwriteAfter:number) => {
|
||||
Snippet.get(this.editor).run(snippet, overwriteBefore, overwriteAfter);
|
||||
});
|
||||
|
||||
this.suggestWidget = new SuggestWidget.SuggestWidget(this.editor, telemetryService, keybindingService, () => {
|
||||
this.suggestWidgetVisible.set(true);
|
||||
}, () => {
|
||||
this.suggestWidgetVisible.reset();
|
||||
});
|
||||
this.suggestWidget.setModel(this.model);
|
||||
this.model = new SuggestModel(this.editor);
|
||||
this.widget = instantiationService.createInstance(SuggestWidget, this.editor, this.model);
|
||||
|
||||
this.triggerCharacterListeners = [];
|
||||
|
||||
this.toDispose = [];
|
||||
this.toDispose.push(this.widget.onDidVisibilityChange(visible => visible ? this.suggestWidgetVisible.set(true) : this.suggestWidgetVisible.reset()));
|
||||
this.toDispose.push(editor.addListener2(EditorCommon.EventType.ConfigurationChanged, () => this.update()));
|
||||
this.toDispose.push(editor.addListener2(EditorCommon.EventType.ModelChanged, () => this.update()));
|
||||
this.toDispose.push(editor.addListener2(EditorCommon.EventType.ModelModeChanged, () => this.update()));
|
||||
|
@ -67,6 +60,9 @@ export class SuggestController implements EditorCommon.IEditorContribution {
|
|||
}));
|
||||
this.toDispose.push(SuggestRegistry.onDidChange(this.update, this));
|
||||
|
||||
// TODO@Joao: what is this?
|
||||
this.toDispose.push(this.model.onDidAccept(e => Snippet.get(this.editor).run(e.snippet, e.overwriteBefore, e.overwriteAfter)));
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
|
@ -78,12 +74,12 @@ export class SuggestController implements EditorCommon.IEditorContribution {
|
|||
this.toDispose = Lifecycle.disposeAll(this.toDispose);
|
||||
this.triggerCharacterListeners = Lifecycle.cAll(this.triggerCharacterListeners);
|
||||
|
||||
if (this.suggestWidget) {
|
||||
this.suggestWidget.destroy();
|
||||
this.suggestWidget = null;
|
||||
if (this.widget) {
|
||||
this.widget.dispose();
|
||||
this.widget = null;
|
||||
}
|
||||
if (this.model) {
|
||||
this.model.destroy();
|
||||
this.model.dispose();
|
||||
this.model = null;
|
||||
}
|
||||
}
|
||||
|
@ -160,38 +156,38 @@ export class SuggestController implements EditorCommon.IEditorContribution {
|
|||
}
|
||||
|
||||
public acceptSelectedSuggestion(): void {
|
||||
if (this.suggestWidget) {
|
||||
this.suggestWidget.acceptSelectedSuggestion();
|
||||
if (this.widget) {
|
||||
this.widget.acceptSelectedSuggestion();
|
||||
}
|
||||
}
|
||||
|
||||
public hideSuggestWidget(): void {
|
||||
if (this.suggestWidget) {
|
||||
this.suggestWidget.cancel();
|
||||
if (this.widget) {
|
||||
this.widget.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public selectNextSuggestion(): void {
|
||||
if (this.suggestWidget) {
|
||||
this.suggestWidget.selectNext();
|
||||
if (this.widget) {
|
||||
this.widget.selectNext();
|
||||
}
|
||||
}
|
||||
|
||||
public selectNextPageSuggestion(): void {
|
||||
if (this.suggestWidget) {
|
||||
this.suggestWidget.selectNextPage();
|
||||
if (this.widget) {
|
||||
this.widget.selectNextPage();
|
||||
}
|
||||
}
|
||||
|
||||
public selectPrevSuggestion(): void {
|
||||
if (this.suggestWidget) {
|
||||
this.suggestWidget.selectPrevious();
|
||||
if (this.widget) {
|
||||
this.widget.selectPrevious();
|
||||
}
|
||||
}
|
||||
|
||||
public selectPrevPageSuggestion(): void {
|
||||
if (this.suggestWidget) {
|
||||
this.suggestWidget.selectPreviousPage();
|
||||
if (this.widget) {
|
||||
this.widget.selectPreviousPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,164 +7,64 @@
|
|||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import {sequence} from 'vs/base/common/async';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { EventEmitter, ListenerUnbind } from 'vs/base/common/eventEmitter';
|
||||
import {onUnexpectedError, isPromiseCanceledError} from 'vs/base/common/errors';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import strings = require('vs/base/common/strings');
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {isFalsyOrEmpty} from 'vs/base/common/arrays';
|
||||
import timer = require('vs/base/common/timer');
|
||||
import { getSnippets } from 'vs/editor/common/modes/modesRegistry';
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import { ISuggestSupport, ISuggestResult, ISuggestion, ISuggestionSorter } from 'vs/editor/common/modes';
|
||||
import {DefaultFilter, IMatch} from 'vs/editor/common/modes/modesFilters';
|
||||
import { ISuggestSupport, ISuggestResult, ISuggestion, ISuggestionCompare, ISuggestionFilter } from 'vs/editor/common/modes';
|
||||
import { CodeSnippet } from 'vs/editor/contrib/snippet/common/snippet';
|
||||
import { IDisposable, disposeAll } from 'vs/base/common/lifecycle';
|
||||
import {SuggestRegistry, ISuggestResult2, suggest} from '../common/suggest';
|
||||
import { SuggestRegistry, ISuggestResult2, suggest } from '../common/suggest';
|
||||
|
||||
enum SuggestState {
|
||||
NOT_ACTIVE = 0,
|
||||
MANUAL_TRIGGER = 1,
|
||||
AUTO_TRIGGER = 2
|
||||
export interface ICancelEvent {
|
||||
retrigger: boolean;
|
||||
}
|
||||
|
||||
export interface SuggestDataEvent {
|
||||
suggestions: { completionItems: CompletionItem[]; currentWord: string; };
|
||||
export interface ITriggerEvent {
|
||||
auto: boolean;
|
||||
characterTriggered: boolean;
|
||||
retrigger: boolean;
|
||||
}
|
||||
|
||||
export interface ISuggestEvent {
|
||||
suggestions: ISuggestResult2[][];
|
||||
currentWord: string;
|
||||
auto: boolean;
|
||||
}
|
||||
|
||||
export class CompletionItem {
|
||||
|
||||
private static _idPool = 0;
|
||||
|
||||
public id: string;
|
||||
public suggestion: ISuggestion;
|
||||
public highlights: IMatch[];
|
||||
public support: ISuggestSupport;
|
||||
public container: ISuggestResult;
|
||||
|
||||
private _resolveDetails:TPromise<CompletionItem>
|
||||
|
||||
constructor(support: ISuggestSupport, suggestion: ISuggestion, container:ISuggestResult) {
|
||||
this.id = '_completion_item_#' + CompletionItem._idPool++;
|
||||
this.support = support;
|
||||
this.suggestion = suggestion;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
resolveDetails(resource:URI, position:EditorCommon.IPosition): TPromise<CompletionItem> {
|
||||
if (!this._resolveDetails) {
|
||||
if (!this.support || typeof this.support.getSuggestionDetails !== 'function') {
|
||||
this._resolveDetails = TPromise.as(this);
|
||||
} else {
|
||||
this._resolveDetails = this.support.getSuggestionDetails(<any>resource, position, this.suggestion).then(value => {
|
||||
this.suggestion = assign(this.suggestion, value);
|
||||
return this;
|
||||
}, err => {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
this._resolveDetails = undefined;
|
||||
} else {
|
||||
onUnexpectedError(err);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
}
|
||||
}
|
||||
return this._resolveDetails;
|
||||
}
|
||||
export interface IAcceptEvent {
|
||||
snippet: CodeSnippet;
|
||||
overwriteBefore: number;
|
||||
overwriteAfter: number;
|
||||
}
|
||||
|
||||
class RawModel {
|
||||
|
||||
private _items: CompletionItem[][] = [];
|
||||
|
||||
public size: number = 0;
|
||||
public incomplete: boolean = false;
|
||||
|
||||
insertSuggestions(rank: number, suggestions: ISuggestResult2[]): boolean {
|
||||
if (suggestions) {
|
||||
let items: CompletionItem[] = [];
|
||||
for (let _suggestions of suggestions) {
|
||||
|
||||
for (let suggestionItem of _suggestions.suggestions) {
|
||||
items.push(new CompletionItem(_suggestions.support, suggestionItem, _suggestions));
|
||||
}
|
||||
|
||||
this.size += _suggestions.suggestions.length;
|
||||
this.incomplete = this.incomplete || _suggestions.incomplete;
|
||||
}
|
||||
this._items[rank] = items;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
select(ctx: SuggestionContext): CompletionItem[] {
|
||||
let result: CompletionItem[] = [];
|
||||
for (let item of this._items) {
|
||||
RawModel._sortAndFilter(item, ctx, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _sortAndFilter(items: CompletionItem[], ctx: SuggestionContext, bucket: CompletionItem[]): void {
|
||||
if (isFalsyOrEmpty(items)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// all items have the same (origin) support. derive sorter and filter
|
||||
// from first
|
||||
const [first] = items;
|
||||
let compare = RawModel._compare;
|
||||
let filter = DefaultFilter;
|
||||
if (first.support) {
|
||||
compare = first.support.getSorter && first.support.getSorter() || compare;
|
||||
filter = first.support.getFilter && first.support.getFilter() || filter;
|
||||
}
|
||||
|
||||
items = items.filter(item => {
|
||||
// set hightlight and filter those that have none
|
||||
item.highlights = filter(ctx.wordBefore, item.suggestion);
|
||||
return !isFalsyOrEmpty(item.highlights);
|
||||
}).sort((a, b) => {
|
||||
// sort suggestions by custom strategy
|
||||
return compare(a.suggestion, b.suggestion)
|
||||
});
|
||||
|
||||
bucket.push(...items);
|
||||
}
|
||||
|
||||
private static _compare(a: ISuggestion, b: ISuggestion):number {
|
||||
return a.label.localeCompare(b.label);
|
||||
}
|
||||
}
|
||||
|
||||
class SuggestionContext {
|
||||
class Context {
|
||||
|
||||
public lineNumber:number;
|
||||
public column:number;
|
||||
public isInEditableRange:boolean;
|
||||
|
||||
private auto: boolean;
|
||||
private shouldAuto: boolean;
|
||||
private isAutoTriggerEnabled: boolean;
|
||||
private lineContentBefore:string;
|
||||
private lineContentAfter:string;
|
||||
|
||||
public wordBefore:string;
|
||||
public wordAfter:string;
|
||||
|
||||
constructor(editor:EditorCommon.ICommonCodeEditor, auto: boolean) {
|
||||
this.auto = auto;
|
||||
|
||||
var model = editor.getModel();
|
||||
var position = editor.getPosition();
|
||||
|
||||
var lineContent = model.getLineContent(position.lineNumber);
|
||||
this.wordBefore = '';
|
||||
this.wordAfter = '';
|
||||
var wordUnderCursor = model.getWordAtPosition(position);
|
||||
constructor(editor:EditorCommon.ICommonCodeEditor, private auto: boolean) {
|
||||
const model = editor.getModel();
|
||||
const position = editor.getPosition();
|
||||
const lineContent = model.getLineContent(position.lineNumber);
|
||||
const wordUnderCursor = model.getWordAtPosition(position);
|
||||
|
||||
if (wordUnderCursor) {
|
||||
this.wordBefore = lineContent.substring(wordUnderCursor.startColumn - 1, position.column - 1);
|
||||
this.wordAfter = lineContent.substring(position.column - 1, wordUnderCursor.endColumn - 1);
|
||||
} else {
|
||||
this.wordBefore = '';
|
||||
this.wordAfter = '';
|
||||
}
|
||||
|
||||
this.lineNumber = position.lineNumber;
|
||||
|
@ -173,69 +73,77 @@ class SuggestionContext {
|
|||
this.lineContentAfter = lineContent.substr(position.column - 1);
|
||||
|
||||
this.isInEditableRange = true;
|
||||
|
||||
if (model.hasEditableRange()) {
|
||||
var editableRange = model.getEditableRange();
|
||||
const editableRange = model.getEditableRange();
|
||||
|
||||
if (!editableRange.containsPosition(position)) {
|
||||
this.isInEditableRange = false;
|
||||
}
|
||||
}
|
||||
|
||||
var lineContext = model.getLineContext(position.lineNumber);
|
||||
var character = model.getLineContent(position.lineNumber).charAt(position.column - 1);
|
||||
this.shouldAuto = SuggestRegistry.all(model)
|
||||
.some(support => support.shouldAutotriggerSuggest(lineContext, position.column - 1, character));
|
||||
const lineContext = model.getLineContext(position.lineNumber);
|
||||
const character = model.getLineContent(position.lineNumber).charAt(position.column - 1);
|
||||
const supports = SuggestRegistry.all(model);
|
||||
this.isAutoTriggerEnabled = supports.some(s => s.shouldAutotriggerSuggest(lineContext, position.column - 1, character));
|
||||
}
|
||||
|
||||
public isValidForAutoSuggest(): boolean {
|
||||
public shouldAutoTrigger(): boolean {
|
||||
if (!this.isAutoTriggerEnabled) {
|
||||
// Support disallows it
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.wordBefore.length === 0) {
|
||||
// Word before position is empty
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.wordAfter.length > 0) {
|
||||
// Word after position is non empty
|
||||
return false;
|
||||
}
|
||||
if (!this.shouldAuto) {
|
||||
// ISuggestSupport#shouldAutotriggerSuggest is againt this
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public isValidForNewContext(newCtx:SuggestionContext):boolean {
|
||||
if (this.lineNumber !== newCtx.lineNumber) {
|
||||
public isDifferentContext(context: Context):boolean {
|
||||
if (this.lineNumber !== context.lineNumber) {
|
||||
// Line number has changed
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (newCtx.column < this.column - this.wordBefore.length) {
|
||||
if (context.column < this.column - this.wordBefore.length) {
|
||||
// column went before word start
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strings.startsWith(newCtx.lineContentBefore, this.lineContentBefore) || this.lineContentAfter !== newCtx.lineContentAfter) {
|
||||
if (!strings.startsWith(context.lineContentBefore, this.lineContentBefore) || this.lineContentAfter !== context.lineContentAfter) {
|
||||
// Line has changed before position
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (newCtx.wordBefore === '' && newCtx.lineContentBefore !== this.lineContentBefore) {
|
||||
if (context.wordBefore === '' && context.lineContentBefore !== this.lineContentBefore) {
|
||||
// Most likely a space has been typed
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public isValidForRetrigger(newCtx:SuggestionContext):boolean {
|
||||
if (!strings.startsWith(this.lineContentBefore, newCtx.lineContentBefore) || this.lineContentAfter !== newCtx.lineContentAfter) {
|
||||
public shouldRetrigger(context: Context):boolean {
|
||||
if (!strings.startsWith(this.lineContentBefore, context.lineContentBefore) || this.lineContentAfter !== context.lineContentAfter) {
|
||||
// Doesn't look like the same line
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.lineContentBefore.length > newCtx.lineContentBefore.length && this.wordBefore.length === 0) {
|
||||
if (this.lineContentBefore.length > context.lineContentBefore.length && this.wordBefore.length === 0) {
|
||||
// Text was deleted and previous current word was empty
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.auto && newCtx.wordBefore.length === 0) {
|
||||
if (this.auto && context.wordBefore.length === 0) {
|
||||
// Currently in auto mode and new current word is empty
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -243,57 +151,55 @@ class SuggestionContext {
|
|||
}
|
||||
}
|
||||
|
||||
export class SuggestModel extends EventEmitter {
|
||||
enum State {
|
||||
Idle = 0,
|
||||
Manual = 1,
|
||||
Auto = 2
|
||||
}
|
||||
|
||||
export class SuggestModel implements IDisposable {
|
||||
|
||||
private editor: EditorCommon.ICommonCodeEditor;
|
||||
private onAccept:(snippet: CodeSnippet, overwriteBefore:number, overwriteAfter:number)=>void;
|
||||
private toDispose: IDisposable[];
|
||||
private autoSuggestDelay:number;
|
||||
|
||||
private triggerAutoSuggestPromise:TPromise<void>;
|
||||
private state:SuggestState;
|
||||
private state:State;
|
||||
|
||||
// Members which make sense when state is active
|
||||
private requestPromise:TPromise<void>;
|
||||
private requestContext:SuggestionContext;
|
||||
private raw:RawModel;
|
||||
private context:Context;
|
||||
|
||||
constructor(editor:EditorCommon.ICommonCodeEditor, onAccept:(snippet: CodeSnippet, overwriteBefore:number, overwriteAfter:number)=>void) {
|
||||
super();
|
||||
this.editor = editor;
|
||||
this.onAccept = onAccept;
|
||||
this.toDispose = [];
|
||||
private raw: ISuggestResult2[][];
|
||||
private incomplete: boolean;
|
||||
|
||||
this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.ConfigurationChanged, () => this.onEditorConfigurationChange()));
|
||||
this.onEditorConfigurationChange();
|
||||
private _onDidCancel: Emitter<ICancelEvent> = new Emitter();
|
||||
public get onDidCancel(): Event<ICancelEvent> { return this._onDidCancel.event; }
|
||||
|
||||
private _onDidTrigger: Emitter<ITriggerEvent> = new Emitter();
|
||||
public get onDidTrigger(): Event<ITriggerEvent> { return this._onDidTrigger.event; }
|
||||
|
||||
private _onDidSuggest: Emitter<ISuggestEvent> = new Emitter();
|
||||
public get onDidSuggest(): Event<ISuggestEvent> { return this._onDidSuggest.event; }
|
||||
|
||||
private _onDidAccept: Emitter<IAcceptEvent> = new Emitter();
|
||||
public get onDidAccept(): Event<IAcceptEvent> { return this._onDidAccept.event; }
|
||||
|
||||
constructor(private editor: EditorCommon.ICommonCodeEditor) {
|
||||
this.state = State.Idle;
|
||||
this.triggerAutoSuggestPromise = null;
|
||||
this.state = SuggestState.NOT_ACTIVE;
|
||||
|
||||
this.requestPromise = null;
|
||||
this.raw = null;
|
||||
this.requestContext = null;
|
||||
this.incomplete = false;
|
||||
this.context = null;
|
||||
|
||||
this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.CursorSelectionChanged, (e: EditorCommon.ICursorSelectionChangedEvent) => {
|
||||
if (!e.selection.isEmpty()) {
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.source !== 'keyboard' || e.reason !== '') {
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
this.onCursorChange();
|
||||
}));
|
||||
this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.ModelChanged, (e) => {
|
||||
this.cancel();
|
||||
}));
|
||||
this.toDispose = [];
|
||||
this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.ConfigurationChanged, () => this.onEditorConfigurationChange()));
|
||||
this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.CursorSelectionChanged, e => this.onCursorChange(e)));
|
||||
this.toDispose.push(this.editor.addListener2(EditorCommon.EventType.ModelChanged, () => this.cancel()));
|
||||
this.onEditorConfigurationChange();
|
||||
}
|
||||
|
||||
public cancel(silent:boolean = false, retrigger:boolean = false):boolean {
|
||||
var actuallyCanceled = (this.state !== SuggestState.NOT_ACTIVE);
|
||||
const actuallyCanceled = this.state !== State.Idle;
|
||||
|
||||
if (this.triggerAutoSuggestPromise) {
|
||||
this.triggerAutoSuggestPromise.cancel();
|
||||
|
@ -305,49 +211,61 @@ export class SuggestModel extends EventEmitter {
|
|||
this.requestPromise = null;
|
||||
}
|
||||
|
||||
this.state = SuggestState.NOT_ACTIVE;
|
||||
this.state = State.Idle;
|
||||
this.raw = null;
|
||||
this.requestContext = null;
|
||||
this.incomplete = false;
|
||||
this.context = null;
|
||||
|
||||
if (!silent) {
|
||||
this.emit('cancel', { retrigger: retrigger });
|
||||
this._onDidCancel.fire({ retrigger });
|
||||
}
|
||||
|
||||
return actuallyCanceled;
|
||||
}
|
||||
|
||||
public getRequestPosition():EditorCommon.IPosition {
|
||||
if(!this.requestContext) {
|
||||
if(!this.context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
lineNumber: this.requestContext.lineNumber,
|
||||
column: this.requestContext.column
|
||||
lineNumber: this.context.lineNumber,
|
||||
column: this.context.column
|
||||
};
|
||||
}
|
||||
|
||||
private isAutoSuggest():boolean {
|
||||
return this.state === SuggestState.AUTO_TRIGGER;
|
||||
return this.state === State.Auto;
|
||||
}
|
||||
|
||||
private onCursorChange():void {
|
||||
private onCursorChange(e: EditorCommon.ICursorSelectionChangedEvent):void {
|
||||
if (!e.selection.isEmpty()) {
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.source !== 'keyboard' || e.reason !== '') {
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SuggestRegistry.has(this.editor.getModel())) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isInactive = this.state === SuggestState.NOT_ACTIVE;
|
||||
const isInactive = this.state === State.Idle;
|
||||
|
||||
if (isInactive && !this.editor.getConfiguration().quickSuggestions) {
|
||||
return;
|
||||
}
|
||||
|
||||
var ctx = new SuggestionContext(this.editor, false);
|
||||
const ctx = new Context(this.editor, false);
|
||||
|
||||
if (isInactive) {
|
||||
// trigger was not called or it was canceled
|
||||
this.cancel();
|
||||
|
||||
if (ctx.isValidForAutoSuggest()) {
|
||||
if (ctx.shouldAutoTrigger()) {
|
||||
this.triggerAutoSuggestPromise = TPromise.timeout(this.autoSuggestDelay);
|
||||
this.triggerAutoSuggestPromise.then(() => {
|
||||
this.triggerAutoSuggestPromise = null;
|
||||
|
@ -355,79 +273,56 @@ export class SuggestModel extends EventEmitter {
|
|||
});
|
||||
}
|
||||
|
||||
} else if (this.raw && this.raw.incomplete) {
|
||||
this.trigger(this.state === SuggestState.AUTO_TRIGGER, undefined, true);
|
||||
} else if (this.raw && this.incomplete) {
|
||||
this.trigger(this.state === State.Auto, undefined, true);
|
||||
} else {
|
||||
this.onNewContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public trigger(auto: boolean, triggerCharacter?: string, retrigger: boolean = false, groups?: ISuggestSupport[][]): void {
|
||||
var model = this.editor.getModel();
|
||||
var characterTriggered = !!triggerCharacter;
|
||||
if (!groups) {
|
||||
groups = SuggestRegistry.orderedGroups(model);
|
||||
}
|
||||
const model = this.editor.getModel();
|
||||
const characterTriggered = !!triggerCharacter;
|
||||
groups = groups || SuggestRegistry.orderedGroups(model);
|
||||
|
||||
if (groups.length === 0) {
|
||||
return;
|
||||
}
|
||||
var ctx = new SuggestionContext(this.editor, auto);
|
||||
|
||||
const ctx = new Context(this.editor, auto);
|
||||
|
||||
if (!ctx.isInEditableRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $tTrigger = timer.start(timer.Topic.EDITOR, 'suggest/TRIGGER');
|
||||
|
||||
// Cancel previous requests, change state & update UI
|
||||
this.cancel(false, retrigger);
|
||||
this.state = (auto || characterTriggered) ? SuggestState.AUTO_TRIGGER : SuggestState.MANUAL_TRIGGER;
|
||||
this.emit('loading', { auto: this.isAutoSuggest(), characterTriggered: characterTriggered, retrigger: retrigger });
|
||||
this.state = (auto || characterTriggered) ? State.Auto : State.Manual;
|
||||
this._onDidTrigger.fire({ auto: this.isAutoSuggest(), characterTriggered, retrigger });
|
||||
|
||||
// Capture context when request was sent
|
||||
this.requestContext = ctx;
|
||||
this.context = ctx;
|
||||
|
||||
// Send mode request
|
||||
var $tRequest = timer.start(timer.Topic.EDITOR, 'suggest/REQUEST');
|
||||
var position = this.editor.getPosition();
|
||||
const position = this.editor.getPosition();
|
||||
|
||||
let raw = new RawModel();
|
||||
let rank = 0;
|
||||
this.requestPromise = suggest(model, position, triggerCharacter, groups).then(all => {
|
||||
for (let suggestions of all) {
|
||||
if (raw.insertSuggestions(rank, suggestions)) {
|
||||
rank++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.requestPromise.then(() => {
|
||||
$tRequest.stop();
|
||||
this.requestPromise = null;
|
||||
|
||||
if (this.state === SuggestState.NOT_ACTIVE) {
|
||||
if (this.state === State.Idle) {
|
||||
return;
|
||||
}
|
||||
|
||||
var snippets = getSnippets(model, position);
|
||||
if (snippets && snippets.suggestions && snippets.suggestions.length > 0) {
|
||||
raw.insertSuggestions(rank, [snippets]);
|
||||
}
|
||||
this.raw = all.concat([[getSnippets(model, position)]]);
|
||||
this.incomplete = all.reduce((r, s) => r || s.reduce((r, s) => r || s.incomplete, false), false);
|
||||
|
||||
if(raw.size > 0) {
|
||||
this.raw = raw;
|
||||
this.onNewContext(new SuggestionContext(this.editor, auto));
|
||||
} else {
|
||||
this.emit('empty', { auto: this.isAutoSuggest() });
|
||||
}
|
||||
}, () => {
|
||||
$tRequest.stop();
|
||||
}).done(() => $tTrigger.stop());
|
||||
this.onNewContext(new Context(this.editor, auto));
|
||||
}).then(null, onUnexpectedError);
|
||||
}
|
||||
|
||||
private onNewContext(ctx:SuggestionContext):void {
|
||||
if (this.requestContext && !this.requestContext.isValidForNewContext(ctx)) {
|
||||
if (this.requestContext.isValidForRetrigger(ctx)) {
|
||||
this.trigger(this.state === SuggestState.AUTO_TRIGGER, undefined, true);
|
||||
private onNewContext(ctx: Context):void {
|
||||
if (this.context && this.context.isDifferentContext(ctx)) {
|
||||
if (this.context.shouldRetrigger(ctx)) {
|
||||
this.trigger(this.state === State.Auto, undefined, true);
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
|
@ -436,42 +331,22 @@ export class SuggestModel extends EventEmitter {
|
|||
}
|
||||
|
||||
if (this.raw) {
|
||||
|
||||
let completionItems = this.raw.select(ctx);
|
||||
|
||||
if (completionItems.length > 0) {
|
||||
this.emit('suggest', <SuggestDataEvent> {
|
||||
suggestions: {
|
||||
completionItems,
|
||||
currentWord: ctx.wordBefore
|
||||
},
|
||||
auto: this.isAutoSuggest()
|
||||
});
|
||||
} else {
|
||||
this.emit('empty', { auto: this.isAutoSuggest() });
|
||||
}
|
||||
this._onDidSuggest.fire({ suggestions: this.raw, currentWord: ctx.wordBefore, auto: this.isAutoSuggest() });
|
||||
}
|
||||
}
|
||||
|
||||
public accept(item: CompletionItem): boolean {
|
||||
public accept(suggestion: ISuggestion, overwriteBefore: number, overwriteAfter: number): boolean {
|
||||
if (this.raw === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var parentSuggestions = item.container;
|
||||
var offsetFromInvocation = this.editor.getPosition().column - this.requestContext.column;
|
||||
|
||||
var overwriteBefore = ((typeof parentSuggestions.overwriteBefore === 'undefined')
|
||||
? parentSuggestions.currentWord.length
|
||||
: parentSuggestions.overwriteBefore) + offsetFromInvocation;
|
||||
|
||||
var overwriteAfter = (typeof parentSuggestions.overwriteAfter === 'undefined')
|
||||
? 0
|
||||
: Math.max(0, parentSuggestions.overwriteAfter);
|
||||
this._onDidAccept.fire({
|
||||
snippet: new CodeSnippet(suggestion.codeSnippet),
|
||||
overwriteBefore: overwriteBefore + (this.editor.getPosition().column - this.context.column),
|
||||
overwriteAfter
|
||||
});
|
||||
|
||||
this.cancel();
|
||||
this.onAccept(new CodeSnippet(item.suggestion.codeSnippet), overwriteBefore, overwriteAfter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -483,9 +358,8 @@ export class SuggestModel extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
public destroy():void {
|
||||
public dispose():void {
|
||||
this.cancel(true);
|
||||
this.toDispose = disposeAll(this.toDispose);
|
||||
this.emit('destroy', null);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ import EditorCommon = require('vs/editor/common/editorCommon');
|
|||
import {withEditorModel} from 'vs/editor/test/common/editorTestUtils';
|
||||
import {Selection} from 'vs/editor/common/core/selection';
|
||||
import {Cursor} from 'vs/editor/common/controller/cursor';
|
||||
import * as Modes from 'vs/editor/common/modes';
|
||||
import {OnEnterSupport} from 'vs/editor/common/modes/supports/onEnter';
|
||||
|
||||
function testShiftCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
|
||||
TU.testCommand(lines, null, selection, (sel) => new ShiftCommand(sel, {
|
||||
|
@ -28,6 +30,69 @@ function testUnshiftCommand(lines: string[], selection: Selection, expectedLines
|
|||
}), expectedLines, expectedSelection);
|
||||
}
|
||||
|
||||
class DocBlockCommentMode implements Modes.IMode {
|
||||
|
||||
public onEnterSupport: Modes.IOnEnterSupport;
|
||||
|
||||
constructor() {
|
||||
this.onEnterSupport = new OnEnterSupport(this.getId(), {
|
||||
brackets: [
|
||||
{ open: '(', close: ')' },
|
||||
{ open: '{', close: '}' },
|
||||
{ open: '[', close: ']' }
|
||||
],
|
||||
regExpRules: [
|
||||
{
|
||||
// e.g. /** | */
|
||||
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
|
||||
afterText: /^\s*\*\/$/,
|
||||
action: { indentAction: Modes.IndentAction.IndentOutdent, appendText: ' * ' }
|
||||
},
|
||||
{
|
||||
// e.g. /** ...|
|
||||
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
|
||||
action: { indentAction: Modes.IndentAction.None, appendText: ' * ' }
|
||||
},
|
||||
{
|
||||
// e.g. * ...|
|
||||
beforeText: /^(\t|(\ \ ))*\ \*\ ([^\*]|\*(?!\/))*$/,
|
||||
action: { indentAction: Modes.IndentAction.None, appendText: '* ' }
|
||||
},
|
||||
{
|
||||
// e.g. */|
|
||||
beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
|
||||
action: { indentAction: Modes.IndentAction.None, removeText: 1 }
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return 'docBlockCommentMode';
|
||||
}
|
||||
|
||||
public toSimplifiedMode(): Modes.IMode {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function testShiftCommandInDocBlockCommentMode(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
|
||||
TU.testCommand(lines, new DocBlockCommentMode(), selection, (sel) => new ShiftCommand(sel, {
|
||||
isUnshift: false,
|
||||
tabSize: 4,
|
||||
oneIndent: '\t'
|
||||
}), expectedLines, expectedSelection);
|
||||
}
|
||||
|
||||
function testUnshiftCommandInDocBlockCommentMode(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
|
||||
TU.testCommand(lines, new DocBlockCommentMode(), selection, (sel) => new ShiftCommand(sel, {
|
||||
isUnshift: true,
|
||||
tabSize: 4,
|
||||
oneIndent: '\t'
|
||||
}), expectedLines, expectedSelection);
|
||||
}
|
||||
|
||||
suite('Editor Commands - ShiftCommand', () => {
|
||||
|
||||
// --------- shift
|
||||
|
@ -212,10 +277,50 @@ suite('Editor Commands - ShiftCommand', () => {
|
|||
'My First Line',
|
||||
'\t\tMy Second Line',
|
||||
' Third Line',
|
||||
'\t',
|
||||
'',
|
||||
'\t123'
|
||||
],
|
||||
new Selection(4, 2, 5, 3)
|
||||
new Selection(4, 1, 5, 3)
|
||||
);
|
||||
});
|
||||
|
||||
test('issue #1120 TAB should not indent empty lines in a multi-line selection', () => {
|
||||
testShiftCommand(
|
||||
[
|
||||
'My First Line',
|
||||
'\t\tMy Second Line',
|
||||
' Third Line',
|
||||
'',
|
||||
'123'
|
||||
],
|
||||
new Selection(1, 1, 5, 2),
|
||||
[
|
||||
'\tMy First Line',
|
||||
'\t\t\tMy Second Line',
|
||||
'\t\tThird Line',
|
||||
'',
|
||||
'\t123'
|
||||
],
|
||||
new Selection(1, 2, 5, 3)
|
||||
);
|
||||
|
||||
testShiftCommand(
|
||||
[
|
||||
'My First Line',
|
||||
'\t\tMy Second Line',
|
||||
' Third Line',
|
||||
'',
|
||||
'123'
|
||||
],
|
||||
new Selection(4, 1, 5, 1),
|
||||
[
|
||||
'My First Line',
|
||||
'\t\tMy Second Line',
|
||||
' Third Line',
|
||||
'\t',
|
||||
'123'
|
||||
],
|
||||
new Selection(4, 2, 5, 1)
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -399,7 +504,7 @@ suite('Editor Commands - ShiftCommand', () => {
|
|||
'\tMy First Line',
|
||||
'\tMy Second Line',
|
||||
'\tThird Line',
|
||||
'\t',
|
||||
'',
|
||||
'\t123'
|
||||
],
|
||||
new Selection(1, 2, 5, 5)
|
||||
|
@ -427,6 +532,65 @@ suite('Editor Commands - ShiftCommand', () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('issue #348: indenting around doc block comments', () => {
|
||||
testShiftCommandInDocBlockCommentMode(
|
||||
[
|
||||
'',
|
||||
'/**',
|
||||
' * a doc comment',
|
||||
' */',
|
||||
'function hello() {}'
|
||||
],
|
||||
new Selection(1,1,5,20),
|
||||
[
|
||||
'',
|
||||
'\t/**',
|
||||
'\t * a doc comment',
|
||||
'\t */',
|
||||
'\tfunction hello() {}'
|
||||
],
|
||||
new Selection(1,1,5,21)
|
||||
);
|
||||
|
||||
testUnshiftCommandInDocBlockCommentMode(
|
||||
[
|
||||
'',
|
||||
'/**',
|
||||
' * a doc comment',
|
||||
' */',
|
||||
'function hello() {}'
|
||||
],
|
||||
new Selection(1,1,5,20),
|
||||
[
|
||||
'',
|
||||
'/**',
|
||||
' * a doc comment',
|
||||
' */',
|
||||
'function hello() {}'
|
||||
],
|
||||
new Selection(1,1,5,20)
|
||||
);
|
||||
|
||||
testUnshiftCommandInDocBlockCommentMode(
|
||||
[
|
||||
'\t',
|
||||
'\t/**',
|
||||
'\t * a doc comment',
|
||||
'\t */',
|
||||
'\tfunction hello() {}'
|
||||
],
|
||||
new Selection(1,1,5,21),
|
||||
[
|
||||
'',
|
||||
'/**',
|
||||
' * a doc comment',
|
||||
' */',
|
||||
'function hello() {}'
|
||||
],
|
||||
new Selection(1,1,5,20)
|
||||
);
|
||||
});
|
||||
|
||||
test('bug #16815:Shift+Tab doesn\'t go back to tabstop', () => {
|
||||
|
||||
var repeatStr = (str:string, cnt:number): string => {
|
||||
|
|
|
@ -756,7 +756,10 @@ suite('Editor Controller - Cursor', () => {
|
|||
|
||||
test('Bug 9121: Auto indent + undo + redo is funky', () => {
|
||||
var model = new Model.Model('', thisHighlighter);
|
||||
var cursor = new Cursor.Cursor(1, new MockConfiguration(null), model, null, false);
|
||||
var cursor = new Cursor.Cursor(1, new MockConfiguration({
|
||||
tabSize: 4,
|
||||
insertSpaces: false
|
||||
}), model, null, false);
|
||||
cursorCommand(cursor, H.Type, { text: '\n' }, null, 'keyboard');
|
||||
assert.equal(model.getValue(EditorCommon.EndOfLinePreference.LF), '\n', 'assert1');
|
||||
|
||||
|
|
|
@ -11,14 +11,14 @@ import Position = require('vs/editor/common/core/position');
|
|||
|
||||
function testGuessIndentation(expectedInsertSpaces:boolean, expectedTabSize:number, text:string[], msg?:string): void {
|
||||
var m = new TextModel.TextModel([], TextModel.TextModel.toRawText(text.join('\n')));
|
||||
var r = m.guessIndentation(1773);
|
||||
var r = m.guessIndentation(1337);
|
||||
m.dispose();
|
||||
|
||||
assert.equal(r.insertSpaces, expectedInsertSpaces, msg);
|
||||
if (expectedInsertSpaces) {
|
||||
assert.equal(r.tabSize, expectedTabSize, msg);
|
||||
} else {
|
||||
assert.equal(r.tabSize, 1773, msg);
|
||||
assert.equal(r.tabSize, 1337, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,26 +64,26 @@ suite('Editor Model - TextModel', () => {
|
|||
test('guess indentation 1', () => {
|
||||
|
||||
// Defaults to tabs
|
||||
guessesTabs([
|
||||
guessesSpaces(1337, [
|
||||
'x',
|
||||
'x'
|
||||
]);
|
||||
|
||||
// Gives preference to tabs
|
||||
guessesTabs([
|
||||
guessesSpaces(1337, [
|
||||
'\tx',
|
||||
'x'
|
||||
]);
|
||||
guessesTabs([
|
||||
guessesSpaces(1337, [
|
||||
'\tx',
|
||||
' x'
|
||||
]);
|
||||
guessesTabs([
|
||||
guessesSpaces(1337, [
|
||||
'\tx',
|
||||
' x'
|
||||
]);
|
||||
|
||||
guessesTabs([
|
||||
guessesSpaces(1337, [
|
||||
'x',
|
||||
' x',
|
||||
' x',
|
||||
|
@ -93,7 +93,7 @@ suite('Editor Model - TextModel', () => {
|
|||
' x',
|
||||
' x'
|
||||
], '7x1 - 1 space is never guessed as an indentation');
|
||||
guessesTabs([
|
||||
guessesSpaces(1337, [
|
||||
'',
|
||||
' ',
|
||||
' ',
|
||||
|
|
|
@ -123,6 +123,10 @@ class MockPluginService extends AbstractPluginService {
|
|||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public deactivate(pluginId:string): void {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
class MockModelService extends ModelServiceImpl {}
|
||||
|
||||
|
|
|
@ -51,19 +51,7 @@ configurationRegistry.registerConfiguration({
|
|||
'description': nls.localize('jsonConfiguration.schema', "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL."),
|
||||
},
|
||||
}
|
||||
},
|
||||
'default' : [
|
||||
{ 'fileMatch': [ '/bower.json', '/.bower.json' ], 'url': 'http://json.schemastore.org/bower' },
|
||||
{ 'fileMatch': [ '/package.json' ], 'url': 'http://json.schemastore.org/package' },
|
||||
{ 'fileMatch': [ '/project.json' ], 'url': 'http://json.schemastore.org/project' },
|
||||
{ 'fileMatch': [ '*.schema.json' ], 'url': 'http://json-schema.org/draft-04/schema#' },
|
||||
{ 'fileMatch': [ '/global.json' ], 'url': 'http://json.schemastore.org/global' },
|
||||
{ 'fileMatch': [ '/tsconfig.json'], 'url': 'http://json.schemastore.org/tsconfig' },
|
||||
{ 'fileMatch': [ '/jsconfig.json' ], 'url': 'http://opentools.azurewebsites.net/jsconfig' },
|
||||
{ 'fileMatch': [ '/.bowerrc' ], 'url': 'http://json.schemastore.org/bowerrc' },
|
||||
{ 'fileMatch': [ '/.jshintrc' ], 'url': 'http://json.schemastore.org/jshintrc' },
|
||||
{ 'fileMatch': [ '/.jscsrc' ], 'url': 'http://json.schemastore.org/jscsrc' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -20,7 +20,7 @@ import {OneWorkerAttr, AllWorkersAttr} from 'vs/platform/thread/common/threadSer
|
|||
import {IThreadService, IThreadSynchronizableObject} from 'vs/platform/thread/common/thread';
|
||||
import {AsyncDescriptor2, createAsyncDescriptor2} from 'vs/platform/instantiation/common/descriptors';
|
||||
import {OnEnterSupport} from 'vs/editor/common/modes/supports/onEnter';
|
||||
import {IJSONContributionRegistry, Extensions, ISchemaContributions} from 'vs/languages/json/common/jsonContributionRegistry';
|
||||
import {IJSONContributionRegistry, Extensions, ISchemaContributions} from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export class JSONMode extends AbstractMode<jsonWorker.JSONWorker> implements Modes.IExtraInfoSupport, Modes.IOutlineSupport, IThreadSynchronizableObject<ISchemaContributions> {
|
||||
|
|
|
@ -29,7 +29,7 @@ import errors = require('vs/base/common/errors');
|
|||
import {IMarkerService, IMarkerData} from 'vs/platform/markers/common/markers';
|
||||
import {IRequestService} from 'vs/platform/request/common/request';
|
||||
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
|
||||
import {ISchemaContributions} from 'vs/languages/json/common/jsonContributionRegistry';
|
||||
import {ISchemaContributions} from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import {IResourceService} from 'vs/editor/common/services/resourceService';
|
||||
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
|
|
|
@ -50,8 +50,7 @@ suite('JSON - Worker', () => {
|
|||
if (schema) {
|
||||
var id = "http://myschemastore/test1";
|
||||
var schemaService = <SchemaService.JSONSchemaService> (<any>worker).schemaService;
|
||||
schemaService.addPreloadedFileSchema(id, schema);
|
||||
schemaService.registerExternalSchema(id, [ "*.json" ]);
|
||||
schemaService.registerExternalSchema(id, [ "*.json" ], schema);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import objects = require('vs/base/common/objects');
|
|||
import strings = require('vs/base/common/strings');
|
||||
import {IPluginDescription} from 'vs/platform/plugins/common/plugins';
|
||||
import {PluginsRegistry} from 'vs/platform/plugins/common/pluginsRegistry';
|
||||
import JSONContributionRegistry = require('vs/languages/json/common/jsonContributionRegistry');
|
||||
import JSONContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry');
|
||||
|
||||
|
||||
export var Extensions = {
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import {IJSONSchema} from 'vs/base/common/jsonSchema';
|
||||
import {PluginsRegistry} from 'vs/platform/plugins/common/pluginsRegistry';
|
||||
import {Registry} from 'vs/platform/platform';
|
||||
import JSONContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry');
|
||||
import strings = require('vs/base/common/strings');
|
||||
import paths = require('vs/base/common/paths');
|
||||
import {INullService} from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
interface IJSONValidationExtensionPoint {
|
||||
fileMatch: string,
|
||||
url: string
|
||||
}
|
||||
|
||||
let schemaRegistry = <JSONContributionRegistry.IJSONContributionRegistry>Registry.as(JSONContributionRegistry.Extensions.JSONContribution);
|
||||
|
||||
let configurationExtPoint = PluginsRegistry.registerExtensionPoint<IJSONValidationExtensionPoint[]>('jsonValidation', {
|
||||
description: nls.localize('contributes.jsonValidation', 'Contributes json schema configuration.'),
|
||||
type: 'array',
|
||||
default: [ { fileMatch: "{{file.json}}", url: "{{url}}" } ],
|
||||
items: {
|
||||
type: 'object',
|
||||
default: { fileMatch: "{{file.json}}", url: "{{url}}" },
|
||||
properties: {
|
||||
fileMatch: {
|
||||
type: 'string',
|
||||
description: nls.localize('contributes.jsonValidation.fileMatch', 'The file pattern to match, for example "package.json" or "*.launch".'),
|
||||
},
|
||||
url: {
|
||||
description: nls.localize('contributes.jsonValidation.url', 'A schema URL (\'http:\', \'https:\') or relative path to the extension folder (\'./\').'),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export class JSONValidationExtensionPoint {
|
||||
|
||||
constructor(
|
||||
@INullService modeService: INullService
|
||||
) {
|
||||
configurationExtPoint.setHandler((extensions) => {
|
||||
for (var i = 0; i < extensions.length; i++) {
|
||||
var extensionValue = <IJSONValidationExtensionPoint[]> extensions[i].value;
|
||||
var collector = extensions[i].collector;
|
||||
var extensionPath = extensions[i].description.extensionFolderPath;
|
||||
|
||||
if (!extensionValue || !Array.isArray(extensionValue)) {
|
||||
collector.error(nls.localize('invalid.jsonValidation', "'configuration.jsonValidation' must be a array"));
|
||||
return;
|
||||
}
|
||||
extensionValue.forEach(extension => {
|
||||
if (typeof extension.fileMatch !== 'string') {
|
||||
collector.error(nls.localize('invalid.fileMatch', "'configuration.jsonValidation.fileMatch' must be defined"));
|
||||
return;
|
||||
}
|
||||
var uri = extension.url;
|
||||
if (typeof extension.url !== 'string') {
|
||||
collector.error(nls.localize('invalid.url', "'configuration.jsonValidation.url' must be a URL or relative path"));
|
||||
return;
|
||||
}
|
||||
if (strings.startsWith(uri, './')) {
|
||||
uri = paths.normalize(paths.join(extensionPath, uri));
|
||||
} else if (!strings.startsWith(uri, 'https:/') && strings.startsWith(uri, 'https:/')) {
|
||||
collector.error(nls.localize('invalid.url.schema', "'configuration.jsonValidation.url' must start with 'http:', 'https:' or './' to reference schemas located in the extension"));
|
||||
return;
|
||||
}
|
||||
schemaRegistry.addSchemaFileAssociation(extension.fileMatch, uri);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import {IPluginDescription, IPluginService, IMessage, IPointListener, IActivationEventListener } from 'vs/platform/plugins/common/plugins';
|
||||
import {IPluginDescription, IPluginService, IMessage, IPointListener, IActivationEventListener, IPluginStatus } from 'vs/platform/plugins/common/plugins';
|
||||
import WinJS = require('vs/base/common/winjs.base');
|
||||
import {IDisposable} from 'vs/base/common/lifecycle';
|
||||
import Errors = require('vs/base/common/errors');
|
||||
|
@ -21,8 +21,8 @@ export interface IPluginExports {
|
|||
}
|
||||
|
||||
export interface IPluginModule {
|
||||
activate(subscriptions: IDisposable[]): WinJS.TPromise<IPluginExports>;
|
||||
deactivate(callback:(err:any, success:boolean)=>void): void;
|
||||
activate(ctx: IPluginContext): WinJS.TPromise<IPluginExports>;
|
||||
deactivate(): void;
|
||||
}
|
||||
|
||||
export interface IPluginContext {
|
||||
|
@ -83,6 +83,7 @@ export abstract class AbstractPluginService implements IPluginService {
|
|||
this.activatedPlugins = {};
|
||||
}
|
||||
|
||||
public abstract deactivate(pluginId:string): void;
|
||||
protected abstract _showMessage(severity:Severity, message:string): void;
|
||||
|
||||
protected showMessage(severity:Severity, source:string, message:string): void {
|
||||
|
@ -111,6 +112,10 @@ export abstract class AbstractPluginService implements IPluginService {
|
|||
return this.activatedPlugins[pluginId].exports;
|
||||
}
|
||||
|
||||
public getPluginsStatus(): { [id: string]: IPluginStatus } {
|
||||
return null;
|
||||
}
|
||||
|
||||
public isActivated(pluginId:string): boolean {
|
||||
return hasOwnProperty.call(this.activatedPlugins, pluginId);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {IPluginDescription, IMessage} from 'vs/platform/plugins/common/plugins';
|
||||
import {IPluginDescription, IMessage, IPluginStatus} from 'vs/platform/plugins/common/plugins';
|
||||
import {PluginsRegistry} from 'vs/platform/plugins/common/pluginsRegistry';
|
||||
import WinJS = require('vs/base/common/winjs.base');
|
||||
import {Remotable, IThreadService} from 'vs/platform/thread/common/thread';
|
||||
|
@ -15,6 +15,7 @@ import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
|
|||
import {PluginHostStorage} from 'vs/platform/storage/common/remotable.storage';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import {IWorkspaceContextService, IConfiguration} from 'vs/platform/workspace/common/workspace';
|
||||
import {disposeAll} from 'vs/base/common/lifecycle';
|
||||
|
||||
class PluginMemento implements IPluginMemento {
|
||||
|
||||
|
@ -64,6 +65,7 @@ export class MainProcessPluginService extends AbstractPluginService {
|
|||
private _telemetryService: ITelemetryService;
|
||||
private _proxy: PluginHostPluginService;
|
||||
private _isDev: boolean;
|
||||
private _pluginsStatus: { [id: string]: IPluginStatus };
|
||||
|
||||
/**
|
||||
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
|
||||
|
@ -83,8 +85,13 @@ export class MainProcessPluginService extends AbstractPluginService {
|
|||
this._threadService = threadService;
|
||||
this._telemetryService = telemetryService;
|
||||
this._proxy = this._threadService.getRemotable(PluginHostPluginService);
|
||||
this._pluginsStatus = {};
|
||||
|
||||
PluginsRegistry.handleExtensionPoints((severity, source, message) => {
|
||||
if (!this._pluginsStatus[source]) {
|
||||
this._pluginsStatus[source] = { messages: [] };
|
||||
}
|
||||
this._pluginsStatus[source].messages.push({ type: severity, source, message });
|
||||
this.showMessage(severity, source, message);
|
||||
});
|
||||
}
|
||||
|
@ -158,6 +165,14 @@ export class MainProcessPluginService extends AbstractPluginService {
|
|||
}
|
||||
}
|
||||
|
||||
public getPluginsStatus(): { [id: string]: IPluginStatus } {
|
||||
return this._pluginsStatus;
|
||||
}
|
||||
|
||||
public deactivate(pluginId:string): void {
|
||||
this._proxy.deactivate(pluginId);
|
||||
}
|
||||
|
||||
// -- overwriting AbstractPluginService
|
||||
|
||||
protected _actualActivatePlugin(pluginDescription: IPluginDescription): WinJS.TPromise<ActivatedPlugin> {
|
||||
|
@ -222,6 +237,28 @@ export class PluginHostPluginService extends AbstractPluginService {
|
|||
}
|
||||
}
|
||||
|
||||
public deactivate(pluginId:string): void {
|
||||
let plugin = this.activatedPlugins[pluginId];
|
||||
if (!plugin) {
|
||||
return;
|
||||
}
|
||||
|
||||
// call deactivate if available
|
||||
try {
|
||||
if (typeof plugin.module.deactivate === 'function') {
|
||||
plugin.module.deactivate();
|
||||
}
|
||||
} catch(err) {
|
||||
// TODO: Do something with err if this is not the shutdown case
|
||||
}
|
||||
|
||||
// clean up subscriptions
|
||||
try {
|
||||
disposeAll(plugin.subscriptions)
|
||||
} catch(err) {
|
||||
// TODO: Do something with err if this is not the shutdown case
|
||||
}
|
||||
}
|
||||
|
||||
// -- overwriting AbstractPluginService
|
||||
|
||||
|
|
|
@ -40,18 +40,38 @@ export interface IMessage {
|
|||
source: string;
|
||||
}
|
||||
|
||||
export interface IPluginStatus {
|
||||
messages: IMessage[];
|
||||
}
|
||||
|
||||
export interface IPluginService {
|
||||
serviceId: ServiceIdentifier<any>;
|
||||
|
||||
activateByEvent(activationEvent:string): TPromise<any>;
|
||||
activateAndGet(pluginId:string): TPromise<any>;
|
||||
activateAndGet<T>(pluginId:string): TPromise<T>;
|
||||
isActivated(pluginId:string): boolean;
|
||||
|
||||
/**
|
||||
* This method should be called only on shutdown!
|
||||
* More work is needed for this to be called any time!
|
||||
*/
|
||||
deactivate(pluginId:string): void;
|
||||
|
||||
/**
|
||||
* To be used only by the platform once on startup.
|
||||
*/
|
||||
registrationDone(errors?:IMessage[]): void;
|
||||
|
||||
registerOneTimeActivationEventListener(activationEvent: string, listener: IActivationEventListener): void;
|
||||
|
||||
get(pluginId:string): any;
|
||||
|
||||
/**
|
||||
* Block on this signal any interactions with extensions.
|
||||
*/
|
||||
onReady(): TPromise<boolean>;
|
||||
getPluginsStatus(): { [id: string]: IPluginStatus };
|
||||
}
|
||||
|
||||
export var INSTANCE:IPluginService = null;
|
|
@ -8,7 +8,7 @@ import {IPluginDescription, IPointListener, IActivationEventListener, IMessage}
|
|||
import {Registry} from 'vs/platform/platform';
|
||||
import Errors = require('vs/base/common/errors');
|
||||
import env = require('vs/base/common/flags');
|
||||
import * as JSONContributionRegistry from 'vs/languages/json/common/jsonContributionRegistry';
|
||||
import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import {IJSONSchema} from 'vs/base/common/jsonSchema';
|
||||
import nls = require('vs/nls');
|
||||
import paths = require('vs/base/common/paths');
|
||||
|
|
|
@ -55,6 +55,7 @@ import {MainProcessPluginService} from 'vs/platform/plugins/common/nativePluginS
|
|||
import {MainThreadDocuments} from 'vs/workbench/api/common/pluginHostDocuments';
|
||||
import {MainProcessTextMateSyntax} from 'vs/editor/node/textMate/TMSyntax';
|
||||
import {MainProcessTextMateSnippet} from 'vs/editor/node/textMate/TMSnippets';
|
||||
import {JSONValidationExtensionPoint} from 'vs/platform/jsonschemas/common/jsonValidationExtensionPoint';
|
||||
import {LanguageConfigurationFileHandler} from 'vs/editor/node/languageConfiguration';
|
||||
import {MainThreadFileSystemEventService} from 'vs/workbench/api/common/pluginHostFileSystemEventService';
|
||||
import {MainThreadQuickOpen} from 'vs/workbench/api/browser/pluginHostQuickOpen';
|
||||
|
@ -355,6 +356,7 @@ export class WorkbenchShell {
|
|||
this.threadService.getRemotable(RemoteTelemetryServiceHelper);
|
||||
this.workbench.getInstantiationService().createInstance(MainProcessTextMateSyntax);
|
||||
this.workbench.getInstantiationService().createInstance(MainProcessTextMateSnippet);
|
||||
this.workbench.getInstantiationService().createInstance(JSONValidationExtensionPoint);
|
||||
this.workbench.getInstantiationService().createInstance(LanguageConfigurationFileHandler);
|
||||
this.threadService.getRemotable(MainThreadConfiguration);
|
||||
this.threadService.getRemotable(MainThreadQuickOpen);
|
||||
|
|
|
@ -104,16 +104,45 @@ interface ITestRunner {
|
|||
|
||||
export class PluginHostMain {
|
||||
|
||||
private _isTerminating: boolean;
|
||||
|
||||
constructor(
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IPluginService private pluginService: IPluginService,
|
||||
@IInstantiationService instantiationService: IInstantiationService
|
||||
) {}
|
||||
) {
|
||||
this._isTerminating = false;
|
||||
}
|
||||
|
||||
public start(): TPromise<void> {
|
||||
return this.readPlugins();
|
||||
}
|
||||
|
||||
public terminate(): void {
|
||||
if (this._isTerminating) {
|
||||
// we are already shutting down...
|
||||
return;
|
||||
}
|
||||
this._isTerminating = true;
|
||||
|
||||
try {
|
||||
let allExtensions = PluginsRegistry.getAllPluginDescriptions();
|
||||
let allExtensionsIds = allExtensions.map(ext => ext.id);
|
||||
let activatedExtensions = allExtensionsIds.filter(id => this.pluginService.isActivated(id));
|
||||
|
||||
activatedExtensions.forEach((extensionId) => {
|
||||
this.pluginService.deactivate(extensionId);
|
||||
});
|
||||
} catch(err) {
|
||||
// TODO: write to log once we have one
|
||||
}
|
||||
|
||||
// Give extensions 1 second to wrap up any async dispose, then exit
|
||||
setTimeout(() => {
|
||||
exit()
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private readPlugins(): TPromise<void> {
|
||||
let collector = new PluginsMessageCollector();
|
||||
let env = this.contextService.getConfiguration().env;
|
||||
|
|
|
@ -16,8 +16,14 @@ interface IRendererConnection {
|
|||
initData: IInitData;
|
||||
}
|
||||
|
||||
// This calls exit directly in case the initialization is not finished and we need to exit
|
||||
// Otherwise, if initialization completed we go to pluginHostMain.terminate()
|
||||
var onTerminate = function() {
|
||||
exit();
|
||||
};
|
||||
|
||||
function connectToRenderer(): TPromise<IRendererConnection> {
|
||||
return new TPromise((c, e) => {
|
||||
return new TPromise<IRendererConnection>((c, e) => {
|
||||
const stats: number[] = [];
|
||||
|
||||
// Listen init data message
|
||||
|
@ -28,7 +34,13 @@ function connectToRenderer(): TPromise<IRendererConnection> {
|
|||
});
|
||||
|
||||
// Listen to all other messages
|
||||
process.on('message', msg => remoteCom.handle(msg));
|
||||
process.on('message', (msg) => {
|
||||
if (msg.type === '__$terminate') {
|
||||
onTerminate();
|
||||
return;
|
||||
}
|
||||
remoteCom.handle(msg);
|
||||
});
|
||||
|
||||
// Print a console message when rejection isn't handled. For details
|
||||
// see https://nodejs.org/api/process.html#process_event_unhandledrejection
|
||||
|
@ -50,7 +62,7 @@ function connectToRenderer(): TPromise<IRendererConnection> {
|
|||
try {
|
||||
process.kill(msg.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
|
||||
} catch (e) {
|
||||
exit();
|
||||
onTerminate();
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
|
@ -86,6 +98,10 @@ TPromise.join<any>([connectToRenderer(), connectToSharedProcess()])
|
|||
const instantiationService = createServices(renderer.remoteCom, renderer.initData, sharedProcessClient);
|
||||
const pluginHostMain = instantiationService.createInstance(PluginHostMain);
|
||||
|
||||
onTerminate = () => {
|
||||
pluginHostMain.terminate();
|
||||
};
|
||||
|
||||
pluginHostMain.start()
|
||||
.done(null, err => console.error(err));
|
||||
});
|
|
@ -55,8 +55,6 @@ export interface IOptions {
|
|||
}
|
||||
|
||||
export function getProxyAgent(rawRequestURL: string, options: IOptions = {}): any {
|
||||
console.log(rawRequestURL, options);
|
||||
|
||||
if (!options.proxyUrl) {
|
||||
return getSystemProxyAgent(rawRequestURL);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import lifecycle = require('vs/base/common/lifecycle');
|
||||
import editorcommon = require('vs/editor/common/editorCommon');
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IDebugService, ModelEvents, ViewModelEvents, IBreakpoint, IRawBreakpoint } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IDebugService, ModelEvents, ViewModelEvents, IBreakpoint, IRawBreakpoint, State } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
|
||||
|
@ -254,10 +254,11 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
|
|||
|
||||
private createBreakpointDecorations(breakpoints: IBreakpoint[]): editorcommon.IModelDeltaDecoration[] {
|
||||
const activated = this.debugService.getModel().areBreakpointsActivated();
|
||||
const debugActive = this.debugService.getState() === State.Running || this.debugService.getState() === State.Stopped;
|
||||
return breakpoints.map((breakpoint) => {
|
||||
return {
|
||||
options: (!breakpoint.enabled || !activated) ? DebugEditorModelManager.BREAKPOINT_DISABLED_DECORATION :
|
||||
breakpoint.verified ? DebugEditorModelManager.BREAKPOINT_VERIFIED_DECORATION : DebugEditorModelManager.BREAKPOINT_DECORATION,
|
||||
debugActive && !breakpoint.verified ? DebugEditorModelManager.BREAKPOINT_UNVERIFIED_DECORATION : DebugEditorModelManager.BREAKPOINT_DECORATION,
|
||||
range: createRange(breakpoint.lineNumber, 1, breakpoint.lineNumber, 2)
|
||||
};
|
||||
});
|
||||
|
@ -275,8 +276,8 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
|
|||
stickiness: editorcommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
};
|
||||
|
||||
private static BREAKPOINT_VERIFIED_DECORATION: editorcommon.IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-glyph-verified',
|
||||
private static BREAKPOINT_UNVERIFIED_DECORATION: editorcommon.IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-glyph-unverified',
|
||||
stickiness: editorcommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
};
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g stroke="#5A5A5A" stroke-width="3" stroke-opacity="0.8"><circle cx="8" cy="8" r="4"/></g></g></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" fill-opacity="0.8"><g fill="#5A5A5A"><circle cx="8" cy="8" r="5"/></g></g></svg>
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 198 B |
|
@ -1 +1 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g stroke="#6C6C6C" stroke-width="3" stroke-opacity="0.6"><circle cx="8" cy="8" r="4"/></g></g></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" fill-opacity="0.6"><g fill="#6C6C6C"><circle cx="8" cy="8" r="5"/></g></g></svg>
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 198 B |
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" fill-opacity="0.4"><g fill="#007ACC"><circle cx="8" cy="8" r="5"/></g></g></svg>
|
After Width: | Height: | Size: 198 B |
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g stroke="#5A5A5A" stroke-width="3" stroke-opacity="0.8"><circle cx="8" cy="8" r="4"/></g></g></svg>
|
After Width: | Height: | Size: 219 B |
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g stroke="#6C6C6C" stroke-width="3" stroke-opacity="0.6"><circle cx="8" cy="8" r="4"/></g></g></svg>
|
After Width: | Height: | Size: 219 B |
|
@ -1 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g fill="#007ACC"><circle cx="8" cy="8" r="5"/></g></g></svg>
|
Before Width: | Height: | Size: 179 B |
|
@ -1 +1 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g stroke="#007ACC" stroke-width="3" stroke-opacity="0.8"><circle cx="8" cy="8" r="4"/></g></g></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g fill="#007ACC"><circle cx="8" cy="8" r="5"/></g></g></svg>
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 179 B |
|
@ -26,15 +26,15 @@
|
|||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-glyph-hint {
|
||||
background: url('breakpoint.svg') center center no-repeat;
|
||||
background: url('breakpoint-hint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-glyph-disabled {
|
||||
background: url('breakpoint-disabled.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-glyph-verified {
|
||||
background: url('breakpoint-verified.svg') center center no-repeat;
|
||||
.monaco-editor .debug-breakpoint-glyph-unverified {
|
||||
background: url('breakpoint-unverified.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-glyph {
|
||||
|
@ -49,11 +49,11 @@
|
|||
background: url('breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-glyph.debug-breakpoint-glyph-verified {
|
||||
.monaco-editor .debug-top-stack-frame-glyph.debug-breakpoint-glyph {
|
||||
background: url('current-and-breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-focused-stack-frame-glyph.debug-breakpoint-glyph-verified {
|
||||
.monaco-editor .debug-focused-stack-frame-glyph.debug-breakpoint-glyph {
|
||||
background: url('stackframe-and-breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
|
@ -271,6 +271,10 @@
|
|||
background: url('breakpoint-disabled-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-breakpoint-glyph-unverified {
|
||||
background: url('breakpoint-unverified-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-top-stack-frame-glyph {
|
||||
background: url('current-arrow-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
@ -279,11 +283,11 @@
|
|||
background: url('stackframe-arrow-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-top-stack-frame-glyph.debug-breakpoint-glyph-verified {
|
||||
.monaco-editor.vs-dark .debug-top-stack-frame-glyph.debug-breakpoint-glyph {
|
||||
background: url('current-and-breakpoint-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .debug-focused-stack-frame-glyph.debug-breakpoint-glyph-verified {
|
||||
.monaco-editor.vs-dark .debug-focused-stack-frame-glyph.debug-breakpoint-glyph {
|
||||
background: url('stackframe-and-breakpoint-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
|
|
|
@ -290,7 +290,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction {
|
|||
|
||||
export class ToggleEnablementAction extends AbstractDebugAction {
|
||||
static ID = 'workbench.debug.viewlet.action.toggleBreakpointEnablement';
|
||||
static LABEL = nls.localize('toggleEnablement', "Toggle Enablement");
|
||||
static LABEL = nls.localize('toggleEnablement', "Enable/Disable Breakpoint");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action toggle-enablement', debugService, keybindingService);
|
||||
|
|
|
@ -601,19 +601,17 @@ export class DebugService extends ee.EventEmitter implements debug.IDebugService
|
|||
}
|
||||
this.session = null;
|
||||
this.partService.removeClass('debugging');
|
||||
this.contextService.updateOptions('editor', {
|
||||
hover: true
|
||||
});
|
||||
this.editorService.focusEditor();
|
||||
|
||||
this.model.clearThreads(true);
|
||||
this.setFocusedStackFrameAndEvaluate(null);
|
||||
this.setStateAndEmit(debug.State.Inactive);
|
||||
|
||||
// Set breakpoints back to unverified since the session ended.
|
||||
const data: {[id: string]: { line: number, verified: boolean } } = { };
|
||||
this.model.getBreakpoints().forEach(bp => data[bp.getId()] = { line: bp.lineNumber, verified: false });
|
||||
this.model.updateBreakpoints(data);
|
||||
|
||||
this.model.clearThreads(true);
|
||||
this.setFocusedStackFrameAndEvaluate(null);
|
||||
this.setStateAndEmit(debug.State.Inactive);
|
||||
this.inDebugMode.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
|||
import platform = require('vs/platform/platform');
|
||||
import pluginsRegistry = require('vs/platform/plugins/common/pluginsRegistry');
|
||||
import editor = require('vs/editor/common/editorCommon');
|
||||
import jsonContributionRegistry = require('vs/languages/json/common/jsonContributionRegistry');
|
||||
import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry');
|
||||
import debug = require('vs/workbench/parts/debug/common/debug');
|
||||
import { SystemVariables } from 'vs/workbench/parts/lib/node/systemVariables';
|
||||
import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter';
|
||||
|
@ -179,7 +179,7 @@ export class ConfigurationManager {
|
|||
} else if (duplicate[attribute] && attribute !== 'type') {
|
||||
// Give priority to the later registered extension.
|
||||
duplicate[attribute] = adapter[attribute];
|
||||
extension.collector.warn(nls.localize('duplicateDebuggerType', "Debug type '{0}' is already registered and has attribute '{1}', ignoring attribute '{1}'.", adapter.type, attribute));
|
||||
extension.collector.error(nls.localize('duplicateDebuggerType', "Debug type '{0}' is already registered and has attribute '{1}', ignoring attribute '{1}'.", adapter.type, attribute));
|
||||
} else {
|
||||
duplicate[attribute] = adapter[attribute];
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
import platform = require('vs/platform/platform');
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import statusbar = require('vs/workbench/browser/parts/statusbar/statusbar');
|
||||
import { ExtensionsStatusbarItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets';
|
||||
import { IGalleryService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { GalleryService } from 'vs/workbench/parts/extensions/node/vsoGalleryService';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
|
@ -17,3 +19,10 @@ registerSingleton(IGalleryService, GalleryService);
|
|||
(<IWorkbenchContributionsRegistry>platform.Registry.as(WorkbenchExtensions.Workbench)).registerWorkbenchContribution(
|
||||
ExtensionsWorkbenchExtension
|
||||
);
|
||||
|
||||
// Register Statusbar item
|
||||
(<statusbar.IStatusbarRegistry>platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor(
|
||||
ExtensionsStatusbarItem,
|
||||
statusbar.StatusbarAlignment.LEFT,
|
||||
10 /* Low Priority */
|
||||
));
|
|
@ -119,4 +119,17 @@
|
|||
-moz-transform: none;
|
||||
-o-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* Status bar */
|
||||
.monaco-shell .extensions-statusbar {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-shell .extensions-statusbar.warning::before {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.monaco-shell .extensions-statusbar.error::before {
|
||||
color: #FF9D00;
|
||||
}
|
|
@ -6,6 +6,9 @@
|
|||
import nls = require('vs/nls');
|
||||
import { Promise } from 'vs/base/common/winjs.base';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IPluginService, IPluginStatus } from 'vs/platform/plugins/common/plugins';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IExtensionsService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IQuickOpenService } from 'vs/workbench/services/quickopen/browser/quickOpenService';
|
||||
|
||||
|
@ -32,6 +35,43 @@ export class ListExtensionsAction extends Action {
|
|||
}
|
||||
}
|
||||
|
||||
export class ShowExtensionsStatusAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showExtensionsStatus';
|
||||
static LABEL = nls.localize('showInstalledExtensions', "Show Extensions Status");
|
||||
private status: { [id: string]: IPluginStatus };
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPluginService pluginService: IPluginService,
|
||||
@IMessageService private messageService: IMessageService
|
||||
|
||||
) {
|
||||
super(id, label, null, true);
|
||||
|
||||
pluginService.onReady().then(() => {
|
||||
this.status = pluginService.getPluginsStatus();
|
||||
});
|
||||
}
|
||||
|
||||
public run(): Promise {
|
||||
Object.keys(this.status).forEach(key => {
|
||||
this.status[key].messages.forEach(m => {
|
||||
if (m.type > Severity.Ignore) {
|
||||
this.messageService.show(m.type, m.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.as(null);
|
||||
}
|
||||
|
||||
protected isEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallExtensionAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.installExtension';
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import dom = require('vs/base/browser/dom');
|
||||
import lifecycle = require('vs/base/common/lifecycle');
|
||||
import statusbar = require('vs/workbench/browser/parts/statusbar/statusbar');
|
||||
import { IPluginService } from 'vs/platform/plugins/common/plugins';
|
||||
import { IQuickOpenService } from 'vs/workbench/services/quickopen/browser/quickOpenService';
|
||||
|
||||
var $ = dom.emmet;
|
||||
|
||||
export class ExtensionsStatusbarItem implements statusbar.IStatusbarItem {
|
||||
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
private domNode: HTMLElement;
|
||||
|
||||
constructor(
|
||||
@IPluginService pluginService: IPluginService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService
|
||||
) {
|
||||
this.toDispose = [];
|
||||
|
||||
pluginService.onReady().then(() => {
|
||||
const pluginsStatus = pluginService.getPluginsStatus();
|
||||
Object.keys(pluginsStatus).forEach(key => {
|
||||
const severity = pluginsStatus[key].messages.reduce((maxSeverity, message) => Math.max(maxSeverity, message.type), Severity.Ignore);
|
||||
this.domNode.classList.add(Severity[severity].toLowerCase());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): lifecycle.IDisposable {
|
||||
this.domNode = dom.append(container, $('.extensions-statusbar octicon octicon-package'));
|
||||
this.domNode.title = nls.localize('extensions', "Extensions"),
|
||||
this.toDispose.push(dom.addDisposableListener(this.domNode, 'click', () => {
|
||||
this.quickOpenService.show('>extensions: ').done(null, errors.onUnexpectedError);
|
||||
}));
|
||||
|
||||
return {
|
||||
dispose: () => lifecycle.disposeAll(this.toDispose)
|
||||
};
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ import { IWorkspaceContextService } from 'vs/workbench/services/workspace/common
|
|||
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
|
||||
import wbaregistry = require('vs/workbench/browser/actionRegistry');
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { ListExtensionsAction, InstallExtensionAction, ListOutdatedExtensionsAction } from './extensionsActions';
|
||||
import { ListExtensionsAction, ShowExtensionsStatusAction, InstallExtensionAction, ListOutdatedExtensionsAction } from './extensionsActions';
|
||||
import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
|
||||
import ipc = require('ipc');
|
||||
|
@ -45,6 +45,7 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution {
|
|||
const extensionsCategory = nls.localize('extensionsCategory', "Extensions");
|
||||
const actionRegistry = (<wbaregistry.IWorkbenchActionRegistry> platform.Registry.as(wbaregistry.Extensions.WorkbenchActions));
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ListExtensionsAction, ListExtensionsAction.ID, ListExtensionsAction.LABEL), extensionsCategory);
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowExtensionsStatusAction, ShowExtensionsStatusAction.ID, ShowExtensionsStatusAction.LABEL), extensionsCategory);
|
||||
|
||||
(<IQuickOpenRegistry>platform.Registry.as(Extensions.Quickopen)).registerQuickOpenHandler(
|
||||
new QuickOpenHandlerDescriptor(
|
||||
|
|
|
@ -208,40 +208,6 @@ suite('Files - TextFileEditorModel', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test("Change after auto save triggered will cause another autosave and twice the events", function(done) {
|
||||
this.timeout(10000); // TODO@Ben test tends to need longer?
|
||||
|
||||
let eventCounter = 0;
|
||||
let m1 = baseInstantiationService.createInstance(TextFileEditorModel, toResource("/path/index.txt"), "utf8");
|
||||
|
||||
(<any>m1).autoSaveDelay = 10;
|
||||
(<any>m1).autoSaveEnabled = true;
|
||||
|
||||
eventService.addListener(EventType.FILE_DIRTY, (e: LocalFileChangeEvent) => {
|
||||
eventCounter++;
|
||||
});
|
||||
|
||||
eventService.addListener(EventType.FILE_SAVED, (e: LocalFileChangeEvent) => {
|
||||
eventCounter++;
|
||||
});
|
||||
|
||||
m1.load().then(() => {
|
||||
m1.textEditorModel.setValue("foo");
|
||||
|
||||
return Promise.timeout(50).then(() => {
|
||||
m1.textEditorModel.setValue("bar");
|
||||
|
||||
return Promise.timeout(20).then(() => {
|
||||
m1.dispose();
|
||||
assert.ok(!m1.isDirty());
|
||||
assert.equal(eventCounter, 4);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("Dirty tracking", function(done) {
|
||||
let resource = toResource("/path/index_async.txt");
|
||||
let i1 = baseInstantiationService.createInstance(FileEditorInput, resource, "text/plain", "utf8");
|
||||
|
|
|
@ -17,7 +17,7 @@ import modesExtensions = require('vs/editor/common/modes/modesRegistry');
|
|||
import errors = require('vs/base/common/errors');
|
||||
import {IQuickOpenService, IPickOpenEntry} from 'vs/workbench/services/quickopen/browser/quickOpenService';
|
||||
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
|
||||
import * as JSONContributionRegistry from 'vs/languages/json/common/jsonContributionRegistry';
|
||||
import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import {IJSONSchema} from 'vs/base/common/jsonSchema';
|
||||
|
||||
import ipc = require('ipc');
|
||||
|
|
|
@ -48,7 +48,7 @@ import { IModel } from 'vs/editor/common/editorCommon';
|
|||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
|
||||
import jsonContributionRegistry = require('vs/languages/json/common/jsonContributionRegistry');
|
||||
import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry');
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
|
||||
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
|
|
|
@ -13,7 +13,7 @@ import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
|
|||
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
|
||||
import {IKeybindingItem, IUserFriendlyKeybinding} from 'vs/platform/keybinding/common/keybindingService';
|
||||
import {IOSupport} from 'vs/platform/keybinding/common/commonKeybindingResolver';
|
||||
import * as JSONContributionRegistry from 'vs/languages/json/common/jsonContributionRegistry';
|
||||
import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import {IJSONSchema} from 'vs/base/common/jsonSchema';
|
||||
|
||||
export abstract class WorkbenchKeybindingService extends KeybindingService {
|
||||
|
|
|
@ -305,7 +305,9 @@ class PluginHostProcessManager {
|
|||
this.terminating = true;
|
||||
|
||||
if (this.pluginHostProcessHandle) {
|
||||
this.pluginHostProcessHandle.kill();
|
||||
this.pluginHostProcessHandle.send({
|
||||
type: '__$terminate'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|