提交 8f143364 编写于 作者: J Johannes Rieken

Merge branch 'master' into joh/fmt

......@@ -30,7 +30,7 @@ var editorEntryPoints = [
prepend: [ 'vs/loader.js' ],
append: [ 'vs/base/worker/workerMain' ],
dest: 'vs/base/worker/workerMain.js'
},
}
];
var editorResources = [
......
......@@ -48,9 +48,6 @@ const indentationFilter = [
'!**/package.json',
'!**/npm-shrinkwrap.json',
'!**/octicons/**',
'!**/vs/languages/sass/test/common/example.scss',
'!**/vs/languages/less/common/parser/less.grammar.txt',
'!**/vs/languages/css/common/buildscripts/css-schema.xml',
'!**/vs/base/common/marked/raw.marked.js',
'!**/vs/base/common/winjs.base.raw.js',
'!**/vs/base/node/terminateProcess.sh',
......@@ -90,7 +87,6 @@ const tslintFilter = [
'!src/vs/base/**/*.test.ts',
'!extensions/typescript/test/colorize-fixtures/**',
'!extensions/vscode-api-tests/testWorkspace/**',
'!src/vs/languages/**/*.test.ts',
'!src/vs/workbench/**/*.test.ts',
'!extensions/**/*.test.ts'
];
......
......@@ -46,8 +46,6 @@ const builtInExtensions = [
const vscodeEntryPoints = _.flatten([
buildfile.entrypoint('vs/workbench/workbench.main'),
buildfile.base,
buildfile.editor,
buildfile.languages,
buildfile.workbench,
buildfile.code
]);
......@@ -60,8 +58,6 @@ const vscodeResources = [
'out-build/paths.js',
'out-build/vs/**/*.{svg,png,cur}',
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh}',
'out-build/vs/base/worker/workerMainCompatibility.html',
'out-build/vs/base/worker/workerMain.{js,js.map}',
'out-build/vs/base/browser/ui/octiconLabel/octicons/**',
'out-build/vs/workbench/browser/media/*-theme.css',
'out-build/vs/workbench/electron-browser/bootstrap/**',
......
{
"name": "monaco-editor-core",
"private": true,
"version": "0.7.1",
"version": "0.7.3",
"description": "A browser based code editor",
"author": "Microsoft Corporation",
"license": "MIT",
......
......@@ -3,9 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
exports.base = require('./vs/base/buildfile').collectModules();
exports.editor = require('./vs/editor/buildfile').collectModules();
exports.languages = require('./vs/languages/buildfile').collectModules();
exports.base = [{
name: 'vs/base/common/worker/simpleWorker',
include: [ 'vs/editor/common/services/editorSimpleWorker' ],
prepend: [ 'vs/loader.js' ],
append: [ 'vs/base/worker/workerMain' ],
dest: 'vs/base/worker/workerMain.js'
}];
exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.main']);
exports.code = require('./vs/code/buildfile').collectModules();
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
exports.collectModules = function() {
return [{
name: 'vs/base/common/worker/workerServer',
include: [ 'vs/editor/common/worker/editorWorkerServer' ],
exclude: [ 'vs/css', 'vs/nls' ]
}, {
name: 'vs/base/common/worker/simpleWorker',
include: [ 'vs/editor/common/services/editorSimpleWorker' ],
exclude: [ 'vs/css', 'vs/nls' ]
}];
};
......@@ -7,22 +7,26 @@
import scorer = require('vs/base/common/scorer');
import strings = require('vs/base/common/strings');
const FileNameMatch = /^(.*)\.([^.]*)|([^.]+)$/;
const FileNameMatch = /^([^.]*)(\.(.*))?$/;
export function compareFileNames(one: string, other: string): number {
let oneMatch = FileNameMatch.exec(one.toLowerCase());
let otherMatch = FileNameMatch.exec(other.toLowerCase());
let oneName = oneMatch[1] || oneMatch[3] || '';
let oneExtension = oneMatch[2] || '';
let oneName = oneMatch[1] || '';
let oneExtension = oneMatch[3] || '';
let otherName = otherMatch[1] || otherMatch[3] || '';
let otherExtension = otherMatch[2] || '';
let otherName = otherMatch[1] || '';
let otherExtension = otherMatch[3] || '';
if (oneName !== otherName) {
return oneName < otherName ? -1 : 1;
}
if (oneExtension === otherExtension) {
return 0;
}
return oneExtension < otherExtension ? -1 : 1;
}
......
......@@ -179,13 +179,16 @@ function _matchesCamelCase(word: string, camelCaseWord: string, i: number, j: nu
}
}
interface ICamelCaseAnalysis {
upperPercent: number;
lowerPercent: number;
alphaPercent: number;
numericPercent: number;
}
// Heuristic to avoid computing camel case matcher for words that don't
// look like camelCaseWords.
function isCamelCaseWord(word: string): boolean {
if (word.length > 60) {
return false;
}
function analyzeCamelCaseWord(word: string): ICamelCaseAnalysis {
let upper = 0, lower = 0, alpha = 0, numeric = 0, code = 0;
for (let i = 0; i < word.length; i++) {
......@@ -202,6 +205,16 @@ function isCamelCaseWord(word: string): boolean {
let alphaPercent = alpha / word.length;
let numericPercent = numeric / word.length;
return { upperPercent, lowerPercent, alphaPercent, numericPercent };
}
function isUpperCaseWord(analysis: ICamelCaseAnalysis): boolean {
const { upperPercent, lowerPercent, alphaPercent, numericPercent } = analysis;
return lowerPercent === 0 && upperPercent > 0.6;
}
function isCamelCaseWord(analysis: ICamelCaseAnalysis): boolean {
const { upperPercent, lowerPercent, alphaPercent, numericPercent } = analysis;
return lowerPercent > 0.2 && upperPercent < 0.8 && alphaPercent > 0.6 && numericPercent < 0.2;
}
......@@ -234,10 +247,20 @@ export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[]
return null;
}
if (!isCamelCaseWord(camelCaseWord)) {
if (camelCaseWord.length > 60) {
return null;
}
const analysis = analyzeCamelCaseWord(camelCaseWord);
if (!isCamelCaseWord(analysis)) {
if (!isUpperCaseWord(analysis)) {
return null;
}
camelCaseWord = camelCaseWord.toLowerCase();
}
let result: IMatch[] = null;
let i = 0;
......
......@@ -7,11 +7,38 @@
import {transformErrorForSerialization} from 'vs/base/common/errors';
import {Disposable} from 'vs/base/common/lifecycle';
import {ErrorCallback, TPromise, ValueCallback} from 'vs/base/common/winjs.base';
import {IWorker, IWorkerFactory} from './workerClient';
import {ShallowCancelThenPromise} from 'vs/base/common/async';
import {isWeb} from 'vs/base/common/platform';
const INITIALIZE = '$initialize';
export interface IWorker {
getId():number;
postMessage(message:string):void;
dispose():void;
}
export interface IWorkerCallback {
(message:string):void;
}
export interface IWorkerFactory {
create(moduleId:string, callback:IWorkerCallback, onErrorCallback:(err:any)=>void):IWorker;
}
let webWorkerWarningLogged = false;
export function logOnceWebWorkerWarning(err: any): void {
if (!isWeb) {
// running tests
return;
}
if (!webWorkerWarningLogged) {
webWorkerWarningLogged = true;
console.warn('Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes. Please see https://github.com/Microsoft/monaco-editor#faq');
}
console.warn(err.message);
}
interface IMessage {
vsWorker: number;
req?: string;
......@@ -308,7 +335,6 @@ export class SimpleWorkerServer {
private initialize(workerId: number, moduleId: string, loaderConfig:any): TPromise<any> {
this._protocol.setWorkerId(workerId);
// TODO@Alex: share this code with workerServer
if (loaderConfig) {
// Remove 'baseUrl', handling it is beyond scope for now
if (typeof loaderConfig.baseUrl !== 'undefined') {
......
/*---------------------------------------------------------------------------------------------
* 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 {onUnexpectedError} from 'vs/base/common/errors';
import {parse, stringify} from 'vs/base/common/marshalling';
import {TPromise} from 'vs/base/common/winjs.base';
import * as workerProtocol from 'vs/base/common/worker/workerProtocol';
import {isWeb} from 'vs/base/common/platform';
export interface IWorker {
getId():number;
postMessage(message:string):void;
dispose():void;
}
let webWorkerWarningLogged = false;
export function logOnceWebWorkerWarning(err: any): void {
if (!isWeb) {
// running tests
return;
}
if (!webWorkerWarningLogged) {
webWorkerWarningLogged = true;
console.warn('Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes. Please see https://github.com/Microsoft/monaco-editor#faq');
}
console.warn(err.message);
}
export interface IWorkerCallback {
(message:string):void;
}
export interface IWorkerFactory {
create(moduleId:string, callback:IWorkerCallback, onErrorCallback:(err:any)=>void):IWorker;
}
interface IActiveRequest {
complete:(value:any)=>void;
error:(err:any)=>void;
progress:(progress:any)=>void;
type:string;
payload:any;
}
export class WorkerClient {
private _lastMessageId:number;
private _promises:{[id:string]:IActiveRequest;};
private _worker:IWorker;
private _messagesQueue:workerProtocol.IClientMessage[];
private _processQueueTimeout:number;
private _waitingForWorkerReply:boolean;
public onModuleLoaded:TPromise<void>;
constructor(workerFactory:IWorkerFactory, moduleId:string) {
this._lastMessageId = 0;
this._promises = {};
this._messagesQueue = [];
this._processQueueTimeout = -1;
this._waitingForWorkerReply = false;
this._worker = workerFactory.create(
'vs/base/common/worker/workerServer',
(msg) => this._onSerializedMessage(msg),
(err) => {
// reject the onModuleLoaded promise, this signals that things are bad
let promiseEntry:IActiveRequest = this._promises[1];
delete this._promises[1];
promiseEntry.error(err);
});
let loaderConfiguration:any = null;
let globalRequire = (<any>window).require;
if (typeof globalRequire.getConfig === 'function') {
// Get the configuration from the Monaco AMD Loader
loaderConfiguration = globalRequire.getConfig();
} else if (typeof (<any>window).requirejs !== 'undefined') {
// Get the configuration from requirejs
loaderConfiguration = (<any>window).requirejs.s.contexts._.config;
}
this.onModuleLoaded = this._sendMessage(workerProtocol.MessageType.INITIALIZE, {
id: this._worker.getId(),
moduleId: moduleId,
loaderConfiguration: loaderConfiguration
});
}
public request(requestName:string, payload:any): TPromise<any> {
if (requestName.charAt(0) === '$') {
throw new Error('Illegal requestName: ' + requestName);
}
let shouldCancelPromise = false,
messagePromise:TPromise<any>;
return new TPromise<any>((c, e, p) => {
// hide the initialize promise inside this
// promise so that it won't be canceled by accident
this.onModuleLoaded.then(() => {
if (!shouldCancelPromise) {
messagePromise = this._sendMessage(requestName, payload).then(c, e, p);
}
}, e, p);
}, () => {
// forward cancel to the proper promise
if(messagePromise) {
messagePromise.cancel();
} else {
shouldCancelPromise = true;
}
});
}
public dispose(): void {
let promises = Object.keys(this._promises);
if (promises.length > 0) {
console.warn('Terminating a worker with ' + promises.length + ' pending promises:');
console.warn(this._promises);
for (let id in this._promises) {
if (promises.hasOwnProperty(id)) {
this._promises[id].error('Worker forcefully terminated');
}
}
}
this._worker.dispose();
}
private _sendMessage(type:string, payload:any):TPromise<any> {
let msg = {
id: ++this._lastMessageId,
type: type,
timestamp: Date.now(),
payload: payload
};
let pc:(value:any)=>void, pe:(err:any)=>void, pp:(progress:any)=>void;
let promise = new TPromise<any>((c, e, p) => {
pc = c;
pe = e;
pp = p;
}, () => {
this._removeMessage(msg.id);
}
);
this._promises[msg.id] = {
complete: pc,
error: pe,
progress: pp,
type: type,
payload: payload
};
this._enqueueMessage(msg);
return promise;
}
private _enqueueMessage(msg:workerProtocol.IClientMessage): void {
let lastIndexSmallerOrEqual = -1,
i:number;
// Find the right index to insert at - keep the queue ordered by timestamp
for (i = this._messagesQueue.length - 1; i >= 0; i--) {
if (this._messagesQueue[i].timestamp <= msg.timestamp) {
lastIndexSmallerOrEqual = i;
break;
}
}
this._messagesQueue.splice(lastIndexSmallerOrEqual + 1, 0, msg);
this._processMessagesQueue();
}
private _removeMessage(msgId:number): void {
for (let i = 0, len = this._messagesQueue.length; i < len; i++) {
if (this._messagesQueue[i].id === msgId) {
if (this._promises.hasOwnProperty(String(msgId))) {
delete this._promises[String(msgId)];
}
this._messagesQueue.splice(i, 1);
this._processMessagesQueue();
return;
}
}
}
private _processMessagesQueue(): void {
if (this._processQueueTimeout !== -1) {
clearTimeout(this._processQueueTimeout);
this._processQueueTimeout = -1;
}
if (this._messagesQueue.length === 0) {
return;
}
if (this._waitingForWorkerReply) {
return;
}
let delayUntilNextMessage = this._messagesQueue[0].timestamp - (new Date()).getTime();
delayUntilNextMessage = Math.max(0, delayUntilNextMessage);
this._processQueueTimeout = setTimeout(() => {
this._processQueueTimeout = -1;
if (this._messagesQueue.length === 0) {
return;
}
this._waitingForWorkerReply = true;
let msg = this._messagesQueue.shift();
this._postMessage(msg);
}, delayUntilNextMessage);
}
private _postMessage(msg:any): void {
if (this._worker) {
this._worker.postMessage(stringify(msg));
}
}
private _onSerializedMessage(msg:string): void {
let message:workerProtocol.IServerMessage = null;
try {
message = parse(msg);
} catch (e) {
// nothing
}
if (message) {
this._onmessage(message);
}
}
private _onmessage(msg:workerProtocol.IServerMessage): void {
if (!msg.monacoWorker) {
return;
}
if (msg.from && msg.from !== this._worker.getId()) {
return;
}
switch (msg.type) {
case workerProtocol.MessageType.REPLY:
let serverReplyMessage = <workerProtocol.IServerReplyMessage>msg;
this._waitingForWorkerReply = false;
if (!this._promises.hasOwnProperty(String(serverReplyMessage.id))) {
this._onError('Received unexpected message from Worker:', msg);
return;
}
switch (serverReplyMessage.action) {
case workerProtocol.ReplyType.COMPLETE:
this._promises[serverReplyMessage.id].complete(serverReplyMessage.payload);
delete this._promises[serverReplyMessage.id];
break;
case workerProtocol.ReplyType.ERROR:
this._onError('Main Thread sent to worker the following message:', {
type: this._promises[serverReplyMessage.id].type,
payload: this._promises[serverReplyMessage.id].payload
});
this._onError('And the worker replied with an error:', serverReplyMessage.payload);
onUnexpectedError(serverReplyMessage.payload);
this._promises[serverReplyMessage.id].error(serverReplyMessage.payload);
delete this._promises[serverReplyMessage.id];
break;
case workerProtocol.ReplyType.PROGRESS:
this._promises[serverReplyMessage.id].progress(serverReplyMessage.payload);
break;
}
break;
case workerProtocol.MessageType.PRINT:
let serverPrintMessage = <workerProtocol.IServerPrintMessage>msg;
this._consoleLog(serverPrintMessage.level, serverPrintMessage.payload);
break;
default:
this._onError('Received unexpected message from worker:', msg);
}
this._processMessagesQueue();
}
_consoleLog(level:string, payload:any): void {
switch (level) {
case workerProtocol.PrintType.LOG:
console.log(payload);
break;
case workerProtocol.PrintType.DEBUG:
console.info(payload);
break;
case workerProtocol.PrintType.INFO:
console.info(payload);
break;
case workerProtocol.PrintType.WARN:
console.warn(payload);
break;
case workerProtocol.PrintType.ERROR:
console.error(payload);
break;
default:
this._onError('Received unexpected message from Worker:', payload);
}
}
_onError(message:string, error?:any): void {
console.error(message);
console.info(error);
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
/**
* A message sent from the UI thread to a worker
*/
export interface IClientMessage {
id:number;
type:string;
timestamp:number;
payload:any;
}
/**
* A message sent from a worker to the UI thread
*/
export interface IServerMessage {
monacoWorker:boolean;
from:number;
req:string;
type:string;
payload:any;
}
/**
* A message sent from a worker to the UI thread in reply to a UI thread request
*/
export interface IServerReplyMessage extends IServerMessage {
id:number;
action:string;
}
/**
* A message sent from a worker to the UI thread for debugging purposes (console.log, console.info, etc.)
*/
export interface IServerPrintMessage extends IServerMessage {
level:string;
}
export var MessageType = {
INITIALIZE: '$initialize',
REPLY: '$reply',
PRINT: '$print'
};
export var ReplyType = {
COMPLETE: 'complete',
ERROR: 'error',
PROGRESS: 'progress'
};
export var PrintType = {
LOG: 'log',
DEBUG: 'debug',
INFO: 'info',
WARN: 'warn',
ERROR: 'error'
};
/*---------------------------------------------------------------------------------------------
* 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 {setUnexpectedErrorHandler, transformErrorForSerialization} from 'vs/base/common/errors';
import {parse, stringify} from 'vs/base/common/marshalling';
import * as workerProtocol from 'vs/base/common/worker/workerProtocol';
export class WorkerServer {
private _postSerializedMessage:(msg:string)=>void;
private _workerId:number;
private _requestHandler:any;
constructor(postSerializedMessage:(msg:string)=>void) {
this._postSerializedMessage = postSerializedMessage;
this._workerId = 0;
this._requestHandler = null;
this._bindConsole();
}
private _bindConsole(): void {
(<any> self).console = {
log: this._sendPrintMessage.bind(this, workerProtocol.PrintType.LOG),
debug: this._sendPrintMessage.bind(this, workerProtocol.PrintType.DEBUG),
info: this._sendPrintMessage.bind(this, workerProtocol.PrintType.INFO),
warn: this._sendPrintMessage.bind(this, workerProtocol.PrintType.WARN),
error: this._sendPrintMessage.bind(this, workerProtocol.PrintType.ERROR)
};
setUnexpectedErrorHandler((e) => {
self.console.error(e);
});
}
private _sendPrintMessage(level:string, ...objects:any[]): void {
let transformedObjects = objects.map((obj) => (obj instanceof Error) ? transformErrorForSerialization(obj) : obj);
let msg:workerProtocol.IServerPrintMessage = {
monacoWorker: true,
from: this._workerId,
req: '0',
type: workerProtocol.MessageType.PRINT,
level: level,
payload: (transformedObjects.length === 1 ? transformedObjects[0] : transformedObjects)
};
this._postMessage(msg);
}
private _sendReply(msgId:number, action:string, payload:any): void {
let msg:workerProtocol.IServerReplyMessage = {
monacoWorker: true,
from: this._workerId,
req: '0',
id: msgId,
type: workerProtocol.MessageType.REPLY,
action: action,
payload: (payload instanceof Error) ? transformErrorForSerialization(payload) : payload
};
this._postMessage(msg);
}
public loadModule(moduleId:string, callback:Function, errorback:(err:any)=>void): void {
// Use the global require to be sure to get the global config
(<any>self).require([moduleId], (...result:any[]) => {
callback(result[0]);
}, errorback);
}
public onmessage(msg:string): void {
this._onmessage(parse(msg));
}
private _postMessage(msg:workerProtocol.IServerMessage): void {
this._postSerializedMessage(stringify(msg));
}
private _onmessage(msg:workerProtocol.IClientMessage): void {
let c = this._sendReply.bind(this, msg.id, workerProtocol.ReplyType.COMPLETE);
let e = this._sendReply.bind(this, msg.id, workerProtocol.ReplyType.ERROR);
let p = this._sendReply.bind(this, msg.id, workerProtocol.ReplyType.PROGRESS);
switch(msg.type) {
case workerProtocol.MessageType.INITIALIZE:
this._workerId = msg.payload.id;
let loaderConfig = msg.payload.loaderConfiguration;
// TODO@Alex: share this code with simpleWorker
if (loaderConfig) {
// Remove 'baseUrl', handling it is beyond scope for now
if (typeof loaderConfig.baseUrl !== 'undefined') {
delete loaderConfig['baseUrl'];
}
if (typeof loaderConfig.paths !== 'undefined') {
if (typeof loaderConfig.paths.vs !== 'undefined') {
delete loaderConfig.paths['vs'];
}
}
let nlsConfig = loaderConfig['vs/nls'];
// We need to have pseudo translation
if (nlsConfig && nlsConfig.pseudo) {
require(['vs/nls'], function(nlsPlugin) {
nlsPlugin.setPseudoTranslation(nlsConfig.pseudo);
});
}
// Since this is in a web worker, enable catching errors
loaderConfig.catchError = true;
(<any>self).require.config(loaderConfig);
}
this.loadModule(msg.payload.moduleId, (handlerModule:any) => {
this._requestHandler = handlerModule.value;
c();
}, e);
break;
default:
this._handleMessage(msg, c, e, p);
break;
}
}
private _handleMessage(msg:workerProtocol.IClientMessage, c:(value:any)=>void, e:(err:any)=>void, p:(progress:any)=>void): void {
if (!this._requestHandler) {
e('Request handler not loaded');
return;
}
let handlerMethod = this._requestHandler[msg.type];
if (typeof handlerMethod !== 'function') {
e('Handler does not have method ' + msg.type);
return;
}
try {
handlerMethod.call(this._requestHandler, this, c, e, p, msg.payload);
} catch (handlerError) {
e(transformErrorForSerialization(handlerError));
}
}
}
export function create(postMessage:(msg:string)=>void): WorkerServer {
return new WorkerServer(postMessage);
}
......@@ -2,15 +2,20 @@
* 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 {ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
import { compareFileNames } from 'vs/base/common/comparers';
import * as assert from 'assert';
suite('Comparers', () => {
test('compareFileNames', () => {
ModesRegistry.registerCompatMode({
id: 'razor',
extensions: ['.cshtml'],
aliases: ['Razor', 'razor'],
mimetypes: ['text/x-cshtml'],
moduleId: 'vs/languages/razor/common/razor',
ctorName: 'RAZORMode'
assert(compareFileNames('', '') === 0, 'empty should be equal');
assert(compareFileNames('abc', 'abc') === 0, 'equal names should be equal');
assert(compareFileNames('.abc', '.abc') === 0, 'equal full names should be equal');
assert(compareFileNames('.env', '.env.example') < 0);
assert(compareFileNames('.env.example', '.gitattributes') < 0);
});
});
......@@ -5,9 +5,7 @@
'use strict';
import * as flags from 'vs/base/common/flags';
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
import {logOnceWebWorkerWarning, IWorker, IWorkerCallback, IWorkerFactory} from 'vs/base/common/worker/workerClient';
import * as dom from 'vs/base/browser/dom';
import {logOnceWebWorkerWarning, IWorker, IWorkerCallback, IWorkerFactory} from 'vs/base/common/worker/simpleWorker';
function defaultGetWorkerUrl(workerId:string, label:string): string {
return require.toUrl('./' + workerId) + '#' + label;
......@@ -52,117 +50,25 @@ class WebWorker implements IWorker {
}
}
/**
* A worker that runs in an iframe and therefore does have its
* own global scope, but no own thread.
*/
class FrameWorker implements IWorker {
private id: number;
private iframe: HTMLIFrameElement;
private onMessage: EventListener;
private loaded: boolean;
private beforeLoadMessages: any[];
private _listeners: IDisposable[];
constructor(moduleId:string, id: number, onMessageCallback:IWorkerCallback) {
this.id = id;
this._listeners = [];
// Collect all messages sent to the worker until the iframe is loaded
this.loaded = false;
this.beforeLoadMessages = [];
this.postMessage(moduleId);
this.iframe = <HTMLIFrameElement> document.createElement('iframe');
this.iframe.id = this.iframeId();
this.iframe.src = require.toUrl('./workerMainCompatibility.html');
(<any> this.iframe).frameborder = this.iframe.height = this.iframe.width = '0';
this.iframe.style.display = 'none';
this._listeners.push(dom.addDisposableListener(this.iframe, 'load', () => this.onLoaded()));
this.onMessage = function(ev:any) {
onMessageCallback(ev.data);
};
this._listeners.push(dom.addDisposableListener(window, 'message', this.onMessage));
document.body.appendChild(this.iframe);
}
public dispose(): void {
this._listeners = dispose(this._listeners);
window.removeEventListener('message', this.onMessage);
window.frames[this.iframeId()].close();
}
private iframeId(): string {
return 'worker_iframe_' + this.id;
}
private onLoaded(): void {
this.loaded = true;
while (this.beforeLoadMessages.length > 0) {
this.postMessage(this.beforeLoadMessages.shift());
}
}
public getId(): number {
return this.id;
}
public postMessage(msg:string): void {
if (this.loaded === true) {
var iframe = window.frames[this.iframeId()];
if (iframe.postMessage) {
iframe.postMessage(msg, '*');
} else {
iframe.contentWindow.postMessage(msg, '*');
}
} else {
this.beforeLoadMessages.push(msg);
}
}
}
export class DefaultWorkerFactory implements IWorkerFactory {
private static LAST_WORKER_ID = 0;
private _label: string;
private _fallbackToIframe:boolean;
private _webWorkerFailedBeforeError:any;
constructor(label:string, fallbackToIframe:boolean) {
constructor(label:string) {
this._label = label;
this._fallbackToIframe = fallbackToIframe;
this._webWorkerFailedBeforeError = false;
}
public create(moduleId:string, onMessageCallback:IWorkerCallback, onErrorCallback:(err:any)=>void):IWorker {
let workerId = (++DefaultWorkerFactory.LAST_WORKER_ID);
if (this._fallbackToIframe) {
if (this._webWorkerFailedBeforeError) {
// Avoid always trying to create web workers if they would just fail...
return new FrameWorker(moduleId, workerId, onMessageCallback);
}
try {
return new WebWorker(moduleId, workerId, this._label || 'anonymous' + workerId, onMessageCallback, (err) => {
logOnceWebWorkerWarning(err);
this._webWorkerFailedBeforeError = err;
onErrorCallback(err);
});
} catch(err) {
logOnceWebWorkerWarning(err);
return new FrameWorker(moduleId, workerId, onMessageCallback);
}
}
if (this._webWorkerFailedBeforeError) {
throw this._webWorkerFailedBeforeError;
}
return new WebWorker(moduleId, workerId, this._label || 'anonymous' + workerId, onMessageCallback, (err) => {
logOnceWebWorkerWarning(err);
this._webWorkerFailedBeforeError = err;
......
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script type="text/javascript" src="../../loader.js"></script>
<script>
require.config({
baseUrl: '../../../',
catchError: true
});
(function() {
var isFirstMessage = true;
var beforeReadyMessages = [];
var loadCode = function(moduleId) {
require([moduleId], function(ws) {
var workerServer = ws.create(function (msg) {
window.parent.postMessage(msg, '*');
});
var readyListener = function (ev) {
workerServer.onmessage(ev.data);
};
if (window.attachEvent) {
window.detachEvent('onmessage', beforeReadyListener);
window.attachEvent('onmessage', readyListener);
} else {
window.onmessage = readyListener;
}
while(beforeReadyMessages.length > 0) {
readyListener({ data: beforeReadyMessages.shift() });
}
});
};
var beforeReadyListener = function (message) {
if (!isFirstMessage) {
beforeReadyMessages.push(message.data);
return;
}
isFirstMessage = false;
loadCode(message.data);
};
if (window.attachEvent) {
window.attachEvent('onmessage', beforeReadyListener);
} else {
window.onmessage = beforeReadyListener;
}
})();
</script>
</head>
<body>
<div>compatibility worker iframe</div>
</body>
</html>
......@@ -43,5 +43,3 @@ import 'vs/editor/contrib/wordHighlighter/common/wordHighlighter';
import 'vs/editor/contrib/folding/browser/folding';
import 'vs/editor/contrib/indentation/common/indentation';
// include these in the editor bundle because they are widely used by many languages
import 'vs/editor/common/languages.common';
......@@ -37,7 +37,6 @@ export function register(language:ILanguageExtensionPoint): void {
export function getLanguages(): ILanguageExtensionPoint[] {
let result:ILanguageExtensionPoint[] = [];
result = result.concat(ModesRegistry.getLanguages());
result = result.concat(ModesRegistry.getCompatModes());
return result;
}
......@@ -105,7 +104,24 @@ export function registerSignatureHelpProvider(languageId:string, provider:modes.
* Register a hover provider (used by e.g. editor hover).
*/
export function registerHoverProvider(languageId:string, provider:modes.HoverProvider): IDisposable {
return modes.HoverProviderRegistry.register(languageId, provider);
return modes.HoverProviderRegistry.register(languageId, {
provideHover: (model:editorCommon.IReadOnlyModel, position:Position, token:CancellationToken): Thenable<modes.Hover> => {
let word = model.getWordAtPosition(position);
return toThenable<modes.Hover>(provider.provideHover(model, position, token)).then((value) => {
if (!value) {
return;
}
if (!value.range && word) {
value.range = new Range(position.lineNumber, word.startColumn, position.column, word.endColumn);
}
if (!value.range) {
value.range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
}
return value;
});
}
});
}
/**
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
exports.collectModules = function() {
return [];
};
......@@ -10,7 +10,7 @@ import {Disposable, IDisposable, dispose} from 'vs/base/common/lifecycle';
import {TPromise} from 'vs/base/common/winjs.base';
import {ServicesAccessor, IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection';
import {IContextKey, IContextKeyServiceTarget, IContextKeyService} from 'vs/platform/contextkey/common/contextkey';
import {ContextKeyExpr, IContextKey, IContextKeyServiceTarget, IContextKeyService} from 'vs/platform/contextkey/common/contextkey';
import {CommonEditorConfiguration} from 'vs/editor/common/config/commonEditorConfig';
import {DefaultConfig} from 'vs/editor/common/config/defaultConfig';
import {Cursor} from 'vs/editor/common/controller/cursor';
......@@ -27,6 +27,8 @@ import {SplitLinesCollection} from 'vs/editor/common/viewModel/splitLinesCollect
import {ViewModel} from 'vs/editor/common/viewModel/viewModelImpl';
import {hash} from 'vs/base/common/hash';
import {EditorModeContext} from 'vs/editor/common/modes/editorModeContext';
import {MenuId, MenuRegistry, IMenuItem} from 'vs/platform/actions/common/actions';
import {CommandsRegistry} from 'vs/platform/commands/common/commands';
import EditorContextKeys = editorCommon.EditorContextKeys;
......@@ -511,7 +513,30 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
) {
throw new Error('Invalid action descriptor, `id`, `label` and `run` are required properties!');
}
// Generate a unique id to allow the same descriptor.id across multiple editor instances
let uniqueId = this.getId() + ':' + descriptor.id;
let action = new DynamicEditorAction(descriptor, this);
// Register the command
CommandsRegistry.registerCommand(uniqueId, () => action.run());
if (descriptor.contextMenuGroupId) {
let menuItem: IMenuItem = {
command: {
id: uniqueId,
title: descriptor.label
},
when: ContextKeyExpr.equals('editorId', this.getId()),
group: descriptor.contextMenuGroupId,
order: descriptor.contextMenuOrder || 0
};
// Register the menu item
MenuRegistry.appendMenuItem(MenuId.EditorContext, menuItem);
}
this._actions[action.id] = action;
}
......
......@@ -3439,6 +3439,20 @@ export interface IActionDescriptor {
* An array of keybindings for the action.
*/
keybindings?: number[];
/**
* Control if the action should show up in the context menu and where.
* The context menu of the editor has these default:
* navigation - The navigation group comes first in all cases.
* 1_modification - This group comes next and contains commands that modify your code.
* 9_cutcopypaste - The last default group with the basic editing commands.
* You can also create your own group.
* Defaults to null (don't show in context menu).
*/
contextMenuGroupId?: string;
/**
* Control the order in the context menu group.
*/
contextMenuOrder?: number;
/**
* The keybinding rule.
*/
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
// base common
import 'vs/base/common/assert';
import 'vs/base/common/async';
import 'vs/base/common/callbackList';
import 'vs/base/common/cancellation';
import 'vs/base/common/collections';
import 'vs/base/common/event';
import 'vs/base/common/events';
import 'vs/base/common/lifecycle';
import 'vs/base/common/paths';
import 'vs/base/common/uri';
// platform common
import 'vs/platform/platform';
import 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import 'vs/platform/workspace/common/workspace';
import 'vs/platform/telemetry/common/telemetry';
// editor common
import 'vs/editor/common/editorCommon';
import 'vs/editor/common/modes';
import 'vs/editor/common/modes/abstractMode';
import 'vs/editor/common/modes/abstractState';
import 'vs/editor/common/modes/monarch/monarchCommon';
import 'vs/editor/common/modes/monarch/monarchLexer';
import 'vs/editor/common/modes/monarch/monarchCompile';
import 'vs/editor/common/modes/languageConfigurationRegistry';
import 'vs/editor/common/modes/supports/suggestSupport';
import 'vs/editor/common/modes/supports/tokenizationSupport';
import 'vs/editor/common/services/modelService';
import 'vs/editor/common/services/modeService';
import 'vs/editor/common/services/compatWorkerService';
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import * as editorCommon from 'vs/editor/common/editorCommon';
import {ModelLine} from 'vs/editor/common/model/modelLine';
import {TextModelWithTokens} from 'vs/editor/common/model/textModelWithTokens';
import {ICompatMirrorModel} from 'vs/editor/common/services/resourceService';
export interface ICompatMirrorModelEvents {
contentChanged: editorCommon.IModelContentChangedEvent[];
}
const NO_TAB_SIZE = 0;
export class CompatMirrorModel extends TextModelWithTokens implements ICompatMirrorModel {
protected _associatedResource:URI;
constructor(versionId:number, value:editorCommon.IRawText, languageId:string, associatedResource?:URI) {
super(['changed', editorCommon.EventType.ModelDispose], value, languageId);
this._setVersionId(versionId);
this._associatedResource = associatedResource;
}
public dispose(): void {
this.emit(editorCommon.EventType.ModelDispose);
super.dispose();
}
public get uri(): URI {
return this._associatedResource;
}
protected _constructLines(rawText:editorCommon.IRawText):void {
super._constructLines(rawText);
// Force EOL to be \n
this._EOL = '\n';
}
public onEvents(events:ICompatMirrorModelEvents) : void {
let changed = false;
for (let i = 0, len = events.contentChanged.length; i < len; i++) {
let contentChangedEvent = events.contentChanged[i];
this._setVersionId(contentChangedEvent.versionId);
switch (contentChangedEvent.changeType) {
case editorCommon.EventType.ModelRawContentChangedFlush:
this._onLinesFlushed(<editorCommon.IModelContentChangedFlushEvent>contentChangedEvent);
changed = true;
break;
case editorCommon.EventType.ModelRawContentChangedLinesDeleted:
this._onLinesDeleted(<editorCommon.IModelContentChangedLinesDeletedEvent>contentChangedEvent);
changed = true;
break;
case editorCommon.EventType.ModelRawContentChangedLinesInserted:
this._onLinesInserted(<editorCommon.IModelContentChangedLinesInsertedEvent>contentChangedEvent);
changed = true;
break;
case editorCommon.EventType.ModelRawContentChangedLineChanged:
this._onLineChanged(<editorCommon.IModelContentChangedLineChangedEvent>contentChangedEvent);
changed = true;
break;
}
}
if (changed) {
this.emit('changed', {});
}
}
private _onLinesFlushed(e:editorCommon.IModelContentChangedFlushEvent): void {
// Flush my lines
this._constructLines(e.detail);
this._resetTokenizationState();
}
private _onLineChanged(e:editorCommon.IModelContentChangedLineChangedEvent) : void {
this._lines[e.lineNumber - 1].applyEdits({}, [{
startColumn: 1,
endColumn: Number.MAX_VALUE,
text: e.detail,
forceMoveMarkers: false
}], NO_TAB_SIZE);
if (this._lineStarts) {
// update prefix sum
this._lineStarts.changeValue(e.lineNumber - 1, this._lines[e.lineNumber - 1].text.length + this._EOL.length);
}
this._invalidateLine(e.lineNumber - 1);
}
private _onLinesDeleted(e:editorCommon.IModelContentChangedLinesDeletedEvent) : void {
var fromLineIndex = e.fromLineNumber - 1,
toLineIndex = e.toLineNumber - 1;
// Save first line's state
var firstLineState = this._lines[fromLineIndex].getState();
this._lines.splice(fromLineIndex, toLineIndex - fromLineIndex + 1);
if (this._lineStarts) {
// update prefix sum
this._lineStarts.removeValues(fromLineIndex, toLineIndex - fromLineIndex + 1);
}
if (fromLineIndex < this._lines.length) {
// This check is always true in real world, but the tests forced this
// Restore first line's state
this._lines[fromLineIndex].setState(firstLineState);
// Invalidate line
this._invalidateLine(fromLineIndex);
}
}
private _onLinesInserted(e:editorCommon.IModelContentChangedLinesInsertedEvent) : void {
var lineIndex:number,
i:number,
splitLines = e.detail.split('\n');
let newLengths:number[] = [];
for (lineIndex = e.fromLineNumber - 1, i = 0; lineIndex < e.toLineNumber; lineIndex++, i++) {
this._lines.splice(lineIndex, 0, new ModelLine(0, splitLines[i], NO_TAB_SIZE));
newLengths.push(splitLines[i].length + this._EOL.length);
}
if (this._lineStarts) {
// update prefix sum
this._lineStarts.insertValues(e.fromLineNumber - 1, newLengths);
}
if (e.fromLineNumber >= 2) {
// This check is always true in real world, but the tests forced this
this._invalidateLine(e.fromLineNumber - 2);
}
}
}
......@@ -15,7 +15,7 @@ export const USUAL_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?';
* The default would look like this:
* /(-?\d*\.\d\w*)|([^\`\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g
*/
export function createWordRegExp(allowInWords:string = ''): RegExp {
function createWordRegExp(allowInWords:string = ''): RegExp {
var usualSeparators = USUAL_WORD_SEPARATORS;
var source = '(-?\\d*\\.\\d\\w*)|([^';
for (var i = 0; i < usualSeparators.length; i++) {
......
......@@ -18,15 +18,6 @@ import {Position} from 'vs/editor/common/core/position';
import {Range} from 'vs/editor/common/core/range';
import Event, {Emitter} from 'vs/base/common/event';
/**
* @internal
*/
export interface ITokenizationResult {
type?:string;
dontMergeWithPrev?:boolean;
nextState?:IState;
}
/**
* @internal
*/
......@@ -34,131 +25,10 @@ export interface IState {
clone():IState;
equals(other:IState):boolean;
getModeId():string;
tokenize(stream:IStream):ITokenizationResult;
getStateData(): IState;
setStateData(state:IState):void;
}
/**
* An IStream is a character & token stream abstraction over a line of text. It
* is never multi-line. The stream can be navigated character by character, or
* token by token, given some token rules.
* @internal
*/
export interface IStream {
/**
* Returns the current character position of the stream on the line.
*/
pos():number;
/**
* Returns true iff the stream is at the end of the line.
*/
eos():boolean;
/**
* Returns the next character in the stream.
*/
peek():string;
/**
* Returns the next character in the stream, and advances it by one character.
*/
next(): string;
next2(): void;
/**
* Advances the stream by `n` characters.
*/
advance(n:number):string;
/**
* Advances the stream until the end of the line.
*/
advanceToEOS():string;
/**
* Brings the stream back `n` characters.
*/
goBack(n:number):void;
/**
* Advances the stream if the next characters validate a condition. A condition can be
*
* - a regular expression (always starting with ^)
* EXAMPLES: /^\d+/, /^function|var|interface|class/
*
* - a string
* EXAMPLES: "1954", "albert"
*/
advanceIfCharCode(charCode: number): string;
advanceIfCharCode2(charCode:number): number;
advanceIfString(condition: string): string;
advanceIfString2(condition: string): number;
advanceIfStringCaseInsensitive(condition: string): string;
advanceIfStringCaseInsensitive2(condition: string): number;
advanceIfRegExp(condition: RegExp): string;
advanceIfRegExp2(condition:RegExp): number;
/**
* Advances the stream while the next characters validate a condition. Check #advanceIf for
* details on the possible types for condition.
*/
advanceWhile(condition:string):string;
advanceWhile(condition:RegExp):string;
/**
* Advances the stream until the some characters validate a condition. Check #advanceIf for
* details on the possible types for condition. The `including` boolean value indicates
* whether the stream will advance the characters that matched the condition as well, or not.
*/
advanceUntil(condition: string, including: boolean): string;
advanceUntil(condition: RegExp, including: boolean): string;
advanceUntilString(condition: string, including: boolean): string;
advanceUntilString2(condition: string, including: boolean): number;
/**
* The token rules define how consecutive characters should be put together as a token,
* or separated into two different tokens. They are given through a separator characters
* string and a whitespace characters string. A separator is always one token. Consecutive
* whitespace is always one token. Everything in between these two token types, is also a token.
*
* EXAMPLE: stream.setTokenRules("+-", " ");
* Setting these token rules defines the tokens for the string "123+456 - 7" as being
* ["123", "+", "456", " ", "-", " ", "7"]
*/
setTokenRules(separators:string, whitespace:string):void;
/**
* Returns the next token, given that the stream was configured with token rules.
*/
peekToken():string;
/**
* Returns the next token, given that the stream was configured with token rules, and advances the
* stream by the exact length of the found token.
*/
nextToken():string;
/**
* Returns the next whitespace, if found. Returns an empty string otherwise.
*/
peekWhitespace():string;
/**
* Returns the next whitespace, if found, and advances the stream by the exact length of the found
* whitespace. Returns an empty string otherwise.
*/
skipWhitespace(): string;
skipWhitespace2(): number;
}
/**
* @internal
*/
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {IState, ITokenizationResult} from 'vs/editor/common/modes';
import {IState} from 'vs/editor/common/modes';
import {AbstractState} from 'vs/editor/common/modes/abstractState';
import {StackElement} from 'vscode-textmate';
......@@ -50,10 +50,6 @@ export class TMState implements IState {
return this._modeId;
}
public tokenize(stream:any):ITokenizationResult {
throw new Error();
}
public getStateData():IState {
return this._parentEmbedderState;
}
......
......@@ -4,77 +4,13 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {TPromise} from 'vs/base/common/winjs.base';
import {AsyncDescriptor1, createAsyncDescriptor1} from 'vs/platform/instantiation/common/descriptors';
import {IInstantiationService, optional} from 'vs/platform/instantiation/common/instantiation';
import {optional} from 'vs/platform/instantiation/common/instantiation';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import * as modes from 'vs/editor/common/modes';
import {TextualSuggestSupport} from 'vs/editor/common/modes/supports/suggestSupport';
import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService';
import * as wordHelper from 'vs/editor/common/model/wordHelper';
import {ICompatWorkerService, ICompatMode} from 'vs/editor/common/services/compatWorkerService';
import {CharCode} from 'vs/base/common/charCode';
export function createWordRegExp(allowInWords:string = ''): RegExp {
return wordHelper.createWordRegExp(allowInWords);
}
export class ModeWorkerManager<W> {
private _descriptor: modes.IModeDescriptor;
private _workerDescriptor: AsyncDescriptor1<string, W>;
private _superWorkerModuleId: string;
private _instantiationService: IInstantiationService;
private _workerPiecePromise:TPromise<W>;
constructor(
descriptor:modes.IModeDescriptor,
workerModuleId:string,
workerClassName:string,
superWorkerModuleId:string,
instantiationService: IInstantiationService
) {
this._descriptor = descriptor;
this._workerDescriptor = createAsyncDescriptor1(workerModuleId, workerClassName);
this._superWorkerModuleId = superWorkerModuleId;
this._instantiationService = instantiationService;
this._workerPiecePromise = null;
}
public worker<T>(runner:(worker:W)=>TPromise<T>): TPromise<T>
public worker<T>(runner:(worker:W)=>T): TPromise<T> {
return this._getOrCreateWorker().then(runner);
}
private _getOrCreateWorker(): TPromise<W> {
if (!this._workerPiecePromise) {
// TODO@Alex: workaround for missing `bundles` config
// First, load the code of the worker super class
let superWorkerCodePromise = (this._superWorkerModuleId ? ModeWorkerManager._loadModule(this._superWorkerModuleId) : TPromise.as(null));
this._workerPiecePromise = superWorkerCodePromise.then(() => {
// Second, load the code of the worker (without instantiating it)
return ModeWorkerManager._loadModule(this._workerDescriptor.moduleName);
}).then(() => {
// Finally, create the mode worker instance
return this._instantiationService.createInstance<string, W>(this._workerDescriptor, this._descriptor.id);
});
}
return this._workerPiecePromise;
}
private static _loadModule(moduleName:string): TPromise<any> {
return new TPromise((c, e, p) => {
// Use the global require to be sure to get the global config
(<any>self).require([moduleName], c, e);
}, () => {
// Cannot cancel loading code
});
}
}
// TODO@Alex: inline to FrankensteinMode, review optional IEditorWorkerService
export abstract class AbstractMode implements modes.IMode {
private _modeId: string;
......@@ -88,59 +24,6 @@ export abstract class AbstractMode implements modes.IMode {
}
}
export abstract class CompatMode extends AbstractMode implements ICompatMode {
public compatWorkerService:ICompatWorkerService;
constructor(modeId:string, compatWorkerService:ICompatWorkerService) {
super(modeId);
this.compatWorkerService = compatWorkerService;
if (this.compatWorkerService) {
this.compatWorkerService.registerCompatMode(this);
}
}
}
export function isDigit(character:string, base:number): boolean {
let c = character.charCodeAt(0);
switch (base) {
case 1:
return c === CharCode.Digit0;
case 2:
return c >= CharCode.Digit0 && c <= CharCode.Digit1;
case 3:
return c >= CharCode.Digit0 && c <= CharCode.Digit2;
case 4:
return c >= CharCode.Digit0 && c <= CharCode.Digit3;
case 5:
return c >= CharCode.Digit0 && c <= CharCode.Digit4;
case 6:
return c >= CharCode.Digit0 && c <= CharCode.Digit5;
case 7:
return c >= CharCode.Digit0 && c <= CharCode.Digit6;
case 8:
return c >= CharCode.Digit0 && c <= CharCode.Digit7;
case 9:
return c >= CharCode.Digit0 && c <= CharCode.Digit8;
case 10:
return c >= CharCode.Digit0 && c <= CharCode.Digit9;
case 11:
return (c >= CharCode.Digit0 && c <= CharCode.Digit9) || (c === CharCode.a) || (c === CharCode.A);
case 12:
return (c >= CharCode.Digit0 && c <= CharCode.Digit9) || (c >= CharCode.a && c <= CharCode.b) || (c >= CharCode.A && c <= CharCode.B);
case 13:
return (c >= CharCode.Digit0 && c <= CharCode.Digit9) || (c >= CharCode.a && c <= CharCode.c) || (c >= CharCode.A && c <= CharCode.C);
case 14:
return (c >= CharCode.Digit0 && c <= CharCode.Digit9) || (c >= CharCode.a && c <= CharCode.d) || (c >= CharCode.A && c <= CharCode.D);
case 15:
return (c >= CharCode.Digit0 && c <= CharCode.Digit9) || (c >= CharCode.a && c <= CharCode.e) || (c >= CharCode.A && c <= CharCode.E);
default:
return (c >= CharCode.Digit0 && c <= CharCode.Digit9) || (c >= CharCode.a && c <= CharCode.f) || (c >= CharCode.A && c <= CharCode.F);
}
}
export class FrankensteinMode extends AbstractMode {
constructor(
......
......@@ -4,10 +4,22 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {IState, IStream, ITokenizationResult} from 'vs/editor/common/modes';
import {IState} from 'vs/editor/common/modes';
import {LineStream} from 'vs/editor/common/modes/lineStream';
/**
* @internal
*/
export interface ITokenizationResult {
type?:string;
dontMergeWithPrev?:boolean;
nextState?:AbstractState;
}
export abstract class AbstractState implements IState {
_abstractStateBrand: void;
private modeId:string;
private stateData:IState;
......@@ -50,7 +62,7 @@ export abstract class AbstractState implements IState {
return false;
}
public abstract tokenize(stream:IStream):ITokenizationResult;
public abstract tokenize(stream:LineStream):ITokenizationResult;
public static safeEquals(a: IState, b: IState): boolean {
if (a === null && b === null) {
......
......@@ -4,66 +4,41 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {IStream} from 'vs/editor/common/modes';
import {CharacterClassifier} from 'vs/editor/common/core/characterClassifier';
class CharacterSet {
private static _CACHE:{ [key:string]:CharacterSet; } = {}; // TODO@Alex unbounded cache
public static getOrCreate(source:string): CharacterSet {
if (!CharacterSet._CACHE.hasOwnProperty(source)) {
CharacterSet._CACHE[source] = new CharacterSet(source);
}
return CharacterSet._CACHE[source];
}
private _classifier: CharacterClassifier<boolean>;
constructor(source:string) {
this._classifier = new CharacterClassifier<boolean>(false);
for (let i = 0, len = source.length; i < len; i++) {
this._classifier.set(source.charCodeAt(i), true);
}
}
public contains(charCode:number): boolean {
return this._classifier.get(charCode);
}
}
export class LineStream implements IStream {
/**
* A LineStream is a character & token stream abstraction over a line of text. It
* is never multi-line. The stream can be navigated character by character, or
* token by token, given some token rules.
* @internal
*/
export class LineStream {
private _source:string;
private _sourceLength:number;
private _pos:number;
private _whitespace:string;
private _whitespaceArr:CharacterSet;
private _separators:string;
private _separatorsArr:CharacterSet;
private _tokenStart:number;
private _tokenEnd:number;
constructor(source:string) {
this._source = source;
this._sourceLength = source.length;
this._pos = 0;
this._whitespace = '\t \u00a0';
this._whitespaceArr = CharacterSet.getOrCreate(this._whitespace);
this._separators = '';
this._separatorsArr = CharacterSet.getOrCreate(this._separators);
this._tokenStart = -1;
this._tokenEnd = -1;
}
/**
* Returns the current character position of the stream on the line.
*/
public pos():number {
return this._pos;
}
/**
* Returns true iff the stream is at the end of the line.
*/
public eos() {
return this._pos >= this._sourceLength;
}
/**
* Returns the next character in the stream.
*/
public peek():string {
// Check EOS
if (this._pos >= this._sourceLength) {
......@@ -72,359 +47,29 @@ export class LineStream implements IStream {
return this._source[this._pos];
}
public next():string {
// Check EOS
if (this._pos >= this._sourceLength) {
throw new Error('Stream is at the end');
}
// Reset peeked token
this._tokenStart = -1;
this._tokenEnd = -1;
return this._source[this._pos++];
}
public next2(): void {
// Check EOS
if (this._pos >= this._sourceLength) {
throw new Error('Stream is at the end');
}
// Reset peeked token
this._tokenStart = -1;
this._tokenEnd = -1;
this._pos++;
}
public advance(n: number): string {
/**
* Advances the stream by `n` characters.
*/
public advance(n: number): void {
if (n === 0) {
return '';
return;
}
const oldPos = this._pos;
this._pos += n;
// Reset peeked token
this._tokenStart = -1;
this._tokenEnd = -1;
return this._source.substring(oldPos, this._pos);
}
private _advance2(n: number): number {
if (n === 0) {
return n;
}
this._pos += n;
// Reset peeked token
this._tokenStart = -1;
this._tokenEnd = -1;
return n;
}
/**
* Advances the stream until the end of the line.
*/
public advanceToEOS():string {
const oldPos = this._pos;
this._pos = this._sourceLength;
this.resetPeekedToken();
return this._source.substring(oldPos, this._pos);
}
/**
* Brings the stream back `n` characters.
*/
public goBack(n:number) {
this._pos -= n;
this.resetPeekedToken();
}
private createPeeker(condition:RegExp|string):()=>number {
if (condition instanceof RegExp) {
return () => {
let result = condition.exec(this._source.substr(this._pos));
if (result === null) {
return 0;
} else if (result.index !== 0) {
throw new Error('Regular expression must begin with the character "^"');
}
return result[0].length;
};
} else if ((typeof condition === 'string') && condition) {
return () => {
const len = condition.length;
let match = (this._pos + len <= this._sourceLength);
for (let i = 0; match && i < len; i++) {
match = this._source.charCodeAt(this._pos + i) === condition.charCodeAt(i);
}
return (match ? len : 0);
};
}
throw new Error('Condition must be either a regular expression, function or a non-empty string');
}
// --- BEGIN `_advanceIfStringCaseInsensitive`
private _advanceIfStringCaseInsensitive(condition:string): number {
const oldPos = this._pos;
const source = this._source;
const len = condition.length;
if (len < 1 || oldPos + len > this._sourceLength) {
return 0;
}
for (let i = 0; i < len; i++) {
if (source.charAt(oldPos + i).toLowerCase() !== condition.charAt(i).toLowerCase()) {
return 0;
}
}
return len;
}
public advanceIfStringCaseInsensitive(condition: string): string {
return this.advance(this._advanceIfStringCaseInsensitive(condition));
}
public advanceIfStringCaseInsensitive2(condition: string): number {
return this._advance2(this._advanceIfStringCaseInsensitive(condition));
}
// --- END
// --- BEGIN `advanceIfString`
private _advanceIfString(condition: string): number {
const oldPos = this._pos;
const source = this._source;
const len = condition.length;
if (len < 1 || oldPos + len > this._sourceLength) {
return 0;
}
for (let i = 0; i < len; i++) {
if (source.charCodeAt(oldPos + i) !== condition.charCodeAt(i)) {
return 0;
}
}
return len;
}
public advanceIfString(condition:string): string {
return this.advance(this._advanceIfString(condition));
}
public advanceIfString2(condition: string): number {
return this._advance2(this._advanceIfString(condition));
}
// --- END
// --- BEGIN `advanceIfString`
private _advanceIfCharCode(charCode:number): number {
if (this._pos < this._sourceLength && this._source.charCodeAt(this._pos) === charCode) {
return 1;
}
return 0;
}
public advanceIfCharCode(charCode: number): string {
return this.advance(this._advanceIfCharCode(charCode));
}
public advanceIfCharCode2(charCode: number): number {
return this._advance2(this._advanceIfCharCode(charCode));
}
// --- END
// --- BEGIN `advanceIfRegExp`
private _advanceIfRegExp(condition:RegExp): number {
if (this._pos >= this._sourceLength) {
return 0;
}
if (!condition.test(this._source.substr(this._pos))) {
return 0;
}
return RegExp.lastMatch.length;
}
public advanceIfRegExp(condition: RegExp): string {
return this.advance(this._advanceIfRegExp(condition));
}
public advanceIfRegExp2(condition: RegExp): number {
return this._advance2(this._advanceIfRegExp(condition));
}
// --- END
private advanceLoop(condition:RegExp|string, isWhile:boolean, including:boolean):string {
if (this.eos()) {
return '';
}
const peeker = this.createPeeker(condition);
const oldPos = this._pos;
let n = 0;
let f = null;
if (isWhile) {
f = (n) => {
return n > 0;
};
} else {
f = (n) => {
return n === 0;
};
}
while (!this.eos() && f(n = peeker())) {
if (n > 0) {
this.advance(n);
} else {
this.next();
}
}
if (including && !this.eos()) {
this.advance(n);
}
return this._source.substring(oldPos, this._pos);
}
public advanceWhile(condition:RegExp|string):string {
return this.advanceLoop(condition, true, false);
}
public advanceUntil(condition:RegExp|string, including:boolean):string {
return this.advanceLoop(condition, false, including);
}
// --- BEGIN `advanceUntilString`
private _advanceUntilString(condition: string, including: boolean): number {
if (this.eos() || condition.length === 0) {
return 0;
}
const oldPos = this._pos;
const index = this._source.indexOf(condition, oldPos);
if (index === -1) {
// String was not found => advanced to `eos`
return (this._sourceLength - oldPos);
}
if (including) {
// String was found => advance to include `condition`
return (index + condition.length - oldPos);
}
// String was found => advance right before `condition`
return (index - oldPos);
}
public advanceUntilString(condition: string, including: boolean): string {
return this.advance(this._advanceUntilString(condition, including));
}
public advanceUntilString2(condition: string, including: boolean): number {
return this._advance2(this._advanceUntilString(condition, including));
}
// --- END
private resetPeekedToken() {
this._tokenStart = -1;
this._tokenEnd = -1;
}
public setTokenRules(separators:string, whitespace:string):void {
if (this._separators !== separators || this._whitespace !== whitespace) {
this._separators = separators;
this._separatorsArr = CharacterSet.getOrCreate(this._separators);
this._whitespace = whitespace;
this._whitespaceArr = CharacterSet.getOrCreate(this._whitespace);
this.resetPeekedToken();
}
}
// --- tokens
public peekToken():string {
if (this._tokenStart !== -1) {
return this._source.substring(this._tokenStart, this._tokenEnd);
}
const source = this._source;
const sourceLength = this._sourceLength;
const whitespaceArr = this._whitespaceArr;
const separatorsArr = this._separatorsArr;
let tokenStart = this._pos;
// Check EOS
if (tokenStart >= sourceLength) {
throw new Error('Stream is at the end');
}
// Skip whitespace
while (whitespaceArr.contains(source.charCodeAt(tokenStart)) && tokenStart < sourceLength) {
tokenStart++;
}
let tokenEnd = tokenStart;
// If a separator is hit, it is a token
if (separatorsArr.contains(source.charCodeAt(tokenEnd)) && tokenEnd < sourceLength) {
tokenEnd++;
} else {
// Advance until a separator or a whitespace is hit
while (!separatorsArr.contains(source.charCodeAt(tokenEnd)) && !whitespaceArr.contains(source.charCodeAt(tokenEnd)) && tokenEnd < sourceLength) {
tokenEnd++;
}
}
// Cache peeked token
this._tokenStart = tokenStart;
this._tokenEnd = tokenEnd;
return source.substring(tokenStart, tokenEnd);
}
public nextToken():string {
// Check EOS
if (this._pos >= this._sourceLength) {
throw new Error('Stream is at the end');
}
// Peek token if necessary
let result:string;
if (this._tokenStart === -1) {
result = this.peekToken();
} else {
result = this._source.substring(this._tokenStart, this._tokenEnd);
}
// Advance to tokenEnd
this._pos = this._tokenEnd;
// Reset peeked token
this._tokenStart = -1;
this._tokenEnd = -1;
return result;
}
// -- whitespace
public peekWhitespace():string {
const source = this._source;
const sourceLength = this._sourceLength;
const whitespaceArr = this._whitespaceArr;
let peek = this._pos;
while (whitespaceArr.contains(source.charCodeAt(peek)) && peek < sourceLength) {
peek++;
}
return source.substring(this._pos, peek);
}
// --- BEGIN `skipWhitespace`
private _skipWhitespace(): number {
const source = this._source;
const sourceLength = this._sourceLength;
const whitespaceArr = this._whitespaceArr;
const oldPos = this._pos;
let peek = this._pos;
while (whitespaceArr.contains(source.charCodeAt(peek)) && peek < sourceLength) {
peek++;
}
return (peek - oldPos);
}
public skipWhitespace(): string {
return this.advance(this._skipWhitespace());
}
public skipWhitespace2(): number {
return this._advance2(this._skipWhitespace());
}
// --- END
}
......@@ -9,18 +9,6 @@ import Event, {Emitter} from 'vs/base/common/event';
import {Registry} from 'vs/platform/platform';
import {ILanguageExtensionPoint} from 'vs/editor/common/services/modeService';
export interface ILegacyLanguageDefinition {
id: string;
extensions: string[];
filenames?: string[];
firstLine?: string;
aliases: string[];
mimetypes: string[];
moduleId: string;
ctorName: string;
deps?: string[];
}
// Define extension point ids
export var Extensions = {
ModesRegistry: 'editor.modesRegistry'
......@@ -28,35 +16,15 @@ export var Extensions = {
export class EditorModesRegistry {
private _compatModes: ILegacyLanguageDefinition[];
private _languages: ILanguageExtensionPoint[];
private _onDidAddCompatModes: Emitter<ILegacyLanguageDefinition[]> = new Emitter<ILegacyLanguageDefinition[]>();
public onDidAddCompatModes: Event<ILegacyLanguageDefinition[]> = this._onDidAddCompatModes.event;
private _onDidAddLanguages: Emitter<ILanguageExtensionPoint[]> = new Emitter<ILanguageExtensionPoint[]>();
public onDidAddLanguages: Event<ILanguageExtensionPoint[]> = this._onDidAddLanguages.event;
constructor() {
this._compatModes = [];
this._languages = [];
}
// --- compat modes
public registerCompatModes(def:ILegacyLanguageDefinition[]): void {
this._compatModes = this._compatModes.concat(def);
this._onDidAddCompatModes.fire(def);
}
public registerCompatMode(def:ILegacyLanguageDefinition): void {
this._compatModes.push(def);
this._onDidAddCompatModes.fire([def]);
}
public getCompatModes(): ILegacyLanguageDefinition[] {
return this._compatModes.slice(0);
}
// --- languages
public registerLanguage(def:ILanguageExtensionPoint): void {
......
......@@ -10,7 +10,7 @@
*/
import * as modes from 'vs/editor/common/modes';
import {AbstractState} from 'vs/editor/common/modes/abstractState';
import {AbstractState, ITokenizationResult} from 'vs/editor/common/modes/abstractState';
import {LineStream} from 'vs/editor/common/modes/lineStream';
import * as monarchCommon from 'vs/editor/common/modes/monarch/monarchCommon';
import {IModeLocator, TokenizationSupport} from 'vs/editor/common/modes/supports/tokenizationSupport';
......@@ -94,7 +94,7 @@ export class MonarchLexer extends AbstractState {
* TODO: there are many optimizations possible here for the common cases
* but for now I concentrated on functionality and correctness.
*/
public tokenize(stream: modes.IStream, noConsumeIsOk?: boolean): modes.ITokenizationResult {
public tokenize(stream: LineStream, noConsumeIsOk?: boolean): ITokenizationResult {
var stackLen0 = this.stack.length; // these are saved to check progress
var groupLen0 = 0;
var state: string = this.stack[0]; // the current state
......@@ -388,7 +388,7 @@ function findBracket(lexer: monarchCommon.ILexer, matched: string) {
export function createTokenizationSupport(_modeService:IModeService, modeId:string, lexer: monarchCommon.ILexer): modes.ITokenizationSupport {
return new TokenizationSupport(_modeService, modeId, {
getInitialState: (): modes.IState => {
getInitialState: (): AbstractState => {
return new MonarchLexer(modeId, _modeService, lexer);
},
......
......@@ -4,9 +4,11 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {IState, IStream, ITokenizationResult, ILineTokens} from 'vs/editor/common/modes';
import {IState, ILineTokens} from 'vs/editor/common/modes';
import {ModeTransition} from 'vs/editor/common/core/modeTransition';
import {Token} from 'vs/editor/common/core/token';
import {ITokenizationResult} from 'vs/editor/common/modes/abstractState';
import {LineStream} from 'vs/editor/common/modes/lineStream';
export class NullState implements IState {
......@@ -41,7 +43,7 @@ export class NullState implements IState {
return this.modeId;
}
public tokenize(stream:IStream):ITokenizationResult {
public tokenize(stream:LineStream):ITokenizationResult {
stream.advanceToEOS();
return { type:'' };
}
......
......@@ -11,6 +11,7 @@ import {NullState, nullTokenize, NULL_MODE_ID} from 'vs/editor/common/modes/null
import {Token} from 'vs/editor/common/core/token';
import {ModeTransition} from 'vs/editor/common/core/modeTransition';
import {IModeService} from 'vs/editor/common/services/modeService';
import {AbstractState, ITokenizationResult} from 'vs/editor/common/modes/abstractState';
export interface ILeavingNestedModeData {
/**
......@@ -26,7 +27,7 @@ export interface ILeavingNestedModeData {
/**
* The state that will be used for continuing tokenization by the parent mode after the nested mode
*/
stateAfterNestedMode: modes.IState;
stateAfterNestedMode: AbstractState;
}
export interface IModeLocator {
......@@ -35,11 +36,11 @@ export interface IModeLocator {
export interface ITokenizationCustomization {
getInitialState():modes.IState;
getInitialState():AbstractState;
enterNestedMode?: (state:modes.IState) => boolean;
enterNestedMode?: (state:AbstractState) => boolean;
getNestedMode?: (state:modes.IState, locator:IModeLocator) => modes.IMode;
getNestedMode?: (state:AbstractState, locator:IModeLocator) => modes.IMode;
/**
* Return null if the line does not leave the nested mode
......@@ -109,7 +110,7 @@ export class TokenizationSupport implements modes.ITokenizationSupport, IDisposa
if (state.getModeId() !== this._modeId) {
return this._nestedTokenize(line, state, deltaOffset, stopAtOffset, [], []);
} else {
return this._myTokenize(line, state, deltaOffset, stopAtOffset, [], []);
return this._myTokenize(line, <AbstractState>state, deltaOffset, stopAtOffset, [], []);
}
}
......@@ -178,9 +179,9 @@ export class TokenizationSupport implements modes.ITokenizationSupport, IDisposa
* Precondition is: state.getMode() === this
* This means we are in the current mode when parsing starts on this line.
*/
private _myTokenize(buffer:string, myState:modes.IState, deltaOffset:number, stopAtOffset:number, prependTokens:Token[], prependModeTransitions:ModeTransition[]):modes.ILineTokens {
private _myTokenize(buffer:string, myState:AbstractState, deltaOffset:number, stopAtOffset:number, prependTokens:Token[], prependModeTransitions:ModeTransition[]):modes.ILineTokens {
let lineStream = new LineStream(buffer);
let tokenResult:modes.ITokenizationResult, beforeTokenizeStreamPos:number;
let tokenResult:ITokenizationResult, beforeTokenizeStreamPos:number;
let previousType:string = null;
myState = myState.clone();
......@@ -228,7 +229,12 @@ export class TokenizationSupport implements modes.ITokenizationSupport, IDisposa
return result;
} else {
// Transition to the nested mode state
myState = nestedModeState;
return {
tokens: prependTokens,
actualStopOffset: lineStream.pos() + deltaOffset,
modeTransitions: prependModeTransitions,
endState: nestedModeState
};
}
}
}
......@@ -251,7 +257,7 @@ export class TokenizationSupport implements modes.ITokenizationSupport, IDisposa
return result;
}
private _enterNestedMode(state:modes.IState): boolean {
private _enterNestedMode(state:AbstractState): boolean {
if (this.defaults.enterNestedMode) {
return false;
}
......@@ -259,7 +265,7 @@ export class TokenizationSupport implements modes.ITokenizationSupport, IDisposa
}
private _getNestedMode(state:modes.IState): modes.IMode {
private _getNestedMode(state:AbstractState): modes.IMode {
if (this.defaults.getNestedMode) {
return null;
}
......@@ -291,7 +297,7 @@ export class TokenizationSupport implements modes.ITokenizationSupport, IDisposa
return this.customization.getNestedMode(state, locator);
}
private _getNestedModeInitialState(state:modes.IState): modes.IState {
private _getNestedModeInitialState(state:AbstractState): modes.IState {
let nestedMode = this._getNestedMode(state);
if (nestedMode) {
let tokenizationSupport = modes.TokenizationRegistry.get(nestedMode.getId());
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import {TPromise} from 'vs/base/common/winjs.base';
import {createDecorator} from 'vs/platform/instantiation/common/instantiation';
import {IRawText} from 'vs/editor/common/editorCommon';
export var ICompatWorkerService = createDecorator<ICompatWorkerService>('compatWorkerService');
export interface IRawModelData {
url: URI;
versionId: number;
value: IRawText;
modeId: string;
}
export interface ICompatMode {
getId(): string;
compatWorkerService: ICompatWorkerService;
}
export interface ICompatWorkerService {
_serviceBrand: any;
isInMainThread: boolean;
registerCompatMode(compatMode:ICompatMode): void;
CompatWorker(obj: ICompatMode, methodName: string, target: Function, param: any[]): TPromise<any>;
}
function findMember(proto: any, target: any): string {
for (let i in proto) {
if (proto[i] === target) {
return i;
}
}
throw new Error('Member not found in prototype');
}
export function CompatWorkerAttr(type: Function, target: Function): void {
let methodName = findMember(type.prototype, target);
type.prototype[methodName] = function(...param: any[]) {
let obj = <ICompatMode>this;
return obj.compatWorkerService.CompatWorker(obj, methodName, target, param);
};
}
/*---------------------------------------------------------------------------------------------
* 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 {TPromise} from 'vs/base/common/winjs.base';
import {IDisposable} from 'vs/base/common/lifecycle';
import {IModel, EventType, IModelContentChangedEvent} from 'vs/editor/common/editorCommon';
import {ICompatWorkerService, ICompatMode} from 'vs/editor/common/services/compatWorkerService';
import {DefaultWorkerFactory} from 'vs/base/worker/defaultWorkerFactory';
import {WorkerClient} from 'vs/base/common/worker/workerClient';
import {IModelService} from 'vs/editor/common/services/modelService';
import {ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
export class MainThreadCompatWorkerService implements ICompatWorkerService {
public _serviceBrand: any;
public isInMainThread = true;
private _workerFactory: DefaultWorkerFactory;
private _worker: WorkerClient;
private _workerCreatedPromise: TPromise<void>;
private _triggerWorkersCreatedPromise: (value: void) => void;
private _modelListeners: { [uri: string]: IDisposable };
constructor(
@IModelService modelService: IModelService
) {
this._workerFactory = new DefaultWorkerFactory('compatWorker', true);
this._worker = null;
this._workerCreatedPromise = new TPromise<void>((c, e, p) => {
this._triggerWorkersCreatedPromise = c;
}, () => {
// Not cancelable
});
this._modelListeners = Object.create(null);
const isInterestingModel = (model:IModel) => {
if (model.isTooLargeForHavingARichMode()) {
return false;
}
let modeId = model.getModeId();
let compatModes = ModesRegistry.getCompatModes();
for (let i = 0; i < compatModes.length; i++) {
if (compatModes[i].id === modeId) {
return true;
}
}
return false;
};
const onModelAdded = (model:IModel) => {
if (!isInterestingModel(model)) {
return;
}
this._modelListeners[model.uri.toString()] = model.addBulkListener((events) => {
let contentChangedEvents = (
events
.filter(e => e.getType() === EventType.ModelRawContentChanged)
.map(e => <IModelContentChangedEvent>e.getData())
);
if (contentChangedEvents.length === 0) {
return;
}
this._call('$', 'acceptModelEvents', [model.uri, { contentChanged: contentChangedEvents }]);
});
this._call('$', 'acceptNewModel', [{
url: model.uri,
versionId: model.getVersionId(),
value: model.toRawText(),
modeId: model.getMode().getId()
}]);
};
const onModelRemoved = (model:IModel) => {
if (this._modelListeners[model.uri.toString()]) {
this._modelListeners[model.uri.toString()].dispose();
delete this._modelListeners[model.uri.toString()];
this._call('$', 'acceptDidDisposeModel', [model.uri]);
}
};
modelService.onModelAdded(onModelAdded);
modelService.onModelRemoved(onModelRemoved);
modelService.onModelModeChanged(event => {
onModelRemoved(event.model);
onModelAdded(event.model);
});
}
public registerCompatMode(compatMode:ICompatMode): void {
this._call('$', 'instantiateCompatMode', [compatMode.getId()]);
}
public CompatWorker(obj: ICompatMode, methodName: string, target: Function, param: any[]): TPromise<any> {
return this._call(obj.getId(), methodName, param);
}
private _ensureWorkers(): void {
if (this._triggerWorkersCreatedPromise) {
// Workers not created yet
this._createWorker();
let complete = this._triggerWorkersCreatedPromise;
this._triggerWorkersCreatedPromise = null;
complete(null);
}
}
private _afterWorkers(): TPromise<void> {
this._ensureWorkers();
let shouldCancelPromise = false;
return new TPromise<void>((c, e, p) => {
// hide the initialize promise inside this
// promise so that it won't be canceled by accident
this._workerCreatedPromise.then(() => {
if (!shouldCancelPromise) {
c(null);
}
}, e, p);
}, () => {
// mark that this promise is canceled
shouldCancelPromise = true;
});
}
private _createWorker(isRetry:boolean = false): void {
this._worker = new WorkerClient(
this._workerFactory,
'vs/editor/common/worker/editorWorkerServer'
);
this._worker.onModuleLoaded = this._worker.request('initialize', {
modesRegistryData: {
compatModes: ModesRegistry.getCompatModes(),
languages: ModesRegistry.getLanguages()
}
}).then(() => {
ModesRegistry.onDidAddCompatModes((m) => this._call('$', 'acceptCompatModes', [m]));
ModesRegistry.onDidAddLanguages((m) => this._call('$', 'acceptLanguages', [m]));
}, (err) => {
this._worker.dispose();
this._worker = null;
if (isRetry) {
console.warn('Creating the web worker already failed twice. Giving up!');
} else {
this._createWorker(true);
}
});
}
private _call(rpcId: string, methodName: string, args:any[]): TPromise<any> {
return this._afterWorkers().then(_ => {
if (this._worker === null) {
throw new Error('Cannot fulfill request...');
}
return this._worker.request('request', {
target: rpcId,
methodName: methodName,
args: args
});
});
}
}
/*---------------------------------------------------------------------------------------------
* 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 {TPromise} from 'vs/base/common/winjs.base';
import {ICompatWorkerService, ICompatMode, IRawModelData} from 'vs/editor/common/services/compatWorkerService';
import {IResourceService} from 'vs/editor/common/services/resourceService';
import {ILanguageExtensionPoint, IModeService} from 'vs/editor/common/services/modeService';
import {ICompatMirrorModelEvents, CompatMirrorModel} from 'vs/editor/common/model/compatMirrorModel';
import {onUnexpectedError} from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import {ILegacyLanguageDefinition, ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
export class CompatWorkerServiceWorker implements ICompatWorkerService {
public _serviceBrand: any;
public isInMainThread = false;
private _compatModes: {[modeId:string]:ICompatMode;};
constructor(
@IResourceService private resourceService: IResourceService,
@IModeService private modeService: IModeService,
modesRegistryData: {
compatModes: ILegacyLanguageDefinition[];
languages: ILanguageExtensionPoint[];
}
) {
ModesRegistry.registerCompatModes(modesRegistryData.compatModes);
ModesRegistry.registerLanguages(modesRegistryData.languages);
this._compatModes = Object.create(null);
}
registerCompatMode(compatMode:ICompatMode): void {
this._compatModes[compatMode.getId()] = compatMode;
}
public handleMainRequest(rpcId: string, methodName: string, args: any[]): any {
if (rpcId === '$') {
switch (methodName) {
case 'acceptNewModel':
return this._acceptNewModel(args[0]);
case 'acceptDidDisposeModel':
return this._acceptDidDisposeModel(args[0]);
case 'acceptModelEvents':
return this._acceptModelEvents(args[0], args[1]);
case 'acceptCompatModes':
return this._acceptCompatModes(args[0]);
case 'acceptLanguages':
return this._acceptLanguages(args[0]);
case 'instantiateCompatMode':
return this._instantiateCompatMode(args[0]);
}
}
let obj = this._compatModes[rpcId];
return TPromise.as(obj[methodName].apply(obj, args));
}
public CompatWorker(obj: ICompatMode, methodName: string, target: Function, param: any[]): TPromise<any> {
return target.apply(obj, param);
}
private _acceptNewModel(data: IRawModelData): TPromise<void> {
// Create & insert the mirror model eagerly in the resource service
let mirrorModel = new CompatMirrorModel(data.versionId, data.value, null, data.url);
this.resourceService.insert(mirrorModel.uri, mirrorModel);
// Block worker execution until the mode is instantiated
return this.modeService.getOrCreateMode(data.modeId).then((mode) => {
// Changing mode should trigger a remove & an add, therefore:
// (1) Remove from resource service
this.resourceService.remove(mirrorModel.uri);
// (2) Change mode
mirrorModel.setMode(mode.getId());
// (3) Insert again to resource service (it will have the new mode)
this.resourceService.insert(mirrorModel.uri, mirrorModel);
});
}
private _acceptDidDisposeModel(uri: URI): void {
let model = <CompatMirrorModel>this.resourceService.get(uri);
this.resourceService.remove(uri);
model.dispose();
}
private _acceptModelEvents(uri: URI, events: ICompatMirrorModelEvents): void {
let model = <CompatMirrorModel>this.resourceService.get(uri);
try {
model.onEvents(events);
} catch (err) {
onUnexpectedError(err);
}
}
private _acceptCompatModes(modes:ILegacyLanguageDefinition[]): void {
ModesRegistry.registerCompatModes(modes);
}
private _acceptLanguages(languages:ILanguageExtensionPoint[]): void {
ModesRegistry.registerLanguages(languages);
}
private _instantiateCompatMode(modeId:string): TPromise<void> {
return this.modeService.getOrCreateMode(modeId).then(_ => void 0);
}
}
......@@ -8,7 +8,7 @@ import {IntervalTimer, ShallowCancelThenPromise, wireCancellationToken} from 'vs
import {Disposable, IDisposable, dispose} from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import {TPromise} from 'vs/base/common/winjs.base';
import {SimpleWorkerClient} from 'vs/base/common/worker/simpleWorker';
import {SimpleWorkerClient, logOnceWebWorkerWarning} from 'vs/base/common/worker/simpleWorker';
import {DefaultWorkerFactory} from 'vs/base/worker/defaultWorkerFactory';
import * as editorCommon from 'vs/editor/common/editorCommon';
import {WordHelper} from 'vs/editor/common/model/textModelWithTokensHelpers';
......@@ -16,7 +16,6 @@ import {IInplaceReplaceSupportResult, ILink, ISuggestResult, LinkProviderRegistr
import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService';
import {IModelService} from 'vs/editor/common/services/modelService';
import {EditorSimpleWorkerImpl} from 'vs/editor/common/services/editorSimpleWorker';
import {logOnceWebWorkerWarning} from 'vs/base/common/worker/workerClient';
/**
* Stop syncing a model to the worker if it was not needed for 1 min.
......@@ -256,7 +255,7 @@ export class EditorWorkerClient extends Disposable {
constructor(modelService: IModelService, label:string) {
super();
this._modelService = modelService;
this._workerFactory = new DefaultWorkerFactory(label, /*do not use iframe*/false);
this._workerFactory = new DefaultWorkerFactory(label);
this._worker = null;
this._modelManager = null;
}
......
......@@ -8,17 +8,11 @@ import {onUnexpectedError} from 'vs/base/common/errors';
import Event, {Emitter} from 'vs/base/common/event';
import * as mime from 'vs/base/common/mime';
import * as strings from 'vs/base/common/strings';
import {ILegacyLanguageDefinition, ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
import {ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
import {ILanguageExtensionPoint} from 'vs/editor/common/services/modeService';
var hasOwnProperty = Object.prototype.hasOwnProperty;
export interface ICompatModeDescriptor {
moduleId: string;
ctorName: string;
deps: string[];
}
export class LanguagesRegistry {
private knownModeIds: { [id: string]: boolean; };
......@@ -27,7 +21,6 @@ export class LanguagesRegistry {
private id2Name: { [id: string]: string; };
private id2Extensions: { [id: string]: string[]; };
private id2Filenames: { [id: string]: string[]; };
private compatModes: { [id: string]: ICompatModeDescriptor; };
private lowerName2Id: { [name: string]: string; };
private id2ConfigurationFiles: { [id:string]: string[]; };
......@@ -41,44 +34,15 @@ export class LanguagesRegistry {
this.id2Name = {};
this.id2Extensions = {};
this.id2Filenames = {};
this.compatModes = {};
this.lowerName2Id = {};
this.id2ConfigurationFiles = {};
if (useModesRegistry) {
this._registerCompatModes(ModesRegistry.getCompatModes());
ModesRegistry.onDidAddCompatModes((m) => this._registerCompatModes(m));
this._registerLanguages(ModesRegistry.getLanguages());
ModesRegistry.onDidAddLanguages((m) => this._registerLanguages(m));
}
}
_registerCompatModes(defs:ILegacyLanguageDefinition[]): void {
let addedModes: string[] = [];
for (let i = 0; i < defs.length; i++) {
let def = defs[i];
this._registerLanguage({
id: def.id,
extensions: def.extensions,
filenames: def.filenames,
firstLine: def.firstLine,
aliases: def.aliases,
mimetypes: def.mimetypes
});
this.compatModes[def.id] = {
moduleId: def.moduleId,
ctorName: def.ctorName,
deps: def.deps
};
addedModes.push(def.id);
}
this._onDidAddModes.fire(addedModes);
}
_registerLanguages(desc:ILanguageExtensionPoint[]): void {
let addedModes: string[] = [];
for (let i = 0; i < desc.length; i++) {
......@@ -263,10 +227,6 @@ export class LanguagesRegistry {
return this.extractModeIds(mimeTypes.join(','));
}
public getCompatMode(modeId: string): ICompatModeDescriptor {
return this.compatModes[modeId] || null;
}
public getExtensions(languageName: string): string[] {
let languageId = this.name2LanguageId[languageName];
if (!languageId) {
......
......@@ -46,7 +46,6 @@ export interface IModeService {
// --- reading
isRegisteredMode(mimetypeOrModeId: string): boolean;
isCompatMode(modeId: string): boolean;
getRegisteredModes(): string[];
getRegisteredLanguageNames(): string[];
getExtensions(alias: string): string[];
......
......@@ -11,7 +11,6 @@ import * as paths from 'vs/base/common/paths';
import {TPromise} from 'vs/base/common/winjs.base';
import mime = require('vs/base/common/mime');
import {IFilesConfiguration} from 'vs/platform/files/common/files';
import {createAsyncDescriptor1} from 'vs/platform/instantiation/common/descriptors';
import {IExtensionService} from 'vs/platform/extensions/common/extensions';
import {IExtensionPointUser, IExtensionMessageCollector, ExtensionsRegistry} from 'vs/platform/extensions/common/extensionsRegistry';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
......@@ -137,7 +136,6 @@ export class ModeServiceImpl implements IModeService {
private _instantiationService: IInstantiationService;
protected _extensionService: IExtensionService;
private _activationPromises: { [modeId: string]: TPromise<modes.IMode>; };
private _instantiatedModes: { [modeId: string]: modes.IMode; };
private _registry: LanguagesRegistry;
......@@ -155,7 +153,6 @@ export class ModeServiceImpl implements IModeService {
this._instantiationService = instantiationService;
this._extensionService = extensionService;
this._activationPromises = {};
this._instantiatedModes = {};
this._registry = new LanguagesRegistry();
......@@ -166,11 +163,6 @@ export class ModeServiceImpl implements IModeService {
return this._registry.isRegisteredMode(mimetypeOrModeId);
}
public isCompatMode(modeId:string): boolean {
let compatModeData = this._registry.getCompatMode(modeId);
return (compatModeData ? true : false);
}
public getRegisteredModes(): string[] {
return this._registry.getRegisteredModes();
}
......@@ -300,61 +292,18 @@ export class ModeServiceImpl implements IModeService {
});
}
private _getOrCreateMode(modeId: string): TPromise<modes.IMode> {
if (this._instantiatedModes.hasOwnProperty(modeId)) {
return TPromise.as(this._instantiatedModes[modeId]);
}
if (this._activationPromises.hasOwnProperty(modeId)) {
return this._activationPromises[modeId];
}
var c, e;
var promise = new TPromise((cc,ee,pp) => { c = cc; e = ee; });
this._activationPromises[modeId] = promise;
this._createMode(modeId).then((mode) => {
this._instantiatedModes[modeId] = mode;
delete this._activationPromises[modeId];
private _getOrCreateMode(modeId: string): modes.IMode {
if (!this._instantiatedModes.hasOwnProperty(modeId)) {
this._instantiatedModes[modeId] = this._instantiationService.createInstance(FrankensteinMode, {
id: modeId
});
this._onDidCreateMode.fire(mode);
this._onDidCreateMode.fire(this._instantiatedModes[modeId]);
this._extensionService.activateByEvent(`onLanguage:${modeId}`).done(null, onUnexpectedError);
return this._instantiatedModes[modeId];
}).then(c, e);
return promise;
}
private _createMode(modeId:string): TPromise<modes.IMode> {
let modeDescriptor = this._createModeDescriptor(modeId);
let compatModeData = this._registry.getCompatMode(modeId);
if (compatModeData) {
// This is a compatibility mode
let resolvedDeps: TPromise<modes.IMode[]> = null;
if (Array.isArray(compatModeData.deps)) {
resolvedDeps = TPromise.join(compatModeData.deps.map(dep => this.getOrCreateMode(dep)));
} else {
resolvedDeps = TPromise.as<modes.IMode[]>(null);
}
return resolvedDeps.then(_ => {
let compatModeAsyncDescriptor = createAsyncDescriptor1<modes.IModeDescriptor, modes.IMode>(compatModeData.moduleId, compatModeData.ctorName);
return this._instantiationService.createInstance(compatModeAsyncDescriptor, modeDescriptor);
});
}
return TPromise.as<modes.IMode>(this._instantiationService.createInstance(FrankensteinMode, modeDescriptor));
return this._instantiatedModes[modeId];
}
private _createModeDescriptor(modeId:string): modes.IModeDescriptor {
return {
id: modeId
};
}
}
export class TokenizationState2Adapter implements modes.IState {
......
......@@ -26,13 +26,6 @@ import {DEFAULT_INDENTATION, DEFAULT_TRIM_AUTO_WHITESPACE} from 'vs/editor/commo
import {IMessageService} from 'vs/platform/message/common/message';
import {PLAINTEXT_MODE_ID} from 'vs/editor/common/modes/modesRegistry';
export interface IRawModelData {
url: URI;
versionId: number;
value: editorCommon.IRawText;
modeId: string;
}
function MODEL_ID(resource: URI): string {
return resource.toString();
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import {createDecorator} from 'vs/platform/instantiation/common/instantiation';
import {ITokenizedModel} from 'vs/editor/common/editorCommon';
export interface ICompatMirrorModel extends ITokenizedModel {
}
// Resource Service
export let IResourceService = createDecorator<IResourceService>('resourceService');
export interface IResourceService {
_serviceBrand: any;
insert(url: URI, element: ICompatMirrorModel): void;
get(url: URI): ICompatMirrorModel;
remove(url: URI): void;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import {IResourceService, ICompatMirrorModel} from 'vs/editor/common/services/resourceService';
class CompatMirrorModelMap {
private _data: {[key:string]:ICompatMirrorModel};
constructor() {
this._data = {};
}
public set(key:string, data:ICompatMirrorModel): void {
this._data[key] = data;
}
public get(key:string): ICompatMirrorModel {
return this._data[key] || null;
}
public contains(key:string): boolean {
return !!this._data[key];
}
public remove(key:string): void {
delete this._data[key];
}
}
export class ResourceService implements IResourceService {
public _serviceBrand: any;
private _map:CompatMirrorModelMap;
constructor() {
this._map = new CompatMirrorModelMap();
}
private static _anonymousModelId(input:string): string {
let r = '';
for (let i = 0; i < input.length; i++) {
let ch = input[i];
if (ch >= '0' && ch <= '9') {
r += '0';
continue;
}
if (ch >= 'a' && ch <= 'z') {
r += 'a';
continue;
}
if (ch >= 'A' && ch <= 'Z') {
r += 'A';
continue;
}
r += ch;
}
return r;
}
public insert(uri:URI, element:ICompatMirrorModel): void {
let key = uri.toString();
if (this._map.contains(key)) {
// There already exists a model with this id => this is a programmer error
throw new Error('ResourceService: Cannot add model ' + ResourceService._anonymousModelId(key) + ' because it already exists!');
}
this._map.set(key, element);
}
public get(uri:URI):ICompatMirrorModel {
let key = uri.toString();
return this._map.get(key);
}
public remove(uri:URI):void {
let key = uri.toString();
this._map.remove(key);
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
// include these in the editor bundle because they are widely used by many languages
import 'vs/editor/common/languages.common';
import Severity from 'vs/base/common/severity';
import {TPromise} from 'vs/base/common/winjs.base';
import {WorkerServer} from 'vs/base/common/worker/workerServer';
import {EventService} from 'vs/platform/event/common/eventService';
import {IEventService} from 'vs/platform/event/common/event';
import {AbstractExtensionService, ActivatedExtension} from 'vs/platform/extensions/common/abstractExtensionService';
import {IExtensionDescription, IExtensionService} from 'vs/platform/extensions/common/extensions';
import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection';
import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService';
import {ModeServiceImpl} from 'vs/editor/common/services/modeServiceImpl';
import {IModeService, ILanguageExtensionPoint} from 'vs/editor/common/services/modeService';
import {ResourceService} from 'vs/editor/common/services/resourceServiceImpl';
import {IResourceService} from 'vs/editor/common/services/resourceService';
import {CompatWorkerServiceWorker} from 'vs/editor/common/services/compatWorkerServiceWorker';
import {ICompatWorkerService} from 'vs/editor/common/services/compatWorkerService';
import {ILegacyLanguageDefinition} from 'vs/editor/common/modes/modesRegistry';
export interface IInitData {
modesRegistryData?: {
compatModes: ILegacyLanguageDefinition[];
languages: ILanguageExtensionPoint[];
};
}
export interface ICallback {
(something:any):void;
}
class WorkerExtensionService extends AbstractExtensionService<ActivatedExtension> {
constructor() {
super(true);
}
protected _showMessage(severity:Severity, msg:string): void {
switch (severity) {
case Severity.Error:
console.error(msg);
break;
case Severity.Warning:
console.warn(msg);
break;
case Severity.Info:
console.info(msg);
break;
default:
console.log(msg);
}
}
protected _createFailedExtension(): ActivatedExtension {
throw new Error('unexpected');
}
protected _actualActivateExtension(extensionDescription: IExtensionDescription): TPromise<ActivatedExtension> {
throw new Error('unexpected');
}
}
export class EditorWorkerServer {
private compatWorkerService:CompatWorkerServiceWorker;
constructor() {
}
public initialize(mainThread:WorkerServer, complete:ICallback, error:ICallback, progress:ICallback, initData:IInitData):void {
const services = new ServiceCollection();
const instantiationService = new InstantiationService(services);
const extensionService = new WorkerExtensionService();
services.set(IExtensionService, extensionService);
const resourceService = new ResourceService();
services.set(IResourceService, resourceService);
services.set(IEventService, new EventService());
const modeService = new ModeServiceImpl(instantiationService, extensionService);
services.set(IModeService, modeService);
this.compatWorkerService = new CompatWorkerServiceWorker(resourceService, modeService, initData.modesRegistryData);
services.set(ICompatWorkerService, this.compatWorkerService);
complete(undefined);
}
public request(mainThread:WorkerServer, complete:ICallback, error:ICallback, progress:ICallback, data:any):void {
try {
TPromise.as(
this.compatWorkerService.handleMainRequest(data.target, data.methodName, data.args)
).then(complete, error);
} catch (err) {
error(err);
}
}
}
export var value = new EditorWorkerServer();
......@@ -16,7 +16,7 @@ import {ActionItem, Separator} from 'vs/base/browser/ui/actionbar/actionbar';
import {IContextMenuService, IContextViewService} from 'vs/platform/contextview/browser/contextView';
import {IKeybindingService} from 'vs/platform/keybinding/common/keybinding';
import {IContextKeyService} from 'vs/platform/contextkey/common/contextkey';
import {IMenuService, IMenu, MenuId} from 'vs/platform/actions/common/actions';
import {IMenuService, MenuId} from 'vs/platform/actions/common/actions';
import {ICommonCodeEditor, IEditorContribution, MouseTargetType, EditorContextKeys, IScrollEvent} from 'vs/editor/common/editorCommon';
import {editorAction, ServicesAccessor, EditorAction} from 'vs/editor/common/editorCommonExtensions';
import {ICodeEditor, IEditorMouseEvent} from 'vs/editor/browser/editorBrowser';
......@@ -39,7 +39,6 @@ export class ContextMenuController implements IEditorContribution {
private _toDispose: IDisposable[] = [];
private _contextMenuIsBeingShownCount: number = 0;
private _editor: ICodeEditor;
private _contextMenu: IMenu;
constructor(
editor: ICodeEditor,
......@@ -51,9 +50,6 @@ export class ContextMenuController implements IEditorContribution {
) {
this._editor = editor;
this._contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService);
this._toDispose.push(this._contextMenu);
this._toDispose.push(this._editor.onContextMenu((e: IEditorMouseEvent) => this._onContextMenu(e)));
this._toDispose.push(this._editor.onDidScrollChange((e: IScrollEvent) => {
if (this._contextMenuIsBeingShownCount > 0) {
......@@ -129,7 +125,10 @@ export class ContextMenuController implements IEditorContribution {
private _getMenuActions(): IAction[] {
const result: IAction[] = [];
const groups = this._contextMenu.getActions();
let contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService);
const groups = contextMenu.getActions();
contextMenu.dispose();
for (let group of groups) {
const [, actions] = group;
......
......@@ -4,12 +4,13 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {IMode, IState, IStream, ITokenizationResult, ITokenizationSupport, TokenizationRegistry} from 'vs/editor/common/modes';
import {AbstractState} from 'vs/editor/common/modes/abstractState';
import {IMode, IState, TokenizationRegistry} from 'vs/editor/common/modes';
import {AbstractState, ITokenizationResult} from 'vs/editor/common/modes/abstractState';
import {TokenizationSupport} from 'vs/editor/common/modes/supports/tokenizationSupport';
import {LineStream} from 'vs/editor/common/modes/lineStream';
let instanceCount = 0;
export function generateMockModeId(): string {
function generateMockModeId(): string {
return 'mockMode' + (++instanceCount);
}
......@@ -45,7 +46,7 @@ export class StateForMockTokenizingMode extends AbstractState {
return true;
}
public tokenize(stream:IStream):ITokenizationResult {
public tokenize(stream:LineStream):ITokenizationResult {
stream.advanceToEOS();
return { type: this._tokenType };
}
......
/*---------------------------------------------------------------------------------------------
* 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 Event from 'vs/base/common/event';
import {IDisposable} from 'vs/base/common/lifecycle';
import {TPromise} from 'vs/base/common/winjs.base';
import * as modes from 'vs/editor/common/modes';
import {IModeService, IModeLookupResult} from 'vs/editor/common/services/modeService';
export class MockModeService implements IModeService {
_serviceBrand: any;
onDidAddModes: Event<string[]> = undefined;
onDidCreateMode: Event<modes.IMode> = undefined;
// --- reading
isRegisteredMode(mimetypeOrModeId: string): boolean {
throw new Error('Not implemented');
}
isCompatMode(modeId: string): boolean {
throw new Error('Not implemented');
}
getRegisteredModes(): string[] {
throw new Error('Not implemented');
}
getRegisteredLanguageNames(): string[] {
throw new Error('Not implemented');
}
getExtensions(alias: string): string[] {
throw new Error('Not implemented');
}
getFilenames(alias: string): string[] {
throw new Error('Not implemented');
}
getMimeForMode(modeId: string): string {
throw new Error('Not implemented');
}
getLanguageName(modeId:string): string {
throw new Error('Not implemented');
}
getModeIdForLanguageName(alias:string): string {
throw new Error('Not implemented');
}
getModeIdByFilenameOrFirstLine(filename: string, firstLine?: string): string {
throw new Error('Not implemented');
}
getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string {
throw new Error('Not implemented');
}
getConfigurationFiles(modeId: string): string[] {
throw new Error('Not implemented');
}
// --- instantiation
lookup(commaSeparatedMimetypesOrCommaSeparatedIds: string): IModeLookupResult[] {
throw new Error('Not implemented');
}
getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): modes.IMode {
throw new Error('Not implemented');
}
getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): TPromise<modes.IMode> {
throw new Error('Not implemented');
}
getOrCreateModeByLanguageName(languageName: string): TPromise<modes.IMode> {
throw new Error('Not implemented');
}
getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?:string): TPromise<modes.IMode> {
throw new Error('Not implemented');
}
registerTokenizationSupport(modeId: string, callback: (mode: modes.IMode) => modes.ITokenizationSupport): IDisposable {
throw new Error('Not implemented');
}
registerTokenizationSupport2(modeId: string, support: modes.TokensProvider): IDisposable {
throw new Error('Not implemented');
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as editorCommon from 'vs/editor/common/editorCommon';
import {ICompatMirrorModelEvents, CompatMirrorModel} from 'vs/editor/common/model/compatMirrorModel';
import {Position} from 'vs/editor/common/core/position';
import {MirrorModel as SimpleMirrorModel} from 'vs/editor/common/services/editorSimpleWorker';
import {DEFAULT_WORD_REGEXP} from 'vs/editor/common/model/wordHelper';
import {TextModel} from 'vs/editor/common/model/textModel';
function createTestMirrorModelFromString(value:string): CompatMirrorModel {
return new CompatMirrorModel(0, TextModel.toRawText(value, TextModel.DEFAULT_CREATION_OPTIONS), null);
}
function contentChangedFlushEvent(detail: editorCommon.IRawText): editorCommon.IModelContentChangedFlushEvent {
return {
changeType: editorCommon.EventType.ModelRawContentChangedFlush,
isRedoing: false,
isUndoing: false,
versionId: 0,
detail: detail
};
}
function contentChangedLinesDeletedEvent(fromLineNumber: number, toLineNumber: number): editorCommon.IModelContentChangedLinesDeletedEvent {
return {
changeType: editorCommon.EventType.ModelRawContentChangedLinesDeleted,
isRedoing: false,
isUndoing: false,
versionId: 0,
fromLineNumber: fromLineNumber,
toLineNumber: toLineNumber
};
}
function contentChangedLinesInsertedEvent(fromLineNumber: number, toLineNumber: number, detail: string): editorCommon.IModelContentChangedLinesInsertedEvent {
return {
changeType: editorCommon.EventType.ModelRawContentChangedLinesInserted,
isRedoing: false,
isUndoing: false,
versionId: 0,
fromLineNumber: fromLineNumber,
toLineNumber: toLineNumber,
detail: detail
};
}
function contentChangedLineChanged(lineNumber: number, detail: string): editorCommon.IModelContentChangedLineChangedEvent {
return {
changeType: editorCommon.EventType.ModelRawContentChangedLineChanged,
isRedoing: false,
isUndoing: false,
versionId: 0,
lineNumber: lineNumber,
detail: detail
};
}
function mirrorModelEvents(contentChanged:editorCommon.IModelContentChangedEvent[]): ICompatMirrorModelEvents {
return {
contentChanged: contentChanged
};
}
suite('Editor Model - MirrorModel', () => {
var mirrorModel:CompatMirrorModel;
setup(() => {
mirrorModel = createTestMirrorModelFromString('line1\nline2\nline3\nline4');
});
teardown(() => {
mirrorModel.dispose();
mirrorModel = null;
});
test('get line start ', () => {
assert.equal(mirrorModel.getOffsetAt(new Position(1, 1)), 0);
assert.equal(mirrorModel.getOffsetAt(new Position(2, 1)), 6);
assert.equal(mirrorModel.getOffsetAt(new Position(3, 1)), 12);
assert.equal(mirrorModel.getOffsetAt(new Position(4, 1)), 18);
assert.equal(mirrorModel.getOffsetAt(new Position(1000, 1)), mirrorModel.getOffsetAt(new Position(mirrorModel.getLineCount(), mirrorModel.getLineMaxColumn(mirrorModel.getLineCount()))));
});
test('get line start /flush event/', () => {
assert.equal(mirrorModel.getOffsetAt(new Position(2, 1)), 6);
assert.equal(mirrorModel.getOffsetAt(new Position(3, 1)), 12);
mirrorModel.onEvents(mirrorModelEvents([contentChangedFlushEvent({
length: -1,
lines: [
'foo',
'bar'
],
BOM: '',
EOL: '\n',
options: {
tabSize: 4,
insertSpaces: true,
trimAutoWhitespace: true,
defaultEOL: editorCommon.DefaultEndOfLine.LF
}
})]));
assert.equal(mirrorModel.getOffsetAt(new Position(1, 1)), 0);
assert.equal(mirrorModel.getOffsetAt(new Position(2, 1)), 4);
});
test('get offset', () => {
assert.equal(mirrorModel.getOffsetAt({lineNumber: 1, column: 1}), 0);
assert.equal(mirrorModel.getOffsetAt({lineNumber: 1, column: 3}), 2);
assert.equal(mirrorModel.getOffsetAt({lineNumber: 2, column: 1}), 6);
assert.equal(mirrorModel.getOffsetAt({lineNumber: 4, column: 6}), 23);
assert.equal(mirrorModel.getOffsetAt({lineNumber: 4, column: 7}), 23);
});
test('get position from offset', () => {
assert.deepEqual(mirrorModel.getPositionAt(0), {lineNumber: 1, column: 1});
assert.deepEqual(mirrorModel.getPositionAt(2), {lineNumber: 1, column: 3});
assert.deepEqual(mirrorModel.getPositionAt(6), {lineNumber: 2, column: 1});
assert.deepEqual(mirrorModel.getPositionAt(23), {lineNumber: 4, column: 6});
assert.deepEqual(mirrorModel.getPositionAt(24), {lineNumber: 4, column: 6});
});
test('get (all/unique) words', () => {
let model = new SimpleMirrorModel(null, [ 'foo bar foo bar' ], '\n', 1);
let uniqueWords = model.getAllUniqueWords(DEFAULT_WORD_REGEXP);
assert.equal(uniqueWords.length, 2);
assert.equal(uniqueWords[0], 'foo');
assert.equal(uniqueWords[1], 'bar');
model = new SimpleMirrorModel(null, [ 'foo bar', 'foo', 'bar' ], '\n', 1);
uniqueWords = model.getAllUniqueWords(DEFAULT_WORD_REGEXP);
assert.equal(uniqueWords.length, 2);
assert.equal(uniqueWords[0], 'foo');
assert.equal(uniqueWords[1], 'bar');
model = new SimpleMirrorModel(null, [ 'toString', 'hasOwnProperty', 'foo' ], '\n', 1);
uniqueWords = model.getAllUniqueWords(DEFAULT_WORD_REGEXP);
assert.equal(uniqueWords.length, 3);
assert.equal(uniqueWords[0], 'toString');
assert.equal(uniqueWords[1], 'hasOwnProperty');
assert.equal(uniqueWords[2], 'foo');
});
test('word at/until pos', () => {
var pos = { lineNumber: 1, column: 3 };
assert.equal(mirrorModel.getWordAtPosition(pos).word, 'line1');
var model = createTestMirrorModelFromString('foo bar 1234 :";\'');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 1}).word, 'foo');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 2}).word, 'foo');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 3}).word, 'foo');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 4}).word, 'foo');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 5}).word, 'bar');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 6}).word, 'bar');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 7}).word, 'bar');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 8}).word, 'bar');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 9}).word, '1234');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 10}).word, '1234');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 11}).word, '1234');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 12}).word, '1234');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 13}).word, '1234');
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 14}), null);
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 15}), null);
assert.equal(model.getWordAtPosition({lineNumber: 1, column: 16}), null);
assert.equal(mirrorModel.getWordUntilPosition(pos).word, 'li');
});
});
suite('Editor Model - MirrorModel Eventing', () => {
var mirrorModel:CompatMirrorModel;
setup(() => {
mirrorModel = createTestMirrorModelFromString('line one\nline two\nline three\nline four');
});
teardown(() => {
mirrorModel.dispose();
mirrorModel = null;
});
test('delete single line', () => {
assert.equal(mirrorModel.getLineContent(3), 'line three');
mirrorModel.onEvents(mirrorModelEvents([contentChangedLinesDeletedEvent(3, 3)]));
assert.equal(mirrorModel.getLineContent(3), 'line four');
});
test('delete multiple lines', () => {
mirrorModel.onEvents(mirrorModelEvents([contentChangedLinesDeletedEvent(1, 2)]));
assert.equal(mirrorModel.getLineContent(1), 'line three');
assert.equal(mirrorModel.getLineContent(2), 'line four');
});
test('delete all lines', () => {
mirrorModel.onEvents(mirrorModelEvents([contentChangedLinesDeletedEvent(1, 4)]));
});
test('add single lines', () => {
assert.equal(mirrorModel.getLineContent(1), 'line one');
mirrorModel.onEvents(mirrorModelEvents([contentChangedLinesInsertedEvent(1, 1, 'foo bar\nbar foo')]));
assert.equal(mirrorModel.getLineContent(1), 'foo bar');
assert.equal(mirrorModel.getLineContent(2), 'line one');
});
test('add multiple lines', () => {
assert.equal(mirrorModel.getLineContent(1), 'line one');
mirrorModel.onEvents(mirrorModelEvents([contentChangedLinesInsertedEvent(1, 2, 'foo bar\nbar foo')]));
assert.equal(mirrorModel.getLineContent(1), 'foo bar');
assert.equal(mirrorModel.getLineContent(2), 'bar foo');
assert.equal(mirrorModel.getLineContent(3), 'line one');
});
test('change line', () => {
assert.equal(mirrorModel.getLineContent(1), 'line one');
mirrorModel.onEvents(mirrorModelEvents([contentChangedLineChanged(1, 'foobar')]));
assert.equal(mirrorModel.getLineContent(1), 'foobar');
});
test('flush model', () => {
assert.equal(mirrorModel.getLineContent(1), 'line one');
assert.equal(mirrorModel.getLineContent(2), 'line two');
mirrorModel.onEvents(mirrorModelEvents([contentChangedFlushEvent({
length: -1,
lines: [
'foo',
'bar'
],
BOM: '',
EOL: '\n',
options: {
tabSize: 4,
insertSpaces: true,
trimAutoWhitespace: true,
defaultEOL: editorCommon.DefaultEndOfLine.LF
}
})]));
assert.equal(mirrorModel.getLineContent(1), 'foo');
assert.equal(mirrorModel.getLineContent(2), 'bar');
});
});
......@@ -7,7 +7,6 @@
import * as assert from 'assert';
import * as editorCommon from 'vs/editor/common/editorCommon';
import {EditableTextModel} from 'vs/editor/common/model/editableTextModel';
import {ICompatMirrorModelEvents, CompatMirrorModel} from 'vs/editor/common/model/compatMirrorModel';
import {MirrorModel2} from 'vs/editor/common/model/mirrorModel2';
import {TextModel} from 'vs/editor/common/model/textModel';
import {Position} from 'vs/editor/common/core/position';
......@@ -90,25 +89,9 @@ export function assertSyncedModels(text:string, callback:(model:EditableTextMode
assertLineMapping(model, 'model');
}
var mirrorModel1 = new CompatMirrorModel(model.getVersionId(), model.toRawText(), null);
assertLineMapping(mirrorModel1, 'mirrorModel1');
var mirrorModel1PrevVersionId = model.getVersionId();
var mirrorModel2 = new MirrorModel2(null, model.toRawText().lines, model.toRawText().EOL, model.getVersionId());
var mirrorModel2PrevVersionId = model.getVersionId();
model.onDidChangeRawContent((e:editorCommon.IModelContentChangedEvent) => {
let versionId = e.versionId;
if (versionId < mirrorModel1PrevVersionId) {
console.warn('Model version id did not advance between edits (1)');
}
mirrorModel1PrevVersionId = versionId;
let mirrorModelEvents:ICompatMirrorModelEvents = {
contentChanged: [e]
};
mirrorModel1.onEvents(mirrorModelEvents);
});
model.onDidChangeContent((e:editorCommon.IModelContentChangedEvent2) => {
let versionId = e.versionId;
if (versionId < mirrorModel2PrevVersionId) {
......@@ -120,17 +103,13 @@ export function assertSyncedModels(text:string, callback:(model:EditableTextMode
var assertMirrorModels = () => {
assertLineMapping(model, 'model');
assertLineMapping(mirrorModel1, 'mirrorModel1');
model._assertLineNumbersOK();
assert.equal(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK');
assert.equal(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK');
assert.equal(mirrorModel1.getValue(), model.getValue(), 'mirror model 1 text OK');
assert.equal(mirrorModel1.getVersionId(), model.getVersionId(), 'mirror model 1 version OK');
};
callback(model, assertMirrorModels);
model.dispose();
mirrorModel1.dispose();
mirrorModel2.dispose();
}
......@@ -9,9 +9,10 @@ import {EditOperation} from 'vs/editor/common/core/editOperation';
import {Position} from 'vs/editor/common/core/position';
import {Range} from 'vs/editor/common/core/range';
import {Model} from 'vs/editor/common/model/model';
import {AbstractState} from 'vs/editor/common/modes/abstractState';
import {AbstractState, ITokenizationResult} from 'vs/editor/common/modes/abstractState';
import * as modes from 'vs/editor/common/modes';
import {TokenizationSupport} from 'vs/editor/common/modes/supports/tokenizationSupport';
import {LineStream} from 'vs/editor/common/modes/lineStream';
// --------- utils
......@@ -38,8 +39,10 @@ suite('Editor Model - Model Modes 1', () => {
public equals(other: modes.IState): boolean {
return this === other;
}
public tokenize(stream:modes.IStream): modes.ITokenizationResult {
calledState.calledFor.push(stream.next());
public tokenize(stream:LineStream): ITokenizationResult {
let chr = stream.peek();
stream.advance(1);
calledState.calledFor.push(chr);
stream.advanceToEOS();
return { type: '' };
}
......@@ -188,11 +191,8 @@ suite('Editor Model - Model Modes 2', () => {
return (other instanceof ModelState2) && (this.prevLineContent === (<ModelState2>other).prevLineContent);
}
public tokenize(stream:modes.IStream):modes.ITokenizationResult {
var line= '';
while (!stream.eos()) {
line+= stream.next();
}
public tokenize(stream:LineStream):ITokenizationResult {
var line= stream.advanceToEOS();
this.prevLineContent= line;
return { type: '' };
}
......@@ -309,7 +309,7 @@ suite('Editor Model - Token Iterator', () => {
class NState extends AbstractState {
private n:number;
private allResults:modes.ITokenizationResult[];
private allResults:ITokenizationResult[];
constructor(modeId:string, n:number) {
super(modeId);
......@@ -325,10 +325,12 @@ suite('Editor Model - Token Iterator', () => {
return true;
}
public tokenize(stream:modes.IStream):modes.ITokenizationResult {
public tokenize(stream:LineStream):ITokenizationResult {
var ndash = this.n, value = '';
while(!stream.eos() && ndash > 0) {
value += stream.next();
let chr = stream.peek();
stream.advance(1);
value += chr;
ndash--;
}
return { type: 'n-' + (this.n - ndash) + '-' + value };
......
......@@ -8,361 +8,34 @@ import {LineStream} from 'vs/editor/common/modes/lineStream';
suite('Editor Modes - LineStream', () => {
test('advanceIf - regex', () => {
var lineStream = new LineStream('...xxx...x.');
assert.equal(lineStream.advanceIfRegExp(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceIfRegExp(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceIfRegExp(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceIfRegExp(/^x/), 'x');
assert.equal(lineStream.advanceIfRegExp(/^x/), 'x');
assert.equal(lineStream.advanceIfRegExp(/^x/), 'x');
assert.equal(lineStream.advanceIfRegExp(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceIfRegExp(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceIfRegExp(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceIfRegExp(/^x/), 'x');
assert.equal(lineStream.advanceIfRegExp(/^x/), '');
lineStream.next();
assert.ok(lineStream.eos());
});
test('advanceWhile - regex', () => {
var lineStream = new LineStream('...xxx...x.');
assert.equal(lineStream.advanceWhile(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceWhile(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceWhile(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceWhile(/^x/), 'xxx');
assert.equal(lineStream.advanceWhile(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceWhile(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceWhile(/^x/), '');
lineStream.next();
assert.equal(lineStream.advanceWhile(/^x/), 'x');
assert.equal(lineStream.advanceWhile(/^x/), '');
lineStream.next();
assert.ok(lineStream.eos());
});
test('advanceUntil - regex', () => {
var lineStream = new LineStream('...x..xx..x');
assert.equal(lineStream.advanceUntil(/^x/, false), '...');
assert.equal(lineStream.advanceUntil(/^x/, false), '');
lineStream.next();
assert.equal(lineStream.advanceUntil(/^x/, false), '..');
assert.equal(lineStream.advanceUntil(/^x/, false), '');
lineStream.next();
assert.equal(lineStream.advanceUntil(/^x/, false), '');
lineStream.next();
assert.equal(lineStream.advanceUntil(/^x/, false), '..');
assert.equal(lineStream.advanceUntil(/^x/, false), '');
lineStream.next();
assert.ok(lineStream.eos());
});
test('advanceUntil - regex (including)', () => {
var lineStream = new LineStream('...x..xx..x');
assert.equal(lineStream.advanceUntil(/^x/, true), '...x');
assert.equal(lineStream.advanceUntil(/^x/, true), '..x');
assert.equal(lineStream.advanceUntil(/^x/, true), 'x');
assert.equal(lineStream.advanceUntil(/^x/, true), '..x');
assert.ok(lineStream.eos());
});
test('advanceIf - string', () => {
var lineStream = new LineStream('...abcabcabc...abc.');
assert.equal(lineStream.advanceIfString('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceIfString('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceIfString('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceIfString('abc'), 'abc');
assert.equal(lineStream.advanceIfString('abc'), 'abc');
assert.equal(lineStream.advanceIfString('abc'), 'abc');
assert.equal(lineStream.advanceIfString('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceIfString('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceIfString('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceIfString('abc'), 'abc');
assert.equal(lineStream.advanceIfString('abc'), '');
lineStream.next();
assert.ok(lineStream.eos());
});
test('advanceWhile - string', () => {
var lineStream = new LineStream('...abcabcabc...abc.');
assert.equal(lineStream.advanceWhile('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceWhile('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceWhile('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceWhile('abc'), 'abcabcabc');
assert.equal(lineStream.advanceWhile('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceWhile('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceWhile('abc'), '');
lineStream.next();
assert.equal(lineStream.advanceWhile('abc'), 'abc');
assert.equal(lineStream.advanceWhile('abc'), '');
lineStream.next();
assert.ok(lineStream.eos());
});
test('advanceUntil - string', () => {
var lineStream = new LineStream('...abc..ab..abc..bc');
assert.equal(lineStream.advanceUntil('abc', false), '...');
assert.equal(lineStream.advanceUntil('abc', false), '');
lineStream.next();
assert.equal(lineStream.advanceUntil('abc', false), 'bc..ab..');
assert.equal(lineStream.advanceUntil('abc', false), '');
lineStream.next();
assert.equal(lineStream.advanceUntil('abc', false), 'bc..bc');
assert.ok(lineStream.eos());
});
test('advanceUntil - string (including)', () => {
var lineStream = new LineStream('...abc..ab..abc..bc');
assert.equal(lineStream.advanceUntil('abc', true), '...abc');
assert.equal(lineStream.advanceUntil('abc', true), '..ab..abc');
assert.equal(lineStream.advanceUntil('abc', true), '..bc');
assert.ok(lineStream.eos());
});
test('skipWhitespace', () => {
var lineStream = new LineStream('\ta bc d \t e ');
assert.equal(lineStream.skipWhitespace(), '\t');
lineStream.next();
assert.equal(lineStream.skipWhitespace(), ' ');
lineStream.next();
lineStream.next();
assert.equal(lineStream.skipWhitespace(), ' ');
lineStream.next();
assert.equal(lineStream.skipWhitespace(), ' \t ');
lineStream.next();
assert.equal(lineStream.skipWhitespace(), ' ');
assert.ok(lineStream.eos());
});
test('peekToken', () => {
var lineStream = new LineStream('a b c edf ');
assert.equal(lineStream.peekToken(), 'a');
lineStream.next();
assert.equal(lineStream.peekToken(), 'b');
lineStream.next();
assert.equal(lineStream.peekToken(), 'b');
lineStream.next();
assert.equal(lineStream.peekToken(), 'c');
lineStream.next();
assert.equal(lineStream.peekToken(), 'c');
lineStream.next();
assert.equal(lineStream.peekToken(), 'c');
lineStream.next();
assert.equal(lineStream.peekToken(), 'edf');
lineStream.next();
assert.equal(lineStream.peekToken(), 'edf');
lineStream.next();
lineStream.next();
lineStream.next();
lineStream.next();
assert.throws(() => { lineStream.peekToken(); });
assert.ok(lineStream.eos());
});
test('nextToken', () => {
var lineStream = new LineStream('a b c edf ');
assert.equal(lineStream.nextToken(), 'a');
assert.equal(lineStream.nextToken(), 'b');
assert.equal(lineStream.nextToken(), 'c');
assert.equal(lineStream.nextToken(), 'edf');
assert.equal(lineStream.nextToken(), '');
assert.throws(() => { lineStream.nextToken(); });
assert.ok(lineStream.eos());
});
function newTokenStream(source, separators, whitespace) {
var lineStream = new LineStream(source);
lineStream.setTokenRules(separators, whitespace);
return lineStream;
}
function checkPos(lineStream,pos) {
assert.equal(lineStream.pos(), pos);
}
function check(lineStream, pos, token) {
checkPos(lineStream, pos);
assert.equal(lineStream.nextToken(), token);
}
test('corner cases', () => {
var input, lineStream;
var noTokens = (lineStream) => {
let noTokens = (lineStream) => {
assert.equal(lineStream.pos(), 0);
assert.ok(lineStream.eos());
};
noTokens(newTokenStream('', '', ''));
noTokens(newTokenStream('', '', 'x'));
noTokens(newTokenStream('', 'x', ''));
noTokens(newTokenStream('', 'x', 'x'));
input = '.....';
lineStream = newTokenStream(input, '.', '');
for (var i = 0; i < input.length; i++) {
check(lineStream, i, '.');
}
input = ' . . . . .';
lineStream = newTokenStream(input, '.', ' ');
for (var i = 0; i < input.length / 2; i++) {
check(lineStream, (i * 2) , '.');
}
input = '. . . . . ';
lineStream = newTokenStream(input, '.', ' ');
for (var i = 0; i < input.length / 2; i++) {
check(lineStream, i === 0 ? 0 : (i * 2) - 1, '.');
}
noTokens(new LineStream(''));
});
test('javascript assign', () => {
var lineStream = newTokenStream(' var foo =bar("foo"); //x ', '+-*/%&|^~!=<>(){}[]\'"\\/?;,', '\t ');
test('advanceToEOS', () => {
var lineStream = new LineStream(' var foo =bar("foo"); //x ');
assert.equal(lineStream.pos(), 0);
assert.equal(lineStream.peekToken(), 'var');
check(lineStream, 0, 'var');
check(lineStream, 5, 'foo');
check(lineStream, 9, '=');
check(lineStream, 11, 'bar');
check(lineStream, 14, '(');
check(lineStream, 15, '"');
check(lineStream, 16, 'foo');
check(lineStream, 19, '"');
check(lineStream, 20, ')');
check(lineStream, 21, ';');
check(lineStream, 22, '/');
check(lineStream, 24, '/');
check(lineStream, 25, 'x');
checkPos(lineStream, 26);
lineStream.skipWhitespace();
assert.ok(lineStream.eos(), 'Stream finished');
});
test('javascript strings', () => {
var lineStream = newTokenStream('x = " my \\"string\\" ";', '=()\\";/', '\t ');
check(lineStream, 0, 'x');
check(lineStream, 1, '=');
check(lineStream, 3, '"');
check(lineStream, 5, 'my');
check(lineStream, 9, '\\');
check(lineStream, 11, '"');
check(lineStream, 12, 'string');
check(lineStream, 18, '\\');
check(lineStream, 19, '"');
check(lineStream, 20, '"');
check(lineStream, 22, ';');
lineStream.advanceToEOS();
assert.ok(lineStream.eos(), 'Stream finished');
});
test('peek', () => {
var lineStream = newTokenStream('albert, bart, charlie, damon, erich', ',', ' ');
var lineStream = new LineStream('albert, bart, charlie, damon, erich');
assert.equal(lineStream.peekToken(), 'albert');
assert.equal(lineStream.peek(), 'a');
lineStream.advance(1);
assert.equal(lineStream.nextToken(), 'albert');
assert.equal(lineStream.nextToken(), ',');
assert.equal(lineStream.peekToken(), 'bart');
assert.equal(lineStream.peek(), ' ');
assert.equal(lineStream.nextToken(), 'bart');
assert.equal(lineStream.peekToken(), ',');
assert.equal(lineStream.peek(), ',');
assert.equal(lineStream.nextToken(), ',');
lineStream.advanceToEOS();
assert.throws(() => { lineStream.peekToken(); });
assert.throws(() => { lineStream.peek(); });
});
test('next', () => {
var lineStream = newTokenStream('albert, bart, charlie, damon, erich', ',', ' ');
assert.equal(lineStream.peekToken(), 'albert');
assert.equal(lineStream.next(), 'a');
assert.equal(lineStream.next(), 'l');
assert.equal(lineStream.next(), 'b');
assert.equal(lineStream.nextToken(), 'ert');
assert.equal(lineStream.nextToken(), ',');
assert.equal(lineStream.nextToken(), 'bart');
assert.equal(lineStream.peekToken(), ',');
assert.equal(lineStream.next(), ',');
assert.equal(lineStream.next(), ' ');
assert.equal(lineStream.next(), 'c');
assert.equal(lineStream.next(), 'h');
assert.equal(lineStream.next(), 'a');
assert.equal(lineStream.next(), 'r');
assert.equal(lineStream.next(), 'l');
assert.equal(lineStream.next(), 'i');
assert.equal(lineStream.next(), 'e');
assert.equal(lineStream.next(), ',');
assert.equal(lineStream.nextToken(), 'damon');
assert.equal(lineStream.peek(), 'l');
lineStream.advanceToEOS();
assert.throws(() => { lineStream.peekToken(); });
assert.throws(() => { lineStream.peek(); });
});
test('next & goBack', () => {
var lineStream = new LineStream('albert, bart, charlie, damon, erich');
lineStream.setTokenRules(',', ' ');
assert.equal(lineStream.peekToken(), 'albert');
assert.equal(lineStream.next(), 'a');
assert.equal(lineStream.next(), 'l');
assert.equal(lineStream.next(), 'b');
assert.equal(lineStream.nextToken(), 'ert');
lineStream.goBack(6);
assert.equal(lineStream.nextToken(), 'albert');
assert.equal(lineStream.next(), ',');
lineStream.goBack(7);
assert.equal(lineStream.nextToken(), 'albert');
assert.equal(lineStream.nextToken(), ',');
assert.equal(lineStream.next(), ' ');
assert.equal(lineStream.next(), 'b');
assert.equal(lineStream.next(), 'a');
lineStream.goBack(3);
assert.equal(lineStream.nextToken(), 'bart');
lineStream.goBack(5);
assert.equal(lineStream.next(), ' ');
lineStream.advanceToEOS();
assert.throws(() => { lineStream.peekToken(); });
assert.throws(() => { lineStream.peek(); });
});
});
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/languages/html/common/html.contribution';
import { TestInstantiationService } from 'vs/test/utils/instantiationTestUtils';
import * as assert from 'assert';
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
import {IExtensionService} from 'vs/platform/extensions/common/extensions';
suite('Editor Modes - Modes Registry', () => {
let instantiationService: TestInstantiationService;
setup(() => {
instantiationService= new TestInstantiationService();
instantiationService.stub(IExtensionService);
});
test('Bug 12104: [f12] createModel not successfully handling mime type list?', () => {
let modeService = instantiationService.createInstance(ModeServiceImpl);
assert.equal(modeService.getModeId('text/html,text/plain'), 'html');
});
});
......@@ -5,11 +5,12 @@
'use strict';
import * as assert from 'assert';
import {IStream, ITokenizationResult, TokenizationRegistry} from 'vs/editor/common/modes';
import {AbstractState} from 'vs/editor/common/modes/abstractState';
import {TokenizationRegistry} from 'vs/editor/common/modes';
import {AbstractState, ITokenizationResult} from 'vs/editor/common/modes/abstractState';
import {TokenizationSupport} from 'vs/editor/common/modes/supports/tokenizationSupport';
import {tokenizeToHtmlContent} from 'vs/editor/common/modes/textToHtmlTokenizer';
import {MockMode} from 'vs/editor/test/common/mocks/mockMode';
import {LineStream} from 'vs/editor/common/modes/lineStream';
suite('Editor Modes - textToHtmlTokenizer', () => {
test('TextToHtmlTokenizer', () => {
......@@ -67,8 +68,10 @@ class State extends AbstractState {
return new State(this.getModeId());
}
public tokenize(stream:IStream):ITokenizationResult {
return { type: stream.next() === '.' ? '' : 'text' };
public tokenize(stream:LineStream):ITokenizationResult {
let chr = stream.peek();
stream.advance(1);
return { type: chr === '.' ? '' : 'text' };
}
}
......
......@@ -6,13 +6,14 @@
import * as assert from 'assert';
import * as modes from 'vs/editor/common/modes';
import {AbstractState} from 'vs/editor/common/modes/abstractState';
import {AbstractState, ITokenizationResult} from 'vs/editor/common/modes/abstractState';
import {handleEvent} from 'vs/editor/common/modes/supports';
import {IModeLocator, ILeavingNestedModeData, TokenizationSupport} from 'vs/editor/common/modes/supports/tokenizationSupport';
import {createMockLineContext} from 'vs/editor/test/common/modesTestUtils';
import {MockMode} from 'vs/editor/test/common/mocks/mockMode';
import {ModeTransition} from 'vs/editor/common/core/modeTransition';
import {Token} from 'vs/editor/common/core/token';
import {LineStream} from 'vs/editor/common/modes/lineStream';
export interface IModeSwitchingDescriptor {
[character:string]:{
......@@ -36,14 +37,28 @@ export class StateMemorizingLastWord extends AbstractState {
return new StateMemorizingLastWord(this.getModeId(), this.descriptor, this.lastWord);
}
public tokenize(stream:modes.IStream):modes.ITokenizationResult {
stream.setTokenRules('[]{}()==--', '\t \u00a0');
if (stream.skipWhitespace() !== '') {
return {
type: ''
};
public tokenize(stream:LineStream):ITokenizationResult {
let contents = stream.advanceToEOS();
stream.goBack(contents.length);
let m = contents.match(/^([\t \u00a0]+)/);
if (m) {
stream.advance(m[0].length);
return { type: '' };
}
var word = stream.nextToken();
m = contents.match(/^([\[\]\{\}\(\)])/);
let word: string;
if (m) {
stream.advance(m[0].length);
word = m[1];
} else {
m = contents.match(/([a-zA-Z]+)/);
stream.advance(m[0].length);
word = m[1];
}
return {
type: this.getModeId() + '.' + word,
nextState: new StateMemorizingLastWord(this.getModeId(), this.descriptor, word)
......@@ -61,7 +76,7 @@ export class SwitchingMode extends MockMode {
modes.TokenizationRegistry.register(this.getId(), new TokenizationSupport(null, this.getId(), this, true));
}
public getInitialState():modes.IState {
public getInitialState():AbstractState {
return new StateMemorizingLastWord(this.getId(), this._switchingModeDescriptor, null);
}
......@@ -167,8 +182,10 @@ suite('Editor Modes - Tokenization', () => {
return new State(this.getModeId());
}
public tokenize(stream:modes.IStream):modes.ITokenizationResult {
return { type: stream.next() === '.' ? '' : 'text' };
public tokenize(stream:LineStream):ITokenizationResult {
let chr = stream.peek();
stream.advance(1);
return { type: chr === '.' ? '' : 'text' };
}
}
......
......@@ -5,11 +5,7 @@
'use strict';
import * as assert from 'assert';
import {Model} from 'vs/editor/common/model/model';
import * as modes from 'vs/editor/common/modes';
import {RichEditSupport, LanguageConfiguration} from 'vs/editor/common/modes/languageConfigurationRegistry';
import {Token} from 'vs/editor/common/core/token';
import {generateMockModeId} from 'vs/editor/test/common/mocks/mockMode';
export interface ITestToken {
startIndex: number;
......@@ -21,10 +17,6 @@ export interface ITestItem {
tokens: ITestToken[];
}
export function assertWords(actual:string[], expected:string[], message?:string): void {
assert.deepEqual(actual, expected, message);
}
export function assertTokenization(tokenizationSupport: modes.ITokenizationSupport, tests: ITestItem[]): void {
var state = tokenizationSupport.getInitialState();
for (var i = 0, len = tests.length; i < len; i++) {
......@@ -37,71 +29,3 @@ export function assertTokenization(tokenizationSupport: modes.ITokenizationSuppo
state = result.endState;
}
}
export interface IOnEnterAsserter {
nothing(oneLineAboveText:string, beforeText:string, afterText:string): void;
indents(oneLineAboveText:string, beforeText:string, afterText:string): void;
outdents(oneLineAboveText:string, beforeText:string, afterText:string): void;
indentsOutdents(oneLineAboveText:string, beforeText:string, afterText:string): void;
}
export function createOnEnterAsserter(conf: LanguageConfiguration): IOnEnterAsserter {
const modeId = generateMockModeId();
const assertOne = (oneLineAboveText:string, beforeText:string, afterText:string, expected: modes.IndentAction) => {
let model = Model.createFromString(
[ oneLineAboveText, beforeText + afterText ].join('\n'),
undefined,
modeId
);
let richEditSupport = new RichEditSupport(modeId, null, conf);
let actual = richEditSupport.onEnter.onEnter(model, { lineNumber: 2, column: beforeText.length + 1 });
if (expected === modes.IndentAction.None) {
assert.equal(actual, null, oneLineAboveText + '\\n' + beforeText + '|' + afterText);
} else {
assert.equal(actual.indentAction, expected, oneLineAboveText + '\\n' + beforeText + '|' + afterText);
}
model.dispose();
};
return {
nothing: (oneLineAboveText:string, beforeText:string, afterText:string): void => {
assertOne(oneLineAboveText, beforeText, afterText, modes.IndentAction.None);
},
indents: (oneLineAboveText:string, beforeText:string, afterText:string): void => {
assertOne(oneLineAboveText, beforeText, afterText, modes.IndentAction.Indent);
},
outdents: (oneLineAboveText:string, beforeText:string, afterText:string): void => {
assertOne(oneLineAboveText, beforeText, afterText, modes.IndentAction.Outdent);
},
indentsOutdents: (oneLineAboveText:string, beforeText:string, afterText:string): void => {
assertOne(oneLineAboveText, beforeText, afterText, modes.IndentAction.IndentOutdent);
}
};
}
export function executeTests(tokenizationSupport: modes.ITokenizationSupport, tests:ITestItem[][]): void {
for (var i = 0, len = tests.length; i < len; i++) {
assert.ok(true, 'TEST #' + i);
executeTest(tokenizationSupport, tests[i]);
}
}
function executeTest(tokenizationSupport: modes.ITokenizationSupport, tests:ITestItem[]): void {
var state = tokenizationSupport.getInitialState();
for (var i = 0, len = tests.length; i < len; i++) {
assert.ok(true, tests[i].line);
var result = tokenizationSupport.tokenize(tests[i].line, state);
if (tests[i].tokens) {
assertTokens(result.tokens, tests[i].tokens, 'Tokenizing line ' + tests[i].line);
}
state = result.endState;
}
}
function assertTokens(actual:Token[], expected:ITestToken[], message?:string): void {
assert.deepEqual(actual, expected, message + ': ' + JSON.stringify(actual, null, '\t'));
}
......@@ -12,13 +12,11 @@ suite('LanguagesRegistry', () => {
test('output mode does not have a name', () => {
let registry = new LanguagesRegistry(false);
registry._registerCompatModes([{
registry._registerLanguages([{
id: 'outputModeId',
extensions: [],
aliases: [null],
mimetypes: ['outputModeMimeType'],
moduleId: 'outputModeModuleId',
ctorName: 'outputModeCtorName'
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), []);
......@@ -27,13 +25,11 @@ suite('LanguagesRegistry', () => {
test('mode with alias does have a name', () => {
let registry = new LanguagesRegistry(false);
registry._registerCompatModes([{
registry._registerLanguages([{
id: 'modeId',
extensions: [],
aliases: ['ModeName'],
mimetypes: ['bla'],
moduleId: 'bla',
ctorName: 'bla'
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['ModeName']);
......@@ -43,13 +39,11 @@ suite('LanguagesRegistry', () => {
test('mode without alias gets a name', () => {
let registry = new LanguagesRegistry(false);
registry._registerCompatModes([{
registry._registerLanguages([{
id: 'modeId',
extensions: [],
aliases: [],
mimetypes: ['bla'],
moduleId: 'bla',
ctorName: 'bla'
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['modeId']);
......@@ -59,22 +53,18 @@ suite('LanguagesRegistry', () => {
test('bug #4360: f# not shown in status bar', () => {
let registry = new LanguagesRegistry(false);
registry._registerCompatModes([{
registry._registerLanguages([{
id: 'modeId',
extensions: ['.ext1'],
aliases: ['ModeName'],
mimetypes: ['bla'],
moduleId: 'bla',
ctorName: 'bla'
}]);
registry._registerCompatModes([{
registry._registerLanguages([{
id: 'modeId',
extensions: ['.ext2'],
aliases: [],
mimetypes: ['bla'],
moduleId: 'bla',
ctorName: 'bla'
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['ModeName']);
......@@ -84,22 +74,18 @@ suite('LanguagesRegistry', () => {
test('issue #5278: Extension cannot override language name anymore', () => {
let registry = new LanguagesRegistry(false);
registry._registerCompatModes([{
registry._registerLanguages([{
id: 'modeId',
extensions: ['.ext1'],
aliases: ['ModeName'],
mimetypes: ['bla'],
moduleId: 'bla',
ctorName: 'bla'
}]);
registry._registerCompatModes([{
registry._registerLanguages([{
id: 'modeId',
extensions: ['.ext2'],
aliases: ['BetterModeName'],
mimetypes: ['bla'],
moduleId: 'bla',
ctorName: 'bla'
}]);
assert.deepEqual(registry.getRegisteredLanguageNames(), ['BetterModeName']);
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import URI from 'vs/base/common/uri';
import {CompatMirrorModel} from 'vs/editor/common/model/compatMirrorModel';
import {ResourceService} from 'vs/editor/common/services/resourceServiceImpl';
import {TextModel} from 'vs/editor/common/model/textModel';
function createTestMirrorModelFromString(value:string): CompatMirrorModel {
return new CompatMirrorModel(0, TextModel.toRawText(value, TextModel.DEFAULT_CREATION_OPTIONS), null);
}
suite('Editor Services - ResourceService', () => {
test('insert, remove, all', () => {
let service = new ResourceService();
service.insert(URI.parse('test://1'), createTestMirrorModelFromString('hi'));
assert.equal(service.get(URI.parse('test://1')).getValue(), 'hi');
service.insert(URI.parse('test://2'), createTestMirrorModelFromString('hi'));
service.remove(URI.parse('test://1'));
service.remove(URI.parse('test://1'));
service.remove(URI.parse('test://2'));
assert.equal(service.get(URI.parse('test://1')), null);
});
test('inserting the same resource twice throws', () => {
let service = new ResourceService();
service.insert(URI.parse('test://path/some-N1ce-name'), createTestMirrorModelFromString('hello'));
assert.throws(() => {
service.insert(URI.parse('test://path/some-N1ce-name'), createTestMirrorModelFromString('hello again'));
});
});
});
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
var EntryPoint = (function() {
function toArray(param) {
if (!param) {
return [];
}
if (!Array.isArray(param)) {
return [param];
}
return param;
}
function EntryPoint(result, modules) {
this.result = result;
this.modules = toArray(modules);
}
EntryPoint.prototype.define = function(moduleId, excludes) {
excludes = toArray(excludes);
this.result.push({
name: moduleId,
exclude: ['vs/css', 'vs/nls'].concat(this.modules).concat(excludes)
});
return new EntryPoint(this.result, this.modules.concat([moduleId].concat(excludes)));
};
EntryPoint.prototype.combine = function(other) {
return new EntryPoint(this.result, this.modules.concat(other.modules));
};
return EntryPoint;
})();
exports.collectModules = function(args) {
var result = [];
// var common = new EntryPoint(result, 'vs/editor/common/languages.common');
// var worker = new EntryPoint(result, ['vs/editor/common/languages.common', 'vs/base/common/worker/workerServer', 'vs/editor/common/worker/editorWorkerServer']);
// ---- beautify-html (shared btw html and xml) -----------------------------
// worker.define('vs/languages/lib/common/beautify-html');
// // ---- handlebars ----------------------------------
// common.define('vs/languages/handlebars/common/handlebars', ['vs/languages/html/common/html']);
// // ---- html ----------------------------------
// common.define('vs/languages/html/common/html')
// .combine(worker)
// .define('vs/languages/html/common/htmlWorker', ['vs/languages/lib/common/beautify-html']);
// // ---- razor -----------------------------------
// common.define('vs/languages/razor/common/razor', ['vs/languages/html/common/html'])
// .combine(worker)
// .define('vs/languages/razor/common/razorWorker', ['vs/languages/html/common/htmlWorker', 'vs/languages/lib/common/beautify-html'] );
return result;
};
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 {ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
ModesRegistry.registerCompatMode({
id: 'handlebars',
extensions: ['.handlebars', '.hbs'],
aliases: ['Handlebars', 'handlebars'],
mimetypes: ['text/x-handlebars-template'],
moduleId: 'vs/languages/handlebars/common/handlebars',
ctorName: 'HandlebarsMode'
});
/*---------------------------------------------------------------------------------------------
* 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 modes = require('vs/editor/common/modes');
import htmlMode = require('vs/languages/html/common/html');
import handlebarsTokenTypes = require('vs/languages/handlebars/common/handlebarsTokenTypes');
import htmlWorker = require('vs/languages/html/common/htmlWorker');
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IModeService} from 'vs/editor/common/services/modeService';
import {LanguageConfigurationRegistry, LanguageConfiguration} from 'vs/editor/common/modes/languageConfigurationRegistry';
import {createWordRegExp} from 'vs/editor/common/modes/abstractMode';
import {wireCancellationToken} from 'vs/base/common/async';
import {ICompatWorkerService} from 'vs/editor/common/services/compatWorkerService';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {TokenizationSupport, ILeavingNestedModeData} from 'vs/editor/common/modes/supports/tokenizationSupport';
export enum States {
HTML,
Expression,
UnescapedExpression
}
export class HandlebarsState extends htmlMode.State {
constructor(modeId:string,
kind:htmlMode.States,
public handlebarsKind:States,
lastTagName:string,
lastAttributeName:string,
embeddedContentType:string,
attributeValueQuote:string,
attributeValueLength:number) {
super(modeId, kind, lastTagName, lastAttributeName, embeddedContentType, attributeValueQuote, attributeValueLength);
}
public makeClone(): HandlebarsState {
return new HandlebarsState(this.getModeId(), this.kind, this.handlebarsKind, this.lastTagName, this.lastAttributeName, this.embeddedContentType, this.attributeValueQuote, this.attributeValueLength);
}
public equals(other:modes.IState):boolean {
if (other instanceof HandlebarsState) {
return (
super.equals(other)
);
}
return false;
}
public tokenize(stream:modes.IStream) : modes.ITokenizationResult {
switch(this.handlebarsKind) {
case States.HTML:
if (stream.advanceIfString('{{{').length > 0) {
this.handlebarsKind = States.UnescapedExpression;
return { type: handlebarsTokenTypes.EMBED_UNESCAPED };
}
else if (stream.advanceIfString('{{').length > 0) {
this.handlebarsKind = States.Expression;
return { type: handlebarsTokenTypes.EMBED };
}
break;
case States.Expression:
case States.UnescapedExpression:
if (this.handlebarsKind === States.Expression && stream.advanceIfString('}}').length > 0) {
this.handlebarsKind = States.HTML;
return { type: handlebarsTokenTypes.EMBED };
}
else if (this.handlebarsKind === States.UnescapedExpression &&stream.advanceIfString('}}}').length > 0) {
this.handlebarsKind = States.HTML;
return { type: handlebarsTokenTypes.EMBED_UNESCAPED };
}
else if(stream.skipWhitespace().length > 0) {
return { type: ''};
}
if(stream.peek() === '#') {
stream.advanceWhile(/^[^\s}]/);
return { type: handlebarsTokenTypes.KEYWORD };
}
if(stream.peek() === '/') {
stream.advanceWhile(/^[^\s}]/);
return { type: handlebarsTokenTypes.KEYWORD };
}
if(stream.advanceIfString('else')) {
var next = stream.peek();
if(next === ' ' || next === '\t' || next === '}') {
return { type: handlebarsTokenTypes.KEYWORD };
}
else {
stream.goBack(4);
}
}
if(stream.advanceWhile(/^[^\s}]/).length > 0) {
return { type: handlebarsTokenTypes.VARIABLE };
}
break;
}
return super.tokenize(stream);
}
}
export class HandlebarsMode extends htmlMode.HTMLMode<htmlWorker.HTMLWorker> {
public static LANG_CONFIG:LanguageConfiguration = {
wordPattern: createWordRegExp('#-?%'),
comments: {
blockComment: ['<!--', '-->']
},
brackets: [
['<!--', '-->'],
['{{', '}}']
],
__electricCharacterSupport: {
embeddedElectricCharacters: ['*', '}', ']', ')']
},
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: '\'', close: '\'' }
],
surroundingPairs: [
{ open: '<', close: '>' },
{ open: '"', close: '"' },
{ open: '\'', close: '\'' }
],
onEnterRules: [
{
beforeText: new RegExp(`<(?!(?:${htmlMode.EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
afterText: /^<\/(\w[\w\d]*)\s*>$/i,
action: { indentAction: modes.IndentAction.IndentOutdent }
},
{
beforeText: new RegExp(`<(?!(?:${htmlMode.EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
action: { indentAction: modes.IndentAction.Indent }
}
],
};
constructor(
descriptor:modes.IModeDescriptor,
@IInstantiationService instantiationService: IInstantiationService,
@IModeService modeService: IModeService,
@ICompatWorkerService compatWorkerService: ICompatWorkerService,
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
@IConfigurationService configurationService: IConfigurationService
) {
super(descriptor, instantiationService, modeService, compatWorkerService, workspaceContextService, configurationService);
}
protected _registerSupports(): void {
modes.SuggestRegistry.register(this.getId(), {
triggerCharacters: ['.', ':', '<', '"', '=', '/'],
provideCompletionItems: (model, position, token): Thenable<modes.ISuggestResult> => {
return wireCancellationToken(token, this._provideCompletionItems(model.uri, position));
}
}, true);
modes.DocumentHighlightProviderRegistry.register(this.getId(), {
provideDocumentHighlights: (model, position, token): Thenable<modes.DocumentHighlight[]> => {
return wireCancellationToken(token, this._provideDocumentHighlights(model.uri, position));
}
}, true);
modes.LinkProviderRegistry.register(this.getId(), {
provideLinks: (model, token): Thenable<modes.ILink[]> => {
return wireCancellationToken(token, this.provideLinks(model.uri));
}
}, true);
LanguageConfigurationRegistry.register(this.getId(), HandlebarsMode.LANG_CONFIG);
modes.TokenizationRegistry.register(this.getId(), new TokenizationSupport(this._modeService, this.getId(), this, true));
}
public getInitialState() : modes.IState {
return new HandlebarsState(this.getId(), htmlMode.States.Content, States.HTML, '', '', '', '', 0);
}
public getLeavingNestedModeData(line:string, state:modes.IState):ILeavingNestedModeData {
var leavingNestedModeData = super.getLeavingNestedModeData(line, state);
if (leavingNestedModeData) {
leavingNestedModeData.stateAfterNestedMode = new HandlebarsState(this.getId(), htmlMode.States.Content, States.HTML, '', '', '', '', 0);
}
return leavingNestedModeData;
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export const EMBED = 'punctuation.expression.unescaped.handlebars';
export const EMBED_UNESCAPED = 'punctuation.expression.handlebars';
export const KEYWORD = 'keyword.helper.handlebars';
export const VARIABLE = 'variable.parameter.handlebars';
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 Modes = require('vs/editor/common/modes');
import modesUtil = require('vs/editor/test/common/modesUtil');
import {htmlTokenTypes} from 'vs/languages/html/common/html';
import handlebarsTokenTypes = require('vs/languages/handlebars/common/handlebarsTokenTypes');
import {HandlebarsMode} from 'vs/languages/handlebars/common/handlebars';
import {MockModeService} from 'vs/editor/test/common/mocks/mockModeService';
import {MockTokenizingMode} from 'vs/editor/test/common/mocks/mockMode';
class HandlebarsMockModeService extends MockModeService {
private _handlebarsMode: HandlebarsMode;
constructor() {
super();
this._handlebarsMode = null;
}
public setHandlebarsMode(handlebarsMode: HandlebarsMode): void {
this._handlebarsMode = handlebarsMode;
}
isRegisteredMode(mimetypeOrModeId: string): boolean {
if (mimetypeOrModeId === 'text/javascript') {
return true;
}
if (mimetypeOrModeId === 'text/x-handlebars-template') {
return true;
}
throw new Error('Not implemented');
}
getModeId(mimetypeOrModeId: string): string {
if (mimetypeOrModeId === 'text/javascript') {
return 'js-mode-id';
}
if (mimetypeOrModeId === 'text/x-handlebars-template') {
return 'handlebars-mode-id';
}
throw new Error('Not implemented');
}
getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): Modes.IMode {
if (commaSeparatedMimetypesOrCommaSeparatedIds === 'js-mode-id') {
return new MockTokenizingMode('mock-js');
}
if (commaSeparatedMimetypesOrCommaSeparatedIds === 'handlebars-mode-id') {
return this._handlebarsMode;
}
throw new Error('Not implemented');
}
}
suite('Handlebars', () => {
var tokenizationSupport: Modes.ITokenizationSupport;
suiteSetup(function() {
let modeService = new HandlebarsMockModeService();
let mode = new HandlebarsMode(
{ id: 'handlebars' },
null,
modeService,
null,
null,
null
);
modeService.setHandlebarsMode(mode);
tokenizationSupport = Modes.TokenizationRegistry.get(mode.getId());
});
test('Just HTML', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<h1>handlebars!</h1>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('h1') },
{ startIndex:3, type: htmlTokenTypes.DELIM_START },
{ startIndex:4, type: '' },
{ startIndex:15, type: htmlTokenTypes.DELIM_END },
{ startIndex:17, type: htmlTokenTypes.getTag('h1') },
{ startIndex:19, type: htmlTokenTypes.DELIM_END }
]}
]);
});
test('Expressions', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<h1>{{ title }}</h1>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('h1') },
{ startIndex:3, type: htmlTokenTypes.DELIM_START },
{ startIndex:4, type: handlebarsTokenTypes.EMBED },
{ startIndex:6, type: '' },
{ startIndex:7, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:12, type: '' },
{ startIndex:13, type: handlebarsTokenTypes.EMBED },
{ startIndex:15, type: htmlTokenTypes.DELIM_END },
{ startIndex:17, type: htmlTokenTypes.getTag('h1') },
{ startIndex:19, type: htmlTokenTypes.DELIM_END }
]}
]);
});
test('Expressions Sans Whitespace', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<h1>{{title}}</h1>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('h1') },
{ startIndex:3, type: htmlTokenTypes.DELIM_START },
{ startIndex:4, type: handlebarsTokenTypes.EMBED },
{ startIndex:6, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:11, type: handlebarsTokenTypes.EMBED },
{ startIndex:13, type: htmlTokenTypes.DELIM_END },
{ startIndex:15, type: htmlTokenTypes.getTag('h1') },
{ startIndex:17, type: htmlTokenTypes.DELIM_END }
]}
]);
});
test('Unescaped Expressions', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<h1>{{{ title }}}</h1>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('h1') },
{ startIndex:3, type: htmlTokenTypes.DELIM_START },
{ startIndex:4, type: handlebarsTokenTypes.EMBED_UNESCAPED },
{ startIndex:7, type: '' },
{ startIndex:8, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:13, type: '' },
{ startIndex:14, type: handlebarsTokenTypes.EMBED_UNESCAPED },
{ startIndex:17, type: htmlTokenTypes.DELIM_END },
{ startIndex:19, type: htmlTokenTypes.getTag('h1') },
{ startIndex:21, type: htmlTokenTypes.DELIM_END }
]}
]);
});
test('Blocks', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<ul>{{#each items}}<li>{{item}}</li>{{/each}}</ul>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('ul') },
{ startIndex:3, type: htmlTokenTypes.DELIM_START },
{ startIndex:4, type: handlebarsTokenTypes.EMBED },
{ startIndex:6, type: handlebarsTokenTypes.KEYWORD },
{ startIndex:11, type: '' },
{ startIndex:12, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:17, type: handlebarsTokenTypes.EMBED },
{ startIndex:19, type: htmlTokenTypes.DELIM_START },
{ startIndex:20, type: htmlTokenTypes.getTag('li') },
{ startIndex:22, type: htmlTokenTypes.DELIM_START },
{ startIndex:23, type: handlebarsTokenTypes.EMBED },
{ startIndex:25, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:29, type: handlebarsTokenTypes.EMBED },
{ startIndex:31, type: htmlTokenTypes.DELIM_END },
{ startIndex:33, type: htmlTokenTypes.getTag('li') },
{ startIndex:35, type: htmlTokenTypes.DELIM_END },
{ startIndex:36, type: handlebarsTokenTypes.EMBED },
{ startIndex:38, type: handlebarsTokenTypes.KEYWORD },
{ startIndex:43, type: handlebarsTokenTypes.EMBED },
{ startIndex:45, type: htmlTokenTypes.DELIM_END },
{ startIndex:47, type: htmlTokenTypes.getTag('ul') },
{ startIndex:49, type: htmlTokenTypes.DELIM_END }
]}
]);
});
test('Multiline', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<div>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('div') },
{ startIndex:4, type: htmlTokenTypes.DELIM_START }
]}, {
line: '{{#if foo}}',
tokens: [
{ startIndex:0, type: handlebarsTokenTypes.EMBED },
{ startIndex:2, type: handlebarsTokenTypes.KEYWORD },
{ startIndex:5, type: '' },
{ startIndex:6, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:9, type: handlebarsTokenTypes.EMBED }
]}, {
line: '<span>{{bar}}</span>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('span') },
{ startIndex:5, type: htmlTokenTypes.DELIM_START },
{ startIndex:6, type: handlebarsTokenTypes.EMBED },
{ startIndex:8, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:11, type: handlebarsTokenTypes.EMBED },
{ startIndex:13, type: htmlTokenTypes.DELIM_END },
{ startIndex:15, type: htmlTokenTypes.getTag('span') },
{ startIndex:19, type: htmlTokenTypes.DELIM_END }
]}, {
line: '{{/if}}',
tokens: null}
]);
});
test('Div end', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '</div>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_END },
{ startIndex:2, type: htmlTokenTypes.getTag('div') },
{ startIndex:5, type: htmlTokenTypes.DELIM_END }
]}
]);
});
// shamelessly stolen from the HTML test bed since Handlebars are a superset of HTML
test('Embedded Content in HTML', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/javascript">var i= 10;</script>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: htmlTokenTypes.ATTRIB_NAME },
{ startIndex:12, type: htmlTokenTypes.DELIM_ASSIGN },
{ startIndex:13, type: htmlTokenTypes.ATTRIB_VALUE },
{ startIndex:30, type: htmlTokenTypes.DELIM_START },
{ startIndex:31, type: 'mock-js' },
{ startIndex:41, type: htmlTokenTypes.DELIM_END },
{ startIndex:43, type: htmlTokenTypes.getTag('script') },
{ startIndex:49, type: htmlTokenTypes.DELIM_END }
]}
]);
});
test('HTML Expressions', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/x-handlebars-template"><h1>{{ title }}</h1></script>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: htmlTokenTypes.ATTRIB_NAME },
{ startIndex:12, type: htmlTokenTypes.DELIM_ASSIGN },
{ startIndex:13, type: htmlTokenTypes.ATTRIB_VALUE },
{ startIndex:41, type: htmlTokenTypes.DELIM_START },
{ startIndex:42, type: htmlTokenTypes.DELIM_START },
{ startIndex:43, type: htmlTokenTypes.getTag('h1') },
{ startIndex:45, type: htmlTokenTypes.DELIM_START },
{ startIndex:46, type: handlebarsTokenTypes.EMBED },
{ startIndex:48, type: '' },
{ startIndex:49, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:54, type: '' },
{ startIndex:55, type: handlebarsTokenTypes.EMBED },
{ startIndex:57, type: htmlTokenTypes.DELIM_END },
{ startIndex:59, type: htmlTokenTypes.getTag('h1') },
{ startIndex:61, type: htmlTokenTypes.DELIM_END },
{ startIndex:62, type: htmlTokenTypes.DELIM_END },
{ startIndex:64, type: htmlTokenTypes.getTag('script') },
{ startIndex:70, type: htmlTokenTypes.DELIM_END }
]}
]);
});
test('Multi-line HTML Expressions', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/x-handlebars-template">',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: htmlTokenTypes.ATTRIB_NAME },
{ startIndex:12, type: htmlTokenTypes.DELIM_ASSIGN },
{ startIndex:13, type: htmlTokenTypes.ATTRIB_VALUE },
{ startIndex:41, type: htmlTokenTypes.DELIM_START }
]}, {
line: '<h1>{{ title }}</h1>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('h1') },
{ startIndex:3, type: htmlTokenTypes.DELIM_START },
{ startIndex:4, type: handlebarsTokenTypes.EMBED },
{ startIndex:6, type: '' },
{ startIndex:7, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:12, type: '' },
{ startIndex:13, type: handlebarsTokenTypes.EMBED },
{ startIndex:15, type: htmlTokenTypes.DELIM_END },
{ startIndex:17, type: htmlTokenTypes.getTag('h1') },
{ startIndex:19, type: htmlTokenTypes.DELIM_END }
]}, {
line: '</script>',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_END },
{ startIndex:2, type: htmlTokenTypes.getTag('script') },
{ startIndex:8, type: htmlTokenTypes.DELIM_END }
]}
]);
});
test('HTML Nested Modes', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '{{foo}}<script></script>{{bar}}',
tokens: [
{ startIndex:0, type: handlebarsTokenTypes.EMBED },
{ startIndex:2, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:5, type: handlebarsTokenTypes.EMBED },
{ startIndex:7, type: htmlTokenTypes.DELIM_START },
{ startIndex:8, type: htmlTokenTypes.getTag('script') },
{ startIndex:14, type: htmlTokenTypes.DELIM_START },
{ startIndex:15, type: htmlTokenTypes.DELIM_END },
{ startIndex:17, type: htmlTokenTypes.getTag('script') },
{ startIndex:23, type: htmlTokenTypes.DELIM_END },
{ startIndex:24, type: handlebarsTokenTypes.EMBED },
{ startIndex:26, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:29, type: handlebarsTokenTypes.EMBED }
]}
]);
});
test('else keyword', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '{{else}}',
tokens: [
{ startIndex:0, type: handlebarsTokenTypes.EMBED },
{ startIndex:2, type: handlebarsTokenTypes.KEYWORD },
{ startIndex:6, type: handlebarsTokenTypes.EMBED }
]}
]);
});
test('else keyword #2', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '{{elseFoo}}',
tokens: [
{ startIndex:0, type: handlebarsTokenTypes.EMBED },
{ startIndex:2, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:9, type: handlebarsTokenTypes.EMBED }
]}
]);
});
test('Token inside attribute', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<a href="/posts/{{permalink}}">',
tokens: [
{ startIndex:0, type: htmlTokenTypes.DELIM_START },
{ startIndex:1, type: htmlTokenTypes.getTag('a') },
{ startIndex:2, type: '' },
{ startIndex:3, type: htmlTokenTypes.ATTRIB_NAME },
{ startIndex:7, type: htmlTokenTypes.DELIM_ASSIGN },
{ startIndex:8, type: htmlTokenTypes.ATTRIB_VALUE },
{ startIndex:16, type: handlebarsTokenTypes.EMBED },
{ startIndex:18, type: handlebarsTokenTypes.VARIABLE },
{ startIndex:27, type: handlebarsTokenTypes.EMBED },
{ startIndex:29, type: htmlTokenTypes.ATTRIB_VALUE },
{ startIndex:30, type: htmlTokenTypes.DELIM_START }
]}
]);
});
});
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "HTML 5.1 W3C Working Draft",
"version": "08 October 2015",
"license": "W3C Document License",
"repositoryURL": "http://www.w3.org/TR/2015/WD-html51-20151008/",
"licenseDetail": [
"Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang). This software or document includes material copied ",
"from or derived from HTML 5.1 W3C Working Draft (http://www.w3.org/TR/2015/WD-html51-20151008/.)",
"",
"THIS DOCUMENT IS PROVIDED \"AS IS,\" AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT ",
"NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF ",
"THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY ",
"PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.",
"",
"COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE ",
"DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS THEREOF.",
"",
"The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to this document or its contents",
"without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders."
]
},
{
"name": "Ionic documentation",
"version": "1.2.4",
"license": "Apache2",
"repositoryURL": "https://github.com/driftyco/ionic-site",
"licenseDetail": [
"Copyright Drifty Co. http://drifty.com/.",
"",
"Apache License",
"",
"Version 2.0, January 2004",
"",
"http://www.apache.org/licenses/",
"",
"TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION",
"",
"1. Definitions.",
"",
"\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.",
"",
"\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.",
"",
"\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.",
"",
"\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.",
"",
"\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.",
"",
"\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.",
"",
"\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).",
"",
"\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.",
"",
"\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"",
"",
"\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.",
"",
"2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.",
"",
"3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.",
"",
"4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:",
"",
"You must give any other recipients of the Work or Derivative Works a copy of this License; and",
"",
"You must cause any modified files to carry prominent notices stating that You changed the files; and",
"",
"You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and",
"",
"If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.",
"",
"5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.",
"",
"6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.",
"",
"7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.",
"",
"8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.",
"",
"9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.",
"",
"END OF TERMS AND CONDITIONS"
]
}
]
此差异已折叠。
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import arrays = require('vs/base/common/arrays');
export const EMPTY_ELEMENTS:string[] = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'];
export function isEmptyElement(e: string) : boolean {
return arrays.binarySearch(EMPTY_ELEMENTS, e,(s1: string, s2: string) => s1.localeCompare(s2)) >= 0;
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册